http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/association/AssociationInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/association/AssociationInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/association/AssociationInstance.java new file mode 100644 index 0000000..6d5f6fe --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/association/AssociationInstance.java @@ -0,0 +1,138 @@ +/* + * 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.association; + +import java.lang.reflect.Type; +import java.util.function.BiFunction; +import org.apache.polygene.api.association.Association; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.AssociationWrapper; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.identity.HasIdentity; +import org.apache.polygene.api.property.Property; + +/** + * Implementation of Association to a single Entity. + */ +public final class AssociationInstance<T> + extends AbstractAssociationInstance<T> + implements Association<T> +{ + private Property<EntityReference> associationState; + + public AssociationInstance( AssociationInfo associationInfo, + BiFunction<EntityReference, Type, Object> entityFunction, + Property<EntityReference> associationState + ) + { + super( associationInfo, entityFunction ); + this.associationState = associationState; + } + + // Association implementation + @Override + public T get() + { + return getEntity( associationState.get() ); + } + + @Override + public void set( T newValue ) + throws IllegalArgumentException + { + checkImmutable(); + checkType( newValue ); + + associationInfo.checkConstraints( newValue ); + + // Change association + associationState.set( EntityReference.create( ((HasIdentity) newValue ).identity().get())); + } + + @Override + public EntityReference reference() + { + return associationState.get(); + } + + public Property<EntityReference> getAssociationState() + { + return associationState; + } + + @Override + public String toString() + { + if( associationState.get() == null ) + { + return ""; + } + else + { + return associationState.get().toString(); + } + } + + @Override + public int hashCode() + { + int hash = associationInfo.hashCode() * 39; // Descriptor + if( associationState.get() != null ) + { + hash = hash * 997 + associationState.get().hashCode(); // State + } + return hash; + } + + @Override + public boolean equals( Object o ) + { + if( this == o ) + { + return true; + } + if( o == null || getClass() != o.getClass() ) + { + return false; + } + Association<?> that = (Association) o; + // Unwrap if needed + while( that instanceof AssociationWrapper ) + { + that = ( (AssociationWrapper) that ).next(); + } + // Descriptor equality + AssociationInstance<?> thatInstance = (AssociationInstance) that; + AssociationDescriptor thatDescriptor = (AssociationDescriptor) thatInstance.associationInfo(); + if( !associationInfo.equals( thatDescriptor ) ) + { + return false; + } + // State equality + if( associationState.get() != null + ? !associationState.get().equals( thatInstance.associationState.get() ) + : thatInstance.associationState.get() != null ) + { + return false; + } + return true; + } +}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/association/AssociationModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/association/AssociationModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/association/AssociationModel.java new file mode 100644 index 0000000..b83021e --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/association/AssociationModel.java @@ -0,0 +1,229 @@ +/* + * 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.association; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import org.apache.polygene.api.association.Association; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.GenericAssociationInfo; +import org.apache.polygene.api.common.MetaInfo; +import org.apache.polygene.api.common.QualifiedName; +import org.apache.polygene.api.constraint.ConstraintViolationException; +import org.apache.polygene.api.entity.Aggregated; +import org.apache.polygene.api.entity.Queryable; +import org.apache.polygene.api.property.Immutable; +import org.apache.polygene.api.util.Classes; +import org.apache.polygene.api.util.Visitable; +import org.apache.polygene.api.util.Visitor; +import org.apache.polygene.bootstrap.BindingException; +import org.apache.polygene.runtime.composite.ValueConstraintsInstance; +import org.apache.polygene.runtime.model.Binder; +import org.apache.polygene.runtime.model.Resolution; + +/** + * Model for an Association. + * + * <p>Equality is based on the Association accessor object (associated type and name), not on the QualifiedName.</p> + */ +public final class AssociationModel + implements AssociationDescriptor, AssociationInfo, Binder, Visitable<AssociationModel> +{ + private MetaInfo metaInfo; + private Type type; + private AccessibleObject accessor; + private QualifiedName qualifiedName; + private ValueConstraintsInstance constraints; + private ValueConstraintsInstance associationConstraints; + private boolean queryable; + private boolean immutable; + private boolean aggregated; + private AssociationInfo builderInfo; + + public AssociationModel( AccessibleObject accessor, + ValueConstraintsInstance valueConstraintsInstance, + ValueConstraintsInstance associationConstraintsInstance, + MetaInfo metaInfo + ) + { + this.metaInfo = metaInfo; + this.constraints = valueConstraintsInstance; + this.associationConstraints = associationConstraintsInstance; + this.accessor = accessor; + initialize(); + } + + private void initialize() + { + this.type = GenericAssociationInfo.associationTypeOf( accessor ); + this.qualifiedName = QualifiedName.fromAccessor( accessor ); + this.immutable = metaInfo.get( Immutable.class ) != null; + this.aggregated = metaInfo.get( Aggregated.class ) != null; + + final Queryable queryable = accessor.getAnnotation( Queryable.class ); + this.queryable = queryable == null || queryable.value(); + } + + @Override + public <T> T metaInfo( Class<T> infoType ) + { + return metaInfo.get( infoType ); + } + + @Override + public QualifiedName qualifiedName() + { + return qualifiedName; + } + + @Override + public Type type() + { + return type; + } + + @Override + public boolean isImmutable() + { + return immutable; + } + + @Override + public boolean isAggregated() + { + return aggregated; + } + + @Override + public AccessibleObject accessor() + { + return accessor; + } + + @Override + public boolean queryable() + { + return queryable; + } + + public AssociationInfo getBuilderInfo() + { + return builderInfo; + } + + @Override + public <ThrowableType extends Throwable> boolean accept( Visitor<? super AssociationModel, ThrowableType> visitor ) + throws ThrowableType + { + return visitor.visit( this ); + } + + @Override + public void checkConstraints( Object value ) + throws ConstraintViolationException + { + constraints.checkConstraints( value, accessor ); + } + + public void checkAssociationConstraints( Association<?> association ) + throws ConstraintViolationException + { + associationConstraints.checkConstraints( association, accessor ); + } + + @Override + public void bind( Resolution resolution ) + throws BindingException + { + builderInfo = new AssociationInfo() + { + @Override + public boolean isImmutable() + { + return false; + } + + @Override + public QualifiedName qualifiedName() + { + return qualifiedName; + } + + @Override + public Type type() + { + return type; + } + + @Override + public void checkConstraints( Object value ) + throws ConstraintViolationException + { + AssociationModel.this.checkConstraints( value ); + } + }; + + if( type instanceof TypeVariable ) + { + + Class mainType = resolution.model().types().findFirst().orElse( null ); + type = Classes.resolveTypeVariable( (TypeVariable) type, ( (Member) accessor ).getDeclaringClass(), mainType ); + } + } + + @Override + public boolean equals( Object o ) + { + if( this == o ) + { + return true; + } + if( o == null || getClass() != o.getClass() ) + { + return false; + } + AssociationModel that = (AssociationModel) o; + return accessor.equals( that.accessor ); + } + + @Override + public int hashCode() + { + return accessor.hashCode(); + } + + @Override + public String toString() + { + if( accessor instanceof Field ) + { + return ( (Field) accessor ).toGenericString(); + } + else + { + return ( (Method) accessor ).toGenericString(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/association/AssociationsModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/association/AssociationsModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/association/AssociationsModel.java new file mode 100644 index 0000000..aad7d83 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/association/AssociationsModel.java @@ -0,0 +1,120 @@ +/* + * 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.association; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Stream; +import org.apache.polygene.api.association.Association; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.AssociationStateHolder; +import org.apache.polygene.api.common.QualifiedName; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.api.util.VisitableHierarchy; + +/** + * Model for Associations. + */ +public final class AssociationsModel + implements VisitableHierarchy<AssociationsModel, AssociationModel> +{ + private final Map<AccessibleObject, AssociationModel> mapAccessorAssociationModel = new LinkedHashMap<>(); + + public AssociationsModel() + { + } + + public Stream<AssociationModel> associations() + { + return mapAccessorAssociationModel.values().stream(); + } + + public void addAssociation( AssociationModel associationModel ) + { + mapAccessorAssociationModel.put( associationModel.accessor(), associationModel ); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super AssociationsModel, ? super AssociationModel, ThrowableType> visitor ) + throws ThrowableType + { + if( visitor.visitEnter( this ) ) + { + for( AssociationModel associationModel : mapAccessorAssociationModel.values() ) + { + if( !associationModel.accept( visitor ) ) + { + break; + } + } + } + return visitor.visitLeave( this ); + } + + public AssociationModel getAssociation( AccessibleObject accessor ) + throws IllegalArgumentException + { + AssociationModel associationModel = mapAccessorAssociationModel.get( accessor ); + if( associationModel == null ) + { + throw new IllegalArgumentException( "No association found with name:" + ( (Member) accessor ).getName() ); + } + return associationModel; + } + + public AssociationDescriptor getAssociationByName( String name ) + throws IllegalArgumentException + { + for( AssociationModel associationModel : mapAccessorAssociationModel.values() ) + { + if( associationModel.qualifiedName().name().equals( name ) ) + { + return associationModel; + } + } + throw new IllegalArgumentException( "No association found with name:" + name ); + } + + public AssociationDescriptor getAssociationByQualifiedName( QualifiedName name ) + throws IllegalArgumentException + { + for( AssociationModel associationModel : mapAccessorAssociationModel.values() ) + { + if( associationModel.qualifiedName().equals( name ) ) + { + return associationModel; + } + } + throw new IllegalArgumentException( "No association found with qualified name:" + name ); + } + + public void checkConstraints( AssociationStateHolder state ) + { + for( AssociationModel associationModel : mapAccessorAssociationModel.values() ) + { + Association<Object> association = state.<Object>associationFor( associationModel.accessor() ); + associationModel.checkAssociationConstraints( association ); + associationModel.checkConstraints( association.get() ); + } + } + +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/association/ManyAssociationInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/association/ManyAssociationInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/association/ManyAssociationInstance.java new file mode 100644 index 0000000..b035745 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/association/ManyAssociationInstance.java @@ -0,0 +1,226 @@ +/* + * 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.association; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.stream.Stream; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.ManyAssociation; +import org.apache.polygene.api.association.ManyAssociationWrapper; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.identity.HasIdentity; +import org.apache.polygene.api.util.NullArgumentException; +import org.apache.polygene.spi.entity.ManyAssociationState; + +/** + * JAVADOC + */ +public class ManyAssociationInstance<T> + extends AbstractAssociationInstance<T> + implements ManyAssociation<T> +{ + private ManyAssociationState manyAssociationState; + + public ManyAssociationInstance( AssociationInfo associationInfo, + BiFunction<EntityReference, Type, Object> associationFunction, + ManyAssociationState manyAssociationState + ) + { + super( associationInfo, associationFunction ); + this.manyAssociationState = manyAssociationState; + } + + @Override + public int count() + { + return manyAssociationState.count(); + } + + @Override + public boolean contains( T entity ) + { + return manyAssociationState.contains( getEntityReference( entity ) ); + } + + @Override + public boolean add( int i, T entity ) + { + NullArgumentException.validateNotNull( "entity", entity ); + checkImmutable(); + checkType( entity ); + associationInfo.checkConstraints( entity ); + return manyAssociationState.add( i, EntityReference.create( ((HasIdentity) entity ).identity().get()) ); + } + + @Override + public boolean add( T entity ) + { + return add( manyAssociationState.count(), entity ); + } + + @Override + public boolean remove( T entity ) + { + NullArgumentException.validateNotNull( "entity", entity ); + checkImmutable(); + checkType( entity ); + + return manyAssociationState.remove( EntityReference.create( ((HasIdentity) entity).identity().get() ) ); + } + + @Override + public T get( int i ) + { + return getEntity( manyAssociationState.get( i ) ); + } + + @Override + public List<T> toList() + { + ArrayList<T> list = new ArrayList<>(); + for( EntityReference entityReference : manyAssociationState ) + { + list.add( getEntity( entityReference ) ); + } + + return list; + } + + @Override + public Set<T> toSet() + { + Set<T> set = new HashSet<>(); + for( EntityReference entityReference : manyAssociationState ) + { + set.add( getEntity( entityReference ) ); + } + + return set; + } + + @Override + public Stream<EntityReference> references() + { + return manyAssociationState.stream(); + } + + @Override + public String toString() + { + return manyAssociationState.toString(); + } + + @Override + public Iterator<T> iterator() + { + return new ManyAssociationIterator( manyAssociationState.iterator() ); + } + + @Override + public boolean equals( Object o ) + { + if( this == o ) + { + return true; + } + if( o == null || getClass() != o.getClass() ) + { + return false; + } + ManyAssociation<?> that = (ManyAssociation) o; + // Unwrap if needed + while( that instanceof ManyAssociationWrapper ) + { + that = ( (ManyAssociationWrapper) that ).next(); + } + // Descriptor equality + ManyAssociationInstance<?> thatInstance = (ManyAssociationInstance) that; + AssociationDescriptor thatDescriptor = (AssociationDescriptor) thatInstance.associationInfo(); + if( !associationInfo.equals( thatDescriptor ) ) + { + return false; + } + // State equality + if( manyAssociationState.count() != thatInstance.manyAssociationState.count() ) + { + return false; + } + for( EntityReference ref : manyAssociationState ) + { + if( !thatInstance.manyAssociationState.contains( ref ) ) + { + return false; + } + } + return true; + } + + @Override + public int hashCode() + { + int hash = associationInfo.hashCode() * 31; // Descriptor + for( EntityReference ref : manyAssociationState ) + { + hash += ref.hashCode() * 7; // State + } + return hash; + } + + public ManyAssociationState getManyAssociationState() + { + return manyAssociationState; + } + + protected class ManyAssociationIterator + implements Iterator<T> + { + private final Iterator<EntityReference> idIterator; + + public ManyAssociationIterator( Iterator<EntityReference> idIterator ) + { + this.idIterator = idIterator; + } + + @Override + public boolean hasNext() + { + return idIterator.hasNext(); + } + + @Override + public T next() + { + return getEntity( idIterator.next() ); + } + + @Override + public void remove() + { + checkImmutable(); + idIterator.remove(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/association/ManyAssociationModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/association/ManyAssociationModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/association/ManyAssociationModel.java new file mode 100644 index 0000000..e8fd0b6 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/association/ManyAssociationModel.java @@ -0,0 +1,265 @@ +/* + * 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.association; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.List; +import java.util.function.BiFunction; +import java.util.stream.Stream; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.GenericAssociationInfo; +import org.apache.polygene.api.association.ManyAssociation; +import org.apache.polygene.api.common.MetaInfo; +import org.apache.polygene.api.common.QualifiedName; +import org.apache.polygene.api.constraint.ConstraintViolation; +import org.apache.polygene.api.constraint.ConstraintViolationException; +import org.apache.polygene.api.entity.Aggregated; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.entity.Queryable; +import org.apache.polygene.api.property.Immutable; +import org.apache.polygene.api.util.Classes; +import org.apache.polygene.api.util.Visitable; +import org.apache.polygene.api.util.Visitor; +import org.apache.polygene.bootstrap.BindingException; +import org.apache.polygene.runtime.composite.ValueConstraintsInstance; +import org.apache.polygene.runtime.model.Binder; +import org.apache.polygene.runtime.model.Resolution; +import org.apache.polygene.runtime.unitofwork.ModuleUnitOfWork; +import org.apache.polygene.runtime.unitofwork.BuilderEntityState; +import org.apache.polygene.spi.entity.EntityState; + +/** + * Model for a ManyAssociation. + * + * <p>Equality is based on the ManyAssociation accessor object (associated type and name), not on the QualifiedName.</p> + */ +public final class ManyAssociationModel + implements AssociationDescriptor, AssociationInfo, Binder, Visitable<ManyAssociationModel> +{ + private final ValueConstraintsInstance associationConstraints; + private final MetaInfo metaInfo; + private Type type; + private final AccessibleObject accessor; + private QualifiedName qualifiedName; + private final ValueConstraintsInstance constraints; + private boolean queryable; + private boolean immutable; + private boolean aggregated; + private AssociationInfo builderInfo; + + public ManyAssociationModel( AccessibleObject accessor, + ValueConstraintsInstance valueConstraintsInstance, + ValueConstraintsInstance associationConstraintsInstance, + MetaInfo metaInfo + ) + { + this.metaInfo = metaInfo; + this.constraints = valueConstraintsInstance; + this.associationConstraints = associationConstraintsInstance; + this.accessor = accessor; + initialize(); + } + + private void initialize() + { + this.type = GenericAssociationInfo.associationTypeOf( accessor ); + this.qualifiedName = QualifiedName.fromAccessor( accessor ); + this.immutable = metaInfo.get( Immutable.class ) != null; + this.aggregated = metaInfo.get( Aggregated.class ) != null; + + final Queryable queryable = accessor.getAnnotation( Queryable.class ); + this.queryable = queryable == null || queryable.value(); + } + + @Override + public <T> T metaInfo( Class<T> infoType ) + { + return metaInfo.get( infoType ); + } + + @Override + public QualifiedName qualifiedName() + { + return qualifiedName; + } + + @Override + public Type type() + { + return type; + } + + @Override + public boolean isImmutable() + { + return immutable; + } + + @Override + public boolean isAggregated() + { + return aggregated; + } + + @Override + public AccessibleObject accessor() + { + return accessor; + } + + @Override + public boolean queryable() + { + return queryable; + } + + public AssociationInfo getBuilderInfo() + { + return builderInfo; + } + + public <T> ManyAssociation<T> newInstance( final ModuleUnitOfWork uow, EntityState state ) + { + return new ManyAssociationInstance<>( state instanceof BuilderEntityState ? builderInfo : this, new BiFunction<EntityReference, Type, Object>() + { + @Override + public Object apply( EntityReference entityReference, Type type ) + { + return uow.get( Classes.RAW_CLASS.apply( type ), entityReference.identity() ); + } + }, state.manyAssociationValueOf( qualifiedName ) ); + } + + @Override + public void checkConstraints( Object composite ) + throws ConstraintViolationException + { + if( constraints != null ) + { + List<ConstraintViolation> violations = constraints.checkConstraints( composite ); + if( !violations.isEmpty() ) + { + Stream<Class<?>> empty = Stream.empty(); + throw new ConstraintViolationException( "", empty, (Member) accessor, violations ); + } + } + } + + public void checkAssociationConstraints( ManyAssociation manyAssociation ) + throws ConstraintViolationException + { + if( associationConstraints != null ) + { + List<ConstraintViolation> violations = associationConstraints.checkConstraints( manyAssociation ); + if( !violations.isEmpty() ) + { + Stream<Class<?>> empty = Stream.empty(); + throw new ConstraintViolationException( "", empty, (Member) accessor, violations ); + } + } + } + + @Override + public <ThrowableType extends Throwable> boolean accept( Visitor<? super ManyAssociationModel, ThrowableType> visitor ) + throws ThrowableType + { + return visitor.visit( this ); + } + + @Override + public void bind( Resolution resolution ) + throws BindingException + { + builderInfo = new AssociationInfo() + { + @Override + public boolean isImmutable() + { + return false; + } + + @Override + public QualifiedName qualifiedName() + { + return qualifiedName; + } + + @Override + public Type type() + { + return type; + } + + @Override + public void checkConstraints( Object value ) + throws ConstraintViolationException + { + ManyAssociationModel.this.checkConstraints( value ); + } + }; + + if( type instanceof TypeVariable ) + { + Class mainType = resolution.model().types().findFirst().orElse( null ); + type = Classes.resolveTypeVariable( (TypeVariable) type, ( (Member) accessor ).getDeclaringClass(), mainType ); + } + } + + @Override + public boolean equals( Object o ) + { + if( this == o ) + { + return true; + } + if( o == null || getClass() != o.getClass() ) + { + return false; + } + + ManyAssociationModel that = (ManyAssociationModel) o; + + return accessor.equals( that.accessor ); + } + + @Override + public int hashCode() + { + return accessor.hashCode(); + } + + @Override + public String toString() + { + if( accessor instanceof Field ) + { + return ( (Field) accessor ).toGenericString(); + } + else + { + return ( (Method) accessor ).toGenericString(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/association/ManyAssociationsModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/association/ManyAssociationsModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/association/ManyAssociationsModel.java new file mode 100644 index 0000000..090b180 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/association/ManyAssociationsModel.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.association; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Stream; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.ManyAssociation; +import org.apache.polygene.api.common.QualifiedName; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.api.util.VisitableHierarchy; +import org.apache.polygene.runtime.unitofwork.ModuleUnitOfWork; +import org.apache.polygene.runtime.value.ValueStateInstance; +import org.apache.polygene.spi.entity.EntityState; + +/** + * Model for ManyAssociations. + */ +public final class ManyAssociationsModel + implements VisitableHierarchy<ManyAssociationsModel, ManyAssociationModel> +{ + private final Map<AccessibleObject, ManyAssociationModel> mapAccessorAssociationModel = new LinkedHashMap<>(); + + public ManyAssociationsModel() + { + } + + public Stream<ManyAssociationModel> manyAssociations() + { + return mapAccessorAssociationModel.values().stream(); + } + + public void addManyAssociation( ManyAssociationModel model ) + { + mapAccessorAssociationModel.put( model.accessor(), model ); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super ManyAssociationsModel, ? super ManyAssociationModel, ThrowableType> visitor ) + throws ThrowableType + { + if( visitor.visitEnter( this ) ) + { + for( ManyAssociationModel associationModel : mapAccessorAssociationModel.values() ) + { + if( !associationModel.accept( visitor ) ) + { + break; + } + } + } + return visitor.visitLeave( this ); + } + + public <T> ManyAssociation<T> newInstance( AccessibleObject accessor, + EntityState entityState, + ModuleUnitOfWork uow ) + { + return mapAccessorAssociationModel.get( accessor ).newInstance( uow, entityState ); + } + + public ManyAssociationModel getManyAssociation( AccessibleObject accessor ) + throws IllegalArgumentException + { + ManyAssociationModel manyAssociationModel = mapAccessorAssociationModel.get( accessor ); + if( manyAssociationModel == null ) + { + throw new IllegalArgumentException( "No many-association found with name:" + ( (Member) accessor ).getName() ); + } + return manyAssociationModel; + } + + public AssociationDescriptor getManyAssociationByName( String name ) + throws IllegalArgumentException + { + for( ManyAssociationModel associationModel : mapAccessorAssociationModel.values() ) + { + if( associationModel.qualifiedName().name().equals( name ) ) + { + return associationModel; + } + } + throw new IllegalArgumentException( "No many-association found with name:" + name ); + } + + public AssociationDescriptor getManyAssociationByQualifiedName( QualifiedName name ) + throws IllegalArgumentException + { + for( ManyAssociationModel associationModel : mapAccessorAssociationModel.values() ) + { + if( associationModel.qualifiedName().equals( name ) ) + { + return associationModel; + } + } + throw new IllegalArgumentException( "No many-association found with qualified name:" + name ); + } + + public void checkConstraints( ValueStateInstance state ) + { + for( ManyAssociationModel manyAssociationModel : mapAccessorAssociationModel.values() ) + { + manyAssociationModel.checkAssociationConstraints( state.manyAssociationFor( manyAssociationModel.accessor() ) ); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/association/NamedAssociationInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/association/NamedAssociationInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/association/NamedAssociationInstance.java new file mode 100644 index 0000000..05aa878 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/association/NamedAssociationInstance.java @@ -0,0 +1,192 @@ +/* + * 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.association; + +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.NamedAssociation; +import org.apache.polygene.api.association.NamedAssociationWrapper; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.identity.HasIdentity; +import org.apache.polygene.api.util.NullArgumentException; +import org.apache.polygene.spi.entity.NamedAssociationState; + +public class NamedAssociationInstance<T> + extends AbstractAssociationInstance<T> + implements NamedAssociation<T> +{ + + private final NamedAssociationState namedAssociationState; + + public NamedAssociationInstance( AssociationInfo associationInfo, + BiFunction<EntityReference, Type, Object> associationFunction, + NamedAssociationState namedAssociationState + ) + { + super( associationInfo, associationFunction ); + this.namedAssociationState = namedAssociationState; + } + + @Override + public Iterator<String> iterator() + { + return namedAssociationState.iterator(); + } + + @Override + public int count() + { + return namedAssociationState.count(); + } + + @Override + public boolean containsName( String name ) + { + return namedAssociationState.containsName( name ); + } + + @Override + public boolean put( String name, T entity ) + { + NullArgumentException.validateNotNull( "entity", entity ); + checkImmutable(); + checkType( entity ); + associationInfo.checkConstraints( entity ); + return namedAssociationState.put( name, EntityReference.create( ((HasIdentity) entity).identity().get() ) ); + } + + @Override + public boolean remove( String name ) + { + checkImmutable(); + return namedAssociationState.remove( name ); + } + + @Override + public T get( String name ) + { + return getEntity( namedAssociationState.get( name ) ); + } + + @Override + public String nameOf( T entity ) + { + return namedAssociationState.nameOf( getEntityReference( entity ) ); + } + + @Override + public Map<String, T> toMap() + { + Map<String, T> map = new HashMap<>(); + for( String name : namedAssociationState ) + { + map.put( name, getEntity( namedAssociationState.get( name ) ) ); + } + return map; + } + + @Override + public Stream<Map.Entry<String, EntityReference>> references() + { + return namedAssociationState.stream(); + } + + @Override + public EntityReference referenceOf( String name ) + { + return namedAssociationState.get( name ); + } + + public Iterable<Map.Entry<String, EntityReference>> getEntityReferences() + { + return Collections.unmodifiableMap( + StreamSupport.stream( namedAssociationState.spliterator(), false ) + .collect( Collectors.toMap( Function.identity(), namedAssociationState::get ) ) + ).entrySet(); + } + + + @Override + public boolean equals( Object o ) + { + if( this == o ) + { + return true; + } + if( o == null || getClass() != o.getClass() ) + { + return false; + } + NamedAssociation<?> that = (NamedAssociation) o; + // Unwrap if needed + while( that instanceof NamedAssociationWrapper ) + { + that = ( (NamedAssociationWrapper) that ).next(); + } + // Descriptor equality + NamedAssociationInstance<?> thatInstance = (NamedAssociationInstance) that; + AssociationDescriptor thatDescriptor = (AssociationDescriptor) thatInstance.associationInfo(); + if( !associationInfo.equals( thatDescriptor ) ) + { + return false; + } + // State equality + if( namedAssociationState.count() != thatInstance.namedAssociationState.count() ) + { + return false; + } + for( String name : namedAssociationState ) + { + if( !thatInstance.namedAssociationState.containsName( name ) ) + { + return false; + } + EntityReference thisReference = namedAssociationState.get( name ); + EntityReference thatReference = thatInstance.namedAssociationState.get( name ); + if( !thisReference.equals( thatReference ) ) + { + return false; + } + } + return true; + } + + @Override + public int hashCode() + { + int hash = associationInfo.hashCode() * 31; // Descriptor + for( String name : namedAssociationState ) + { + hash += name.hashCode(); + hash += namedAssociationState.get( name ).hashCode() * 7; // State + } + return hash; + } + +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/association/NamedAssociationModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/association/NamedAssociationModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/association/NamedAssociationModel.java new file mode 100644 index 0000000..bfcec63 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/association/NamedAssociationModel.java @@ -0,0 +1,265 @@ +/* + * 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.association; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.List; +import java.util.function.BiFunction; +import java.util.stream.Stream; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.GenericAssociationInfo; +import org.apache.polygene.api.association.NamedAssociation; +import org.apache.polygene.api.common.MetaInfo; +import org.apache.polygene.api.common.QualifiedName; +import org.apache.polygene.api.constraint.ConstraintViolation; +import org.apache.polygene.api.constraint.ConstraintViolationException; +import org.apache.polygene.api.entity.Aggregated; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.entity.Queryable; +import org.apache.polygene.api.property.Immutable; +import org.apache.polygene.api.util.Classes; +import org.apache.polygene.api.util.Visitable; +import org.apache.polygene.api.util.Visitor; +import org.apache.polygene.bootstrap.BindingException; +import org.apache.polygene.runtime.composite.ValueConstraintsInstance; +import org.apache.polygene.runtime.model.Binder; +import org.apache.polygene.runtime.model.Resolution; +import org.apache.polygene.runtime.unitofwork.ModuleUnitOfWork; +import org.apache.polygene.runtime.unitofwork.BuilderEntityState; +import org.apache.polygene.spi.entity.EntityState; + +/** + * Model for a NamedAssociation. + * + * <p>Equality is based on the NamedAssociation accessor object (associated type and name), not on the QualifiedName.</p> + */ +public final class NamedAssociationModel + implements AssociationDescriptor, AssociationInfo, Binder, Visitable<NamedAssociationModel> +{ + private final ValueConstraintsInstance associationConstraints; + private final MetaInfo metaInfo; + private Type type; + private final AccessibleObject accessor; + private QualifiedName qualifiedName; + private final ValueConstraintsInstance constraints; + private boolean queryable; + private boolean immutable; + private boolean aggregated; + private AssociationInfo builderInfo; + + public NamedAssociationModel( AccessibleObject accessor, + ValueConstraintsInstance valueConstraintsInstance, + ValueConstraintsInstance associationConstraintsInstance, + MetaInfo metaInfo + ) + { + this.metaInfo = metaInfo; + this.constraints = valueConstraintsInstance; + this.associationConstraints = associationConstraintsInstance; + this.accessor = accessor; + initialize(); + } + + private void initialize() + { + this.type = GenericAssociationInfo.associationTypeOf( accessor ); + this.qualifiedName = QualifiedName.fromAccessor( accessor ); + this.immutable = metaInfo.get( Immutable.class ) != null; + this.aggregated = metaInfo.get( Aggregated.class ) != null; + + final Queryable queryable = accessor.getAnnotation( Queryable.class ); + this.queryable = queryable == null || queryable.value(); + } + + @Override + public <T> T metaInfo( Class<T> infoType ) + { + return metaInfo.get( infoType ); + } + + @Override + public QualifiedName qualifiedName() + { + return qualifiedName; + } + + @Override + public Type type() + { + return type; + } + + @Override + public boolean isImmutable() + { + return immutable; + } + + @Override + public boolean isAggregated() + { + return aggregated; + } + + @Override + public AccessibleObject accessor() + { + return accessor; + } + + @Override + public boolean queryable() + { + return queryable; + } + + public AssociationInfo getBuilderInfo() + { + return builderInfo; + } + + public <T> NamedAssociation<T> newInstance( final ModuleUnitOfWork uow, EntityState state ) + { + return new NamedAssociationInstance<>( state instanceof BuilderEntityState ? builderInfo : this, new BiFunction<EntityReference, Type, Object>() + { + @Override + public Object apply( EntityReference entityReference, Type type ) + { + return uow.get( Classes.RAW_CLASS.apply( type ), entityReference.identity() ); + } + }, state.namedAssociationValueOf( qualifiedName ) ); + } + + @Override + public void checkConstraints( Object composite ) + throws ConstraintViolationException + { + if( constraints != null ) + { + List<ConstraintViolation> violations = constraints.checkConstraints( composite ); + if( !violations.isEmpty() ) + { + Stream<Class<?>> empty = Stream.empty(); + throw new ConstraintViolationException( "", empty, (Member) accessor, violations ); + } + } + } + + public void checkAssociationConstraints( NamedAssociation association ) + throws ConstraintViolationException + { + if( associationConstraints != null ) + { + List<ConstraintViolation> violations = associationConstraints.checkConstraints( association ); + if( !violations.isEmpty() ) + { + Stream<Class<?>> empty = Stream.empty(); + throw new ConstraintViolationException( "", empty, (Member) accessor, violations ); + } + } + } + + @Override + public <ThrowableType extends Throwable> boolean accept( Visitor<? super NamedAssociationModel, ThrowableType> visitor ) + throws ThrowableType + { + return visitor.visit( this ); + } + + @Override + public void bind( Resolution resolution ) + throws BindingException + { + builderInfo = new AssociationInfo() + { + @Override + public boolean isImmutable() + { + return false; + } + + @Override + public QualifiedName qualifiedName() + { + return qualifiedName; + } + + @Override + public Type type() + { + return type; + } + + @Override + public void checkConstraints( Object value ) + throws ConstraintViolationException + { + NamedAssociationModel.this.checkConstraints( value ); + } + }; + + if( type instanceof TypeVariable ) + { + Class mainType = resolution.model().types().findFirst().orElse( null ); + type = Classes.resolveTypeVariable( (TypeVariable) type, ( (Member) accessor ).getDeclaringClass(), mainType ); + } + } + + @Override + public boolean equals( Object o ) + { + if( this == o ) + { + return true; + } + if( o == null || getClass() != o.getClass() ) + { + return false; + } + + NamedAssociationModel that = (NamedAssociationModel) o; + + return accessor.equals( that.accessor ); + } + + @Override + public int hashCode() + { + return accessor.hashCode(); + } + + @Override + public String toString() + { + if( accessor instanceof Field ) + { + return ( (Field) accessor ).toGenericString(); + } + else + { + return ( (Method) accessor ).toGenericString(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/association/NamedAssociationsModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/association/NamedAssociationsModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/association/NamedAssociationsModel.java new file mode 100644 index 0000000..513b95a --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/association/NamedAssociationsModel.java @@ -0,0 +1,130 @@ +/* + * 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.association; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Stream; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.NamedAssociation; +import org.apache.polygene.api.common.QualifiedName; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.api.util.VisitableHierarchy; +import org.apache.polygene.runtime.unitofwork.ModuleUnitOfWork; +import org.apache.polygene.runtime.value.ValueStateInstance; +import org.apache.polygene.spi.entity.EntityState; + +/** + * Model for NamedAssociations. + */ +public final class NamedAssociationsModel + implements VisitableHierarchy<NamedAssociationsModel, NamedAssociationModel> +{ + private final Map<AccessibleObject, NamedAssociationModel> mapAccessorAssociationModel = new LinkedHashMap<>(); + + public NamedAssociationsModel() + { + } + + public Stream<NamedAssociationModel> namedAssociations() + { + return mapAccessorAssociationModel.values().stream(); + } + + public void addNamedAssociation( NamedAssociationModel model ) + { + mapAccessorAssociationModel.put( model.accessor(), model ); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super NamedAssociationsModel, ? super NamedAssociationModel, ThrowableType> visitor ) + throws ThrowableType + { + if( visitor.visitEnter( this ) ) + { + for( NamedAssociationModel associationModel : mapAccessorAssociationModel.values() ) + { + if( !associationModel.accept( visitor ) ) + { + break; + } + } + } + return visitor.visitLeave( this ); + } + + public <T> NamedAssociation<T> newInstance( AccessibleObject accessor, + EntityState entityState, + ModuleUnitOfWork uow ) + { + return mapAccessorAssociationModel.get( accessor ).newInstance( uow, entityState ); + } + + public NamedAssociationModel getNamedAssociation( AccessibleObject accessor ) + throws IllegalArgumentException + { + if( false ) + { + return (NamedAssociationModel) getNamedAssociationByName( QualifiedName.fromAccessor( accessor ).name() ); + } + NamedAssociationModel namedAssociationModel = mapAccessorAssociationModel.get( accessor ); + if( namedAssociationModel == null ) + { + throw new IllegalArgumentException( "No named-association found with name:" + ( (Member) accessor ).getName() ); + } + return namedAssociationModel; + } + + public AssociationDescriptor getNamedAssociationByName( String name ) + throws IllegalArgumentException + { + for( NamedAssociationModel associationModel : mapAccessorAssociationModel.values() ) + { + if( associationModel.qualifiedName().name().equals( name ) ) + { + return associationModel; + } + } + throw new IllegalArgumentException( "No named-association found with name:" + name ); + } + + public AssociationDescriptor getNamedAssociationByQualifiedName( QualifiedName name ) + throws IllegalArgumentException + { + for( NamedAssociationModel associationModel : mapAccessorAssociationModel.values() ) + { + if( associationModel.qualifiedName().equals( name ) ) + { + return associationModel; + } + } + throw new IllegalArgumentException( "No named-association found with qualified name:" + name ); + } + + public void checkConstraints( ValueStateInstance state ) + { + for( NamedAssociationModel associationModel : mapAccessorAssociationModel.values() ) + { + associationModel.checkAssociationConstraints( state.namedAssociationFor( associationModel.accessor() ) ); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/AndAppliesToFilter.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/AndAppliesToFilter.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/AndAppliesToFilter.java new file mode 100644 index 0000000..51e5695 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/AndAppliesToFilter.java @@ -0,0 +1,47 @@ +/* + * 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 org.apache.polygene.api.common.AppliesToFilter; + +/** + * JAVADOC + */ +final class AndAppliesToFilter + implements AppliesToFilter +{ + private final AppliesToFilter left; + private final AppliesToFilter right; + + AndAppliesToFilter( AppliesToFilter left, AppliesToFilter right ) + { + this.left = left; + this.right = right; + } + + @Override + public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> fragmentClass ) + { + return left.appliesTo( method, mixin, compositeType, fragmentClass ) && + right.appliesTo( method, mixin, compositeType, fragmentClass ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/AnnotationAppliesToFilter.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/AnnotationAppliesToFilter.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/AnnotationAppliesToFilter.java new file mode 100644 index 0000000..006f24b --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/AnnotationAppliesToFilter.java @@ -0,0 +1,47 @@ +/* + * 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 org.apache.polygene.api.common.AppliesToFilter; + +/** + * JAVADOC + */ +final class AnnotationAppliesToFilter + implements AppliesToFilter +{ + @SuppressWarnings( "raw" ) + private final Class annotationType; + + @SuppressWarnings( "raw" ) + AnnotationAppliesToFilter( Class type ) + { + this.annotationType = type; + } + + @Override + @SuppressWarnings( "unchecked" ) + public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> fragmentClass ) + { + return method.getAnnotation( annotationType ) != null; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ApplicationAssemblyFactoryImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ApplicationAssemblyFactoryImpl.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ApplicationAssemblyFactoryImpl.java new file mode 100644 index 0000000..46c8cee --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ApplicationAssemblyFactoryImpl.java @@ -0,0 +1,79 @@ +/* + * 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.bootstrap.ApplicationAssembly; +import org.apache.polygene.bootstrap.ApplicationAssemblyFactory; +import org.apache.polygene.bootstrap.Assembler; +import org.apache.polygene.bootstrap.AssemblyException; +import org.apache.polygene.bootstrap.LayerAssembly; +import org.apache.polygene.bootstrap.ModuleAssembly; + +/** + * Factory for ApplicationAssembly. + */ +public final class ApplicationAssemblyFactoryImpl + implements ApplicationAssemblyFactory +{ + @Override + public ApplicationAssembly newApplicationAssembly( Assembler assembler ) + throws AssemblyException + { + return newApplicationAssembly( new Assembler[][][]{ { { assembler } } } ); + } + + @Override + public ApplicationAssembly newApplicationAssembly( Assembler[][][] assemblers ) + throws AssemblyException + { + ApplicationAssembly applicationAssembly = newApplicationAssembly(); + + // Build all layers bottom-up + LayerAssembly below = null; + for( int layer = assemblers.length - 1; layer >= 0; layer-- ) + { + // Create Layer + LayerAssembly layerAssembly = applicationAssembly.layer( "Layer " + ( layer + 1 ) ); + for( int module = 0; module < assemblers[ layer ].length; module++ ) + { + // Create Module + ModuleAssembly moduleAssembly = layerAssembly.module( "Module " + ( module + 1 ) ); + for( Assembler assembler : assemblers[ layer ][ module ] ) + { + // Register Assembler + assembler.assemble( moduleAssembly ); + } + } + if( below != null ) + { + layerAssembly.uses( below ); // Link layers + } + below = layerAssembly; + } + return applicationAssembly; + } + + @Override + public ApplicationAssembly newApplicationAssembly() + { + return new ApplicationAssemblyImpl(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ApplicationAssemblyImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ApplicationAssemblyImpl.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ApplicationAssemblyImpl.java new file mode 100644 index 0000000..ccc571b --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ApplicationAssemblyImpl.java @@ -0,0 +1,157 @@ +/* + * 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.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.apache.polygene.api.activation.Activator; +import org.apache.polygene.api.common.MetaInfo; +import org.apache.polygene.api.structure.Application; +import org.apache.polygene.bootstrap.ApplicationAssembly; +import org.apache.polygene.bootstrap.AssemblyVisitor; +import org.apache.polygene.bootstrap.LayerAssembly; +import org.apache.polygene.bootstrap.ModuleAssembly; + +/** + * The representation of an entire application. From + * this you can set information about the application + * and create LayerAssemblies. + */ +public final class ApplicationAssemblyImpl + implements ApplicationAssembly +{ + private final Map<String, LayerAssemblyImpl> layerAssemblies = new LinkedHashMap<>(); + private String name = "Application"; + private String version = "1.0"; // Default version + private Application.Mode mode; + private final MetaInfo metaInfo = new MetaInfo(); + private final List<Class<? extends Activator<Application>>> activators = new ArrayList<>(); + + public ApplicationAssemblyImpl() + { + mode = Application.Mode.valueOf( System.getProperty( "mode", "production" ) ); + } + + @Override + public LayerAssembly layer( String name ) + { + if( name != null ) + { + LayerAssemblyImpl existing = layerAssemblies.get( name ); + if( existing != null ) + { + return existing; + } + } + LayerAssemblyImpl layerAssembly = new LayerAssemblyImpl( this, name ); + layerAssemblies.put( name, layerAssembly ); + return layerAssembly; + } + + @Override + public ModuleAssembly module( String layerName, String moduleName ) + { + return layer( layerName ).module( moduleName ); + } + + @Override + public ApplicationAssembly setName( String name ) + { + this.name = name; + return this; + } + + @Override + public ApplicationAssembly setVersion( String version ) + { + this.version = version; + return this; + } + + @Override + public ApplicationAssembly setMode( Application.Mode mode ) + { + this.mode = mode; + return this; + } + + @Override + public ApplicationAssembly setMetaInfo( Object info ) + { + metaInfo.set( info ); + return this; + } + + @Override + @SafeVarargs + public final ApplicationAssembly withActivators( Class<? extends Activator<Application>>... activators ) + { + this.activators.addAll( Arrays.asList( activators ) ); + return this; + } + + @Override + public <ThrowableType extends Throwable> void visit( AssemblyVisitor<ThrowableType> visitor ) + throws ThrowableType + { + visitor.visitApplication( this ); + for( LayerAssemblyImpl layerAssembly : layerAssemblies.values() ) + { + layerAssembly.visit( visitor ); + } + } + + public Collection<LayerAssemblyImpl> layerAssemblies() + { + return layerAssemblies.values(); + } + + public List<Class<? extends Activator<Application>>> activators() + { + return activators; + } + + public MetaInfo metaInfo() + { + return metaInfo; + } + + @Override + public String name() + { + return name; + } + + public String version() + { + return version; + } + + @Override + public Application.Mode mode() + { + return mode; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ApplicationModelFactoryImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ApplicationModelFactoryImpl.java b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ApplicationModelFactoryImpl.java new file mode 100644 index 0000000..a39c521 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/bootstrap/ApplicationModelFactoryImpl.java @@ -0,0 +1,211 @@ +/* + * 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.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.polygene.api.composite.ModelDescriptor; +import org.apache.polygene.api.structure.Application; +import org.apache.polygene.api.structure.ApplicationDescriptor; +import org.apache.polygene.api.structure.Layer; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.bootstrap.ApplicationAssembly; +import org.apache.polygene.bootstrap.ApplicationModelFactory; +import org.apache.polygene.bootstrap.AssemblyException; +import org.apache.polygene.bootstrap.BindingException; +import org.apache.polygene.bootstrap.LayerAssembly; +import org.apache.polygene.runtime.activation.ActivatorsModel; +import org.apache.polygene.runtime.composite.CompositeMethodModel; +import org.apache.polygene.runtime.injection.InjectedFieldModel; +import org.apache.polygene.runtime.model.Binder; +import org.apache.polygene.runtime.model.Resolution; +import org.apache.polygene.runtime.structure.ApplicationModel; +import org.apache.polygene.runtime.structure.LayerModel; +import org.apache.polygene.runtime.structure.ModuleModel; +import org.apache.polygene.runtime.structure.UsedLayersModel; + +/** + * Factory for Applications. + */ +public final class ApplicationModelFactoryImpl + implements ApplicationModelFactory +{ + @Override + public ApplicationDescriptor newApplicationModel( ApplicationAssembly assembly ) + throws AssemblyException + { + AssemblyHelper helper = createAssemblyHelper( assembly ); + + ApplicationAssemblyImpl applicationAssembly = (ApplicationAssemblyImpl) assembly; + ActivatorsModel<Application> applicationActivators = new ActivatorsModel<>( applicationAssembly.activators() ); + List<LayerModel> layerModels = new ArrayList<>(); + final ApplicationModel applicationModel = new ApplicationModel( applicationAssembly.name(), + applicationAssembly.version(), + applicationAssembly.mode(), + applicationAssembly.metaInfo(), + applicationActivators, + layerModels ); + Map<LayerAssembly, LayerModel> mapAssemblyModel = new HashMap<>(); + Map<LayerAssembly, List<LayerModel>> mapUsedLayers = new HashMap<>(); + + // Build all layers + List<LayerAssemblyImpl> layerAssemblies = new ArrayList<>( applicationAssembly.layerAssemblies() ); + for( LayerAssemblyImpl layerAssembly : layerAssemblies ) + { + List<LayerModel> usedLayers = new ArrayList<>(); + mapUsedLayers.put( layerAssembly, usedLayers ); + + UsedLayersModel usedLayersModel = new UsedLayersModel( usedLayers ); + List<ModuleModel> moduleModels = new ArrayList<>(); + String name = layerAssembly.name(); + if( name == null ) + { + throw new AssemblyException( "Layer must have name set" ); + } + ActivatorsModel<Layer> layerActivators = new ActivatorsModel<>( layerAssembly.activators() ); + LayerModel layerModel = new LayerModel( name, layerAssembly.metaInfo(), usedLayersModel, layerActivators, moduleModels ); + + for( ModuleAssemblyImpl moduleAssembly : layerAssembly.moduleAssemblies() ) + { + moduleModels.add( moduleAssembly.assembleModule( layerModel, helper ) ); + } + mapAssemblyModel.put( layerAssembly, layerModel ); + layerModels.add( layerModel ); + } + + // Populate used layer lists + for( LayerAssemblyImpl layerAssembly : layerAssemblies ) + { + Set<LayerAssembly> usesLayers = layerAssembly.uses(); + List<LayerModel> usedLayers = mapUsedLayers.get( layerAssembly ); + for( LayerAssembly usesLayer : usesLayers ) + { + LayerModel layerModel = mapAssemblyModel.get( usesLayer ); + usedLayers.add( layerModel ); + } + } + + // Bind model + // This will resolve all dependencies + try + { +// applicationModel.bind(); + applicationModel.accept( new BindingVisitor( applicationModel ) ); + } + catch( BindingException e ) + { + throw new AssemblyException( "Unable to bind: " + applicationModel, e ); + } + + return applicationModel; + } + + private AssemblyHelper createAssemblyHelper( ApplicationAssembly assembly ) + { + if( assembly instanceof ApplicationAssemblyImpl ) + { + ApplicationAssemblyImpl impl = (ApplicationAssemblyImpl) assembly; + AssemblyHelper helper = impl.metaInfo().get( AssemblyHelper.class ); + if( helper != null ) + { + return helper; + } + } + return new AssemblyHelper(); + } + + private static class BindingVisitor + implements HierarchicalVisitor<Object, Object, BindingException> + { + private LayerModel layer; + private ModuleModel module; + private ModelDescriptor objectDescriptor; + private CompositeMethodModel compositeMethodModel; + + private Resolution resolution; + private final ApplicationModel applicationModel; + + private BindingVisitor( ApplicationModel applicationModel ) + { + this.applicationModel = applicationModel; + } + + @Override + public boolean visitEnter( Object visited ) + throws BindingException + { + if( visited instanceof Binder ) + { + Binder binder = (Binder) visited; + binder.bind( resolution ); + + return false; + } + else if( visited instanceof CompositeMethodModel ) + { + compositeMethodModel = (CompositeMethodModel) visited; + resolution = new Resolution( applicationModel, layer, module, objectDescriptor, compositeMethodModel, null ); + } + else if( visited instanceof ModelDescriptor ) + { + objectDescriptor = (ModelDescriptor) visited; + resolution = new Resolution( applicationModel, layer, module, objectDescriptor, null, null ); + } + else if( visited instanceof InjectedFieldModel ) + { + InjectedFieldModel fieldModel = (InjectedFieldModel) visited; + fieldModel.bind( new Resolution( applicationModel, layer, module, + objectDescriptor, compositeMethodModel, fieldModel.field() ) ); + } + else if( visited instanceof ModuleModel ) + { + module = (ModuleModel) visited; + } + else if( visited instanceof LayerModel ) + { + layer = (LayerModel) visited; + } + + return true; + } + + @Override + public boolean visitLeave( Object visited ) + throws BindingException + { + return true; + } + + @Override + public boolean visit( Object visited ) + throws BindingException + { + if( visited instanceof Binder ) + { + ( (Binder) visited ).bind( resolution ); + } + return true; + } + } +} \ No newline at end of file
