/* * 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.qi4j.runtime.bootstrap; import java.lang.annotation.Annotation; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Member; import org.qi4j.api.association.Association; import org.qi4j.api.association.GenericAssociationInfo; import org.qi4j.api.association.ManyAssociation; import org.qi4j.api.common.InvalidApplicationException; import org.qi4j.api.common.MetaInfo; import org.qi4j.api.common.Optional; import org.qi4j.api.common.QualifiedName; import org.qi4j.api.common.UseDefaults; import org.qi4j.api.constraint.Constraint; import org.qi4j.api.entity.EntityComposite; import org.qi4j.api.property.GenericPropertyInfo; import org.qi4j.api.property.Immutable; import org.qi4j.api.property.Property; import org.qi4j.api.util.Annotations; import org.qi4j.api.util.Classes; import org.qi4j.bootstrap.AssociationDeclarations; import org.qi4j.bootstrap.EntityAssembly; import org.qi4j.bootstrap.ManyAssociationDeclarations; import org.qi4j.bootstrap.StateDeclarations; import org.qi4j.runtime.association.AssociationModel; import org.qi4j.runtime.association.AssociationsModel; import org.qi4j.runtime.association.ManyAssociationModel; import org.qi4j.runtime.association.ManyAssociationsModel; import org.qi4j.runtime.composite.MixinsModel; import org.qi4j.runtime.composite.StateModel; import org.qi4j.runtime.composite.ValueConstraintsInstance; import org.qi4j.runtime.composite.ValueConstraintsModel; import org.qi4j.runtime.entity.EntityMixinsModel; import org.qi4j.runtime.entity.EntityModel; import org.qi4j.runtime.entity.EntityStateModel; import org.qi4j.runtime.property.PropertyModel; import static org.qi4j.api.util.Annotations.isType; import static org.qi4j.api.util.Classes.typeOf; import static org.qi4j.functional.Iterables.filter; import static org.qi4j.functional.Iterables.first; /** * Declaration of a EntityComposite. */ public final class EntityAssemblyImpl extends CompositeAssemblyImpl implements EntityAssembly { private AssociationDeclarations associationDeclarations; private ManyAssociationDeclarations manyAssociationDeclarations; protected AssociationsModel associationsModel; protected ManyAssociationsModel manyAssociationsModel; public EntityAssemblyImpl( Class<?> entityType ) { super( entityType ); // The composite must always implement EntityComposite, as a marker interface if( !EntityComposite.class.isAssignableFrom( entityType ) ) { types.add( EntityComposite.class ); } } @Override protected MixinsModel createMixinsModel() { return new EntityMixinsModel(); } @Override protected StateModel createStateModel() { return new EntityStateModel( propertiesModel, associationsModel, manyAssociationsModel ); } EntityModel newEntityModel( StateDeclarations stateDeclarations, AssociationDeclarations associationDecs, ManyAssociationDeclarations manyAssociationDecs, AssemblyHelper helper ) { this.associationDeclarations = associationDecs; this.manyAssociationDeclarations = manyAssociationDecs; try { associationsModel = new AssociationsModel(); manyAssociationsModel = new ManyAssociationsModel(); buildComposite( helper, stateDeclarations ); EntityModel entityModel = new EntityModel( types, visibility, metaInfo, (EntityMixinsModel) mixinsModel, (EntityStateModel) stateModel, compositeMethodsModel ); return entityModel; } catch( Exception e ) { throw new InvalidApplicationException( "Could not register " + types, e ); } } @Override protected void addStateFor( AccessibleObject accessor, Iterable<Class<? extends Constraint<?, ?>>> constraintClasses ) { String stateName = QualifiedName.fromAccessor( accessor ).name(); if( registeredStateNames.contains( stateName ) ) { return; // Skip already registered names } Class<?> accessorType = Classes.RAW_CLASS.map( typeOf( accessor ) ); if( Property.class.isAssignableFrom( accessorType ) ) { propertiesModel.addProperty( newPropertyModel( accessor, constraintClasses ) ); registeredStateNames.add( stateName ); } else if( Association.class.isAssignableFrom( accessorType ) ) { associationsModel.addAssociation( newAssociationModel( accessor, constraintClasses ) ); registeredStateNames.add( stateName ); } else if( ManyAssociation.class.isAssignableFrom( accessorType ) ) { manyAssociationsModel.addManyAssociation( newManyAssociationModel( accessor, constraintClasses ) ); registeredStateNames.add( stateName ); } } @Override protected PropertyModel newPropertyModel( AccessibleObject accessor, Iterable<Class<? extends Constraint<?, ?>>> constraintClasses ) { Iterable<Annotation> annotations = Annotations.findAccessorAndTypeAnnotationsIn( accessor ); boolean optional = first( filter( isType( Optional.class ), annotations ) ) != null; ValueConstraintsModel valueConstraintsModel = constraintsFor( annotations, GenericPropertyInfo.propertyTypeOf( accessor ), ( (Member) accessor ) .getName(), optional, constraintClasses, accessor ); ValueConstraintsInstance valueConstraintsInstance = null; if( valueConstraintsModel.isConstrained() ) { valueConstraintsInstance = valueConstraintsModel.newInstance(); } MetaInfo metaInfo = stateDeclarations.metaInfoFor( accessor ); Object defaultValue = stateDeclarations.initialValueOf( accessor ); boolean useDefaults = metaInfo.get( UseDefaults.class ) != null || stateDeclarations.useDefaults( accessor ); boolean immutable = this.immutable || metaInfo.get( Immutable.class ) != null; PropertyModel propertyModel = new PropertyModel( accessor, immutable, useDefaults, valueConstraintsInstance, metaInfo, defaultValue ); return propertyModel; } public AssociationModel newAssociationModel( AccessibleObject accessor, Iterable<Class<? extends Constraint<?, ?>>> constraintClasses ) { Iterable<Annotation> annotations = Annotations.findAccessorAndTypeAnnotationsIn( accessor ); boolean optional = first( filter( isType( Optional.class ), annotations ) ) != null; // Constraints for Association references ValueConstraintsModel valueConstraintsModel = constraintsFor( annotations, GenericAssociationInfo .associationTypeOf( accessor ), ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); ValueConstraintsInstance valueConstraintsInstance = null; if( valueConstraintsModel.isConstrained() ) { valueConstraintsInstance = valueConstraintsModel.newInstance(); } // Constraints for the Association itself valueConstraintsModel = constraintsFor( annotations, Association.class, ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); ValueConstraintsInstance associationValueConstraintsInstance = null; if( valueConstraintsModel.isConstrained() ) { associationValueConstraintsInstance = valueConstraintsModel.newInstance(); } MetaInfo metaInfo = associationDeclarations.metaInfoFor( accessor ); AssociationModel associationModel = new AssociationModel( accessor, valueConstraintsInstance, associationValueConstraintsInstance, metaInfo ); return associationModel; } public ManyAssociationModel newManyAssociationModel( AccessibleObject accessor, Iterable<Class<? extends Constraint<?, ?>>> constraintClasses ) { Iterable<Annotation> annotations = Annotations.findAccessorAndTypeAnnotationsIn( accessor ); boolean optional = first( filter( isType( Optional.class ), annotations ) ) != null; // Constraints for entities in ManyAssociation ValueConstraintsModel valueConstraintsModel = constraintsFor( annotations, GenericAssociationInfo .associationTypeOf( accessor ), ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); ValueConstraintsInstance valueConstraintsInstance = null; if( valueConstraintsModel.isConstrained() ) { valueConstraintsInstance = valueConstraintsModel.newInstance(); } // Constraints for the ManyAssociation itself valueConstraintsModel = constraintsFor( annotations, ManyAssociation.class, ( (Member) accessor ).getName(), optional, constraintClasses, accessor ); ValueConstraintsInstance manyValueConstraintsInstance = null; if( valueConstraintsModel.isConstrained() ) { manyValueConstraintsInstance = valueConstraintsModel.newInstance(); } MetaInfo metaInfo = manyAssociationDeclarations.metaInfoFor( accessor ); ManyAssociationModel associationModel = new ManyAssociationModel( accessor, valueConstraintsInstance, manyValueConstraintsInstance, metaInfo ); return associationModel; } }