http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/CompositeAssemblyImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/CompositeAssemblyImpl.java b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/CompositeAssemblyImpl.java new file mode 100644 index 0000000..458ff79 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/CompositeAssemblyImpl.java @@ -0,0 +1,837 @@ +/* + * 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.apache.zest.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.apache.zest.api.common.MetaInfo; +import org.apache.zest.api.common.Optional; +import org.apache.zest.api.common.QualifiedName; +import org.apache.zest.api.common.UseDefaults; +import org.apache.zest.api.common.Visibility; +import org.apache.zest.api.composite.InvalidCompositeException; +import org.apache.zest.api.concern.Concerns; +import org.apache.zest.api.constraint.Constraint; +import org.apache.zest.api.constraint.ConstraintDeclaration; +import org.apache.zest.api.constraint.Constraints; +import org.apache.zest.api.constraint.Name; +import org.apache.zest.api.entity.Lifecycle; +import org.apache.zest.api.injection.scope.State; +import org.apache.zest.api.injection.scope.This; +import org.apache.zest.api.mixin.Initializable; +import org.apache.zest.api.mixin.Mixins; +import org.apache.zest.api.property.GenericPropertyInfo; +import org.apache.zest.api.property.Immutable; +import org.apache.zest.api.property.Property; +import org.apache.zest.api.sideeffect.SideEffects; +import org.apache.zest.api.type.HasTypes; +import org.apache.zest.api.util.Annotations; +import org.apache.zest.api.util.Classes; +import org.apache.zest.api.util.Fields; +import org.apache.zest.bootstrap.StateDeclarations; +import org.apache.zest.functional.ForEach; +import org.apache.zest.functional.Function; +import org.apache.zest.functional.HierarchicalVisitorAdapter; +import org.apache.zest.functional.Iterables; +import org.apache.zest.functional.Specification; +import org.apache.zest.functional.Visitor; +import org.apache.zest.runtime.composite.AbstractConstraintModel; +import org.apache.zest.runtime.composite.CompositeConstraintModel; +import org.apache.zest.runtime.composite.CompositeMethodModel; +import org.apache.zest.runtime.composite.CompositeMethodsModel; +import org.apache.zest.runtime.composite.ConcernModel; +import org.apache.zest.runtime.composite.ConcernsModel; +import org.apache.zest.runtime.composite.ConstraintModel; +import org.apache.zest.runtime.composite.ConstraintsModel; +import org.apache.zest.runtime.composite.GenericSpecification; +import org.apache.zest.runtime.composite.MixinModel; +import org.apache.zest.runtime.composite.MixinsModel; +import org.apache.zest.runtime.composite.SideEffectModel; +import org.apache.zest.runtime.composite.SideEffectsModel; +import org.apache.zest.runtime.composite.StateModel; +import org.apache.zest.runtime.composite.ValueConstraintsInstance; +import org.apache.zest.runtime.composite.ValueConstraintsModel; +import org.apache.zest.runtime.injection.DependencyModel; +import org.apache.zest.runtime.property.PropertiesModel; +import org.apache.zest.runtime.property.PropertyModel; + +import static java.util.Arrays.asList; +import static org.apache.zest.api.util.Annotations.hasAnnotation; +import static org.apache.zest.api.util.Annotations.isType; +import static org.apache.zest.api.util.Annotations.type; +import static org.apache.zest.api.util.Classes.classHierarchy; +import static org.apache.zest.api.util.Classes.interfacesOf; +import static org.apache.zest.api.util.Classes.isAssignableFrom; +import static org.apache.zest.api.util.Classes.typeOf; +import static org.apache.zest.api.util.Classes.typesOf; +import static org.apache.zest.api.util.Classes.wrapperClass; +import static org.apache.zest.functional.Iterables.addAll; +import static org.apache.zest.functional.Iterables.cast; +import static org.apache.zest.functional.Iterables.empty; +import static org.apache.zest.functional.Iterables.filter; +import static org.apache.zest.functional.Iterables.first; +import static org.apache.zest.functional.Iterables.flatten; +import static org.apache.zest.functional.Iterables.flattenIterables; +import static org.apache.zest.functional.Iterables.iterable; +import static org.apache.zest.functional.Iterables.map; +import static org.apache.zest.functional.Iterables.matchesAny; +import static org.apache.zest.functional.Iterables.toList; +import static org.apache.zest.functional.Specifications.and; +import static org.apache.zest.functional.Specifications.in; +import static org.apache.zest.functional.Specifications.not; +import static org.apache.zest.functional.Specifications.or; +import static org.apache.zest.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 ); + } +}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ConfigurationAssemblyImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ConfigurationAssemblyImpl.java b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ConfigurationAssemblyImpl.java new file mode 100644 index 0000000..f755d85 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ConfigurationAssemblyImpl.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.zest.runtime.bootstrap; + +import org.apache.zest.bootstrap.ConfigurationAssembly; + +/** + * Declaration of a EntityComposite. + */ +public final class ConfigurationAssemblyImpl + implements ConfigurationAssembly +{ + private ValueAssemblyImpl value; + private EntityAssemblyImpl entity; + + public ConfigurationAssemblyImpl( Class<?> mainType ) + { + value = new ValueAssemblyImpl( mainType ); + entity = new EntityAssemblyImpl( mainType ); + } + + @Override + public Iterable<Class<?>> types() + { + return value.types(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ConfigurationDeclarationImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ConfigurationDeclarationImpl.java b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ConfigurationDeclarationImpl.java new file mode 100644 index 0000000..1076390 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ConfigurationDeclarationImpl.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.zest.runtime.bootstrap; + +import org.apache.zest.api.common.Visibility; +import org.apache.zest.bootstrap.ConfigurationDeclaration; + +import static java.util.Arrays.asList; + +/** + * Declaration of a Composite. Created by {@link org.apache.zest.bootstrap.ModuleAssembly#configurations(Class[])}. + */ +public final class ConfigurationDeclarationImpl + implements ConfigurationDeclaration +{ + private final Iterable<EntityAssemblyImpl> entities; + private final Iterable<ValueAssemblyImpl> values; + + public ConfigurationDeclarationImpl( Iterable<EntityAssemblyImpl> entities, Iterable<ValueAssemblyImpl> values ) + { + this.entities = entities; + this.values = values; + } + + @Override + public ConfigurationDeclaration setMetaInfo( Object info ) + { + for( EntityAssemblyImpl entity : entities ) + { + entity.metaInfo.set( info ); + } + for( ValueAssemblyImpl value : values ) + { + value.metaInfo.set( info ); + } + return this; + } + + @Override + public ConfigurationDeclaration visibleIn( Visibility visibility ) + { + for( EntityAssemblyImpl entity : entities ) + { + entity.visibility = visibility; + } + for( ValueAssemblyImpl value : values ) + { + value.visibility = visibility; + } + return this; + } + + @Override + public ConfigurationDeclaration withConcerns( Class<?>... concerns ) + { + for( EntityAssemblyImpl entity : entities ) + { + entity.concerns.addAll( asList( concerns ) ); + } + for( ValueAssemblyImpl value : values ) + { + value.concerns.addAll( asList( concerns ) ); + } + return this; + } + + @Override + public ConfigurationDeclaration withSideEffects( Class<?>... sideEffects ) + { + for( EntityAssemblyImpl entity : entities ) + { + entity.sideEffects.addAll( asList( sideEffects ) ); + } + for( ValueAssemblyImpl value : values ) + { + value.sideEffects.addAll( asList( sideEffects ) ); + } + return this; + } + + @Override + public ConfigurationDeclaration withMixins( Class<?>... mixins ) + { + for( EntityAssemblyImpl entity : entities ) + { + entity.mixins.addAll( asList( mixins ) ); + } + for( ValueAssemblyImpl value : values ) + { + value.mixins.addAll( asList( mixins ) ); + } + return this; + } + + @Override + public ConfigurationDeclaration withTypes( Class<?>... types ) + { + for( EntityAssemblyImpl entity : entities ) + { + entity.types.addAll( asList( types ) ); + } + for( ValueAssemblyImpl value : values ) + { + value.types.addAll( asList( types ) ); + } + return this; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/EntityAssemblyImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/EntityAssemblyImpl.java b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/EntityAssemblyImpl.java new file mode 100644 index 0000000..6daba43 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/EntityAssemblyImpl.java @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2007-2011, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2014, Paul Merlin. 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.apache.zest.runtime.bootstrap; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import org.apache.zest.api.association.Association; +import org.apache.zest.api.association.GenericAssociationInfo; +import org.apache.zest.api.association.ManyAssociation; +import org.apache.zest.api.association.NamedAssociation; +import org.apache.zest.api.common.InvalidApplicationException; +import org.apache.zest.api.common.MetaInfo; +import org.apache.zest.api.common.Optional; +import org.apache.zest.api.common.QualifiedName; +import org.apache.zest.api.common.UseDefaults; +import org.apache.zest.api.constraint.Constraint; +import org.apache.zest.api.entity.EntityComposite; +import org.apache.zest.api.property.GenericPropertyInfo; +import org.apache.zest.api.property.Immutable; +import org.apache.zest.api.property.Property; +import org.apache.zest.api.util.Annotations; +import org.apache.zest.api.util.Classes; +import org.apache.zest.bootstrap.AssociationDeclarations; +import org.apache.zest.bootstrap.EntityAssembly; +import org.apache.zest.bootstrap.ManyAssociationDeclarations; +import org.apache.zest.bootstrap.NamedAssociationDeclarations; +import org.apache.zest.bootstrap.StateDeclarations; +import org.apache.zest.runtime.association.AssociationModel; +import org.apache.zest.runtime.association.AssociationsModel; +import org.apache.zest.runtime.association.ManyAssociationModel; +import org.apache.zest.runtime.association.ManyAssociationsModel; +import org.apache.zest.runtime.association.NamedAssociationModel; +import org.apache.zest.runtime.association.NamedAssociationsModel; +import org.apache.zest.runtime.composite.MixinsModel; +import org.apache.zest.runtime.composite.StateModel; +import org.apache.zest.runtime.composite.ValueConstraintsInstance; +import org.apache.zest.runtime.composite.ValueConstraintsModel; +import org.apache.zest.runtime.entity.EntityMixinsModel; +import org.apache.zest.runtime.entity.EntityModel; +import org.apache.zest.runtime.entity.EntityStateModel; +import org.apache.zest.runtime.property.PropertyModel; + +import static org.apache.zest.api.util.Annotations.isType; +import static org.apache.zest.api.util.Classes.typeOf; +import static org.apache.zest.functional.Iterables.filter; +import static org.apache.zest.functional.Iterables.first; + +/** + * Declaration of a EntityComposite. + */ +public final class EntityAssemblyImpl + extends CompositeAssemblyImpl + implements EntityAssembly +{ + private AssociationDeclarations associationDeclarations; + private ManyAssociationDeclarations manyAssociationDeclarations; + private NamedAssociationDeclarations namedAssociationDeclarations; + private AssociationsModel associationsModel; + private ManyAssociationsModel manyAssociationsModel; + private NamedAssociationsModel namedAssociationsModel; + + public EntityAssemblyImpl( Class<?> entityType ) + { + super( entityType ); + // The composite must always implement EntityComposite, as a marker interface + if( !EntityComposite.class.isAssignableFrom( entityType ) ) + { + types.add( EntityComposite.class ); + } + } + + @Override + protected MixinsModel createMixinsModel() + { + return new EntityMixinsModel(); + } + + @Override + protected StateModel createStateModel() + { + return new EntityStateModel( propertiesModel, associationsModel, manyAssociationsModel, namedAssociationsModel ); + } + + EntityModel newEntityModel( + StateDeclarations stateDeclarations, + AssociationDeclarations associationDecs, + ManyAssociationDeclarations manyAssociationDecs, + NamedAssociationDeclarations namedAssociationDecs, + AssemblyHelper helper + ) + { + this.associationDeclarations = associationDecs; + this.manyAssociationDeclarations = manyAssociationDecs; + this.namedAssociationDeclarations = namedAssociationDecs; + try + { + associationsModel = new AssociationsModel(); + manyAssociationsModel = new ManyAssociationsModel(); + namedAssociationsModel = new NamedAssociationsModel(); + buildComposite( helper, stateDeclarations ); + + EntityModel entityModel = new EntityModel( + types, visibility, metaInfo, (EntityMixinsModel) mixinsModel, (EntityStateModel) stateModel, compositeMethodsModel ); + + return entityModel; + } + catch( Exception e ) + { + throw new InvalidApplicationException( "Could not register " + types, e ); + } + } + + @Override + 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 + } + + Class<?> accessorType = Classes.RAW_CLASS.map( typeOf( accessor ) ); + if( Property.class.isAssignableFrom( accessorType ) ) + { + propertiesModel.addProperty( newPropertyModel( accessor, constraintClasses ) ); + registeredStateNames.add( stateName ); + } + else if( Association.class.isAssignableFrom( accessorType ) ) + { + associationsModel.addAssociation( newAssociationModel( accessor, constraintClasses ) ); + registeredStateNames.add( stateName ); + } + else if( ManyAssociation.class.isAssignableFrom( accessorType ) ) + { + manyAssociationsModel.addManyAssociation( newManyAssociationModel( accessor, constraintClasses ) ); + registeredStateNames.add( stateName ); + } + else if( NamedAssociation.class.isAssignableFrom( accessorType ) ) + { + namedAssociationsModel.addNamedAssociation( newNamedAssociationModel( accessor, constraintClasses ) ); + registeredStateNames.add( stateName ); + } + } + + @Override + 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 defaultValue = 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, defaultValue ); + return propertyModel; + } + + public AssociationModel newAssociationModel( AccessibleObject accessor, + Iterable<Class<? extends Constraint<?, ?>>> constraintClasses + ) + { + Iterable<Annotation> annotations = Annotations.findAccessorAndTypeAnnotationsIn( accessor ); + boolean optional = first( filter( isType( Optional.class ), annotations ) ) != null; + + // Constraints for Association references + ValueConstraintsModel valueConstraintsModel = constraintsFor( annotations, GenericAssociationInfo + .associationTypeOf( accessor ), ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); + ValueConstraintsInstance valueConstraintsInstance = null; + if( valueConstraintsModel.isConstrained() ) + { + valueConstraintsInstance = valueConstraintsModel.newInstance(); + } + + // Constraints for the Association itself + valueConstraintsModel = constraintsFor( annotations, Association.class, ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); + ValueConstraintsInstance associationValueConstraintsInstance = null; + if( valueConstraintsModel.isConstrained() ) + { + associationValueConstraintsInstance = valueConstraintsModel.newInstance(); + } + + MetaInfo metaInfo = associationDeclarations.metaInfoFor( accessor ); + AssociationModel associationModel = new AssociationModel( accessor, valueConstraintsInstance, associationValueConstraintsInstance, metaInfo ); + return associationModel; + } + + public ManyAssociationModel newManyAssociationModel( AccessibleObject accessor, + Iterable<Class<? extends Constraint<?, ?>>> constraintClasses + ) + { + Iterable<Annotation> annotations = Annotations.findAccessorAndTypeAnnotationsIn( accessor ); + boolean optional = first( filter( isType( Optional.class ), annotations ) ) != null; + + // Constraints for entities in ManyAssociation + ValueConstraintsModel valueConstraintsModel = constraintsFor( annotations, GenericAssociationInfo + .associationTypeOf( accessor ), ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); + ValueConstraintsInstance valueConstraintsInstance = null; + if( valueConstraintsModel.isConstrained() ) + { + valueConstraintsInstance = valueConstraintsModel.newInstance(); + } + + // Constraints for the ManyAssociation itself + valueConstraintsModel = constraintsFor( annotations, ManyAssociation.class, ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); + ValueConstraintsInstance manyValueConstraintsInstance = null; + if( valueConstraintsModel.isConstrained() ) + { + manyValueConstraintsInstance = valueConstraintsModel.newInstance(); + } + MetaInfo metaInfo = manyAssociationDeclarations.metaInfoFor( accessor ); + ManyAssociationModel associationModel = new ManyAssociationModel( accessor, valueConstraintsInstance, manyValueConstraintsInstance, metaInfo ); + return associationModel; + } + + public NamedAssociationModel newNamedAssociationModel( AccessibleObject accessor, + Iterable<Class<? extends Constraint<?, ?>>> constraintClasses + ) + { + Iterable<Annotation> annotations = Annotations.findAccessorAndTypeAnnotationsIn( accessor ); + boolean optional = first( filter( isType( Optional.class ), annotations ) ) != null; + + // Constraints for entities in NamedAssociation + ValueConstraintsModel valueConstraintsModel = constraintsFor( annotations, GenericAssociationInfo + .associationTypeOf( accessor ), ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); + ValueConstraintsInstance valueConstraintsInstance = null; + if( valueConstraintsModel.isConstrained() ) + { + valueConstraintsInstance = valueConstraintsModel.newInstance(); + } + + // Constraints for the NamedAssociation itself + valueConstraintsModel = constraintsFor( annotations, NamedAssociation.class, ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); + ValueConstraintsInstance namedValueConstraintsInstance = null; + if( valueConstraintsModel.isConstrained() ) + { + namedValueConstraintsInstance = valueConstraintsModel.newInstance(); + } + MetaInfo metaInfo = namedAssociationDeclarations.metaInfoFor( accessor ); + NamedAssociationModel associationModel = new NamedAssociationModel( accessor, valueConstraintsInstance, namedValueConstraintsInstance, metaInfo ); + return associationModel; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/EntityDeclarationImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/EntityDeclarationImpl.java b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/EntityDeclarationImpl.java new file mode 100644 index 0000000..ada560e --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/EntityDeclarationImpl.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2007, Rickard Ãberg. 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.apache.zest.runtime.bootstrap; + +import org.apache.zest.api.common.Visibility; +import org.apache.zest.bootstrap.EntityDeclaration; + +import static java.util.Arrays.asList; + +/** + * Declaration of a Composite. Created by {@link org.apache.zest.bootstrap.ModuleAssembly#transients(Class[])}. + */ +public final class EntityDeclarationImpl + implements EntityDeclaration +{ + private final Iterable<EntityAssemblyImpl> entities; + + public EntityDeclarationImpl( Iterable<EntityAssemblyImpl> entities ) + { + this.entities = entities; + } + + @Override + public EntityDeclaration setMetaInfo( Object info ) + { + for( EntityAssemblyImpl entity : entities ) + { + entity.metaInfo.set( info ); + } + return this; + } + + @Override + public EntityDeclaration visibleIn( Visibility visibility ) + { + for( EntityAssemblyImpl entity : entities ) + { + entity.visibility = visibility; + } + return this; + } + + @Override + public EntityDeclaration withConcerns( Class<?>... concerns ) + { + for( EntityAssemblyImpl entity : entities ) + { + entity.concerns.addAll( asList( concerns ) ); + } + return this; + } + + @Override + public EntityDeclaration withSideEffects( Class<?>... sideEffects ) + { + for( EntityAssemblyImpl entity : entities ) + { + entity.sideEffects.addAll( asList( sideEffects ) ); + } + return this; + } + + @Override + public EntityDeclaration withMixins( Class<?>... mixins ) + { + for( EntityAssemblyImpl entity : entities ) + { + entity.mixins.addAll( asList( mixins ) ); + } + return this; + } + + @Override + public EntityDeclaration withTypes( Class<?>... types ) + { + for( EntityAssemblyImpl entity : entities ) + { + entity.types.addAll( asList( types ) ); + } + return this; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ImplementsMethodAppliesToFilter.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ImplementsMethodAppliesToFilter.java b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ImplementsMethodAppliesToFilter.java new file mode 100644 index 0000000..f7ec1ec --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ImplementsMethodAppliesToFilter.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2008, Rickard Ãberg. 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.apache.zest.runtime.bootstrap; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import org.apache.zest.api.common.AppliesToFilter; + +/** + * JAVADOC + */ +final class ImplementsMethodAppliesToFilter + implements AppliesToFilter +{ + @Override + public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> fragmentClass ) + { + try + { + return !Modifier.isAbstract( fragmentClass.getMethod( method.getName(), method.getParameterTypes() ) + .getModifiers() ); + } + catch( NoSuchMethodException e ) + { + return false; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ImportedServiceAssemblyImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ImportedServiceAssemblyImpl.java b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ImportedServiceAssemblyImpl.java new file mode 100644 index 0000000..a07b4f8 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ImportedServiceAssemblyImpl.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2007, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2012, Paul Merlin. + * + * 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.apache.zest.runtime.bootstrap; + +import java.util.ArrayList; +import java.util.List; +import org.apache.zest.api.activation.Activator; +import org.apache.zest.api.common.InvalidApplicationException; +import org.apache.zest.api.common.MetaInfo; +import org.apache.zest.api.common.Visibility; +import org.apache.zest.api.service.ServiceImporter; +import org.apache.zest.api.service.importer.InstanceImporter; +import org.apache.zest.bootstrap.ImportedServiceAssembly; +import org.apache.zest.functional.Iterables; +import org.apache.zest.runtime.activation.ActivatorsModel; +import org.apache.zest.runtime.service.ImportedServiceModel; + +/** + * Declaration of an imported Service. + * + * Created by {@link org.apache.zest.runtime.bootstrap.ModuleAssemblyImpl#importedServices(Class[])}. + */ +public final class ImportedServiceAssemblyImpl + implements ImportedServiceAssembly +{ + private final Class<?> serviceType; + private final ModuleAssemblyImpl moduleAssembly; + @SuppressWarnings( "raw" ) + Class<? extends ServiceImporter> serviceProvider = InstanceImporter.class; + String identity; + boolean importOnStartup = false; + MetaInfo metaInfo = new MetaInfo(); + Visibility visibility = Visibility.module; + List<Class<? extends Activator<?>>> activators = new ArrayList<>(); + + public ImportedServiceAssemblyImpl( Class<?> serviceType, ModuleAssemblyImpl moduleAssembly ) + { + this.serviceType = serviceType; + this.moduleAssembly = moduleAssembly; + } + + @Override + public Iterable<Class<?>> types() + { + return Iterables.<Class<?>>iterable( serviceType ); + } + + @SuppressWarnings( {"raw", "unchecked"} ) + void addImportedServiceModel( List<ImportedServiceModel> serviceModels ) + { + try + { + String id = identity; + if( id == null ) + { + id = generateId( serviceModels, serviceType ); + } + + ImportedServiceModel serviceModel = new ImportedServiceModel( serviceType, + visibility, + serviceProvider, + id, + importOnStartup, + new MetaInfo( metaInfo ).withAnnotations( serviceType ), + new ActivatorsModel( activators ), + moduleAssembly.name() ); + serviceModels.add( serviceModel ); + } + catch( Exception e ) + { + throw new InvalidApplicationException( "Could not register " + serviceType.getName(), e ); + } + } + + @SuppressWarnings( "raw" ) + private String generateId( List<ImportedServiceModel> serviceModels, Class serviceType ) + { + // Find identity that is not yet used + int idx = 0; + String id = serviceType.getSimpleName(); + boolean invalid; + do + { + invalid = false; + for( ImportedServiceModel serviceModel : serviceModels ) + { + if( serviceModel.identity().equals( id ) ) + { + idx++; + id = serviceType.getSimpleName() + "_" + idx; + invalid = true; + break; + } + } + } + while( invalid ); + return id; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ImportedServiceDeclarationImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ImportedServiceDeclarationImpl.java b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ImportedServiceDeclarationImpl.java new file mode 100644 index 0000000..d1263e1 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/bootstrap/ImportedServiceDeclarationImpl.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2007, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2012, Paul Merlin. + * + * 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.apache.zest.runtime.bootstrap; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.apache.zest.api.activation.Activator; +import org.apache.zest.api.common.Visibility; +import org.apache.zest.api.service.ServiceImporter; +import org.apache.zest.api.service.qualifier.ServiceTags; +import org.apache.zest.bootstrap.ImportedServiceDeclaration; + +/** + * Declaration of an imported Service. + */ +public final class ImportedServiceDeclarationImpl + implements ImportedServiceDeclaration +{ + private final Iterable<ImportedServiceAssemblyImpl> assemblies; + + public ImportedServiceDeclarationImpl( Iterable<ImportedServiceAssemblyImpl> assemblies ) + { + this.assemblies = assemblies; + } + + @Override + public ImportedServiceDeclaration importOnStartup() + { + for( ImportedServiceAssemblyImpl assembly : assemblies ) + { + assembly.importOnStartup = true; + } + return this; + } + + @Override + public ImportedServiceDeclaration visibleIn( Visibility visibility ) + { + for( ImportedServiceAssemblyImpl assembly : assemblies ) + { + assembly.visibility = visibility; + } + return this; + } + + @Override + @SuppressWarnings( "raw" ) + public ImportedServiceDeclaration importedBy( Class<? extends ServiceImporter> sip ) + { + for( ImportedServiceAssemblyImpl assembly : assemblies ) + { + assembly.serviceProvider = sip; + } + return this; + } + + @Override + public ImportedServiceDeclaration identifiedBy( String identity ) + { + for( ImportedServiceAssemblyImpl assembly : assemblies ) + { + assembly.identity = identity; + } + return this; + } + + @Override + public ImportedServiceDeclaration taggedWith( String... tags ) + { + for( ImportedServiceAssemblyImpl serviceAssembly : assemblies ) + { + ServiceTags previousTags = serviceAssembly.metaInfo.get( ServiceTags.class ); + if( previousTags != null ) + { + List<String> tagList = new ArrayList<>(); + Collections.addAll( tagList, previousTags.tags() ); + Collections.addAll( tagList, tags ); + serviceAssembly.metaInfo.set( new ServiceTags( tagList.toArray( new String[ tagList.size() ] ) ) ); + } + else + { + serviceAssembly.metaInfo.set( new ServiceTags( tags ) ); + } + } + + return this; + } + + @Override + public ImportedServiceDeclaration setMetaInfo( Object serviceAttribute ) + { + for( ImportedServiceAssemblyImpl assembly : assemblies ) + { + assembly.metaInfo.set( serviceAttribute ); + } + return this; + } + + @Override + @SafeVarargs + public final ImportedServiceDeclaration withActivators( Class<? extends Activator<?>>... activators ) + { + for ( ImportedServiceAssemblyImpl serviceAssembly : assemblies ) { + serviceAssembly.activators.addAll( Arrays.asList( activators ) ); + } + return this; + } +} \ No newline at end of file
