| /* |
| * Copyright (c) 2007-2011, Rickard Öberg. All Rights Reserved. |
| * Copyright (c) 2008-2013, Niclas Hedhman. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. |
| * |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.qi4j.runtime.bootstrap; |
| |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.AccessibleObject; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.Member; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.lang.reflect.Proxy; |
| import java.lang.reflect.Type; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import org.qi4j.api.common.MetaInfo; |
| import org.qi4j.api.common.Optional; |
| import org.qi4j.api.common.QualifiedName; |
| import org.qi4j.api.common.UseDefaults; |
| import org.qi4j.api.common.Visibility; |
| import org.qi4j.api.composite.InvalidCompositeException; |
| import org.qi4j.api.concern.Concerns; |
| import org.qi4j.api.constraint.Constraint; |
| import org.qi4j.api.constraint.ConstraintDeclaration; |
| import org.qi4j.api.constraint.Constraints; |
| import org.qi4j.api.constraint.Name; |
| import org.qi4j.api.entity.Lifecycle; |
| import org.qi4j.api.injection.scope.State; |
| import org.qi4j.api.injection.scope.This; |
| import org.qi4j.api.mixin.Initializable; |
| import org.qi4j.api.mixin.Mixins; |
| import org.qi4j.api.property.GenericPropertyInfo; |
| import org.qi4j.api.property.Immutable; |
| import org.qi4j.api.property.Property; |
| import org.qi4j.api.sideeffect.SideEffects; |
| import org.qi4j.api.type.HasTypes; |
| import org.qi4j.api.util.Annotations; |
| import org.qi4j.api.util.Classes; |
| import org.qi4j.api.util.Fields; |
| import org.qi4j.bootstrap.StateDeclarations; |
| import org.qi4j.functional.ForEach; |
| import org.qi4j.functional.Function; |
| import org.qi4j.functional.HierarchicalVisitorAdapter; |
| import org.qi4j.functional.Iterables; |
| import org.qi4j.functional.Specification; |
| import org.qi4j.functional.Visitor; |
| import org.qi4j.runtime.composite.AbstractConstraintModel; |
| import org.qi4j.runtime.composite.CompositeConstraintModel; |
| import org.qi4j.runtime.composite.CompositeMethodModel; |
| import org.qi4j.runtime.composite.CompositeMethodsModel; |
| import org.qi4j.runtime.composite.ConcernModel; |
| import org.qi4j.runtime.composite.ConcernsModel; |
| import org.qi4j.runtime.composite.ConstraintModel; |
| import org.qi4j.runtime.composite.ConstraintsModel; |
| import org.qi4j.runtime.composite.GenericSpecification; |
| import org.qi4j.runtime.composite.MixinModel; |
| import org.qi4j.runtime.composite.MixinsModel; |
| import org.qi4j.runtime.composite.SideEffectModel; |
| import org.qi4j.runtime.composite.SideEffectsModel; |
| import org.qi4j.runtime.composite.StateModel; |
| import org.qi4j.runtime.composite.ValueConstraintsInstance; |
| import org.qi4j.runtime.composite.ValueConstraintsModel; |
| import org.qi4j.runtime.injection.DependencyModel; |
| import org.qi4j.runtime.property.PropertiesModel; |
| import org.qi4j.runtime.property.PropertyModel; |
| |
| import static java.util.Arrays.asList; |
| import static org.qi4j.api.util.Annotations.hasAnnotation; |
| import static org.qi4j.api.util.Annotations.isType; |
| import static org.qi4j.api.util.Annotations.type; |
| import static org.qi4j.api.util.Classes.classHierarchy; |
| import static org.qi4j.api.util.Classes.interfacesOf; |
| import static org.qi4j.api.util.Classes.isAssignableFrom; |
| import static org.qi4j.api.util.Classes.typeOf; |
| import static org.qi4j.api.util.Classes.typesOf; |
| import static org.qi4j.api.util.Classes.wrapperClass; |
| import static org.qi4j.functional.Iterables.addAll; |
| import static org.qi4j.functional.Iterables.cast; |
| import static org.qi4j.functional.Iterables.empty; |
| import static org.qi4j.functional.Iterables.filter; |
| import static org.qi4j.functional.Iterables.first; |
| import static org.qi4j.functional.Iterables.flatten; |
| import static org.qi4j.functional.Iterables.flattenIterables; |
| import static org.qi4j.functional.Iterables.iterable; |
| import static org.qi4j.functional.Iterables.map; |
| import static org.qi4j.functional.Iterables.matchesAny; |
| import static org.qi4j.functional.Iterables.toList; |
| import static org.qi4j.functional.Specifications.and; |
| import static org.qi4j.functional.Specifications.in; |
| import static org.qi4j.functional.Specifications.not; |
| import static org.qi4j.functional.Specifications.or; |
| import static org.qi4j.functional.Specifications.translate; |
| |
| /** |
| * Declaration of a Composite. |
| */ |
| public abstract class CompositeAssemblyImpl |
| implements HasTypes |
| { |
| protected List<Class<?>> concerns = new ArrayList<>(); |
| protected List<Class<?>> sideEffects = new ArrayList<>(); |
| protected List<Class<?>> mixins = new ArrayList<>(); |
| protected List<Class<?>> types = new ArrayList<>(); |
| protected MetaInfo metaInfo = new MetaInfo(); |
| protected Visibility visibility = Visibility.module; |
| |
| protected boolean immutable; |
| protected PropertiesModel propertiesModel; |
| protected StateModel stateModel; |
| protected MixinsModel mixinsModel; |
| protected CompositeMethodsModel compositeMethodsModel; |
| private AssemblyHelper helper; |
| protected StateDeclarations stateDeclarations; |
| |
| protected Set<String> registeredStateNames = new HashSet<>(); |
| |
| public CompositeAssemblyImpl( Class<?> mainType ) |
| { |
| types.add( mainType ); |
| } |
| |
| @Override |
| public Iterable<Class<?>> types() |
| { |
| return types; |
| } |
| |
| protected StateModel createStateModel() |
| { |
| return new StateModel( propertiesModel ); |
| } |
| |
| protected MixinsModel createMixinsModel() |
| { |
| return new MixinsModel(); |
| } |
| |
| protected void buildComposite( AssemblyHelper helper, |
| StateDeclarations stateDeclarations |
| ) |
| { |
| this.stateDeclarations = stateDeclarations; |
| this.helper = helper; |
| for( Class<?> compositeType : types ) |
| { |
| metaInfo = new MetaInfo( metaInfo ).withAnnotations( compositeType ); |
| addAnnotationsMetaInfo( compositeType, metaInfo ); |
| } |
| |
| immutable = metaInfo.get( Immutable.class ) != null; |
| propertiesModel = new PropertiesModel(); |
| stateModel = createStateModel(); |
| mixinsModel = createMixinsModel(); |
| compositeMethodsModel = new CompositeMethodsModel( mixinsModel ); |
| |
| // Implement composite methods |
| ArrayList<Type> allTypes = getTypes( this.types ); |
| Iterable<Class<? extends Constraint<?, ?>>> constraintClasses = constraintDeclarations( getTypes( this.types ) ); |
| Iterable<Class<?>> concernClasses = flatten( concerns, concernDeclarations( allTypes ) ); |
| Iterable<Class<?>> sideEffectClasses = flatten( sideEffects, sideEffectDeclarations( allTypes ) ); |
| Iterable<Class<?>> mixinClasses = flatten( mixins, mixinDeclarations( this.types ) ); |
| implementMixinType( types, constraintClasses, concernClasses, sideEffectClasses, mixinClasses ); |
| |
| // Add state from methods and fields |
| addState( constraintClasses ); |
| } |
| |
| protected void addAnnotationsMetaInfo( Class<?> type, MetaInfo compositeMetaInfo ) |
| { |
| Class[] declaredInterfaces = type.getInterfaces(); |
| for( int i = declaredInterfaces.length - 1; i >= 0; i-- ) |
| { |
| addAnnotationsMetaInfo( declaredInterfaces[ i], compositeMetaInfo ); |
| } |
| compositeMetaInfo.withAnnotations( type ); |
| } |
| |
| protected void implementMixinType( Iterable<? extends Class<?>> types, |
| Iterable<Class<? extends Constraint<?, ?>>> constraintClasses, |
| Iterable<Class<?>> concernClasses, |
| Iterable<Class<?>> sideEffectClasses, |
| Iterable<Class<?>> mixinClasses |
| ) |
| { |
| Set<Class<?>> thisDependencies = new HashSet<>(); |
| for( Class<?> mixinType : types ) |
| { |
| for( Method method : mixinType.getMethods() ) |
| { |
| if( !compositeMethodsModel.isImplemented( method ) |
| && !Proxy.class.equals( method.getDeclaringClass().getSuperclass() ) |
| && !Proxy.class.equals( method.getDeclaringClass() ) |
| && !Modifier.isStatic( method.getModifiers() ) ) |
| { |
| MixinModel mixinModel = implementMethod( method, mixinClasses ); |
| ConcernsModel concernsModel = concernsFor( |
| method, |
| mixinModel.mixinClass(), |
| Iterables.<Class<?>>flatten( concernDeclarations( mixinModel.mixinClass() ), |
| concernClasses ) |
| ); |
| SideEffectsModel sideEffectsModel = sideEffectsFor( |
| method, |
| mixinModel.mixinClass(), |
| Iterables.<Class<?>>flatten( sideEffectDeclarations( mixinModel.mixinClass() ), |
| sideEffectClasses ) |
| ); |
| method.setAccessible( true ); |
| ConstraintsModel constraints = constraintsFor( |
| method, |
| Iterables.<Class<? extends Constraint<?, ?>>>flatten( constraintDeclarations( mixinModel.mixinClass() ), |
| constraintClasses ) |
| ); |
| CompositeMethodModel methodComposite = new CompositeMethodModel( |
| method, |
| constraints, |
| concernsModel, |
| sideEffectsModel, |
| mixinsModel |
| ); |
| |
| // Implement @This references |
| Iterable<Class<?>> map = map( new DependencyModel.InjectionTypeFunction(), |
| filter( new DependencyModel.ScopeSpecification( This.class ), |
| methodComposite.dependencies() ) ); |
| Iterable<Class<?>> map1 = map( new DependencyModel.InjectionTypeFunction(), |
| filter( new DependencyModel.ScopeSpecification( This.class ), |
| mixinModel.dependencies() ) ); |
| @SuppressWarnings( "unchecked" ) |
| Iterable<Class<?>> filter = filter( |
| not( in( Initializable.class, Lifecycle.class, InvocationHandler.class ) ), |
| map( Classes.RAW_CLASS, interfacesOf( mixinModel.mixinClass() ) ) |
| ); |
| Iterable<? extends Class<?>> flatten = flatten( map, map1, filter ); |
| addAll( thisDependencies, flatten ); |
| |
| compositeMethodsModel.addMethod( methodComposite ); |
| } |
| } |
| // Add type to set of mixin types |
| mixinsModel.addMixinType( mixinType ); |
| } |
| |
| // Implement all @This dependencies that were found |
| for( Class<?> thisDependency : thisDependencies ) |
| { |
| // Add additional declarations from the @This type |
| Iterable<Class<? extends Constraint<?, ?>>> typeConstraintClasses = flatten( |
| constraintClasses, |
| constraintDeclarations( thisDependency ) ); |
| Iterable<Class<?>> typeConcernClasses = flatten( |
| concernClasses, |
| concernDeclarations( thisDependency ) ); |
| Iterable<Class<?>> typeSideEffectClasses = flatten( |
| sideEffectClasses, |
| sideEffectDeclarations( thisDependency ) ); |
| Iterable<Class<?>> typeMixinClasses = flatten( |
| mixinClasses, |
| mixinDeclarations( thisDependency ) ); |
| |
| @SuppressWarnings( "unchecked" ) |
| Iterable<? extends Class<?>> singleton = iterable( thisDependency ); |
| implementMixinType( singleton, typeConstraintClasses, typeConcernClasses, typeSideEffectClasses, typeMixinClasses ); |
| } |
| } |
| |
| @SuppressWarnings( "raw" ) |
| protected MixinModel implementMethod( Method method, Iterable<Class<?>> mixinDeclarations ) |
| { |
| MixinModel implementationModel = mixinsModel.mixinFor( method ); |
| if( implementationModel != null ) |
| { |
| return implementationModel; |
| } |
| Class mixinClass = findTypedImplementation( method, mixinDeclarations ); |
| if( mixinClass != null ) |
| { |
| return implementMethodWithClass( method, mixinClass ); |
| } |
| |
| // Check generic implementations |
| mixinClass = findGenericImplementation( method, mixinDeclarations ); |
| if( mixinClass != null ) |
| { |
| return implementMethodWithClass( method, mixinClass ); |
| } |
| |
| throw new InvalidCompositeException( "No implementation found for method \n " + method.toGenericString() |
| + "\nin\n " + types ); |
| } |
| |
| @SuppressWarnings( {"raw", "unchecked"} ) |
| private Class findTypedImplementation( final Method method, Iterable<Class<?>> mixins ) |
| { |
| // Check if mixinClass implements the method. If so, check if the mixinClass is generic or if the filter passes. |
| // If a mixinClass is both generic AND non-generic at the same time, then the filter applies to the non-generic |
| // side only. |
| Specification<Class<?>> appliesToSpec = new Specification<Class<?>>() |
| { |
| @Override |
| public boolean satisfiedBy( Class<?> item ) |
| { |
| return helper.appliesTo( item, method, types, item ); |
| } |
| }; |
| return first( filter( and( isAssignableFrom( method.getDeclaringClass() ), |
| or( GenericSpecification.INSTANCE, appliesToSpec ) ), |
| mixins ) ); |
| } |
| |
| @SuppressWarnings( "unchecked" ) |
| private Class<?> findGenericImplementation( final Method method, Iterable<Class<?>> mixins ) |
| { |
| // Check if mixinClass is generic and the applies-to filter passes |
| return first( filter( and( GenericSpecification.INSTANCE, new Specification<Class<?>>() |
| { |
| @Override |
| public boolean satisfiedBy( Class<?> item ) |
| { |
| return helper.appliesTo( item, method, types, item ); |
| } |
| } ), mixins ) ); |
| } |
| |
| private MixinModel implementMethodWithClass( Method method, Class mixinClass ) |
| { |
| MixinModel mixinModel = mixinsModel.getMixinModel( mixinClass ); |
| if( mixinModel == null ) |
| { |
| mixinModel = helper.getMixinModel( mixinClass ); |
| mixinsModel.addMixinModel( mixinModel ); |
| } |
| |
| mixinsModel.addMethodMixin( method, mixinModel ); |
| |
| return mixinModel; |
| } |
| |
| protected void addState( final Iterable<Class<? extends Constraint<?, ?>>> constraintClasses ) |
| { |
| // Add method state |
| compositeMethodsModel.accept( new HierarchicalVisitorAdapter<Object, Object, RuntimeException>() |
| { |
| @Override |
| public boolean visitEnter( Object visited ) |
| throws RuntimeException |
| { |
| if( visited instanceof CompositeMethodModel ) |
| { |
| CompositeMethodModel methodModel = (CompositeMethodModel) visited; |
| if( methodModel.method().getParameterTypes().length == 0 ) |
| { |
| addStateFor( methodModel.method(), constraintClasses ); |
| } |
| |
| return false; |
| } |
| |
| return super.visitEnter( visited ); |
| } |
| } ); |
| |
| // Add field state |
| mixinsModel.accept( new HierarchicalVisitorAdapter<Object, Object, RuntimeException>() |
| { |
| @Override |
| public boolean visitEnter( Object visited ) |
| throws RuntimeException |
| { |
| if( visited instanceof MixinModel ) |
| { |
| MixinModel model = (MixinModel) visited; |
| Visitor<Field, RuntimeException> addState = new Visitor<Field, RuntimeException>() |
| { |
| @Override |
| public boolean visit( Field visited ) |
| throws RuntimeException |
| { |
| addStateFor( visited, constraintClasses ); |
| return true; |
| } |
| }; |
| ForEach.forEach( Fields.FIELDS_OF.map( model.mixinClass() ) ). |
| filter( Annotations.hasAnnotation( State.class ) ). |
| visit( addState ); |
| return false; |
| } |
| return super.visitEnter( visited ); |
| } |
| } ); |
| } |
| |
| protected void addStateFor( AccessibleObject accessor, |
| Iterable<Class<? extends Constraint<?, ?>>> constraintClasses |
| ) |
| { |
| String stateName = QualifiedName.fromAccessor( accessor ).name(); |
| |
| if( registeredStateNames.contains( stateName ) ) |
| { |
| return; // Skip already registered names |
| } |
| |
| if( Property.class.isAssignableFrom( Classes.RAW_CLASS.map( typeOf( accessor ) ) ) ) |
| { |
| propertiesModel.addProperty( newPropertyModel( accessor, constraintClasses ) ); |
| registeredStateNames.add( stateName ); |
| } |
| } |
| |
| protected PropertyModel newPropertyModel( AccessibleObject accessor, |
| Iterable<Class<? extends Constraint<?, ?>>> constraintClasses |
| ) |
| { |
| Iterable<Annotation> annotations = Annotations.findAccessorAndTypeAnnotationsIn( accessor ); |
| boolean optional = first( filter( isType( Optional.class ), annotations ) ) != null; |
| ValueConstraintsModel valueConstraintsModel = constraintsFor( |
| annotations, |
| GenericPropertyInfo.propertyTypeOf( accessor ), |
| ( (Member) accessor ).getName(), |
| optional, |
| constraintClasses, |
| accessor ); |
| ValueConstraintsInstance valueConstraintsInstance = null; |
| if( valueConstraintsModel.isConstrained() ) |
| { |
| valueConstraintsInstance = valueConstraintsModel.newInstance(); |
| } |
| MetaInfo metaInfo = stateDeclarations.metaInfoFor( accessor ); |
| Object initialValue = stateDeclarations.initialValueOf( accessor ); |
| boolean useDefaults = metaInfo.get( UseDefaults.class ) != null || stateDeclarations.useDefaults( accessor ); |
| boolean immutable = this.immutable || metaInfo.get( Immutable.class ) != null; |
| PropertyModel propertyModel = new PropertyModel( |
| accessor, |
| immutable, |
| useDefaults, |
| valueConstraintsInstance, |
| metaInfo, |
| initialValue ); |
| return propertyModel; |
| } |
| |
| // Model |
| private ConstraintsModel constraintsFor( Method method, |
| Iterable<Class<? extends Constraint<?, ?>>> constraintClasses |
| ) |
| { |
| List<ValueConstraintsModel> parameterConstraintModels = Collections.emptyList(); |
| Annotation[][] parameterAnnotations = method.getParameterAnnotations(); |
| Type[] parameterTypes = method.getGenericParameterTypes(); |
| boolean constrained = false; |
| for( int i = 0; i < parameterAnnotations.length; i++ ) |
| { |
| Annotation[] parameterAnnotation = parameterAnnotations[i]; |
| |
| Name nameAnnotation = (Name) first( filter( isType( Name.class ), iterable( parameterAnnotation ) ) ); |
| String name = nameAnnotation == null ? "param" + ( i + 1 ) : nameAnnotation.value(); |
| |
| boolean optional = first( filter( isType( Optional.class ), iterable( parameterAnnotation ) ) ) != null; |
| ValueConstraintsModel parameterConstraintsModel = constraintsFor( |
| asList( parameterAnnotation ), |
| parameterTypes[i], |
| name, |
| optional, |
| constraintClasses, |
| method ); |
| if( parameterConstraintsModel.isConstrained() ) |
| { |
| constrained = true; |
| } |
| |
| if( parameterConstraintModels.isEmpty() ) |
| { |
| parameterConstraintModels = new ArrayList<>(); |
| } |
| parameterConstraintModels.add( parameterConstraintsModel ); |
| } |
| |
| if( !constrained ) |
| { |
| return new ConstraintsModel( Collections.<ValueConstraintsModel>emptyList() ); |
| } |
| else |
| { |
| return new ConstraintsModel( parameterConstraintModels ); |
| } |
| } |
| |
| protected ValueConstraintsModel constraintsFor( |
| Iterable<Annotation> constraintAnnotations, |
| Type valueType, |
| String name, |
| boolean optional, |
| Iterable<Class<? extends Constraint<?, ?>>> constraintClasses, |
| AccessibleObject accessor |
| ) |
| { |
| valueType = wrapperClass( valueType ); |
| |
| List<AbstractConstraintModel> constraintModels = new ArrayList<>(); |
| nextConstraint: |
| for( Annotation constraintAnnotation : filter( translate( type(), hasAnnotation( ConstraintDeclaration.class ) ), |
| constraintAnnotations ) ) |
| { |
| // Check composite declarations first |
| Class<? extends Annotation> annotationType = constraintAnnotation.annotationType(); |
| for( Class<? extends Constraint<?, ?>> constraint : constraintClasses ) |
| { |
| if( helper.appliesTo( constraint, annotationType, valueType ) ) |
| { |
| constraintModels.add( new ConstraintModel( constraintAnnotation, constraint ) ); |
| continue nextConstraint; |
| } |
| } |
| |
| // Check the annotation itself |
| Constraints constraints = annotationType.getAnnotation( Constraints.class ); |
| if( constraints != null ) |
| { |
| for( Class<? extends Constraint<?, ?>> constraintClass : constraints.value() ) |
| { |
| if( helper.appliesTo( constraintClass, annotationType, valueType ) ) |
| { |
| constraintModels.add( new ConstraintModel( constraintAnnotation, constraintClass ) ); |
| continue nextConstraint; |
| } |
| } |
| } |
| |
| // No implementation found! |
| // Check if if it's a composite constraints |
| Iterable<Annotation> annotations = iterable( annotationType.getAnnotations() ); |
| if( matchesAny( translate( type(), hasAnnotation( ConstraintDeclaration.class ) ), annotations ) ) |
| { |
| ValueConstraintsModel valueConstraintsModel = constraintsFor( |
| annotations, |
| valueType, |
| name, |
| optional, |
| constraintClasses, |
| accessor ); |
| CompositeConstraintModel compositeConstraintModel = new CompositeConstraintModel( |
| constraintAnnotation, |
| valueConstraintsModel ); |
| constraintModels.add( compositeConstraintModel ); |
| continue nextConstraint; |
| } |
| |
| throw new InvalidCompositeException( |
| "Cannot find implementation of constraint @" |
| + annotationType.getSimpleName() |
| + " for " |
| + valueType |
| + " in method " |
| + ( (Member) accessor ).getName() |
| + " of composite " + types ); |
| } |
| |
| return new ValueConstraintsModel( constraintModels, name, optional ); |
| } |
| |
| private ConcernsModel concernsFor( Method method, |
| Class<?> mixinClass, |
| Iterable<Class<?>> concernClasses |
| ) |
| { |
| List<ConcernModel> concernsFor = new ArrayList<>(); |
| for( Class<?> concern : concernClasses ) |
| { |
| if( helper.appliesTo( concern, method, types, mixinClass ) ) |
| { |
| concernsFor.add( helper.getConcernModel( concern ) ); |
| } |
| else |
| { |
| // Lookup method in mixin |
| if( !InvocationHandler.class.isAssignableFrom( mixinClass ) ) |
| { |
| try |
| { |
| Method mixinMethod = mixinClass.getMethod( method.getName(), method.getParameterTypes() ); |
| if( helper.appliesTo( concern, mixinMethod, types, mixinClass ) ) |
| { |
| concernsFor.add( helper.getConcernModel( concern ) ); |
| } |
| } |
| catch( NoSuchMethodException e ) |
| { |
| // Ignore |
| } |
| } |
| } |
| } |
| |
| // Check annotations on method that have @Concerns annotations themselves |
| for( Annotation annotation : method.getAnnotations() ) |
| { |
| @SuppressWarnings( "raw" ) |
| Concerns concerns = annotation.annotationType().getAnnotation( Concerns.class ); |
| if( concerns != null ) |
| { |
| for( Class<?> concern : concerns.value() ) |
| { |
| if( helper.appliesTo( concern, method, types, mixinClass ) ) |
| { |
| concernsFor.add( helper.getConcernModel( concern ) ); |
| } |
| } |
| } |
| } |
| |
| if( concernsFor.isEmpty() ) |
| { |
| return ConcernsModel.EMPTY_CONCERNS; |
| } |
| else |
| { |
| return new ConcernsModel( concernsFor ); |
| } |
| } |
| |
| private SideEffectsModel sideEffectsFor( Method method, |
| Class<?> mixinClass, |
| Iterable<Class<?>> sideEffectClasses |
| ) |
| { |
| List<SideEffectModel> sideEffectsFor = new ArrayList<>(); |
| for( Class<?> sideEffect : sideEffectClasses ) |
| { |
| if( helper.appliesTo( sideEffect, method, types, mixinClass ) ) |
| { |
| sideEffectsFor.add( helper.getSideEffectModel( sideEffect ) ); |
| } |
| else |
| { |
| // Lookup method in mixin |
| if( !InvocationHandler.class.isAssignableFrom( mixinClass ) ) |
| { |
| try |
| { |
| Method mixinMethod = mixinClass.getMethod( method.getName(), method.getParameterTypes() ); |
| if( helper.appliesTo( sideEffect, mixinMethod, types, mixinClass ) ) |
| { |
| sideEffectsFor.add( helper.getSideEffectModel( sideEffect ) ); |
| } |
| } |
| catch( NoSuchMethodException e ) |
| { |
| // Ignore |
| } |
| } |
| } |
| } |
| |
| if( sideEffectsFor.isEmpty() ) |
| { |
| return SideEffectsModel.EMPTY_SIDEEFFECTS; |
| } |
| else |
| { |
| return new SideEffectsModel( sideEffectsFor ); |
| } |
| } |
| |
| @SuppressWarnings( "unchecked" ) |
| private Iterable<Class<? extends Constraint<?, ?>>> constraintDeclarations( Class<?> type ) |
| { |
| ArrayList<Type> allTypes = getTypes( type ); |
| return constraintDeclarations( allTypes ); |
| } |
| |
| private Iterable<Class<? extends Constraint<?, ?>>> constraintDeclarations( ArrayList<Type> allTypes ) |
| { |
| // Find all constraints and flatten them into an iterable |
| Function<Type, Iterable<Class<? extends Constraint<?, ?>>>> function = new Function<Type, Iterable<Class<? extends Constraint<?, ?>>>>() |
| { |
| @Override |
| public Iterable<Class<? extends Constraint<?, ?>>> map( Type type ) |
| { |
| Constraints constraints = Annotations.annotationOn( type, Constraints.class ); |
| if( constraints == null ) |
| { |
| return empty(); |
| } |
| else |
| { |
| return iterable( constraints.value() ); |
| } |
| } |
| }; |
| Iterable<Class<? extends Constraint<?, ?>>> flatten = flattenIterables( map( function, allTypes ) ); |
| return toList( flatten ); |
| } |
| |
| @SuppressWarnings( "unchecked" ) |
| private Iterable<Class<?>> concernDeclarations( Class<?> type ) |
| { |
| Iterable<? extends Class<?>> iterable = iterable( type ); |
| return concernDeclarations( getTypes( iterable ) ); |
| } |
| |
| private Iterable<Class<?>> concernDeclarations( ArrayList<Type> allTypes ) |
| { |
| // Find all concerns and flattern them into an iterable |
| Function<Type, Iterable<Class<?>>> function = new Function<Type, Iterable<Class<?>>>() |
| { |
| @Override |
| public Iterable<Class<?>> map( Type type ) |
| { |
| Concerns concerns = Annotations.annotationOn( type, Concerns.class ); |
| if( concerns == null ) |
| { |
| return empty(); |
| } |
| else |
| { |
| return iterable( concerns.value() ); |
| } |
| } |
| }; |
| Iterable<Class<?>> flatten = flattenIterables( map( function, allTypes ) ); |
| return toList( flatten ); |
| } |
| |
| @SuppressWarnings( "unchecked" ) |
| protected Iterable<Class<?>> sideEffectDeclarations( Class<?> type ) |
| { |
| Iterable<? extends Class<?>> iterable = iterable( type ); |
| return sideEffectDeclarations( getTypes( iterable ) ); |
| } |
| |
| protected Iterable<Class<?>> sideEffectDeclarations( ArrayList<Type> allTypes ) |
| { |
| // Find all side-effects and flattern them into an iterable |
| Function<Type, Iterable<Class<?>>> function = new Function<Type, Iterable<Class<?>>>() |
| { |
| @Override |
| public Iterable<Class<?>> map( Type type ) |
| { |
| SideEffects sideEffects = Annotations.annotationOn( type, SideEffects.class ); |
| if( sideEffects == null ) |
| { |
| return empty(); |
| } |
| else |
| { |
| return iterable( sideEffects.value() ); |
| } |
| } |
| }; |
| Iterable<Class<?>> flatten = flattenIterables( map( function, allTypes ) ); |
| return toList( flatten ); |
| } |
| |
| private ArrayList<Type> getTypes( Class<?> type ) |
| { |
| Iterable<? extends Class<?>> iterable = iterable( type ); |
| return getTypes( iterable ); |
| } |
| |
| private ArrayList<Type> getTypes( Iterable<? extends Class<?>> typess ) |
| { |
| // Find side-effect declarations |
| ArrayList<Type> allTypes = new ArrayList<>(); |
| for( Class<?> type : typess ) |
| { |
| Iterable<Type> types; |
| if( type.isInterface() ) |
| { |
| types = typesOf( type ); |
| } |
| else |
| { |
| types = cast( classHierarchy( type ) ); |
| } |
| addAll( allTypes, types ); |
| } |
| return allTypes; |
| } |
| |
| @SuppressWarnings( "unchecked" ) |
| protected Iterable<Class<?>> mixinDeclarations( Class<?> type ) |
| { |
| Iterable<? extends Class<?>> iterable = iterable( type ); |
| return mixinDeclarations( iterable ); |
| } |
| |
| protected Iterable<Class<?>> mixinDeclarations( Iterable<? extends Class<?>> typess ) |
| { |
| // Find mixin declarations |
| ArrayList<Type> allTypes = new ArrayList<>(); |
| for( Class<?> type : typess ) |
| { |
| Iterable<Type> types = typesOf( type ); |
| addAll( allTypes, types ); |
| } |
| |
| // Find all mixins and flattern them into an iterable |
| Function<Type, Iterable<Class<?>>> function = new Function<Type, Iterable<Class<?>>>() |
| { |
| @Override |
| public Iterable<Class<?>> map( Type type ) |
| { |
| Mixins mixins = Annotations.annotationOn( type, Mixins.class ); |
| if( mixins == null ) |
| { |
| return empty(); |
| } |
| else |
| { |
| return iterable( mixins.value() ); |
| } |
| } |
| }; |
| Iterable<Class<?>> flatten = flattenIterables( map( function, allTypes ) ); |
| return toList( flatten ); |
| } |
| } |