http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/AssemblyHelper.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/AssemblyHelper.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/AssemblyHelper.java new file mode 100644 index 0000000..1263e29 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/AssemblyHelper.java @@ -0,0 +1,214 @@ +/* + * 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.polygene.runtime.bootstrap; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import org.apache.polygene.api.common.AppliesTo; +import org.apache.polygene.api.common.AppliesToFilter; +import org.apache.polygene.api.common.ConstructionException; +import org.apache.polygene.api.constraint.Constraint; +import org.apache.polygene.runtime.composite.ConcernModel; +import org.apache.polygene.runtime.composite.ConstraintDeclaration; +import org.apache.polygene.runtime.composite.FragmentClassLoader; +import org.apache.polygene.runtime.composite.MixinModel; +import org.apache.polygene.runtime.composite.SideEffectModel; + +/** + * This helper is used when building the application model. It keeps track + * of already created classloaders and various models + */ +@SuppressWarnings("WeakerAccess") +public class AssemblyHelper +{ + private Map<Class, Class> instantiationClasses = new HashMap<>(); + private Map<Class, ConstraintDeclaration> constraintDeclarations = new HashMap<>(); + private Map<ClassLoader, FragmentClassLoader> modifierClassLoaders = new HashMap<>(); + private Map<Class<?>, AppliesToFilter> appliesToInstances = new HashMap<>(); + + protected MixinModel getMixinModel(Class mixinClass) + { + return new MixinModel( mixinClass, instantiationClass( mixinClass ) ); + } + + protected ConcernModel getConcernModel(Class concernClass) + { + return new ConcernModel( concernClass, instantiationClass( concernClass ) ); + } + + protected SideEffectModel getSideEffectModel(Class sideEffectClass) + { + return new SideEffectModel( sideEffectClass, instantiationClass( sideEffectClass ) ); + } + + protected Class instantiationClass(Class fragmentClass) + { + Class instantiationClass = fragmentClass; + if( !InvocationHandler.class.isAssignableFrom( fragmentClass ) ) + { + instantiationClass = instantiationClasses.get( fragmentClass ); + + if( instantiationClass == null ) + { + try + { + FragmentClassLoader fragmentLoader = getModifierClassLoader( fragmentClass.getClassLoader() ); + instantiationClass = fragmentLoader.loadFragmentClass( fragmentClass ); + instantiationClasses.put( fragmentClass, instantiationClass ); + } + catch( ClassNotFoundException | VerifyError e ) + { + throw new ConstructionException( "Could not generate mixin subclass " + fragmentClass.getName(), e ); + } + } + } + return instantiationClass; + } + + protected FragmentClassLoader getModifierClassLoader( ClassLoader classLoader ) + { + FragmentClassLoader cl = modifierClassLoaders.get( classLoader ); + if( cl == null ) + { + cl = instantiateFragmentClassLoader( classLoader ); + modifierClassLoaders.put( classLoader, cl ); + } + return cl; + } + + protected FragmentClassLoader instantiateFragmentClassLoader( ClassLoader classLoader ) + { + return new FragmentClassLoader( classLoader ); + } + + public boolean appliesTo( Class<?> fragmentClass, Method method, Iterable<Class<?>> types, Class<?> mixinClass ) + { + AppliesToFilter appliesToFilter = appliesToInstances.get( fragmentClass ); + if( appliesToFilter == null ) + { + appliesToFilter = createAppliesToFilter( fragmentClass ); + appliesToInstances.put( fragmentClass, appliesToFilter ); + } + for( Class<?> compositeType : types ) + { + if( appliesToFilter.appliesTo( method, mixinClass, compositeType, fragmentClass ) ) + { + return true; + } + } + return false; + } + + protected AppliesToFilter createAppliesToFilter( Class<?> fragmentClass ) + { + AppliesToFilter result = null; + if( !InvocationHandler.class.isAssignableFrom( fragmentClass ) ) + { + result = new TypedFragmentAppliesToFilter(); + if( Modifier.isAbstract( fragmentClass.getModifiers() ) ) + { + result = new AndAppliesToFilter( result, new ImplementsMethodAppliesToFilter() ); + } + } + result = applyAppliesTo( result, fragmentClass ); + if( result == null ) + { + return AppliesToFilter.ALWAYS; + } + return result; + } + + protected AppliesToFilter applyAppliesTo( AppliesToFilter existing, Class<?> modifierClass ) + { + AppliesTo appliesTo = modifierClass.getAnnotation( AppliesTo.class ); + if( appliesTo != null ) + { + // Use "or" for all filters specified in the annotation + AppliesToFilter appliesToAnnotation = null; + for( Class<?> appliesToClass : appliesTo.value() ) + { + AppliesToFilter filter; + if( AppliesToFilter.class.isAssignableFrom( appliesToClass ) ) + { + try + { + @SuppressWarnings("unchecked") + Constructor<AppliesToFilter> cons = (Constructor<AppliesToFilter>) appliesToClass.getDeclaredConstructor(); + cons.setAccessible(true); + filter = cons.newInstance(); + } + catch( Exception e ) + { + throw new ConstructionException( e ); + } + } + else if( Annotation.class.isAssignableFrom( appliesToClass ) ) + { + filter = new AnnotationAppliesToFilter( appliesToClass ); + } + else // Type check + { + filter = new TypeCheckAppliesToFilter( appliesToClass ); + } + + if( appliesToAnnotation == null ) + { + appliesToAnnotation = filter; + } + else + { + appliesToAnnotation = new OrAppliesToFilter( appliesToAnnotation, filter ); + } + } + // Add to the rest of the rules using "and" + if( existing == null ) + { + return appliesToAnnotation; + } + else + { + return new AndAppliesToFilter( existing, appliesToAnnotation ); + } + } + return existing; + } + + public boolean appliesTo( Class<? extends Constraint<?, ?>> constraint, + Class<? extends Annotation> annotationType, + Type valueType + ) + { + ConstraintDeclaration constraintDeclaration = constraintDeclarations.get( constraint ); + if( constraintDeclaration == null ) + { + constraintDeclaration = new ConstraintDeclaration( constraint ); + constraintDeclarations.put( constraint, constraintDeclaration ); + } + + return constraintDeclaration.appliesTo( annotationType, valueType ); + } +}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/CompositeAssemblyImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/CompositeAssemblyImpl.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/CompositeAssemblyImpl.java new file mode 100644 index 0000000..1e0fb26 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/CompositeAssemblyImpl.java @@ -0,0 +1,909 @@ +/* + * 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.polygene.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.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.polygene.api.association.Association; +import org.apache.polygene.api.association.GenericAssociationInfo; +import org.apache.polygene.api.association.ManyAssociation; +import org.apache.polygene.api.association.NamedAssociation; +import org.apache.polygene.api.common.MetaInfo; +import org.apache.polygene.api.common.Optional; +import org.apache.polygene.api.common.QualifiedName; +import org.apache.polygene.api.common.UseDefaults; +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.api.composite.InvalidCompositeException; +import org.apache.polygene.api.concern.Concerns; +import org.apache.polygene.api.constraint.Constraint; +import org.apache.polygene.api.constraint.ConstraintDeclaration; +import org.apache.polygene.api.constraint.Constraints; +import org.apache.polygene.api.constraint.Name; +import org.apache.polygene.api.entity.Lifecycle; +import org.apache.polygene.api.injection.scope.State; +import org.apache.polygene.api.injection.scope.This; +import org.apache.polygene.api.mixin.Initializable; +import org.apache.polygene.api.mixin.Mixins; +import org.apache.polygene.api.property.GenericPropertyInfo; +import org.apache.polygene.api.property.Immutable; +import org.apache.polygene.api.property.Property; +import org.apache.polygene.api.sideeffect.SideEffects; +import org.apache.polygene.api.type.HasTypes; +import org.apache.polygene.api.util.Annotations; +import org.apache.polygene.api.util.Classes; +import org.apache.polygene.api.util.Fields; +import org.apache.polygene.api.util.HierarchicalVisitorAdapter; +import org.apache.polygene.bootstrap.StateDeclarations; +import org.apache.polygene.runtime.association.AssociationModel; +import org.apache.polygene.runtime.association.AssociationsModel; +import org.apache.polygene.runtime.association.ManyAssociationModel; +import org.apache.polygene.runtime.association.ManyAssociationsModel; +import org.apache.polygene.runtime.association.NamedAssociationModel; +import org.apache.polygene.runtime.association.NamedAssociationsModel; +import org.apache.polygene.runtime.composite.AbstractConstraintModel; +import org.apache.polygene.runtime.composite.CompositeConstraintModel; +import org.apache.polygene.runtime.composite.CompositeMethodModel; +import org.apache.polygene.runtime.composite.CompositeMethodsModel; +import org.apache.polygene.runtime.composite.ConcernModel; +import org.apache.polygene.runtime.composite.ConcernsModel; +import org.apache.polygene.runtime.composite.ConstraintModel; +import org.apache.polygene.runtime.composite.ConstraintsModel; +import org.apache.polygene.runtime.composite.Genericpredicate; +import org.apache.polygene.runtime.composite.MixinModel; +import org.apache.polygene.runtime.composite.MixinsModel; +import org.apache.polygene.runtime.composite.SideEffectModel; +import org.apache.polygene.runtime.composite.SideEffectsModel; +import org.apache.polygene.runtime.composite.StateModel; +import org.apache.polygene.runtime.composite.ValueConstraintsInstance; +import org.apache.polygene.runtime.composite.ValueConstraintsModel; +import org.apache.polygene.runtime.injection.Dependencies; +import org.apache.polygene.runtime.injection.DependencyModel; +import org.apache.polygene.runtime.property.PropertiesModel; +import org.apache.polygene.runtime.property.PropertyModel; + +import static java.util.stream.Stream.concat; +import static org.apache.polygene.api.util.Annotations.isType; +import static org.apache.polygene.api.util.Annotations.typeHasAnnotation; +import static org.apache.polygene.api.util.Classes.classHierarchy; +import static org.apache.polygene.api.util.Classes.interfacesOf; +import static org.apache.polygene.api.util.Classes.isAssignableFrom; +import static org.apache.polygene.api.util.Classes.typeOf; +import static org.apache.polygene.api.util.Classes.typesOf; +import static org.apache.polygene.api.util.Classes.wrapperClass; + +/** + * 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 Stream<Class<?>> types() + { + return types.stream(); + } + + 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(); + compositeMethodsModel = new CompositeMethodsModel( mixinsModel ); + + // Implement composite methods + List<Class<?>> constraintClasses = toList( constraintDeclarations( getAllTypes() ) ); + List<Class<?>> concernClasses = toList( concat( concerns.stream(), concernDeclarations( getAllTypes() ) ) ); + List<Class<?>> sideEffectClasses = toList( concat( sideEffects.stream(), sideEffectDeclarations( getAllTypes() ) ) ); + List<Class<?>> mixinClasses = toList( concat( mixins.stream(), mixinDeclarations( getAllTypes() ) ) ); + //noinspection unchecked + implementMixinType( types, + constraintClasses, + concernClasses, + sideEffectClasses, + mixinClasses + ); + + // Add state from methods and fields + //noinspection unchecked + addState( constraintClasses ); + } + + private List<Class<?>> toList( Stream<Class<?>> stream ) + { + return stream.collect( Collectors.toList() ); + } + + 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( List<? extends Class<?>> types, + List<Class<?>> constraintClasses, + List<Class<?>> concernClasses, + List<Class<?>> sideEffectClasses, + List<Class<?>> mixinClasses + ) + { + Set<Class<?>> thisDependencies = new HashSet<>(); + types.forEach( mixinType -> { + 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(), + concat( concernDeclarations( mixinModel.mixinClass() ), + concernClasses.stream() ) + ); + SideEffectsModel sideEffectsModel = sideEffectsFor( + method, + mixinModel.mixinClass(), + concat( sideEffectDeclarations( mixinModel.mixinClass() ), + sideEffectClasses.stream() ) + ); + method.setAccessible( true ); + ConstraintsModel constraints = constraintsFor( + method, + toList( concat( constraintDeclarations( mixinModel.mixinClass() ), + constraintClasses.stream() ) ) + ); + CompositeMethodModel methodComposite = new CompositeMethodModel( + method, + constraints, + concernsModel, + sideEffectsModel, + mixinsModel + ); + + Stream<? extends Dependencies> source = Stream.of( methodComposite, mixinModel ); + source.flatMap( Dependencies::dependencies ) + .filter( new DependencyModel.ScopeSpecification( This.class ) ) + .map( DependencyModel::rawInjectionType ) + .forEach( thisDependencies::add ); + + interfacesOf( mixinModel.mixinClass() ) + .map( Classes.RAW_CLASS ) + .filter( clazz -> Stream.of( Initializable.class, Lifecycle.class, InvocationHandler.class ) + .noneMatch( c -> c.equals( clazz ) ) ) + .forEach( thisDependencies::add ); + + compositeMethodsModel.addMethod( methodComposite ); + } + } + // Add type to set of mixin types + mixinsModel.addMixinType( mixinType ); + } ); + + // Implement all @This dependencies that were found + thisDependencies.forEach( thisDependency -> { + // Add additional declarations from the @This type + Stream<Class<?>> typeConstraintClasses = concat( + constraintClasses.stream(), + constraintDeclarations( thisDependency ) ); + Stream<Class<?>> typeConcernClasses = concat( + concernClasses.stream(), + concernDeclarations( thisDependency ) ); + Stream<Class<?>> typeSideEffectClasses = concat( + sideEffectClasses.stream(), + sideEffectDeclarations( thisDependency ) ); + Stream<Class<?>> typeMixinClasses = concat( + mixinClasses.stream(), + mixinDeclarations( thisDependency ) ); + List<? extends Class<?>> singleton = Collections.singletonList( thisDependency ); + implementMixinType( singleton, + toList( typeConstraintClasses ), + toList( typeConcernClasses ), + toList( typeSideEffectClasses ), + toList( typeMixinClasses ) + ); + } ); + } + + @SuppressWarnings( "raw" ) + protected MixinModel implementMethod( Method method, List<Class<?>> mixinDeclarations ) + { + MixinModel implementationModel = mixinsModel.mixinFor( method ); + if( implementationModel != null ) + { + return implementationModel; + } + Class mixinClass = findTypedImplementation( method, mixinDeclarations.stream() ); + if( mixinClass != null ) + { + return implementMethodWithClass( method, mixinClass ); + } + + // Check generic implementations + mixinClass = findGenericImplementation( method, mixinDeclarations.stream() ); + if( mixinClass != null ) + { + return implementMethodWithClass( method, mixinClass ); + } + + throw new InvalidCompositeException( "No implementation found for method \n " + method.toGenericString() + + "\nin\n " + types ); + } + + private Class<?> findTypedImplementation( final Method method, Stream<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. + Predicate<Class<?>> appliesToSpec = item -> helper.appliesTo( item, method, types, item ); + return mixins.filter( isAssignableFrom( method.getDeclaringClass() ) + .and( Genericpredicate.INSTANCE.or( appliesToSpec ) ) ) + .findFirst().orElse( null ); + } + + private Class<?> findGenericImplementation( final Method method, Stream<Class<?>> mixins ) + { + // Check if mixinClass is generic and the applies-to filter passes + Predicate<Class<?>> appliesToSpec = item -> helper.appliesTo( item, method, types, item ); + return mixins.filter( Genericpredicate.INSTANCE.and( appliesToSpec ) ).findFirst().orElse( null ); + } + + 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 List<Class<?>> 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; + Consumer<Field> addState = field -> addStateFor( field, constraintClasses ); + Fields.FIELDS_OF.apply( model.mixinClass() ) + .filter( Annotations.hasAnnotation( State.class ) ) + .forEach( addState ); + return false; + } + return super.visitEnter( visited ); + } + } ); + } + + protected void addStateFor( AccessibleObject accessor, List<Class<?>> constraintClasses ) + { + String stateName = QualifiedName.fromAccessor( accessor ).name(); + + if( registeredStateNames.contains( stateName ) ) + { + return; // Skip already registered names + } + + Class<?> accessorType = Classes.RAW_CLASS.apply( 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 ); + } + } + + protected AssociationsModel associationsModel() + { + return null; + } + + protected ManyAssociationsModel manyAssociationsModel() + { + return null; + } + + protected NamedAssociationsModel namedAssociationsModel() + { + return null; + } + + protected PropertyModel newPropertyModel( AccessibleObject accessor, + List<Class<?>> constraintClasses + ) + { + List<Annotation> annotations = Annotations.findAccessorAndTypeAnnotationsIn( accessor ); + boolean optional = annotations.stream().anyMatch( isType( Optional.class ) ); + ValueConstraintsModel valueConstraintsModel = constraintsFor( + annotations.stream(), + GenericPropertyInfo.propertyTypeOf( accessor ), + ( (Member) accessor ).getName(), + optional, + constraintClasses, + accessor ); + ValueConstraintsInstance valueConstraintsInstance = null; + if( valueConstraintsModel.isConstrained() ) + { + valueConstraintsInstance = valueConstraintsModel.newInstance(); + } + MetaInfo metaInfo = stateDeclarations.metaInfoFor( accessor ); + UseDefaults useDefaultsDeclaration = metaInfo.get( UseDefaults.class ); + Object initialValue = stateDeclarations.initialValueOf( accessor ); + if( initialValue == null && useDefaultsDeclaration != null ) + { + initialValue = useDefaultsDeclaration.value(); + } + boolean useDefaults = useDefaultsDeclaration != null || stateDeclarations.useDefaults( accessor ); + boolean immutable = this.immutable || metaInfo.get( Immutable.class ) != null; + return new PropertyModel( + accessor, + immutable, + useDefaults, + valueConstraintsInstance, + metaInfo, + initialValue + ); + } + + // Model + private ConstraintsModel constraintsFor( Method method, + List<Class<?>> 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) Stream.of( parameterAnnotation ).filter( isType( Name.class ) ) + .findFirst().orElse( null ); + String name = nameAnnotation == null ? "param" + ( i + 1 ) : nameAnnotation.value(); + + boolean optional = Stream.of( parameterAnnotation ).filter( isType( Optional.class ) ) + .findFirst().isPresent(); + ValueConstraintsModel parameterConstraintsModel = constraintsFor( + Arrays.stream( 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( + Stream<Annotation> constraintAnnotations, + Type valueType, + String name, + boolean optional, + Iterable<Class<?>> constraintClasses, + AccessibleObject accessor + ) + { + valueType = wrapperClass( valueType ); + + List<AbstractConstraintModel> constraintModels = new ArrayList<>(); + List<Annotation> filtered = constraintAnnotations + .filter( typeHasAnnotation( ConstraintDeclaration.class ) ) + .collect( Collectors.toList() ); + + // TODO: This massive block below should be cleaned up. + nextConstraint: + for( Annotation constraintAnnotation : filtered ) + { + // Check composite declarations first + Class<? extends Annotation> annotationType = constraintAnnotation.annotationType(); + for( Class<?> constraint : constraintClasses ) + { + Class<? extends Constraint<?, ?>> constraintType = (Class<? extends Constraint<?, ?>>) constraint; + if( helper.appliesTo( constraintType, annotationType, valueType ) ) + { + constraintModels.add( new ConstraintModel( constraintAnnotation, constraintType ) ); + 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 + if( Arrays.stream( annotationType.getAnnotations() ) + .anyMatch( typeHasAnnotation( ConstraintDeclaration.class ) ) ) + { + ValueConstraintsModel valueConstraintsModel = constraintsFor( + Arrays.stream( annotationType.getAnnotations() ), + valueType, + name, + optional, + constraintClasses, + accessor ); + CompositeConstraintModel compositeConstraintModel = new CompositeConstraintModel( + constraintAnnotation, + valueConstraintsModel ); + constraintModels.add( compositeConstraintModel ); + } + else + { + 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, + Stream<Class<?>> concernClasses + ) + { + List<ConcernModel> concernsFor = new ArrayList<>(); + concernClasses.forEach( concern -> { + if( helper.appliesTo( concern, method, types, mixinClass ) ) + { + addConcernOrRepositionIfExists( concernsFor, 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 ) ) + { + addConcernOrRepositionIfExists( concernsFor, 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 ) ) + { + ConcernModel concernModel = helper.getConcernModel( concern ); + addConcernOrRepositionIfExists( concernsFor, concernModel ); + } + } + } + } + + if( concernsFor.isEmpty() ) + { + return ConcernsModel.EMPTY_CONCERNS; + } + else + { + return new ConcernsModel( concernsFor ); + } + } + + private void addConcernOrRepositionIfExists( List<ConcernModel> concernsFor, ConcernModel concernModel ) + { + // This remove/add is to allow re-ordering of the concerns + concernsFor.remove( concernModel ); + concernsFor.add( concernModel ); + } + + private SideEffectsModel sideEffectsFor( Method method, + Class<?> mixinClass, + Stream<Class<?>> sideEffectClasses + ) + { + List<SideEffectModel> sideEffectsFor = new ArrayList<>(); + sideEffectClasses.forEach( sideEffect -> { + SideEffectModel sideEffectModel = helper.getSideEffectModel( sideEffect ); + if( helper.appliesTo( sideEffect, method, types, mixinClass ) ) + { + addSideEffectOrRepositionIfExists( sideEffectsFor, sideEffectModel ); + } + 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 ) ) + { + addSideEffectOrRepositionIfExists( sideEffectsFor, sideEffectModel ); + } + } + catch( NoSuchMethodException e ) + { + // Ignore + } + } + } + } ); + + // Check annotations on method that have @Concerns annotations themselves + for( Annotation annotation : method.getAnnotations() ) + { + @SuppressWarnings( "raw" ) + SideEffects sideEffects = annotation.annotationType().getAnnotation( SideEffects.class ); + if( sideEffects != null ) + { + for( Class<?> sideEffect : sideEffects.value() ) + { + if( helper.appliesTo( sideEffect, method, types, mixinClass ) ) + { + SideEffectModel sideEffectModel = helper.getSideEffectModel( sideEffect ); + addSideEffectOrRepositionIfExists( sideEffectsFor, sideEffectModel ); + } + } + } + } + + if( sideEffectsFor.isEmpty() ) + { + return SideEffectsModel.EMPTY_SIDEEFFECTS; + } + else + { + return new SideEffectsModel( sideEffectsFor ); + } + } + + private void addSideEffectOrRepositionIfExists( List<SideEffectModel> sideEffectsFor, + SideEffectModel sideEffectModel + ) + { + // This add/remove is to allow reording of SideEffects. + sideEffectsFor.remove( sideEffectModel ); + sideEffectsFor.add( sideEffectModel ); + } + + @SuppressWarnings( "unchecked" ) + private Stream<Class<?>> constraintDeclarations( Class<?> type ) + { + Stream<? extends Type> types = getTypes( type ); + return constraintDeclarations( types ); + } + + private Stream<Class<?>> constraintDeclarations( Stream<? extends Type> types ) + { + return types + .filter( mixinType -> Annotations.annotationOn( mixinType, Constraints.class ) != null ) + .flatMap( mixinType -> Arrays.stream( Annotations.annotationOn( mixinType, Constraints.class ).value() ) ); + } + + @SuppressWarnings( "unchecked" ) + private Stream<Class<?>> concernDeclarations( Class<?> type ) + { + Stream<? extends Type> types = getTypes( type ); + return concernDeclarations( types ); + } + + private Stream<Class<?>> concernDeclarations( Stream<? extends Type> types ) + { + return types + .filter( mixinType -> Annotations.annotationOn( mixinType, Concerns.class ) != null ) + .flatMap( new Function<Type, Stream<? extends Class<?>>>() + { + @Override + public Stream<? extends Class<?>> apply( Type mixinType ) + { + return Arrays.stream( Annotations.annotationOn( mixinType, Concerns.class ).value() ); + } + } ); + } + + @SuppressWarnings( "unchecked" ) + protected Stream<Class<?>> sideEffectDeclarations( Class<?> type ) + { + Stream<? extends Type> types = getTypes( type ); + return sideEffectDeclarations( types ); + } + + private Stream<Class<?>> sideEffectDeclarations( Stream<? extends Type> types ) + { + return types + .filter( mixinType -> Annotations.annotationOn( mixinType, SideEffects.class ) != null ) + .flatMap( mixinType -> Arrays.stream( Annotations.annotationOn( mixinType, SideEffects.class ).value() ) ); + } + + protected Stream<Class<?>> mixinDeclarations( Class<?> type ) + { + //Stream<? extends Type> types = typesOf( type ); + return mixinDeclarations( Stream.of( type ) ); + } + + private Stream<Class<?>> mixinDeclarations( Stream<? extends Class> types ) + { + return types.flatMap( this::getTypes ).flatMap( Classes::typesOf ) + .filter( mixinType -> Annotations.annotationOn( mixinType, Mixins.class ) != null ) + .flatMap( mixinType -> Arrays.stream( Annotations.annotationOn( mixinType, Mixins.class ).value() ) ); + } + + private Stream<Class> getAllTypes() + { + return this.types.stream().flatMap( this::getTypes ); + } + + private Stream<Class> getTypes( Class<?> clazz ) + { + if( clazz.isInterface() ) + { + return typesOf( clazz ).map( Classes.RAW_CLASS ); + } + else + { + return classHierarchy( clazz ).map( Classes.RAW_CLASS ); + } + } + + public AssociationModel newAssociationModel( AccessibleObject accessor, + List<Class<?>> constraintClasses + ) + { + List<Annotation> annotations = Annotations.findAccessorAndTypeAnnotationsIn( accessor ); + boolean optional = annotations.stream().anyMatch( isType( Optional.class ) ); + + // Constraints for Association references + ValueConstraintsModel constraintsModel = constraintsFor( annotations.stream(), GenericAssociationInfo + .associationTypeOf( accessor ), ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); + ValueConstraintsInstance valueConstraintsInstance; + if( constraintsModel.isConstrained() ) + { + valueConstraintsInstance = constraintsModel.newInstance(); + } + else + { + valueConstraintsInstance = new ValueConstraintsInstance( Collections.emptyList(), ( (Member) accessor ).getName(), true ); + } + + // Constraints for the Association itself + constraintsModel = constraintsFor( annotations.stream(), Association.class, ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); + ValueConstraintsInstance associationValueConstraintsInstance; + if( constraintsModel.isConstrained() ) + { + associationValueConstraintsInstance = constraintsModel.newInstance(); + } + else + { + associationValueConstraintsInstance = new ValueConstraintsInstance( Collections.emptyList(), ( (Member) accessor ).getName(), true ); + } + + MetaInfo metaInfo = stateDeclarations.metaInfoFor( accessor ); + return new AssociationModel( accessor, valueConstraintsInstance, associationValueConstraintsInstance, metaInfo ); + } + + public ManyAssociationModel newManyAssociationModel( AccessibleObject accessor, + List<Class<?>> constraintClasses + ) + { + List<Annotation> annotations = Annotations.findAccessorAndTypeAnnotationsIn( accessor ); + boolean optional = annotations.stream().anyMatch( isType( Optional.class ) ); + + // Constraints for entities in ManyAssociation + ValueConstraintsModel valueConstraintsModel = constraintsFor( annotations.stream(), 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.stream(), ManyAssociation.class, ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); + ValueConstraintsInstance manyValueConstraintsInstance = null; + if( valueConstraintsModel.isConstrained() ) + { + manyValueConstraintsInstance = valueConstraintsModel.newInstance(); + } + MetaInfo metaInfo = stateDeclarations.metaInfoFor( accessor ); + return new ManyAssociationModel( accessor, valueConstraintsInstance, manyValueConstraintsInstance, metaInfo ); + } + + public NamedAssociationModel newNamedAssociationModel( AccessibleObject accessor, + List<Class<?>> constraintClasses + ) + { + List<Annotation> annotations = Annotations.findAccessorAndTypeAnnotationsIn( accessor ); + boolean optional = annotations.stream().anyMatch( isType( Optional.class ) ); + + // Constraints for entities in NamedAssociation + ValueConstraintsModel valueConstraintsModel = constraintsFor( annotations.stream(), 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.stream(), NamedAssociation.class, ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); + ValueConstraintsInstance namedValueConstraintsInstance = null; + if( valueConstraintsModel.isConstrained() ) + { + namedValueConstraintsInstance = valueConstraintsModel.newInstance(); + } + MetaInfo metaInfo = stateDeclarations.metaInfoFor( accessor ); + return new NamedAssociationModel( accessor, valueConstraintsInstance, namedValueConstraintsInstance, metaInfo ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ConfigurationAssemblyImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ConfigurationAssemblyImpl.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ConfigurationAssemblyImpl.java new file mode 100644 index 0000000..8bbee50 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ConfigurationAssemblyImpl.java @@ -0,0 +1,45 @@ +/* + * 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.polygene.runtime.bootstrap; + +import java.util.stream.Stream; +import org.apache.polygene.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 Stream<Class<?>> types() + { + return value.types(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ConfigurationDeclarationImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ConfigurationDeclarationImpl.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ConfigurationDeclarationImpl.java new file mode 100644 index 0000000..45c71d5 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ConfigurationDeclarationImpl.java @@ -0,0 +1,126 @@ +/* + * 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.polygene.runtime.bootstrap; + +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.bootstrap.ConfigurationDeclaration; + +import static java.util.Arrays.asList; + +/** + * Declaration of a Composite. Created by {@link org.apache.polygene.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/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/EntityAssemblyImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/EntityAssemblyImpl.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/EntityAssemblyImpl.java new file mode 100644 index 0000000..605084c --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/EntityAssemblyImpl.java @@ -0,0 +1,121 @@ +/* + * 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.polygene.runtime.bootstrap; + +import org.apache.polygene.api.common.InvalidApplicationException; +import org.apache.polygene.api.entity.EntityComposite; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.bootstrap.AssociationDeclarations; +import org.apache.polygene.bootstrap.EntityAssembly; +import org.apache.polygene.bootstrap.ManyAssociationDeclarations; +import org.apache.polygene.bootstrap.NamedAssociationDeclarations; +import org.apache.polygene.bootstrap.StateDeclarations; +import org.apache.polygene.runtime.association.AssociationsModel; +import org.apache.polygene.runtime.association.ManyAssociationsModel; +import org.apache.polygene.runtime.association.NamedAssociationsModel; +import org.apache.polygene.runtime.composite.MixinsModel; +import org.apache.polygene.runtime.composite.StateModel; +import org.apache.polygene.runtime.entity.EntityMixinsModel; +import org.apache.polygene.runtime.entity.EntityModel; +import org.apache.polygene.runtime.entity.EntityStateModel; + +/** + * 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 AssociationsModel associationsModel() + { + return associationsModel; + } + + @Override + protected ManyAssociationsModel manyAssociationsModel() + { + return manyAssociationsModel; + } + + @Override + protected NamedAssociationsModel namedAssociationsModel() + { + return namedAssociationsModel; + } + + @Override + protected StateModel createStateModel() + { + return new EntityStateModel( propertiesModel, associationsModel, manyAssociationsModel, namedAssociationsModel ); + } + + EntityModel newEntityModel( + ModuleDescriptor module, + 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 ); + + return new EntityModel( module, types, visibility, metaInfo, + (EntityMixinsModel) mixinsModel, + (EntityStateModel) stateModel, compositeMethodsModel ); + } + catch( Exception e ) + { + throw new InvalidApplicationException( "Could not register " + types, e ); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/EntityDeclarationImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/EntityDeclarationImpl.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/EntityDeclarationImpl.java new file mode 100644 index 0000000..ee8adf4 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/EntityDeclarationImpl.java @@ -0,0 +1,100 @@ +/* + * 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.polygene.runtime.bootstrap; + +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.bootstrap.EntityDeclaration; + +import static java.util.Arrays.asList; + +/** + * Declaration of a Composite. Created by {@link org.apache.polygene.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/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ImplementsMethodAppliesToFilter.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ImplementsMethodAppliesToFilter.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ImplementsMethodAppliesToFilter.java new file mode 100644 index 0000000..fd5b04c --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ImplementsMethodAppliesToFilter.java @@ -0,0 +1,46 @@ +/* + * 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.polygene.runtime.bootstrap; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import org.apache.polygene.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/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ImportedServiceAssemblyImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ImportedServiceAssemblyImpl.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ImportedServiceAssemblyImpl.java new file mode 100644 index 0000000..c455aed --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ImportedServiceAssemblyImpl.java @@ -0,0 +1,125 @@ +/* + * 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.polygene.runtime.bootstrap; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import org.apache.polygene.api.activation.Activator; +import org.apache.polygene.api.common.InvalidApplicationException; +import org.apache.polygene.api.common.MetaInfo; +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.api.identity.Identity; +import org.apache.polygene.api.identity.StringIdentity; +import org.apache.polygene.api.service.ServiceImporter; +import org.apache.polygene.api.service.importer.InstanceImporter; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.bootstrap.ImportedServiceAssembly; +import org.apache.polygene.runtime.activation.ActivatorsModel; +import org.apache.polygene.runtime.service.ImportedServiceModel; + +/** + * Declaration of an imported Service. + * + * Created by {@link org.apache.polygene.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 Stream<Class<?>> types() + { + return Stream.of( serviceType ); + } + + @SuppressWarnings( { "raw", "unchecked" } ) + void addImportedServiceModel( ModuleDescriptor module, List<ImportedServiceModel> serviceModels ) + { + try + { + Identity id; + if( identity == null ) + { + id = generateId( serviceModels, serviceType ); + } + else + { + id = new StringIdentity( identity ); + } + + ImportedServiceModel serviceModel = new ImportedServiceModel( module, + 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 Identity generateId( List<ImportedServiceModel> serviceModels, Class serviceType ) + { + // Find reference that is not yet used + int idx = 0; + Identity id = new StringIdentity( serviceType.getSimpleName() ); + boolean invalid; + do + { + invalid = false; + for( ImportedServiceModel serviceModel : serviceModels ) + { + if( serviceModel.identity().equals( id ) ) + { + idx++; + id = new StringIdentity( 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/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ImportedServiceDeclarationImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ImportedServiceDeclarationImpl.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ImportedServiceDeclarationImpl.java new file mode 100644 index 0000000..257414e --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ImportedServiceDeclarationImpl.java @@ -0,0 +1,128 @@ +/* + * 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.polygene.runtime.bootstrap; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.apache.polygene.api.activation.Activator; +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.api.service.ServiceImporter; +import org.apache.polygene.api.service.qualifier.ServiceTags; +import org.apache.polygene.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
