001/** 002 * Copyright 2013 John Ericksen 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.androidtransfuse.analysis.astAnalyzer; 017 018import com.google.common.collect.ImmutableSet; 019import org.androidtransfuse.adapter.*; 020import org.androidtransfuse.analysis.AnalysisContext; 021import org.androidtransfuse.analysis.astAnalyzer.validation.AnnotationValidator; 022import org.androidtransfuse.analysis.astAnalyzer.validation.AnnotationValidatorBuilder; 023import org.androidtransfuse.annotations.*; 024import org.androidtransfuse.model.InjectionNode; 025 026import javax.inject.Inject; 027import java.util.Arrays; 028 029/** 030 * @author John Ericksen 031 */ 032public class AnnotationValidationAnalysis implements ASTAnalysis { 033 034 private final AnnotationValidator annotationValidator; 035 036 @Inject 037 public AnnotationValidationAnalysis(AnnotationValidatorBuilder builder) { 038 039 //built in specialty qualifiers 040 builder.given(Extra.class).requires(Inject.class, "@Extra annotation must be accompanied by @Inject"); 041 builder.given(Extra.class).parameterMatches("value", "^[a-zA-Z][a-zA-Z0-9_]*$", "@Extra value parameter must follow Java Bean syntax"); 042 builder.given(View.class).requires(Inject.class, "@View annotation must be accompanied by @Inject"); 043 builder.given(Preference.class).requires(Inject.class, "@Preference annotation must be accompanied by @Inject"); 044 builder.given(Resource.class).requires(Inject.class, "@Resource annotation must be accompanied by @Inject"); 045 builder.given(SystemService.class).requires(Inject.class, "@SystemService annotation must be accompanied by @Inject"); 046 047 //activity metadata 048 builder.given(Layout.class).requires(Arrays.asList(Activity.class, Fragment.class), "@Layout annotation must be accompanied by @Activity"); 049 builder.given(LayoutHandler.class).requires(Arrays.asList(Activity.class, Fragment.class), "@LayoutHandler annotation must be accompanied by @Activity"); 050 builder.given(MetaData.class).requires(Arrays.asList(Activity.class, Application.class, Service.class, BroadcastReceiver.class), "@MetaData annotation must be accompanied by a component annotation"); 051 builder.given(MetaDataSet.class).requires(Arrays.asList(Activity.class, Application.class, Service.class, BroadcastReceiver.class), "@MetaDataSet annotation must be accompanied by a component annotation"); 052 builder.given(Activity.class).parameterMatches("name", "^[a-zA-Z][a-zA-Z0-9_]*$", "@Activity name parameter must follow Java Bean syntax"); 053 builder.given(Service.class).parameterMatches("name", "^[a-zA-Z][a-zA-Z0-9_]*$", "@Service name parameter must follow Java Bean syntax"); 054 builder.given(Fragment.class).parameterMatches("name", "^[a-zA-Z][a-zA-Z0-9_]*$", "@Fragment name parameter must follow Java Bean syntax"); 055 builder.given(BroadcastReceiver.class).parameterMatches("name", "^[a-zA-Z][a-zA-Z0-9_]*$", "@BroadcastReceiver name parameter must follow Java Bean syntax"); 056 builder.given(Application.class).parameterMatches("name", "^[a-zA-Z][a-zA-Z0-9_]*$", "@Application name parameter must follow Java Bean syntax"); 057 058 //module metadata 059 builder.given(Bind.class).requires(TransfuseModule.class, "@Bind annotation must be accompanied by @TransfuseModule"); 060 builder.given(Bindings.class).requires(TransfuseModule.class, "@Bindings annotation must be accompanied by @TransfuseModule"); 061 builder.given(BindInterceptor.class).requires(TransfuseModule.class, "@BindInterceptor annotation must be accompanied by @TransfuseModule"); 062 builder.given(BindInterceptors.class).requires(TransfuseModule.class, "@BindInterceptors annotation must be accompanied by @TransfuseModule"); 063 builder.given(BindProvider.class).requires(TransfuseModule.class, "@BindProvider annotation must be accompanied by @TransfuseModule"); 064 builder.given(BindProviders.class).requires(TransfuseModule.class, "@BindProviders annotation must be accompanied by @TransfuseModule"); 065 builder.given(DefineScope.class).requires(TransfuseModule.class, "@DefineScope annotation must be accompanied by @TransfuseModule"); 066 builder.given(DefineScopes.class).requires(TransfuseModule.class, "@DefineScopes annotation must be accompanied by @TransfuseModule"); 067 builder.given(UsesPermission.class).requires(TransfuseModule.class, "@UsesPermission annotation must be accompanied by @TransfuseModule"); 068 builder.given(UsesPermissions.class).requires(TransfuseModule.class, "@UsesPermissions annotation must be accompanied by @TransfuseModule"); 069 builder.given(UsesSdk.class).requires(TransfuseModule.class, "@UsesSdk annotation must be accompanied by @TransfuseModule"); 070 071 builder.given(Provides.class).requires(TransfuseModule.class, "@Provides annotation must be accompanied by @TransfuseModule"); 072 073 annotationValidator = builder.build(); 074 } 075 076 @Override 077 public void analyzeType(InjectionNode injectionNode, ASTType astType, AnalysisContext context) { 078 079 for (ASTAnnotation annotation : astType.getAnnotations()) { 080 validateAnnotation(annotation, astType, astType.getAnnotations()); 081 } 082 083 for (ASTConstructor astConstructor : astType.getConstructors()) { 084 for (ASTAnnotation annotation : astConstructor.getAnnotations()) { 085 validateAnnotation(annotation, astConstructor, astConstructor.getAnnotations(), astType.getAnnotations()); 086 087 for (ASTParameter astParameter : astConstructor.getParameters()) { 088 validateParameter(astParameter, astConstructor, astType); 089 } 090 } 091 } 092 } 093 094 @Override 095 public void analyzeMethod(InjectionNode injectionNode, ASTType concreteType, ASTMethod astMethod, AnalysisContext context) { 096 for (ASTAnnotation annotation : astMethod.getAnnotations()) { 097 validateAnnotation(annotation, astMethod, astMethod.getAnnotations(), concreteType.getAnnotations()); 098 099 for (ASTParameter astParameter : astMethod.getParameters()) { 100 validateParameter(astParameter, astMethod, concreteType); 101 } 102 } 103 } 104 105 @Override 106 public void analyzeField(InjectionNode injectionNode, ASTType concreteType, ASTField astField, AnalysisContext context) { 107 for (ASTAnnotation annotation : astField.getAnnotations()) { 108 validateAnnotation(annotation, astField, astField.getAnnotations(), concreteType.getAnnotations()); 109 } 110 } 111 112 private void validateParameter(ASTParameter parameter, ASTBase containingAST, ASTType containingType){ 113 for (ASTAnnotation paramAnnotation : parameter.getAnnotations()) { 114 validateAnnotation(paramAnnotation, parameter, parameter.getAnnotations(), containingAST.getAnnotations(), containingType.getAnnotations()); 115 } 116 } 117 118 private void validateAnnotation(ASTAnnotation annotation, ASTBase astBase, ImmutableSet<ASTAnnotation>... applicableAnnotations){ 119 annotationValidator.validate(annotation, astBase, flatten(applicableAnnotations)); 120 } 121 122 private ImmutableSet<ASTAnnotation> flatten(ImmutableSet<ASTAnnotation>... sets){ 123 ImmutableSet.Builder<ASTAnnotation> combineBuilder = ImmutableSet.builder(); 124 for (ImmutableSet<ASTAnnotation> set : sets) { 125 combineBuilder.addAll(set); 126 } 127 return combineBuilder.build(); 128 } 129}