http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityModel.java b/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityModel.java new file mode 100644 index 0000000..aa45b18 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityModel.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2008, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2008, 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.entity; + +import java.lang.reflect.Method; +import org.apache.zest.api.association.AssociationDescriptor; +import org.apache.zest.api.common.ConstructionException; +import org.apache.zest.api.common.MetaInfo; +import org.apache.zest.api.common.Visibility; +import org.apache.zest.api.composite.CompositeInstance; +import org.apache.zest.api.constraint.ConstraintViolationException; +import org.apache.zest.api.entity.EntityDescriptor; +import org.apache.zest.api.entity.EntityReference; +import org.apache.zest.api.entity.Identity; +import org.apache.zest.api.entity.Queryable; +import org.apache.zest.api.property.PropertyDescriptor; +import org.apache.zest.api.property.StateHolder; +import org.apache.zest.api.unitofwork.EntityCompositeAlreadyExistsException; +import org.apache.zest.api.util.Annotations; +import org.apache.zest.functional.Iterables; +import org.apache.zest.runtime.composite.CompositeMethodsModel; +import org.apache.zest.runtime.composite.CompositeModel; +import org.apache.zest.runtime.property.PropertyModel; +import org.apache.zest.runtime.structure.ModuleUnitOfWork; +import org.apache.zest.spi.entity.EntityState; +import org.apache.zest.spi.entitystore.EntityAlreadyExistsException; +import org.apache.zest.spi.entitystore.EntityStoreException; +import org.apache.zest.spi.entitystore.EntityStoreUnitOfWork; +import org.apache.zest.spi.module.ModuleSpi; + +import static org.apache.zest.functional.Iterables.filter; +import static org.apache.zest.functional.Iterables.first; +import static org.apache.zest.functional.Iterables.flattenIterables; +import static org.apache.zest.functional.Iterables.map; + +/** + * JAVADOC + */ +public final class EntityModel + extends CompositeModel + implements EntityDescriptor +{ + private static final Method IDENTITY_METHOD; + + static + { + try + { + IDENTITY_METHOD = Identity.class.getMethod( "identity" ); + } + catch( NoSuchMethodException e ) + { + throw new InternalError( "Zest Core Runtime codebase is corrupted. Contact Zest team: ModuleUnitOfWork" ); + } + } + + private final boolean queryable; + + public EntityModel( Iterable<Class<?>> types, + Visibility visibility, + MetaInfo info, + EntityMixinsModel mixinsModel, + EntityStateModel stateModel, + CompositeMethodsModel compositeMethodsModel + ) + { + super( types, visibility, info, mixinsModel, stateModel, compositeMethodsModel ); + + final Queryable queryable = first( Iterables.<Queryable>cast( + filter( Annotations.isType( Queryable.class ), + flattenIterables( map( Annotations.ANNOTATIONS_OF, types ) ) ) ) ); + this.queryable = queryable == null || queryable.value(); + } + + @Override + public boolean queryable() + { + return queryable; + } + + @Override + public EntityStateModel state() + { + return (EntityStateModel) super.state(); + } + + public EntityInstance newInstance( ModuleUnitOfWork uow, ModuleSpi moduleInstance, EntityState state ) + { + EntityInstance instance = new EntityInstance( uow, moduleInstance, this, state ); + return instance; + } + + public Object[] newMixinHolder() + { + return mixinsModel.newMixinHolder(); + } + + public Object newMixin( Object[] mixins, + EntityStateInstance entityState, + EntityInstance entityInstance, + Method method + ) + { + return ( (EntityMixinsModel) mixinsModel ).newMixin( entityInstance, entityState, mixins, method ); + } + + public EntityState newEntityState( EntityStoreUnitOfWork store, ModuleSpi module, EntityReference identity ) + throws ConstraintViolationException, EntityStoreException + { + try + { + // New EntityState + EntityState entityState = store.newEntityState( module, identity, this ); + + // Set identity property + PropertyDescriptor persistentPropertyDescriptor = state().propertyModelFor( IDENTITY_METHOD ); + entityState.setPropertyValue( persistentPropertyDescriptor.qualifiedName(), identity.identity() ); + + return entityState; + } + catch( EntityAlreadyExistsException e ) + { + throw new EntityCompositeAlreadyExistsException( identity ); + } + catch( EntityStoreException e ) + { + throw new ConstructionException( "Could not create new entity in store", e ); + } + } + + public void initState( ModuleSpi module, EntityState entityState ) + { + // Set new properties to default value + for( PropertyModel propertyDescriptor : state().properties() ) + { + entityState.setPropertyValue( propertyDescriptor.qualifiedName(), propertyDescriptor.initialValue( module ) ); + } + + // Set new associations to null + for( AssociationDescriptor associationDescriptor : state().associations() ) + { + entityState.setAssociationValue( associationDescriptor.qualifiedName(), null ); + } + + // Set new many-associations to empty + for( AssociationDescriptor associationDescriptor : state().manyAssociations() ) + { + entityState.manyAssociationValueOf( associationDescriptor.qualifiedName() ); + } + + // Set new named-associations to empty + for( AssociationDescriptor associationDescriptor : state().namedAssociations() ) + { + entityState.namedAssociationValueOf( associationDescriptor.qualifiedName() ); + } + } + + public void invokeLifecycle( boolean create, Object[] mixins, CompositeInstance instance, StateHolder state ) + { + ( (EntityMixinsModel) mixinsModel ).invokeLifecycle( create, mixins, instance, state ); + } +}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityPropertyInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityPropertyInstance.java b/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityPropertyInstance.java new file mode 100644 index 0000000..5ac473f --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityPropertyInstance.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2007, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2008, Edward Yakop. 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.entity; + +import org.apache.zest.runtime.property.PropertyInfo; +import org.apache.zest.runtime.property.PropertyInstance; +import org.apache.zest.spi.entity.EntityState; + +/** + * {@code EntityPropertyInstance} represents a property whose value must be backed by an EntityState. + */ +public class EntityPropertyInstance<T> + extends PropertyInstance<T> +{ + private final EntityState entityState; + + /** + * Construct an instance of {@code PropertyInstance} with the specified arguments. + * + * @param aPropertyInfo The property info. This argument must not be {@code null}. + * @param entityState EntityState + */ + @SuppressWarnings( "unchecked" ) + public EntityPropertyInstance( PropertyInfo aPropertyInfo, EntityState entityState ) + { + super( aPropertyInfo, (T) entityState.propertyValueOf( aPropertyInfo.qualifiedName() ) ); + this.entityState = entityState; + } + + /** + * Sets this property value. + * + * @param aNewValue The new value. + */ + @Override + public void set( T aNewValue ) + { + super.set( aNewValue ); + entityState.setPropertyValue( model.qualifiedName(), aNewValue ); + } +} \ 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/entity/EntityStateInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityStateInstance.java b/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityStateInstance.java new file mode 100644 index 0000000..fe528b3 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityStateInstance.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2008-2011, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2008-2013, Niclas Hedhman. 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.entity; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import org.apache.zest.api.association.Association; +import org.apache.zest.api.association.AssociationDescriptor; +import org.apache.zest.api.association.AssociationStateHolder; +import org.apache.zest.api.association.ManyAssociation; +import org.apache.zest.api.association.NamedAssociation; +import org.apache.zest.api.entity.EntityReference; +import org.apache.zest.api.property.Property; +import org.apache.zest.api.property.PropertyDescriptor; +import org.apache.zest.api.unitofwork.UnitOfWork; +import org.apache.zest.api.util.Classes; +import org.apache.zest.functional.Function; +import org.apache.zest.functional.Function2; +import org.apache.zest.functional.Iterables; +import org.apache.zest.runtime.association.AssociationInstance; +import org.apache.zest.runtime.association.AssociationModel; +import org.apache.zest.runtime.association.ManyAssociationInstance; +import org.apache.zest.runtime.association.ManyAssociationModel; +import org.apache.zest.runtime.association.NamedAssociationInstance; +import org.apache.zest.runtime.association.NamedAssociationModel; +import org.apache.zest.runtime.composite.ConstraintsCheck; +import org.apache.zest.runtime.property.PropertyModel; +import org.apache.zest.runtime.unitofwork.BuilderEntityState; +import org.apache.zest.spi.entity.EntityState; + +/** + * TODO + */ +public final class EntityStateInstance + implements AssociationStateHolder +{ + private Map<AccessibleObject, Object> state; + + private final EntityStateModel stateModel; + private EntityState entityState; + private final Function2<EntityReference, Type, Object> entityFunction; + + public EntityStateInstance( EntityStateModel stateModel, final UnitOfWork uow, EntityState entityState ) + { + this.stateModel = stateModel; + this.entityState = entityState; + + entityFunction = new Function2<EntityReference, Type, Object>() + { + @Override + public Object map( EntityReference entityReference, Type type ) + { + return uow.get( Classes.RAW_CLASS.map( type ), entityReference.identity() ); + } + }; + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> Property<T> propertyFor( AccessibleObject accessor ) + throws IllegalArgumentException + { + Map<AccessibleObject, Object> state = state(); + + Property<T> property = (Property<T>) state.get( accessor ); + + if( property == null ) + { + PropertyModel entityPropertyModel = stateModel.propertyModelFor( accessor ); + property = new EntityPropertyInstance<>( + entityState instanceof BuilderEntityState + ? entityPropertyModel.getBuilderInfo() + : entityPropertyModel, + entityState ); + state.put( accessor, property ); + } + + return property; + } + + @Override + public Iterable<Property<?>> properties() + { + return Iterables.map( new Function<PropertyDescriptor, Property<?>>() + { + @Override + public Property<?> map( PropertyDescriptor propertyDescriptor ) + { + return propertyFor( propertyDescriptor.accessor() ); + } + }, stateModel.properties() ); + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> Association<T> associationFor( AccessibleObject accessor ) + throws IllegalArgumentException + { + Map<AccessibleObject, Object> state = state(); + Association<T> association = (Association<T>) state.get( accessor ); + + if( association == null ) + { + final AssociationModel associationModel = stateModel.getAssociation( accessor ); + association = new AssociationInstance<>( + entityState instanceof BuilderEntityState + ? associationModel.getBuilderInfo() + : associationModel, + entityFunction, + new Property<EntityReference>() + { + @Override + public EntityReference get() + { + return entityState.associationValueOf( associationModel.qualifiedName() ); + } + + @Override + public void set( EntityReference newValue ) + throws IllegalArgumentException, IllegalStateException + { + entityState.setAssociationValue( associationModel.qualifiedName(), newValue ); + } + } ); + state.put( accessor, association ); + } + + return association; + } + + @Override + public Iterable<Association<?>> allAssociations() + { + return Iterables.map( new Function<AssociationDescriptor, Association<?>>() + { + @Override + public Association<?> map( AssociationDescriptor associationDescriptor ) + { + return associationFor( associationDescriptor.accessor() ); + } + }, stateModel.associations() ); + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> ManyAssociation<T> manyAssociationFor( AccessibleObject accessor ) + { + Map<AccessibleObject, Object> state = state(); + + ManyAssociation<T> manyAssociation = (ManyAssociation<T>) state.get( accessor ); + + if( manyAssociation == null ) + { + ManyAssociationModel associationModel = stateModel.getManyAssociation( accessor ); + manyAssociation = new ManyAssociationInstance<>( + entityState instanceof BuilderEntityState + ? associationModel.getBuilderInfo() + : associationModel, + entityFunction, + entityState.manyAssociationValueOf( associationModel.qualifiedName() ) ); + state.put( accessor, manyAssociation ); + } + + return manyAssociation; + } + + @Override + public Iterable<ManyAssociation<?>> allManyAssociations() + { + return Iterables.map( new Function<AssociationDescriptor, ManyAssociation<?>>() + { + @Override + public ManyAssociation<?> map( AssociationDescriptor associationDescriptor ) + { + return manyAssociationFor( associationDescriptor.accessor() ); + } + }, stateModel.manyAssociations() ); + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> NamedAssociation<T> namedAssociationFor( AccessibleObject accessor ) + { + Map<AccessibleObject, Object> state = state(); + + NamedAssociation<T> namedAssociation = (NamedAssociation<T>) state.get( accessor ); + + if( namedAssociation == null ) + { + NamedAssociationModel associationModel = stateModel.getNamedAssociation( accessor ); + namedAssociation = new NamedAssociationInstance<>( + entityState instanceof BuilderEntityState + ? associationModel.getBuilderInfo() + : associationModel, + entityFunction, + entityState.namedAssociationValueOf( associationModel.qualifiedName() ) ); + state.put( accessor, namedAssociation ); + } + + return namedAssociation; + } + + @Override + public Iterable<? extends NamedAssociation<?>> allNamedAssociations() + { + return Iterables.map( new Function<AssociationDescriptor, NamedAssociation<?>>() + { + @Override + public NamedAssociation<?> map( AssociationDescriptor associationDescriptor ) + { + return namedAssociationFor( associationDescriptor.accessor() ); + } + }, stateModel.namedAssociations() ); + } + + public void checkConstraints() + { + for( PropertyDescriptor propertyDescriptor : stateModel.properties() ) + { + ConstraintsCheck constraints = (ConstraintsCheck) propertyDescriptor; + Property<Object> property = this.propertyFor( propertyDescriptor.accessor() ); + constraints.checkConstraints( property.get() ); + } + + for( AssociationDescriptor associationDescriptor : stateModel.associations() ) + { + ConstraintsCheck constraints = (ConstraintsCheck) associationDescriptor; + Association<Object> association = this.associationFor( associationDescriptor.accessor() ); + constraints.checkConstraints( association.get() ); + } + + // TODO Should ManyAssociations and NamedAssociations be checked too? + } + + private Map<AccessibleObject, Object> state() + { + if( state == null ) + { + state = new HashMap<>(); + } + + return state; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityStateModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityStateModel.java b/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityStateModel.java new file mode 100644 index 0000000..8b9b56e --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/entity/EntityStateModel.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2008-2011, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2008-2013, Niclas Hedhman. 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.entity; + +import java.lang.reflect.AccessibleObject; +import org.apache.zest.api.association.AssociationDescriptor; +import org.apache.zest.api.association.AssociationStateDescriptor; +import org.apache.zest.api.common.QualifiedName; +import org.apache.zest.functional.HierarchicalVisitor; +import org.apache.zest.functional.VisitableHierarchy; +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.StateModel; +import org.apache.zest.runtime.property.PropertiesModel; + +/** + * Model for EntityComposite state. + */ +public final class EntityStateModel + extends StateModel + implements AssociationStateDescriptor +{ + private final AssociationsModel associationsModel; + private final ManyAssociationsModel manyAssociationsModel; + private final NamedAssociationsModel namedAssociationsModel; + + public EntityStateModel( PropertiesModel propertiesModel, + AssociationsModel associationsModel, + ManyAssociationsModel manyAssociationsModel, + NamedAssociationsModel namedAssociationsModel ) + { + super( propertiesModel ); + this.associationsModel = associationsModel; + this.manyAssociationsModel = manyAssociationsModel; + this.namedAssociationsModel = namedAssociationsModel; + } + + public AssociationModel getAssociation( AccessibleObject accessor ) + throws IllegalArgumentException + { + return associationsModel.getAssociation( accessor ); + } + + @Override + public AssociationDescriptor getAssociationByName( String name ) + throws IllegalArgumentException + { + return associationsModel.getAssociationByName( name ); + } + + @Override + public AssociationDescriptor getAssociationByQualifiedName( QualifiedName name ) + throws IllegalArgumentException + { + return associationsModel.getAssociationByQualifiedName( name ); + } + + public ManyAssociationModel getManyAssociation( AccessibleObject accessor ) + throws IllegalArgumentException + { + return manyAssociationsModel.getManyAssociation( accessor ); + } + + @Override + public AssociationDescriptor getManyAssociationByName( String name ) + throws IllegalArgumentException + { + return manyAssociationsModel.getManyAssociationByName( name ); + } + + @Override + public AssociationDescriptor getManyAssociationByQualifiedName( QualifiedName name ) + throws IllegalArgumentException + { + return manyAssociationsModel.getManyAssociationByQualifiedName( name ); + } + + public NamedAssociationModel getNamedAssociation( AccessibleObject accessor ) + throws IllegalArgumentException + { + return namedAssociationsModel.getNamedAssociation( accessor ); + } + + @Override + public AssociationDescriptor getNamedAssociationByName( String name ) + throws IllegalArgumentException + { + return namedAssociationsModel.getNamedAssociationByName( name ); + } + + @Override + public AssociationDescriptor getNamedAssociationByQualifiedName( QualifiedName name ) + throws IllegalArgumentException + { + return namedAssociationsModel.getNamedAssociationByQualifiedName( name ); + } + + @Override + public Iterable<AssociationModel> associations() + { + return associationsModel.associations(); + } + + @Override + public Iterable<ManyAssociationModel> manyAssociations() + { + return manyAssociationsModel.manyAssociations(); + } + + @Override + public Iterable<NamedAssociationModel> namedAssociations() + { + return namedAssociationsModel.namedAssociations(); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> visitor ) + throws ThrowableType + { + if( visitor.visitEnter( this ) ) + { + if( ( (VisitableHierarchy<Object, Object>) propertiesModel ).accept( visitor ) ) + { + if( ( (VisitableHierarchy<AssociationsModel, AssociationModel>) associationsModel ).accept( visitor ) ) + { + if( ( (VisitableHierarchy<ManyAssociationsModel, ManyAssociationModel>) manyAssociationsModel ).accept( visitor ) ) + { + ( (VisitableHierarchy<NamedAssociationsModel, NamedAssociationModel>) namedAssociationsModel ).accept( visitor ); + } + } + } + } + return visitor.visitLeave( this ); + } + +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/Dependencies.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/Dependencies.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/Dependencies.java new file mode 100644 index 0000000..f2b3cf5 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/Dependencies.java @@ -0,0 +1,38 @@ +/* + * 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.injection; + +import org.apache.zest.functional.Function; + +/** + * TODO + */ +public interface Dependencies +{ + public static Function<Dependencies, Iterable<DependencyModel>> DEPENDENCIES_FUNCTION = new Function<Dependencies, Iterable<DependencyModel>>() + { + @Override + public Iterable<DependencyModel> map( Dependencies dependencies ) + { + return dependencies.dependencies(); + } + }; + + Iterable<DependencyModel> dependencies(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/DependencyModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/DependencyModel.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/DependencyModel.java new file mode 100644 index 0000000..8ceecba --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/DependencyModel.java @@ -0,0 +1,412 @@ +/* + * 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.injection; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Collections; +import org.apache.zest.api.common.ConstructionException; +import org.apache.zest.api.common.Optional; +import org.apache.zest.api.composite.DependencyDescriptor; +import org.apache.zest.bootstrap.BindingException; +import org.apache.zest.bootstrap.InvalidInjectionException; +import org.apache.zest.functional.Function; +import org.apache.zest.functional.Iterables; +import org.apache.zest.functional.Specification; +import org.apache.zest.functional.Visitable; +import org.apache.zest.functional.Visitor; +import org.apache.zest.runtime.injection.provider.InjectionProviderException; +import org.apache.zest.runtime.model.Binder; +import org.apache.zest.runtime.model.Resolution; + +import static org.apache.zest.api.util.Annotations.isType; +import static org.apache.zest.functional.Iterables.iterable; + +/** + * JAVADOC + * move all the extraction code to a TypeUtils class + */ +public final class DependencyModel + implements Binder, DependencyDescriptor, Visitable<DependencyModel> +{ + public static boolean isOptional( Annotation injectionAnnotation, Annotation[] annotations ) + { + if( Iterables.matchesAny( isType( Optional.class ), iterable( annotations ) ) ) + { + return true; + } + + Method[] methods = injectionAnnotation.annotationType().getMethods(); + for( Method method : methods ) + { + if( method.getName().equals( "optional" ) ) + { + try + { + return (Boolean) method.invoke( injectionAnnotation ); + } + catch( Throwable e ) + { + return false; + } + } + } + + return false; + } + + // Model + private final Annotation injectionAnnotation; + private final Type injectionType; + private final Class<?> injectedClass; + private final Class<?> rawInjectionClass; + private final boolean optional; + private final Annotation[] annotations; + + // Binding + private InjectionProvider injectionProvider; + + public DependencyModel( Annotation injectionAnnotation, + Type genericType, + Class<?> injectedClass, + boolean optional, + Annotation[] annotations + ) + { + this.injectionAnnotation = injectionAnnotation; + this.injectedClass = injectedClass; + + this.injectionType = genericType; + this.optional = optional; + this.annotations = annotations; + this.rawInjectionClass = mapPrimitiveTypes( extractRawInjectionClass( injectedClass, injectionType ) ); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( Visitor<? super DependencyModel, ThrowableType> visitor ) + throws ThrowableType + { + return visitor.visit( this ); + } + + private Class<?> extractRawInjectionClass( Class<?> injectedClass, final Type injectionType ) + { + // Calculate raw injection type + if( injectionType instanceof Class ) + { + return (Class<?>) injectionType; + } + else if( injectionType instanceof ParameterizedType ) + { + return (Class<?>) ( (ParameterizedType) injectionType ).getRawType(); + } + else if( injectionType instanceof TypeVariable ) + { + return extractRawInjectionClass( injectedClass, (TypeVariable<?>) injectionType ); + } + throw new IllegalArgumentException( + "Could not extract the rawInjectionClass of " + injectedClass + " and " + injectionType ); + } + + private Class<?> extractRawInjectionClass( Class<?> injectedClass, TypeVariable<?> injectionTypeVariable ) + { + int index = 0; + for( TypeVariable<?> typeVariable : injectionTypeVariable.getGenericDeclaration().getTypeParameters() ) + { + if( injectionTypeVariable.getName().equals( typeVariable.getName() ) ) + { + return (Class<?>) getActualType( injectedClass, index ); + } + index++; + } + throw new IllegalArgumentException( + "Could not extract the rawInjectionClass of " + injectedClass + " and " + injectionTypeVariable ); + } + + // todo continue refactoring + + private Type getActualType( Class<?> injectedClass, int index ) + { + // Type index found - map it to actual type + Type genericType = injectedClass; + Type type = null; + + while( !Object.class.equals( genericType ) && type == null ) + { + genericType = ( (Class<?>) genericType ).getGenericSuperclass(); + if( genericType instanceof ParameterizedType ) + { + type = ( (ParameterizedType) genericType ).getActualTypeArguments()[ index ]; + } + else + { + Type[] genericInterfaces = ( (Class<?>) genericType ).getGenericInterfaces(); + if( genericInterfaces.length > index ) + { + type = genericInterfaces[ index ]; + if( type instanceof ParameterizedType ) + { + type = ( (ParameterizedType) type ).getActualTypeArguments()[ index ]; + } + // TODO type may still be one of the generic interfaces??? + } + } + } + + if( type == null ) + { + type = Object.class; // Generic type with no constraints so Object is fine + } + + return type; + } + + // FIXME This method is unused, remove it. + private Type extractDependencyType( Type injectionType ) + { + if( injectionType instanceof ParameterizedType ) + { + return ( (ParameterizedType) injectionType ).getActualTypeArguments()[ 0 ]; + } + else if( injectionType instanceof TypeVariable ) + { + return ( (TypeVariable) injectionType ).getBounds()[ 0 ]; + } + return injectionType; + } + + // Model + @Override + public Annotation injectionAnnotation() + { + return injectionAnnotation; + } + + @Override + public Type injectionType() + { + return injectionType; + } + + @Override + public Class<?> injectedClass() + { + return injectedClass; + } + + /** + * Get the raw dependency type. + * <p> + * If the dependency uses generics this is the raw type, + * and otherwise it is the type of the field. + * <p> + * Examples: + * <p> + * {@code @Service MyService service} -> MyService + * <p> + * {@code @Entity Iterable<Foo> fooList} -> Iterable + * <p> + * {@code @Entity Query<Foo> fooQuery} -> Query + * + * @return raw injection type. + */ + @Override + public Class<?> rawInjectionType() + { + return rawInjectionClass; + } + + @Override + public boolean optional() + { + return optional; + } + + @Override + public Annotation[] annotations() + { + return annotations; + } + + @Override + public void bind( Resolution resolution ) + throws BindingException + { + InjectionProviderFactory providerFactory = resolution.application().injectionProviderFactory(); + + try + { + injectionProvider = providerFactory.newInjectionProvider( resolution, this ); + + if( injectionProvider == null && !optional ) + { + String message = + "[Module " + resolution.module() + .name() + "] Non-optional @" + rawInjectionClass.getName() + " was not bound in " + injectedClass + .getName(); + throw new ConstructionException( message ); + } + } + catch( InvalidInjectionException e ) + { + throw new BindingException( "Could not bind dependency injection", e ); + } + } + + // Context + public Object inject( InjectionContext context ) + { + if( injectionProvider == null ) + { + return null; + } + Object injectedValue; + try + { + injectedValue = injectionProvider.provideInjection( context ); + } + catch( InjectionProviderException e ) + { + Throwable ex = e; + if( ex.getCause() != null ) + { + ex = ex.getCause(); + } + + String message = "[Module " + context.module().name() + "] InjectionProvider unable to resolve @" + + injectionAnnotation.annotationType().getSimpleName() + " " + injectionType.toString(); + throw new ConstructionException( message, ex ); + } + if( injectedValue == null && !optional ) + { + String simpleName = injectionAnnotation.annotationType().getSimpleName(); + String message = "[Module " + context.module().name() + "] Non-optional @" + + simpleName + " " + injectionType.toString() + + " was null in " + injectedClass.getName(); + if( simpleName.toLowerCase().contains( "service" ) ) + { + message = message + ". Did you mean the @Service injection scope?"; + } + throw new ConstructionException( message ); + } + return getInjectedValue( injectedValue ); + } + + @SuppressWarnings( "unchecked" ) + private Object getInjectedValue( Object injectionResult ) + { + if( injectionResult == null ) + { + return null; + } + + if( injectionResult instanceof Iterable ) + { + if( Iterable.class.isAssignableFrom( rawInjectionClass ) || rawInjectionClass.isInstance( + injectionResult ) ) + { + return injectionResult; + } + else + { + return Iterables.first( (Iterable) injectionResult ); + } + } + else + { + if( Iterable.class.equals( injectionType ) ) + { + return Collections.singleton( injectionResult ); + } + } + return injectionResult; + } + + private final static Class<?>[] primitiveTypeMapping = { + boolean.class, Boolean.class, + byte.class, Byte.class, + short.class, Short.class, + char.class, Character.class, + long.class, Long.class, + double.class, Double.class, + float.class, Float.class, + int.class, Integer.class, + }; + + private Class<?> mapPrimitiveTypes( Class<?> rawInjectionType ) + { + if( rawInjectionType == null || !rawInjectionType.isPrimitive() ) + { + return rawInjectionType; + } + for( int i = 0; i < primitiveTypeMapping.length; i += 2 ) + { + if( primitiveTypeMapping[ i ].equals( rawInjectionType ) ) + { + return primitiveTypeMapping[ i + 1 ]; + } + } + return rawInjectionType; + } + + public boolean hasScope( final Class<? extends Annotation> scope ) + { + return scope == null || scope.equals( injectionAnnotation().annotationType() ); + } + + public Class<? extends Annotation> injectionAnnotationType() + { + if( injectionAnnotation == null ) + { + return null; + } + return injectionAnnotation.annotationType(); + } + + @Override + public String toString() + { + return injectionAnnotation + " for " + injectionType + " in " + injectedClass.getName(); + } + + public static class ScopeSpecification + implements Specification<DependencyModel> + { + private final Class<? extends Annotation> scope; + + public ScopeSpecification( Class<? extends Annotation> scope ) + { + this.scope = scope; + } + + @Override + public boolean satisfiedBy( DependencyModel model ) + { + return model.hasScope( scope ); + } + } + + public static class InjectionTypeFunction + implements Function<DependencyModel, Class<?>> + { + @Override + public Class<?> map( DependencyModel dependencyModel ) + { + return dependencyModel.rawInjectionType(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedFieldModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedFieldModel.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedFieldModel.java new file mode 100644 index 0000000..60977a5 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedFieldModel.java @@ -0,0 +1,147 @@ +/* + * 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.injection; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.Collection; +import org.apache.zest.api.composite.InjectedFieldDescriptor; +import org.apache.zest.api.util.Classes; +import org.apache.zest.bootstrap.BindingException; +import org.apache.zest.bootstrap.InjectionException; +import org.apache.zest.functional.HierarchicalVisitor; +import org.apache.zest.functional.Specification; +import org.apache.zest.functional.VisitableHierarchy; +import org.apache.zest.runtime.composite.TransientInstance; +import org.apache.zest.runtime.model.Resolution; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singleton; + +/** + * JAVADOC + */ +public final class InjectedFieldModel + implements InjectedFieldDescriptor, VisitableHierarchy<InjectedFieldModel, DependencyModel> +{ + private DependencyModel dependencyModel; + private Field injectedField; + + public InjectedFieldModel( Field injectedField, DependencyModel dependencyModel ) + { + injectedField.setAccessible( true ); + this.injectedField = injectedField; + this.dependencyModel = dependencyModel; + } + + @Override + public DependencyModel dependency() + { + return dependencyModel; + } + + @Override + public Field field() + { + return injectedField; + } + + public void bind( Resolution resolution ) + throws BindingException + { + dependencyModel.bind( resolution.forField( injectedField ) ); + } + + public void inject( InjectionContext context, Object instance ) + { + Object value = dependencyModel.inject( context ); + try + { + injectedField.set( instance, value ); + } + catch( IllegalAccessException e ) + { + throw new InjectionException( e ); + } + catch( IllegalArgumentException e ) + { + String valueClassName; + if( Proxy.isProxyClass( value.getClass() ) ) + { + InvocationHandler invocationHandler = Proxy.getInvocationHandler( value ); + if( invocationHandler instanceof TransientInstance ) + { + TransientInstance handler = (TransientInstance) invocationHandler; + valueClassName = Classes.toString( handler.descriptor().types() ) + + " in [" + handler.module().name() + "] of [" + handler.layer().name() + "]"; + } + else + { + valueClassName = invocationHandler.toString(); + } + } + else + { + valueClassName = value.getClass().getName(); + } + StringBuilder annotBuilder = new StringBuilder(); + for( Annotation annot : injectedField.getAnnotations() ) + { + String s = annot.toString(); + annotBuilder.append( "@" ).append( s.substring( s.lastIndexOf( '.' ) + 1, s.length() - 2 ) ); + annotBuilder.append( " " ); + } + String annots = annotBuilder.toString(); + String message = "Can not inject the field\n " + + injectedField.getDeclaringClass() + + "\n {\n " + annots + "\n " + + injectedField.getType().getSimpleName() + " " + injectedField.getName() + + "\n }\nwith value \n " + value + "\nof type\n " + + valueClassName; + throw new InjectionException( message, e ); + } + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super InjectedFieldModel, ? super DependencyModel, ThrowableType> visitor ) + throws ThrowableType + { + if( visitor.visitEnter( this ) ) + { + visitor.visit( dependencyModel ); + } + return visitor.visitLeave( this ); + } + + public Collection<DependencyModel> filter( Specification<DependencyModel> specification ) + { + if( specification.satisfiedBy( dependencyModel ) ) + { + return singleton( dependencyModel ); + } + else + { + return emptyList(); + } + } + + @Override + public String toString() + { + return "InjectedFieldModel{" + ", injectedField=" + injectedField + '}'; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedFieldsModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedFieldsModel.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedFieldsModel.java new file mode 100644 index 0000000..e61bc39 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedFieldsModel.java @@ -0,0 +1,124 @@ +/* + * 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.injection; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.List; +import org.apache.zest.api.injection.InjectionScope; +import org.apache.zest.api.util.Classes; +import org.apache.zest.api.util.Fields; +import org.apache.zest.functional.Function; +import org.apache.zest.functional.HierarchicalVisitor; +import org.apache.zest.functional.Iterables; +import org.apache.zest.functional.VisitableHierarchy; + +import static org.apache.zest.api.util.Annotations.hasAnnotation; +import static org.apache.zest.api.util.Annotations.type; +import static org.apache.zest.functional.Iterables.filter; +import static org.apache.zest.functional.Iterables.first; +import static org.apache.zest.functional.Iterables.iterable; +import static org.apache.zest.functional.Specifications.translate; + +/** + * JAVADOC + */ +public final class InjectedFieldsModel + implements Dependencies, VisitableHierarchy<Object, Object> +{ + private final List<InjectedFieldModel> fields = new ArrayList<InjectedFieldModel>(); + + public InjectedFieldsModel( Class fragmentClass ) + { + Iterable<Field> mappedFields = Fields.FIELDS_OF.map( fragmentClass ); + for( Field field : mappedFields ) + { + Annotation injectionAnnotation = first( filter( translate( type(), hasAnnotation( InjectionScope.class ) ), iterable( field + .getAnnotations() ) ) ); + if( injectionAnnotation != null ) + { + addModel( fragmentClass, field, injectionAnnotation ); + } + } + } + + private void addModel( Class fragmentClass, Field field, Annotation injectionAnnotation ) + { + Type genericType = field.getGenericType(); + if( genericType instanceof ParameterizedType ) + { + genericType = new ParameterizedTypeInstance( ( (ParameterizedType) genericType ).getActualTypeArguments(), ( (ParameterizedType) genericType ) + .getRawType(), ( (ParameterizedType) genericType ).getOwnerType() ); + + for( int i = 0; i < ( (ParameterizedType) genericType ).getActualTypeArguments().length; i++ ) + { + Type type = ( (ParameterizedType) genericType ).getActualTypeArguments()[ i ]; + if( type instanceof TypeVariable ) + { + type = Classes.resolveTypeVariable( (TypeVariable) type, field.getDeclaringClass(), fragmentClass ); + ( (ParameterizedType) genericType ).getActualTypeArguments()[ i ] = type; + } + } + } + + boolean optional = DependencyModel.isOptional( injectionAnnotation, field.getAnnotations() ); + DependencyModel dependencyModel = new DependencyModel( injectionAnnotation, genericType, fragmentClass, optional, field + .getAnnotations() ); + InjectedFieldModel injectedFieldModel = new InjectedFieldModel( field, dependencyModel ); + this.fields.add( injectedFieldModel ); + } + + @Override + public Iterable<DependencyModel> dependencies() + { + return Iterables.map( new Function<InjectedFieldModel, DependencyModel>() + { + @Override + public DependencyModel map( InjectedFieldModel injectedFieldModel ) + { + return injectedFieldModel.dependency(); + } + }, fields ); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + if( modelVisitor.visitEnter( this ) ) + { + for( InjectedFieldModel field : fields ) + { + if( !field.accept( modelVisitor ) ) + { + break; + } + } + } + return modelVisitor.visitLeave( this ); + } + + public void inject( InjectionContext context, Object instance ) + { + for( InjectedFieldModel field : fields ) + { + field.inject( context, instance ); + } + } +} \ 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/injection/InjectedMethodModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedMethodModel.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedMethodModel.java new file mode 100644 index 0000000..b0ec660 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedMethodModel.java @@ -0,0 +1,86 @@ +/* + * 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.injection; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import org.apache.zest.api.composite.InjectedMethodDescriptor; +import org.apache.zest.bootstrap.InjectionException; +import org.apache.zest.functional.HierarchicalVisitor; +import org.apache.zest.functional.VisitableHierarchy; + +/** + * JAVADOC + */ +public final class InjectedMethodModel + implements InjectedMethodDescriptor, Dependencies, VisitableHierarchy<Object, Object> +{ + // Model + private Method method; + private InjectedParametersModel parameters; + + public InjectedMethodModel( Method method, InjectedParametersModel parameters ) + { + this.method = method; + this.method.setAccessible( true ); + this.parameters = parameters; + } + + @Override + public Method method() + { + return method; + } + + @Override + public Iterable<DependencyModel> dependencies() + { + return parameters.dependencies(); + } + + // Context + public void inject( InjectionContext context, Object instance ) + throws InjectionException + { + Object[] params = parameters.newParametersInstance( context ); + try + { + if( !method.isAccessible() ) + { + method.setAccessible( true ); + } + method.invoke( instance, params ); + } + catch( IllegalAccessException e ) + { + throw new InjectionException( e ); + } + catch( InvocationTargetException e ) + { + throw new InjectionException( e.getTargetException() ); + } + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> visitor ) + throws ThrowableType + { + if( visitor.visitEnter( this ) ) + { + parameters.accept( visitor ); + } + return visitor.visitLeave( this ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedMethodsModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedMethodsModel.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedMethodsModel.java new file mode 100644 index 0000000..82121a4 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedMethodsModel.java @@ -0,0 +1,124 @@ +/* + * 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.injection; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.List; +import org.apache.zest.api.injection.InjectionScope; +import org.apache.zest.api.util.Annotations; +import org.apache.zest.api.util.Classes; +import org.apache.zest.api.util.Methods; +import org.apache.zest.functional.HierarchicalVisitor; +import org.apache.zest.functional.Specifications; +import org.apache.zest.functional.VisitableHierarchy; + +import static org.apache.zest.api.util.Annotations.hasAnnotation; +import static org.apache.zest.functional.Iterables.filter; +import static org.apache.zest.functional.Iterables.first; +import static org.apache.zest.functional.Iterables.flattenIterables; +import static org.apache.zest.functional.Iterables.iterable; +import static org.apache.zest.functional.Iterables.map; + +/** + * JAVADOC + */ +public final class InjectedMethodsModel + implements Dependencies, VisitableHierarchy<Object, Object> +{ + // Model + private final List<InjectedMethodModel> methodModels = new ArrayList<InjectedMethodModel>(); + + public InjectedMethodsModel( Class fragmentClass ) + { + nextMethod: + for( Method method : Methods.METHODS_OF.map( fragmentClass ) ) + { + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); + if( parameterAnnotations.length > 0 ) + { + InjectedParametersModel parametersModel = new InjectedParametersModel(); + final Type[] genericParameterTypes = method.getGenericParameterTypes(); + for( int i = 0; i < parameterAnnotations.length; i++ ) + { + Annotation injectionAnnotation = first( filter( Specifications.translate( Annotations.type(), hasAnnotation( InjectionScope.class ) ), iterable( parameterAnnotations[ i ] ) ) ); + if( injectionAnnotation == null ) + { + continue nextMethod; + } + + Type genericType = genericParameterTypes[ i ]; + if( genericType instanceof ParameterizedType ) + { + genericType = new ParameterizedTypeInstance( ( (ParameterizedType) genericType ).getActualTypeArguments(), ( (ParameterizedType) genericType ) + .getRawType(), ( (ParameterizedType) genericType ).getOwnerType() ); + + for( int j = 0; j < ( (ParameterizedType) genericType ).getActualTypeArguments().length; j++ ) + { + Type type = ( (ParameterizedType) genericType ).getActualTypeArguments()[ j ]; + if( type instanceof TypeVariable ) + { + type = Classes.resolveTypeVariable( (TypeVariable) type, method.getDeclaringClass(), fragmentClass ); + ( (ParameterizedType) genericType ).getActualTypeArguments()[ j ] = type; + } + } + } + + boolean optional = DependencyModel.isOptional( injectionAnnotation, parameterAnnotations[ i ] ); + DependencyModel dependencyModel = new DependencyModel( injectionAnnotation, genericType, fragmentClass, optional, parameterAnnotations[ i ] ); + parametersModel.addDependency( dependencyModel ); + } + InjectedMethodModel methodModel = new InjectedMethodModel( method, parametersModel ); + methodModels.add( methodModel ); + } + } + } + + @Override + public Iterable<DependencyModel> dependencies() + { + return flattenIterables( map( Dependencies.DEPENDENCIES_FUNCTION, methodModels ) ); + } + + // Context + public void inject( InjectionContext context, Object instance ) + { + for( InjectedMethodModel methodModel : methodModels ) + { + methodModel.inject( context, instance ); + } + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> visitor ) + throws ThrowableType + { + if( visitor.visitEnter( this ) ) + { + for( InjectedMethodModel methodModel : methodModels ) + { + if( !methodModel.accept( visitor ) ) + { + break; + } + } + } + return visitor.visitLeave( 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/injection/InjectedParametersModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedParametersModel.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedParametersModel.java new file mode 100644 index 0000000..2bb8360 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectedParametersModel.java @@ -0,0 +1,103 @@ +/* + * 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.injection; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.apache.zest.api.composite.InjectedParametersDescriptor; +import org.apache.zest.functional.HierarchicalVisitor; +import org.apache.zest.functional.Specification; +import org.apache.zest.functional.VisitableHierarchy; + +/** + * JAVADOC + */ +public final class InjectedParametersModel + implements InjectedParametersDescriptor, Dependencies, VisitableHierarchy<Object, Object> +{ + private final List<DependencyModel> parameterDependencies; + + public InjectedParametersModel() + { + parameterDependencies = new ArrayList<DependencyModel>(); + } + + @Override + public Iterable<DependencyModel> dependencies() + { + return parameterDependencies; + } + + // Context + public Object[] newParametersInstance( InjectionContext context ) + { + Object[] parametersInstance = new Object[ parameterDependencies.size() ]; + + // Inject parameterDependencies + for( int j = 0; j < parameterDependencies.size(); j++ ) + { + DependencyModel dependencyModel = parameterDependencies.get( j ); + Object parameter = dependencyModel.inject( context ); + parametersInstance[ j ] = parameter; + } + + return parametersInstance; + } + + public void addDependency( DependencyModel dependency ) + { + parameterDependencies.add( dependency ); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> visitor ) + throws ThrowableType + { + if( visitor.visitEnter( this ) ) + { + for( DependencyModel parameterDependency : parameterDependencies ) + { + if( !visitor.visit( parameterDependency ) ) + { + break; + } + } + } + return visitor.visitLeave( this ); + } + + public Collection<DependencyModel> filter( Specification<DependencyModel> specification ) + { + ArrayList<DependencyModel> result = new ArrayList<DependencyModel>(); + for( DependencyModel model : parameterDependencies ) + { + if( specification.satisfiedBy( model ) ) + { + result.add( model ); + } + } + return result; + } + + @Override + public String toString() + { + return "InjectedParametersModel{" + + "parameterDependencies=" + parameterDependencies + + '}'; + } +} + http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectionContext.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectionContext.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectionContext.java new file mode 100644 index 0000000..f9cefac --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectionContext.java @@ -0,0 +1,121 @@ +/* + * 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.injection; + +import org.apache.zest.api.composite.CompositeInstance; +import org.apache.zest.api.property.StateHolder; +import org.apache.zest.runtime.composite.ProxyReferenceInvocationHandler; +import org.apache.zest.runtime.composite.UsesInstance; +import org.apache.zest.runtime.structure.ModuleInstance; +import org.apache.zest.spi.module.ModuleSpi; + +/** + * JAVADOC + */ +public final class InjectionContext +{ + private final ModuleSpi moduleInstance; + private CompositeInstance compositeInstance; + private UsesInstance uses; + private StateHolder state; + private Object next; // Only used for concerns and side-effects + private ProxyReferenceInvocationHandler proxyHandler; + private Object instance; // Only used for inner classes + + // For mixins + + public InjectionContext( CompositeInstance compositeInstance, UsesInstance uses, StateHolder state ) + { + this.moduleInstance = (ModuleInstance) compositeInstance.module(); + this.compositeInstance = compositeInstance; + this.uses = uses; + this.state = state; + } + + // For concerns and side-effects + public InjectionContext( ModuleSpi moduleInstance, Object next, ProxyReferenceInvocationHandler proxyHandler ) + { + this.moduleInstance = moduleInstance; + this.next = next; + this.proxyHandler = proxyHandler; + } + + public InjectionContext( ModuleSpi moduleInstance, UsesInstance uses ) + { + this.moduleInstance = moduleInstance; + this.uses = uses; + } + + // For inner classes + public InjectionContext( ModuleSpi moduleInstance, UsesInstance uses, Object instance ) + { + this.moduleInstance = moduleInstance; + this.uses = uses; + this.instance = instance; + } + + public ModuleSpi module() + { + return moduleInstance; + } + + public CompositeInstance compositeInstance() + { + return compositeInstance; + } + + public UsesInstance uses() + { + return uses; + } + + public StateHolder state() + { + return state; + } + + public Object next() + { + return next; + } + + public Object instance() + { + return instance; + } + + public ProxyReferenceInvocationHandler proxyHandler() + { + return proxyHandler; + } + + public void setUses( UsesInstance uses ) + { + this.uses = uses; + } + + @Override + public String toString() + { + return "InjectionContext{" + + "compositeInstance=" + compositeInstance + + ", module=" + moduleInstance + + ", uses=" + uses + + ", state=" + state + + ", next=" + next + + ", proxyHandler=" + proxyHandler + + '}'; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectionProvider.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectionProvider.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectionProvider.java new file mode 100644 index 0000000..f69c24e --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectionProvider.java @@ -0,0 +1,26 @@ +/* + * 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.injection; + +import org.apache.zest.runtime.injection.provider.InjectionProviderException; + +/** + * JAVADOC + */ +public interface InjectionProvider +{ + Object provideInjection( InjectionContext context ) + throws InjectionProviderException; +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectionProviderFactory.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectionProviderFactory.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectionProviderFactory.java new file mode 100644 index 0000000..a993367 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/InjectionProviderFactory.java @@ -0,0 +1,37 @@ +/* + * 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.injection; + +import org.apache.zest.bootstrap.InvalidInjectionException; +import org.apache.zest.runtime.model.Resolution; + +/** + * JAVADOC + */ +public interface InjectionProviderFactory +{ + /** + * Binding a dependency given an injection resolution. If no binding + * can be found, return null. If the dependency is optional the dependency will + * then be explicitly set to null. + * + * @param resolution Injection resolution + * @param dependencyModel Dependency model + * @return InjectionProvider + * @throws InvalidInjectionException if the injection is invalid + */ + InjectionProvider newInjectionProvider( Resolution resolution, DependencyModel dependencyModel ) + throws InvalidInjectionException; +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/ParameterizedTypeInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/ParameterizedTypeInstance.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/ParameterizedTypeInstance.java new file mode 100644 index 0000000..6a161b0 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/ParameterizedTypeInstance.java @@ -0,0 +1,65 @@ +/* + * 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.injection; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; + +/** + * TODO + */ +public class ParameterizedTypeInstance + implements ParameterizedType +{ + private Type[] actualTypeArguments; + private Type rawType; + private Type ownerType; + + public ParameterizedTypeInstance( Type[] actualTypeArguments, Type rawType, Type ownerType ) + { + this.actualTypeArguments = actualTypeArguments; + this.rawType = rawType; + this.ownerType = ownerType; + } + + @Override + public Type[] getActualTypeArguments() + { + return actualTypeArguments; + } + + @Override + public Type getRawType() + { + return rawType; + } + + @Override + public Type getOwnerType() + { + return ownerType; + } + + @Override + public String toString() + { + return rawType.toString() + Arrays.asList( actualTypeArguments ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/CachingInjectionProviderDecorator.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/CachingInjectionProviderDecorator.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/CachingInjectionProviderDecorator.java new file mode 100644 index 0000000..300e1d7 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/CachingInjectionProviderDecorator.java @@ -0,0 +1,57 @@ +/* + * 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.injection.provider; + +import org.apache.zest.runtime.injection.InjectionContext; +import org.apache.zest.runtime.injection.InjectionProvider; + +/** + * If a dependency resolution should be a singleton, wrap it with this + * to provide a single instance "cache". + */ +public final class CachingInjectionProviderDecorator + implements InjectionProvider +{ + private final InjectionProvider decoratedProvider; + private volatile Object singletonInstance; + + public CachingInjectionProviderDecorator( InjectionProvider injectionProvider ) + { + this.decoratedProvider = injectionProvider; + } + + public InjectionProvider decoratedProvider() + { + return decoratedProvider; + } + + @Override + public Object provideInjection( InjectionContext context ) + throws InjectionProviderException + { + if( singletonInstance == null ) + { + synchronized( this ) + { + if( singletonInstance == null ) + { + singletonInstance = decoratedProvider.provideInjection( context ); + } + } + } + + return singletonInstance; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/CachingInjectionProviderFactoryDecorator.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/CachingInjectionProviderFactoryDecorator.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/CachingInjectionProviderFactoryDecorator.java new file mode 100644 index 0000000..cdb4f5a --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/CachingInjectionProviderFactoryDecorator.java @@ -0,0 +1,50 @@ +/* + * 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.injection.provider; + +import org.apache.zest.bootstrap.InvalidInjectionException; +import org.apache.zest.runtime.injection.DependencyModel; +import org.apache.zest.runtime.injection.InjectionProvider; +import org.apache.zest.runtime.injection.InjectionProviderFactory; +import org.apache.zest.runtime.model.Resolution; + +/** + * JAVADOC + */ +public class CachingInjectionProviderFactoryDecorator + implements InjectionProviderFactory +{ + private final InjectionProviderFactory decoratedFactory; + + public CachingInjectionProviderFactoryDecorator( InjectionProviderFactory decoratedFactory ) + { + this.decoratedFactory = decoratedFactory; + } + + @Override + public InjectionProvider newInjectionProvider( Resolution resolution, DependencyModel dependencyModel ) + throws InvalidInjectionException + { + InjectionProvider injectionProvider = decoratedFactory.newInjectionProvider( resolution, dependencyModel ); + if( injectionProvider != null ) + { + return new CachingInjectionProviderDecorator( injectionProvider ); + } + else + { + return null; + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/InjectionProviderException.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/InjectionProviderException.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/InjectionProviderException.java new file mode 100644 index 0000000..abdae00 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/InjectionProviderException.java @@ -0,0 +1,32 @@ +/* + * 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.injection.provider; + +/** + * JAVADOC + */ +public class InjectionProviderException + extends RuntimeException +{ + public InjectionProviderException( String string ) + { + super( string ); + } + + public InjectionProviderException( String string, Throwable throwable ) + { + super( string, throwable ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/InjectionProviderFactoryStrategy.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/InjectionProviderFactoryStrategy.java b/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/InjectionProviderFactoryStrategy.java new file mode 100644 index 0000000..8d87bb9 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/injection/provider/InjectionProviderFactoryStrategy.java @@ -0,0 +1,102 @@ +/* + * 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.injection.provider; + +import java.lang.annotation.Annotation; +import java.util.HashMap; +import java.util.Map; +import org.apache.zest.api.common.MetaInfo; +import org.apache.zest.api.composite.InvalidValueCompositeException; +import org.apache.zest.api.composite.ModelDescriptor; +import org.apache.zest.api.concern.internal.ConcernFor; +import org.apache.zest.api.injection.scope.Invocation; +import org.apache.zest.api.injection.scope.Service; +import org.apache.zest.api.injection.scope.State; +import org.apache.zest.api.injection.scope.Structure; +import org.apache.zest.api.injection.scope.This; +import org.apache.zest.api.injection.scope.Uses; +import org.apache.zest.api.sideeffect.internal.SideEffectFor; +import org.apache.zest.api.value.ValueComposite; +import org.apache.zest.bootstrap.InvalidInjectionException; +import org.apache.zest.runtime.injection.DependencyModel; +import org.apache.zest.runtime.injection.InjectionProvider; +import org.apache.zest.runtime.injection.InjectionProviderFactory; +import org.apache.zest.runtime.model.Resolution; + +import static org.apache.zest.functional.Iterables.first; + +/** + * JAVADOC + */ +public final class InjectionProviderFactoryStrategy + implements InjectionProviderFactory +{ + private final Map<Class<? extends Annotation>, InjectionProviderFactory> generalProviderFactories = new HashMap<Class<? extends Annotation>, InjectionProviderFactory>(); + private final Map<Class<? extends Annotation>, InjectionProviderFactory> valuesProviderFactories = new HashMap<Class<? extends Annotation>, InjectionProviderFactory>(); + private MetaInfo metaInfo; + + public InjectionProviderFactoryStrategy( MetaInfo metaInfo ) + { + this.metaInfo = metaInfo; + valuesProviderFactories.put( This.class, new ThisInjectionProviderFactory() ); + ModifiesInjectionProviderFactory modifiesInjectionProviderFactory = new ModifiesInjectionProviderFactory(); + valuesProviderFactories.put( ConcernFor.class, modifiesInjectionProviderFactory ); + valuesProviderFactories.put( SideEffectFor.class, modifiesInjectionProviderFactory ); + valuesProviderFactories.put( State.class, new StateInjectionProviderFactory() ); + + valuesProviderFactories.put( Structure.class, new CachingInjectionProviderFactoryDecorator( new StructureInjectionProviderFactory() ) ); + valuesProviderFactories.put( Service.class, new CachingInjectionProviderFactoryDecorator( new ServiceInjectionProviderFactory() ) ); + generalProviderFactories.put( Invocation.class, new InvocationInjectionProviderFactory() ); + generalProviderFactories.put( Uses.class, new UsesInjectionProviderFactory() ); + } + + @Override + public InjectionProvider newInjectionProvider( Resolution resolution, DependencyModel dependencyModel ) + throws InvalidInjectionException + { + Class<? extends Annotation> injectionAnnotationType = dependencyModel.injectionAnnotation().annotationType(); + InjectionProviderFactory factory1 = generalProviderFactories.get( injectionAnnotationType ); + InjectionProviderFactory factory2 = valuesProviderFactories.get( injectionAnnotationType ); + if( factory1 == null && factory2 == null ) + { + InjectionProviderFactory factory = metaInfo.get( InjectionProviderFactory.class ); + if( factory != null ) + { + return factory.newInjectionProvider( resolution, dependencyModel ); + } + else + { + throw new InvalidInjectionException( "Unknown injection annotation @" + injectionAnnotationType.getSimpleName() ); + } + } + ModelDescriptor composite = resolution.model(); + Class<?> compositeType = first( composite.types() ); + if( factory1 != null && ValueComposite.class.isAssignableFrom( compositeType ) ) + { + throw new InvalidValueCompositeException( "@" + injectionAnnotationType.getSimpleName() + " is not allowed in ValueComposites: " + compositeType ); + } + + InjectionProviderFactory factory; + if( factory1 == null ) + { + factory = factory2; + } + else + { + factory = factory1; + } + return factory.newInjectionProvider( resolution, dependencyModel ); + } +}
