http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/UnitOfWorkInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/UnitOfWorkInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/UnitOfWorkInstance.java new file mode 100644 index 0000000..81e4907 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/UnitOfWorkInstance.java @@ -0,0 +1,510 @@ +/* + * 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.unitofwork; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import org.apache.polygene.api.common.MetaInfo; +import org.apache.polygene.api.entity.EntityComposite; +import org.apache.polygene.api.entity.EntityDescriptor; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.metrics.MetricNames; +import org.apache.polygene.api.metrics.MetricsCounter; +import org.apache.polygene.api.metrics.MetricsCounterFactory; +import org.apache.polygene.api.metrics.MetricsProvider; +import org.apache.polygene.api.metrics.MetricsTimer; +import org.apache.polygene.api.metrics.MetricsTimerFactory; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.api.type.HasTypes; +import org.apache.polygene.api.unitofwork.ConcurrentEntityModificationException; +import org.apache.polygene.api.unitofwork.NoSuchEntityException; +import org.apache.polygene.api.unitofwork.NoSuchEntityTypeException; +import org.apache.polygene.api.unitofwork.UnitOfWork; +import org.apache.polygene.api.unitofwork.UnitOfWorkCallback; +import org.apache.polygene.api.unitofwork.UnitOfWorkCompletionException; +import org.apache.polygene.api.unitofwork.UnitOfWorkException; +import org.apache.polygene.api.unitofwork.UnitOfWorkOptions; +import org.apache.polygene.api.usecase.Usecase; +import org.apache.polygene.runtime.entity.EntityInstance; +import org.apache.polygene.runtime.entity.EntityModel; +import org.apache.polygene.spi.entity.EntityState; +import org.apache.polygene.spi.entity.EntityStatus; +import org.apache.polygene.spi.entitystore.ConcurrentEntityStateModificationException; +import org.apache.polygene.spi.entitystore.EntityNotFoundException; +import org.apache.polygene.spi.entitystore.EntityStore; +import org.apache.polygene.spi.entitystore.EntityStoreUnitOfWork; +import org.apache.polygene.spi.entitystore.StateCommitter; +import org.apache.polygene.spi.module.ModuleSpi; + +import static org.apache.polygene.api.unitofwork.UnitOfWorkCallback.UnitOfWorkStatus.COMPLETED; +import static org.apache.polygene.api.unitofwork.UnitOfWorkCallback.UnitOfWorkStatus.DISCARDED; + +public final class UnitOfWorkInstance +{ + private static final ThreadLocal<Stack<UnitOfWorkInstance>> CURRENT = new ThreadLocal<Stack<UnitOfWorkInstance>>() + { + @Override + protected Stack<UnitOfWorkInstance> initialValue() + { + return new Stack<>(); + } + }; + + public static Stack<UnitOfWorkInstance> getCurrent() + { + return CURRENT.get(); + } + + private final HashMap<EntityReference, EntityInstance> instanceCache = new HashMap<>(); + private final HashMap<EntityStore, EntityStoreUnitOfWork> storeUnitOfWork = new HashMap<>(); + private final ModuleSpi module; + private final Usecase usecase; + private final Instant currentTime; + private final MetricsProvider metrics; + + private boolean open; + private boolean paused; + + private MetricsCounter metricsCounter; + private MetricsTimer metricsTimer; + private MetricsTimer.Context metricsTimerContext; + private MetaInfo metaInfo; + private List<UnitOfWorkCallback> callbacks; + + public UnitOfWorkInstance(ModuleSpi module, Usecase usecase, Instant currentTime, MetricsProvider metrics ) + { + this.module = module; + this.usecase = usecase; + this.currentTime = currentTime; + this.metrics = metrics; + + this.open = true; + getCurrent().push( this ); + this.paused = false; + startCapture(); + } + + public Instant currentTime() + { + return currentTime; + } + + public EntityStoreUnitOfWork getEntityStoreUnitOfWork( EntityStore store ) + { + EntityStoreUnitOfWork uow = storeUnitOfWork.get( store ); + if( uow == null ) + { + uow = store.newUnitOfWork( module.descriptor(), usecase, currentTime ); + storeUnitOfWork.put( store, uow ); + } + return uow; + } + + public <T> T get( EntityReference reference, + UnitOfWork uow, + Iterable<? extends EntityDescriptor> potentialModels, + Class<T> mixinType + ) + throws NoSuchEntityTypeException, NoSuchEntityException + { + checkOpen(); + + EntityInstance entityInstance = instanceCache.get( reference ); + if( entityInstance == null ) + { // Not yet in cache + + // Check if this is a root UoW, or if no parent UoW knows about this entity + EntityState entityState = null; + EntityModel model = null; + ModuleDescriptor module = null; + // Figure out what EntityStore to use + for( EntityDescriptor potentialModel : potentialModels ) + { + EntityStore store = ((ModuleSpi) potentialModel.module().instance()).entityStore(); + EntityStoreUnitOfWork storeUow = getEntityStoreUnitOfWork( store ); + try + { + entityState = storeUow.entityStateOf( potentialModel.module(), reference ); + } + catch( EntityNotFoundException e ) + { + continue; + } + + // Get the selected model + model = (EntityModel) entityState.entityDescriptor(); + module = potentialModel.module(); + } + + // Check if model was found + if( model == null ) + { + // Check if state was found + if( entityState == null ) + { + throw new NoSuchEntityException( reference, mixinType, usecase ); + } + else + { + throw new NoSuchEntityTypeException( mixinType.getName(), module.name(), module.typeLookup() ); + } + } + // Create instance + entityInstance = new EntityInstance( uow, model, entityState ); + instanceCache.put( reference, entityInstance ); + } + else + { + // Check if it has been removed + if( entityInstance.status() == EntityStatus.REMOVED ) + { + throw new NoSuchEntityException( reference, mixinType, usecase ); + } + } + + return entityInstance.proxy(); + } + + public Usecase usecase() + { + return usecase; + } + + public MetaInfo metaInfo() + { + if( metaInfo == null ) + { + metaInfo = new MetaInfo(); + } + + return metaInfo; + } + + public void pause() + { + if( !paused ) + { + paused = true; + getCurrent().pop(); + + UnitOfWorkOptions unitOfWorkOptions = metaInfo().get( UnitOfWorkOptions.class ); + if( unitOfWorkOptions == null ) + { + unitOfWorkOptions = usecase().metaInfo( UnitOfWorkOptions.class ); + } + + if( unitOfWorkOptions != null ) + { + if( unitOfWorkOptions.isPruneOnPause() ) + { + List<EntityReference> prunedInstances = null; + for( EntityInstance entityInstance : instanceCache.values() ) + { + if( entityInstance.status() == EntityStatus.LOADED ) + { + if( prunedInstances == null ) + { + prunedInstances = new ArrayList<>(); + } + prunedInstances.add( entityInstance.reference() ); + } + } + if( prunedInstances != null ) + { + prunedInstances.forEach( instanceCache::remove ); + } + } + } + } + else + { + throw new UnitOfWorkException( "Unit of work is not active" ); + } + } + + public void resume() + { + if( paused ) + { + paused = false; + getCurrent().push( this ); + } + else + { + throw new UnitOfWorkException( "Unit of work has not been paused" ); + } + } + + public void complete() + throws UnitOfWorkCompletionException + { + checkOpen(); + + // Copy list so that it cannot be modified during completion + List<UnitOfWorkCallback> currentCallbacks = callbacks == null ? null : new ArrayList<>( callbacks ); + + // Commit state to EntityStores + List<StateCommitter> committers = applyChanges(); + + // Check callbacks + notifyBeforeCompletion( currentCallbacks ); + + // Commit all changes + committers.forEach( StateCommitter::commit ); + + close(); + + // Call callbacks + notifyAfterCompletion( currentCallbacks, COMPLETED ); + + callbacks = currentCallbacks; + } + + public void discard() + { + if( !isOpen() ) + { + return; + } + close(); + + // Copy list so that it cannot be modified during completion + List<UnitOfWorkCallback> currentCallbacks = callbacks == null ? null : new ArrayList<>( callbacks ); + + // Call callbacks + notifyAfterCompletion( currentCallbacks, DISCARDED ); + storeUnitOfWork.values().forEach( EntityStoreUnitOfWork::discard ); + callbacks = currentCallbacks; + } + + private void close() + { + checkOpen(); + + if( !isPaused() ) + { + getCurrent().pop(); + } + endCapture(); + open = false; + } + + public boolean isOpen() + { + return open; + } + + public void addUnitOfWorkCallback( UnitOfWorkCallback callback ) + { + if( callbacks == null ) + { + callbacks = new ArrayList<>(); + } + + callbacks.add( callback ); + } + + public void removeUnitOfWorkCallback( UnitOfWorkCallback callback ) + { + if( callbacks != null ) + { + callbacks.remove( callback ); + } + } + + public void addEntity( EntityInstance instance ) + { + instanceCache.put( instance.reference(), instance ); + } + + private List<StateCommitter> applyChanges() + throws UnitOfWorkCompletionException + { + List<StateCommitter> committers = new ArrayList<>(); + for( EntityStoreUnitOfWork entityStoreUnitOfWork : storeUnitOfWork.values() ) + { + try + { + StateCommitter committer = entityStoreUnitOfWork.applyChanges(); + committers.add( committer ); + } + catch( Exception e ) + { + // Cancel all previously prepared stores + committers.forEach( StateCommitter::cancel ); + + if( e instanceof ConcurrentEntityStateModificationException ) + { + // If we cancelled due to concurrent modification, then create the proper exception for it! + ConcurrentEntityStateModificationException mee = (ConcurrentEntityStateModificationException) e; + Collection<EntityReference> modifiedEntityIdentities = mee.modifiedEntities(); + Map<EntityComposite, HasTypes> modifiedEntities = new HashMap<>(); + for( EntityReference modifiedEntityIdentity : modifiedEntityIdentities ) + { + instanceCache.values().stream() + .filter( instance -> instance.reference().equals( modifiedEntityIdentity ) ) + .forEach( instance -> modifiedEntities.put( instance.<EntityComposite>proxy(), instance ) ); + } + throw new ConcurrentEntityModificationException( modifiedEntities, usecase ); + } + else + { + throw new UnitOfWorkCompletionException( e ); + } + } + } + return committers; + } + + private void notifyBeforeCompletion( List<UnitOfWorkCallback> callbacks ) + throws UnitOfWorkCompletionException + { + // Notify explicitly registered callbacks + if( callbacks != null ) + { + callbacks.forEach( UnitOfWorkCallback::beforeCompletion ); + } + + // Notify entities + try + { + for( EntityInstance instance : instanceCache.values() ) + { + boolean isCallback = instance.proxy() instanceof UnitOfWorkCallback; + boolean isNotRemoved = !instance.status().equals( EntityStatus.REMOVED ); + if( isCallback && isNotRemoved ) + { + UnitOfWorkCallback callback = UnitOfWorkCallback.class.cast( instance.proxy() ); + callback.beforeCompletion(); + } + } + } + catch( UnitOfWorkCompletionException e ) + { + throw e; + } + catch( Exception e ) + { + throw new UnitOfWorkCompletionException( e ); + } + } + + private void notifyAfterCompletion( List<UnitOfWorkCallback> callbacks, + final UnitOfWorkCallback.UnitOfWorkStatus status + ) + { + if( callbacks != null ) + { + for( UnitOfWorkCallback callback : callbacks ) + { + try + { + callback.afterCompletion( status ); + } + catch( Exception e ) + { + // Ignore + } + } + } + + // Notify entities + try + { + for( EntityInstance instance : instanceCache.values() ) + { + boolean isCallback = instance.proxy() instanceof UnitOfWorkCallback; + boolean isNotRemoved = !instance.status().equals( EntityStatus.REMOVED ); + if( isCallback && isNotRemoved ) + { + UnitOfWorkCallback callback = UnitOfWorkCallback.class.cast( instance.proxy() ); + callback.afterCompletion( status ); + } + } + } + catch( Exception e ) + { + // Ignore + } + } + + public void checkOpen() + { + if( !isOpen() ) + { + throw new UnitOfWorkException( "Unit of work has been closed" ); + } + } + + public boolean isPaused() + { + return paused; + } + + @Override + public String toString() + { + return "UnitOfWork " + hashCode() + "(" + usecase + "): entities:" + instanceCache.size(); + } + + public void remove( EntityReference entityReference ) + { + instanceCache.remove( entityReference ); + } + + private void startCapture() + { + getMetricsCounter().increment(); + metricsTimerContext = getMetricsTimer().start(); + } + + private void endCapture() + { + getMetricsCounter().decrement(); + metricsTimerContext.stop(); + metricsTimerContext = null; + } + + private MetricsCounter getMetricsCounter() + { + if( metricsCounter == null ) + { + MetricsCounterFactory metricsFactory = metrics.createFactory( MetricsCounterFactory.class ); + metricsCounter = metricsFactory.createCounter( MetricNames.nameFor( module, UnitOfWork.class, "counter" ) ); + } + return metricsCounter; + } + + private MetricsTimer getMetricsTimer() + { + if( metricsTimer == null ) + { + MetricsTimerFactory metricsFactory = metrics.createFactory( MetricsTimerFactory.class ); + metricsTimer = metricsFactory.createTimer( MetricNames.nameFor( module, UnitOfWork.class, "timer" ) ); + } + return metricsTimer; + } +}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/value/ManyAssociationValueState.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/value/ManyAssociationValueState.java b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ManyAssociationValueState.java new file mode 100644 index 0000000..c386802 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ManyAssociationValueState.java @@ -0,0 +1,106 @@ +/* + * 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.value; + +import java.util.Iterator; +import java.util.List; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.spi.entity.ManyAssociationState; + +/** + * ManyAssociationState implementation for Value composites. + */ +public class ManyAssociationValueState + implements ManyAssociationState +{ + private List<EntityReference> references; + + public ManyAssociationValueState( List<EntityReference> references ) + { + this.references = references; + } + + @Override + public int count() + { + return references.size(); + } + + @Override + public boolean contains( EntityReference entityReference ) + { + return references.contains( entityReference ); + } + + @Override + public boolean add( int i, EntityReference entityReference ) + { + if( references.contains( entityReference ) ) + { + return false; + } + + references.add( i, entityReference ); + return true; + } + + @Override + public boolean remove( EntityReference entity ) + { + boolean removed = references.remove( entity ); + return removed; + } + + @Override + public EntityReference get( int i ) + { + return references.get( i ); + } + + @Override + public Iterator<EntityReference> iterator() + { + final Iterator<EntityReference> iter = references.iterator(); + + return new Iterator<EntityReference>() + { + EntityReference current; + + @Override + public boolean hasNext() + { + return iter.hasNext(); + } + + @Override + public EntityReference next() + { + current = iter.next(); + return current; + } + + @Override + public void remove() + { + iter.remove(); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/value/NamedAssociationValueState.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/value/NamedAssociationValueState.java b/core/runtime/src/main/java/org/apache/polygene/runtime/value/NamedAssociationValueState.java new file mode 100644 index 0000000..50c20ca --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/value/NamedAssociationValueState.java @@ -0,0 +1,85 @@ +/* + * 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.value; + +import java.util.Iterator; +import java.util.Map; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.spi.entity.NamedAssociationState; + +public class NamedAssociationValueState + implements NamedAssociationState +{ + private final Map<String, EntityReference> references; + + public NamedAssociationValueState( Map<String, EntityReference> references ) + { + this.references = references; + } + + @Override + public int count() + { + return references.size(); + } + + @Override + public boolean containsName( String name ) + { + return references.containsKey( name ); + } + + @Override + public boolean put( String name, EntityReference entityReference ) + { + return references.put( name, entityReference ) != null; + } + + @Override + public boolean remove( String name ) + { + return references.remove( name ) != null; + } + + @Override + public EntityReference get( String name ) + { + return references.get( name ); + } + + @Override + public String nameOf( EntityReference entityReference ) + { + for( Map.Entry<String, EntityReference> entry : references.entrySet() ) + { + if( entry.getValue().equals( entityReference ) ) + { + return entry.getKey(); + } + } + return null; + } + + @Override + public Iterator<String> iterator() + { + return references.keySet().iterator(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/value/ReferenceProperty.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/value/ReferenceProperty.java b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ReferenceProperty.java new file mode 100644 index 0000000..6dd6d18 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ReferenceProperty.java @@ -0,0 +1,54 @@ +/* + * 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.value; + +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.property.Property; + +/** + * The reference for an Association + */ +public class ReferenceProperty + implements Property<EntityReference> +{ + EntityReference reference; + + public ReferenceProperty() + { + } + + public ReferenceProperty( EntityReference reference ) + { + this.reference = reference; + } + + @Override + public EntityReference get() + { + return reference; + } + + @Override + public void set( EntityReference newValue ) + throws IllegalArgumentException, IllegalStateException + { + reference = newValue; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueBuilderInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueBuilderInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueBuilderInstance.java new file mode 100644 index 0000000..7ecb745 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueBuilderInstance.java @@ -0,0 +1,86 @@ +/* + * 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.value; + +import org.apache.polygene.api.association.AssociationStateHolder; +import org.apache.polygene.api.common.ConstructionException; +import org.apache.polygene.api.composite.Composite; +import org.apache.polygene.api.value.NoSuchValueException; +import org.apache.polygene.api.value.ValueBuilder; +import org.apache.polygene.api.value.ValueDescriptor; +import org.apache.polygene.runtime.composite.StateResolver; +import org.apache.polygene.runtime.structure.ModuleInstance; + +/** + * Implementation of ValueBuilder + */ +public final class ValueBuilderInstance<T> + implements ValueBuilder<T> +{ + + private final ModuleInstance currentModule; + private final ValueInstance prototypeInstance; + + public ValueBuilderInstance( ValueDescriptor compositeModel, + ModuleInstance currentModule, + StateResolver stateResolver + ) + { + ValueStateInstance state = new ValueStateInstance( compositeModel, currentModule, stateResolver ); + ValueModel model = (ValueModel) compositeModel; + prototypeInstance = model.newValueInstance( state ); + prototypeInstance.prepareToBuild(); + this.currentModule = currentModule; + } + + @Override + public T prototype() + { + return prototypeInstance.<T>proxy(); + } + + @Override + public AssociationStateHolder state() + { + return prototypeInstance.state(); + } + + @Override + public <K> K prototypeFor( Class<K> mixinType ) + { + return prototypeInstance.newProxy( mixinType ); + } + + @Override + @SuppressWarnings( "unchecked" ) + public T newInstance() + throws ConstructionException + { + Class<Composite> valueType = (Class<Composite>) prototypeInstance.types().findFirst().orElse( null ); + + ValueDescriptor valueModel = currentModule.typeLookup().lookupValueModel( valueType ); + + if( valueModel == null ) + { + throw new NoSuchValueException( valueType.getName(), currentModule.name(), currentModule.typeLookup() ); + } + return new ValueBuilderWithPrototype<>( valueModel, currentModule, prototype() ).newInstance(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueBuilderWithPrototype.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueBuilderWithPrototype.java b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueBuilderWithPrototype.java new file mode 100644 index 0000000..c9a1023 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueBuilderWithPrototype.java @@ -0,0 +1,202 @@ +/* + * 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.value; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.AssociationStateHolder; +import org.apache.polygene.api.common.ConstructionException; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.property.PropertyDescriptor; +import org.apache.polygene.api.value.ValueBuilder; +import org.apache.polygene.api.value.ValueComposite; +import org.apache.polygene.api.value.ValueDescriptor; +import org.apache.polygene.runtime.composite.FunctionStateResolver; +import org.apache.polygene.runtime.composite.MixinModel; +import org.apache.polygene.runtime.composite.MixinsModel; +import org.apache.polygene.runtime.composite.StateResolver; +import org.apache.polygene.runtime.composite.UsesInstance; +import org.apache.polygene.runtime.injection.InjectionContext; +import org.apache.polygene.runtime.structure.ModuleInstance; + +/** + * Implementation of ValueBuilder with a prototype supplied + */ +public class ValueBuilderWithPrototype<T> + implements ValueBuilder<T> +{ + private ValueInstance prototypeInstance; + private final ValueModel valueModel; + + public ValueBuilderWithPrototype( ValueDescriptor compositeModelModule, + ModuleInstance currentModule, + T prototype + ) + { + valueModel = (ValueModel) compositeModelModule; + MixinsModel mixinsModel = valueModel.mixinsModel(); + Object[] mixins = mixinsModel.newMixinHolder(); + final ValueStateInstance prototypeState = ValueInstance.valueInstanceOf( (ValueComposite) prototype ).state(); + StateResolver resolver = new FunctionStateResolver( + new PropertyDescriptorFunction( prototypeState ), + new AssociationDescriptorEntityReferenceFunction( prototypeState ), + new AssociationDescriptorIterableFunction( prototypeState ), + new AssociationDescriptorMapFunction( prototypeState ) + ); + ValueStateInstance state = new ValueStateInstance( compositeModelModule, currentModule, resolver ); + ValueInstance valueInstance = new ValueInstance( + valueModel, + mixins, + state + ); + + int i = 0; + InjectionContext injectionContext = new InjectionContext( valueInstance, UsesInstance.EMPTY_USES, state ); + for( MixinModel mixinModel : mixinsModel.mixinModels() ) + { + mixins[ i++ ] = mixinModel.newInstance( injectionContext ); + } + + valueInstance.prepareToBuild(); + this.prototypeInstance = valueInstance; + } + + @Override + public T prototype() + { + verifyUnderConstruction(); + return prototypeInstance.<T>proxy(); + } + + @Override + public AssociationStateHolder state() + { + verifyUnderConstruction(); + return prototypeInstance.state(); + } + + @Override + public <K> K prototypeFor( Class<K> mixinType ) + { + verifyUnderConstruction(); + return prototypeInstance.newProxy( mixinType ); + } + + @Override + public T newInstance() + throws ConstructionException + { + verifyUnderConstruction(); + + // Set correct info's (immutable) on the state + prototypeInstance.prepareBuilderState(); + + // Check that it is valid + valueModel.checkConstraints( prototypeInstance.state() ); + + try + { + return prototypeInstance.<T>proxy(); + } + finally + { + // Invalidate builder + prototypeInstance = null; + } + } + + private void verifyUnderConstruction() + { + if( prototypeInstance == null ) + { + throw new IllegalStateException( "ValueBuilder instances cannot be reused" ); + } + } + + private static class PropertyDescriptorFunction + implements Function<PropertyDescriptor, Object> + { + private final ValueStateInstance prototypeState; + + public PropertyDescriptorFunction( ValueStateInstance prototypeState ) + { + this.prototypeState = prototypeState; + } + + @Override + public Object apply( PropertyDescriptor descriptor ) + { + return prototypeState.propertyFor( descriptor.accessor() ).get(); + } + } + + private static class AssociationDescriptorEntityReferenceFunction + implements Function<AssociationDescriptor, EntityReference> + { + private final ValueStateInstance prototypeState; + + public AssociationDescriptorEntityReferenceFunction( ValueStateInstance prototypeState ) + { + this.prototypeState = prototypeState; + } + + @Override + public EntityReference apply( AssociationDescriptor descriptor ) + { + return prototypeState.associationFor( descriptor.accessor() ).reference(); + } + } + + private static class AssociationDescriptorIterableFunction + implements Function<AssociationDescriptor, Stream<EntityReference>> + { + private final ValueStateInstance prototypeState; + + public AssociationDescriptorIterableFunction( ValueStateInstance prototypeState ) + { + this.prototypeState = prototypeState; + } + + @Override + public Stream<EntityReference> apply( AssociationDescriptor descriptor ) + { + return prototypeState.manyAssociationFor( descriptor.accessor() ).references(); + } + } + + private static class AssociationDescriptorMapFunction + implements Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> + { + private final ValueStateInstance prototypeState; + + public AssociationDescriptorMapFunction( ValueStateInstance prototypeState ) + { + this.prototypeState = prototypeState; + } + + @Override + public Stream<Map.Entry<String, EntityReference>> apply( AssociationDescriptor descriptor ) + { + return prototypeState.namedAssociationFor( descriptor.accessor() ).references(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueBuilderWithState.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueBuilderWithState.java b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueBuilderWithState.java new file mode 100644 index 0000000..236e3b9 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueBuilderWithState.java @@ -0,0 +1,97 @@ +/* + * 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.value; + +import org.apache.polygene.api.association.AssociationStateHolder; +import org.apache.polygene.api.common.ConstructionException; +import org.apache.polygene.api.value.ValueBuilder; +import org.apache.polygene.api.value.ValueDescriptor; +import org.apache.polygene.runtime.composite.StateResolver; +import org.apache.polygene.runtime.structure.ModuleInstance; + +public class ValueBuilderWithState<T> implements ValueBuilder<T> +{ + private final ValueDescriptor model; + private ValueInstance prototypeInstance; + + public ValueBuilderWithState( ValueDescriptor compositeModelModule, + ModuleInstance currentModule, + StateResolver stateResolver ) + { + ValueStateInstance state = new ValueStateInstance( compositeModelModule, currentModule, stateResolver ); + ValueInstance instance = ((ValueModel) compositeModelModule).newValueInstance( state ); + instance.prepareToBuild(); + this.model = compositeModelModule; + this.prototypeInstance = instance; + } + + @Override + public T prototype() + { + verifyUnderConstruction(); + return prototypeInstance.<T>proxy(); + } + + @Override + public AssociationStateHolder state() + { + verifyUnderConstruction(); + return prototypeInstance.state(); + } + + @Override + public <K> K prototypeFor( Class<K> mixinType ) + { + verifyUnderConstruction(); + + return prototypeInstance.newProxy( mixinType ); + } + + @Override + public T newInstance() + throws ConstructionException + { + verifyUnderConstruction(); + + // Set correct info's (immutable) on the state + prototypeInstance.prepareBuilderState(); + + // Check that it is valid + ((ValueModel) model).checkConstraints( prototypeInstance.state() ); + + try + { + return prototypeInstance.<T>proxy(); + } + finally + { + // Invalidate builder + prototypeInstance = null; + } + } + + private void verifyUnderConstruction() + { + if( prototypeInstance == null ) + { + throw new IllegalStateException( "ValueBuilder instances cannot be reused" ); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueInstance.java new file mode 100644 index 0000000..12ba7d9 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueInstance.java @@ -0,0 +1,172 @@ +/* + * 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.value; + +import java.lang.reflect.Proxy; +import org.apache.polygene.api.composite.CompositeInstance; +import org.apache.polygene.api.value.ValueComposite; +import org.apache.polygene.runtime.composite.MixinsInstance; +import org.apache.polygene.runtime.composite.TransientInstance; +import org.apache.polygene.runtime.property.PropertyInstance; +import org.apache.polygene.spi.module.ModuleSpi; + +/** + * ValueComposite instance + */ +public final class ValueInstance + extends TransientInstance + implements CompositeInstance, MixinsInstance +{ + public static ValueInstance valueInstanceOf( ValueComposite composite ) + { + return (ValueInstance) Proxy.getInvocationHandler( composite ); + } + + public ValueInstance( ValueModel compositeModel, + Object[] mixins, + ValueStateInstance state + ) + { + super( compositeModel, mixins, state ); + } + + /** + * Perform equals with {@code o} argument. + * <p> + * The definition of equals() for the Value is that if both the state and descriptor are equal, + * then the values are equal. + * </p> + * + * @param o The other object to compare. + * + * @return Returns a {@code boolean} indicator whether this object is equals the other. + */ + @Override + public boolean equals( Object o ) + { + if( this == o ) + { + return true; + } + if( o == null || !Proxy.isProxyClass( o.getClass() ) ) + { + return false; + } + + try + { + ValueInstance that = (ValueInstance) Proxy.getInvocationHandler( o ); + // Descriptor equality + if( !descriptor().equals( that.descriptor() ) ) + { + return false; + } + // State equality + return state.equals( that.state ); + } + catch( ClassCastException e ) + { + return false; + } + } + + @Override + public ValueStateInstance state() + { + return (ValueStateInstance) state; + } + + @Override + public ValueModel descriptor() + { + return (ValueModel) compositeModel; + } + + /** + * When a ValueBuilder is about to start, ensure that all state has builder infos, i.e. they are mutable. + */ + public void prepareToBuild() + { + descriptor().state().properties().forEach( propertyDescriptor -> { + PropertyInstance<Object> propertyInstance = + (PropertyInstance<Object>) state.propertyFor( propertyDescriptor.accessor() ); + + propertyInstance.prepareToBuild( propertyDescriptor ); + } ); + + descriptor().state().associations().forEach( associationDescriptor -> { + state().associationFor( associationDescriptor.accessor() ) + .setAssociationInfo( associationDescriptor.getBuilderInfo() ); + } ); + + descriptor().state().manyAssociations().forEach( associationDescriptor -> { + state().manyAssociationFor( associationDescriptor.accessor() ) + .setAssociationInfo( associationDescriptor.getBuilderInfo() ); + } ); + + descriptor().state().namedAssociations().forEach( associationDescriptor -> { + state().namedAssociationFor( associationDescriptor.accessor() ) + .setAssociationInfo( associationDescriptor.getBuilderInfo() ); + } ); + } + + /** + * When a ValueBuilder is finished and is about to instantiate a Value, call this to ensure that the state has correct + * settings, i.e. is immutable. + */ + public void prepareBuilderState() + { + descriptor().state().properties().forEach( propertyDescriptor -> { + PropertyInstance<Object> propertyInstance = + (PropertyInstance<Object>) state.propertyFor( propertyDescriptor.accessor() ); + propertyInstance.prepareBuilderState( propertyDescriptor ); + } ); + + descriptor().state().associations().forEach( associationDescriptor -> { + state().associationFor( associationDescriptor.accessor() ).setAssociationInfo( associationDescriptor ); + } ); + + descriptor().state().manyAssociations().forEach( associationDescriptor -> { + state().manyAssociationFor( associationDescriptor.accessor() ).setAssociationInfo( associationDescriptor ); + } ); + + descriptor().state().namedAssociations().forEach( associationDescriptor -> { + state().namedAssociationFor( associationDescriptor.accessor() ).setAssociationInfo( associationDescriptor ); + } ); + } + + /** + * Calculate hash code. + * + * @return the hashcode of this instance. + */ + @Override + public int hashCode() + { + int hash = compositeModel.hashCode() * 23; // Descriptor + return hash + state.hashCode() * 5; // State + } + + @Override + public String toString() + { + return ( (ModuleSpi) module().instance() ).valueSerialization().serialize( this.<ValueComposite>proxy() ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueModel.java new file mode 100644 index 0000000..2148095 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueModel.java @@ -0,0 +1,145 @@ +/* + * 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.value; + +import java.lang.reflect.Member; +import java.lang.reflect.Type; +import java.util.List; +import java.util.stream.Stream; +import org.apache.polygene.api.common.MetaInfo; +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.api.constraint.ConstraintViolationException; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.api.type.ValueCompositeType; +import org.apache.polygene.api.util.Classes; +import org.apache.polygene.api.value.ValueDescriptor; +import org.apache.polygene.runtime.composite.CompositeMethodsModel; +import org.apache.polygene.runtime.composite.CompositeModel; +import org.apache.polygene.runtime.composite.MixinModel; +import org.apache.polygene.runtime.composite.MixinsModel; +import org.apache.polygene.runtime.composite.UsesInstance; +import org.apache.polygene.runtime.injection.InjectionContext; +import org.apache.polygene.runtime.unitofwork.UnitOfWorkInstance; + +/** + * Model for ValueComposites + */ +public final class ValueModel extends CompositeModel + implements ValueDescriptor +{ + private ValueCompositeType valueType; + + public ValueModel( final ModuleDescriptor module, + final List<Class<?>> types, + final Visibility visibility, + final MetaInfo metaInfo, + final MixinsModel mixinsModel, + final ValueStateModel stateModel, + final CompositeMethodsModel compositeMethodsModel + ) + { + super( module, types, visibility, metaInfo, mixinsModel, stateModel, compositeMethodsModel ); + + valueType = new ValueCompositeType( this ); + } + + @Override + public ValueCompositeType valueType() + { + return valueType; + } + + @Override + public ValueStateModel state() + { + return (ValueStateModel) super.state(); + } + + // This method is ONLY called by ValueBuilders + void checkConstraints( ValueStateInstance state ) + throws ConstraintViolationException + { + stateModel.properties().forEach( + propertyModel -> + { + try + { + propertyModel.checkConstraints( state.propertyFor( propertyModel.accessor() ).get() ); + } + catch( ConstraintViolationException e ) + { + throw new ConstraintViolationException( "<builder>", propertyModel.valueType() + .types(), (Member) propertyModel.accessor(), e.constraintViolations() ); + } + } + ); + + // IF no UnitOfWork is active, then the Association checks shouldn't be done. + if( UnitOfWorkInstance.getCurrent().empty() ) + { + return; + } + ( (ValueStateModel) stateModel ).associations().forEach( + associationModel -> + { + try + { + associationModel.checkConstraints( state.associationFor( associationModel.accessor() ).get() ); + } + catch( ConstraintViolationException e ) + { + Stream<? extends Type> types = Classes.interfacesOf( associationModel.type() ); + throw new ConstraintViolationException( "<builder>", types, (Member) associationModel.accessor(), e.constraintViolations() ); + } + } + ); + + ( (ValueStateModel) stateModel ).manyAssociations().forEach( associationModel -> + associationModel.checkAssociationConstraints( state + .manyAssociationFor( associationModel + .accessor() ) ) + ); + + ( (ValueStateModel) stateModel ).namedAssociations().forEach( associationModel -> + associationModel.checkAssociationConstraints( state + .namedAssociationFor( associationModel + .accessor() ) ) + ); + } + + public ValueInstance newValueInstance( ValueStateInstance state ) + { + Object[] mixins = mixinsModel.newMixinHolder(); + + ValueInstance instance = new ValueInstance( this, mixins, state ); + + // Instantiate all mixins + int i = 0; + InjectionContext injectionContext = new InjectionContext( instance, UsesInstance.EMPTY_USES, state ); + for( MixinModel mixinModel : mixinsModel.mixinModels() ) + { + mixins[ i++ ] = mixinModel.newInstance( injectionContext ); + } + + // Return + return instance; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueStateInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueStateInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueStateInstance.java new file mode 100644 index 0000000..aab6597 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueStateInstance.java @@ -0,0 +1,234 @@ +/* + * 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.value; + +import java.lang.reflect.AccessibleObject; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import org.apache.polygene.api.association.AssociationStateHolder; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.value.ValueDescriptor; +import org.apache.polygene.runtime.association.AssociationInfo; +import org.apache.polygene.runtime.association.AssociationInstance; +import org.apache.polygene.runtime.association.ManyAssociationInstance; +import org.apache.polygene.runtime.association.NamedAssociationInstance; +import org.apache.polygene.runtime.composite.StateResolver; +import org.apache.polygene.runtime.property.PropertyInfo; +import org.apache.polygene.runtime.property.PropertyInstance; +import org.apache.polygene.runtime.structure.ModuleInstance; +import org.apache.polygene.runtime.unitofwork.EntityFunction; + +import static java.util.stream.Collectors.toList; +import static org.apache.polygene.api.util.Collectors.toMap; + +/** + * TODO + */ +public final class ValueStateInstance + implements AssociationStateHolder +{ + private final Map<AccessibleObject, PropertyInstance<?>> properties; + private final Map<AccessibleObject, AssociationInstance<?>> associations; + private final Map<AccessibleObject, ManyAssociationInstance<?>> manyAssociations; + private final Map<AccessibleObject, NamedAssociationInstance<?>> namedAssociations; + + public ValueStateInstance( Map<AccessibleObject, PropertyInstance<?>> properties, + Map<AccessibleObject, AssociationInstance<?>> associations, + Map<AccessibleObject, ManyAssociationInstance<?>> manyAssociations, + Map<AccessibleObject, NamedAssociationInstance<?>> namedAssociations + ) + { + this.properties = properties; + this.associations = associations; + this.manyAssociations = manyAssociations; + this.namedAssociations = namedAssociations; + } + + public ValueStateInstance( ValueDescriptor compositeModelModule, + ModuleInstance currentModule, + StateResolver stateResolver + ) + { + EntityFunction entityFunction = new EntityFunction( currentModule.unitOfWorkFactory() ); + + ValueModel valueModel = (ValueModel) compositeModelModule; + this.properties = new LinkedHashMap<>(); + valueModel.state().properties().forEach( propertyDescriptor -> { + PropertyInfo builderInfo = propertyDescriptor.getBuilderInfo(); + Object value = stateResolver.getPropertyState( propertyDescriptor ); + PropertyInstance<Object> propertyInstance = new PropertyInstance<>( builderInfo, value ); + properties.put( propertyDescriptor.accessor(), propertyInstance ); + } ); + + this.associations = new LinkedHashMap<>(); + valueModel.state().associations().forEach( associationDescriptor -> { + AssociationInfo builderInfo = associationDescriptor.getBuilderInfo(); + EntityReference value = stateResolver.getAssociationState( associationDescriptor ); + AssociationInstance<Object> associationInstance1 = new AssociationInstance<>( + builderInfo, + entityFunction, + new ReferenceProperty( value ) ); + associations.put( associationDescriptor.accessor(), associationInstance1 ); + } ); + + this.manyAssociations = new LinkedHashMap<>(); + valueModel.state().manyAssociations().forEach( associationDescriptor -> { + AssociationInfo builderInfo = associationDescriptor.getBuilderInfo(); + List<EntityReference> value = stateResolver.getManyAssociationState( associationDescriptor ) + .collect( toList() ); + ManyAssociationValueState manyAssociationState = new ManyAssociationValueState( value ); + ManyAssociationInstance<Object> associationInstance = new ManyAssociationInstance<>( + builderInfo, + entityFunction, + manyAssociationState ); + manyAssociations.put( associationDescriptor.accessor(), associationInstance ); + } ); + + this.namedAssociations = new LinkedHashMap<>(); + valueModel.state().namedAssociations().forEach( associationDescriptor -> { + AssociationInfo builderInfo = associationDescriptor.getBuilderInfo(); + Map<String, EntityReference> value = stateResolver.getNamedAssociationState( associationDescriptor ) + .collect( toMap( LinkedHashMap::new ) ); + NamedAssociationValueState namedAssociationState = new NamedAssociationValueState( value ); + NamedAssociationInstance<Object> associationInstance = new NamedAssociationInstance<>( + builderInfo, + entityFunction, + namedAssociationState ); + namedAssociations.put( associationDescriptor.accessor(), associationInstance ); + } ); + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> PropertyInstance<T> propertyFor( AccessibleObject accessor ) + throws IllegalArgumentException + { + PropertyInstance<T> property = (PropertyInstance<T>) properties.get( accessor ); + + if( property == null ) + { + throw new IllegalArgumentException( "No such property:" + accessor ); + } + + return property; + } + + @Override + public Stream<PropertyInstance<?>> properties() + { + return properties.values().stream(); + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> AssociationInstance<T> associationFor( AccessibleObject accessor ) + { + AssociationInstance<T> association = (AssociationInstance<T>) associations.get( accessor ); + + if( association == null ) + { + throw new IllegalArgumentException( "No such association:" + accessor ); + } + + return association; + } + + @Override + public Stream<AssociationInstance<?>> allAssociations() + { + return associations.values().stream(); + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> ManyAssociationInstance<T> manyAssociationFor( AccessibleObject accessor ) + { + ManyAssociationInstance<T> manyAssociation = (ManyAssociationInstance<T>) manyAssociations.get( accessor ); + + if( manyAssociation == null ) + { + throw new IllegalArgumentException( "No such many-association:" + accessor ); + } + + return manyAssociation; + } + + @Override + public Stream<ManyAssociationInstance<?>> allManyAssociations() + { + return manyAssociations.values().stream(); + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> NamedAssociationInstance<T> namedAssociationFor( AccessibleObject accessor ) + { + NamedAssociationInstance<T> namedAssociation = (NamedAssociationInstance<T>) namedAssociations.get( accessor ); + + if( namedAssociation == null ) + { + throw new IllegalArgumentException( "No such named-association:" + accessor ); + } + + return namedAssociation; + } + + @Override + public Stream<? extends NamedAssociationInstance<?>> allNamedAssociations() + { + return namedAssociations.values().stream(); + } + + @SuppressWarnings( "SimplifiableIfStatement" ) + @Override + public boolean equals( Object obj ) + { + if( !( obj instanceof ValueStateInstance ) ) + { + return false; + } + ValueStateInstance state = (ValueStateInstance) obj; + if( !properties.equals( state.properties ) ) + { + return false; + } + if( !associations.equals( state.associations ) ) + { + return false; + } + if( !manyAssociations.equals( state.manyAssociations ) ) + { + return false; + } + return namedAssociations.equals( state.namedAssociations ); + } + + @Override + public int hashCode() + { + int result = properties.hashCode(); + result = 31 * result + associations.hashCode(); + result = 31 * result + manyAssociations.hashCode(); + result = 31 * result + namedAssociations.hashCode(); + return result; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueStateModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueStateModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueStateModel.java new file mode 100644 index 0000000..73c6a17 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValueStateModel.java @@ -0,0 +1,133 @@ +/* + * 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.value; + +import java.util.stream.Stream; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.AssociationStateDescriptor; +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.association.AssociationModel; +import org.apache.polygene.runtime.association.AssociationsModel; +import org.apache.polygene.runtime.association.ManyAssociationModel; +import org.apache.polygene.runtime.association.ManyAssociationsModel; +import org.apache.polygene.runtime.association.NamedAssociationModel; +import org.apache.polygene.runtime.association.NamedAssociationsModel; +import org.apache.polygene.runtime.composite.StateModel; +import org.apache.polygene.runtime.property.PropertiesModel; + +/** + * Model for ValueComposite state. + */ +public final class ValueStateModel + extends StateModel + implements AssociationStateDescriptor +{ + private final AssociationsModel associationsModel; + private final ManyAssociationsModel manyAssociationsModel; + private final NamedAssociationsModel namedAssociationsModel; + + public ValueStateModel( PropertiesModel propertiesModel, + AssociationsModel associationsModel, + ManyAssociationsModel manyAssociationsModel, + NamedAssociationsModel namedAssociationsModel + ) + { + super( propertiesModel ); + this.associationsModel = associationsModel; + this.manyAssociationsModel = manyAssociationsModel; + this.namedAssociationsModel = namedAssociationsModel; + } + + @Override + public AssociationDescriptor getAssociationByName( String name ) + { + return associationsModel.getAssociationByName( name ); + } + + @Override + public AssociationDescriptor getAssociationByQualifiedName( QualifiedName name ) + { + return associationsModel.getAssociationByQualifiedName( name ); + } + + @Override + public AssociationDescriptor getManyAssociationByName( String name ) + { + return manyAssociationsModel.getManyAssociationByName( name ); + } + + @Override + public AssociationDescriptor getManyAssociationByQualifiedName( QualifiedName name ) + { + return manyAssociationsModel.getManyAssociationByQualifiedName( name ); + } + + @Override + public AssociationDescriptor getNamedAssociationByName( String name ) + { + return namedAssociationsModel.getNamedAssociationByName( name ); + } + + @Override + public AssociationDescriptor getNamedAssociationByQualifiedName( QualifiedName name ) + { + return namedAssociationsModel.getNamedAssociationByQualifiedName( name ); + } + + @Override + public Stream<AssociationModel> associations() + { + return associationsModel.associations(); + } + + @Override + public Stream<ManyAssociationModel> manyAssociations() + { + return manyAssociationsModel.manyAssociations(); + } + + @Override + public Stream<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/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValuesModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValuesModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValuesModel.java new file mode 100644 index 0000000..caa875b --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/value/ValuesModel.java @@ -0,0 +1,68 @@ +/* + * 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.value; + +import java.util.List; +import java.util.stream.Stream; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.api.util.VisitableHierarchy; +import org.apache.polygene.api.value.ValueDescriptor; + +/** + * JAVADOC + */ +public final class ValuesModel + implements VisitableHierarchy<Object, Object> +{ + private final List<ValueModel> valueModels; + + public ValuesModel( List<ValueModel> valueModels ) + { + this.valueModels = valueModels; + } + + public Stream<ValueModel> models() + { + return valueModels.stream(); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> visitor ) + throws ThrowableType + { + if( visitor.visitEnter( this ) ) + { + for( ValueModel valueModel : valueModels ) + { + if( !valueModel.accept( visitor ) ) + { + break; + } + } + } + return visitor.visitLeave( this ); + } + + public Stream<? extends ValueDescriptor> stream() + { + return valueModels.stream(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/zest/runtime/ZestRuntimeImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/ZestRuntimeImpl.java b/core/runtime/src/main/java/org/apache/zest/runtime/ZestRuntimeImpl.java deleted file mode 100644 index 269496c..0000000 --- a/core/runtime/src/main/java/org/apache/zest/runtime/ZestRuntimeImpl.java +++ /dev/null @@ -1,360 +0,0 @@ -/* - * 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; - -import java.lang.reflect.InvocationHandler; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Stream; -import org.apache.zest.api.PolygeneAPI; -import org.apache.zest.api.association.AbstractAssociation; -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.AssociationWrapper; -import org.apache.zest.api.association.ManyAssociation; -import org.apache.zest.api.association.ManyAssociationWrapper; -import org.apache.zest.api.association.NamedAssociation; -import org.apache.zest.api.association.NamedAssociationWrapper; -import org.apache.zest.api.composite.Composite; -import org.apache.zest.api.composite.CompositeDescriptor; -import org.apache.zest.api.composite.CompositeInstance; -import org.apache.zest.api.composite.ModelDescriptor; -import org.apache.zest.api.composite.TransientComposite; -import org.apache.zest.api.composite.TransientDescriptor; -import org.apache.zest.api.entity.EntityComposite; -import org.apache.zest.api.entity.EntityDescriptor; -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.property.PropertyWrapper; -import org.apache.zest.api.property.StateHolder; -import org.apache.zest.api.service.ServiceComposite; -import org.apache.zest.api.service.ServiceDescriptor; -import org.apache.zest.api.service.ServiceReference; -import org.apache.zest.api.structure.ModuleDescriptor; -import org.apache.zest.api.unitofwork.UnitOfWork; -import org.apache.zest.api.value.ValueComposite; -import org.apache.zest.api.value.ValueDescriptor; -import org.apache.zest.bootstrap.ApplicationAssemblyFactory; -import org.apache.zest.bootstrap.ApplicationModelFactory; -import org.apache.zest.bootstrap.PolygeneRuntime; -import org.apache.zest.runtime.association.AbstractAssociationInstance; -import org.apache.zest.runtime.bootstrap.ApplicationAssemblyFactoryImpl; -import org.apache.zest.runtime.bootstrap.ApplicationModelFactoryImpl; -import org.apache.zest.runtime.composite.ProxyReferenceInvocationHandler; -import org.apache.zest.runtime.composite.TransientInstance; -import org.apache.zest.runtime.entity.EntityInstance; -import org.apache.zest.runtime.property.PropertyInstance; -import org.apache.zest.runtime.service.ImportedServiceReferenceInstance; -import org.apache.zest.runtime.service.ServiceInstance; -import org.apache.zest.runtime.service.ServiceReferenceInstance; -import org.apache.zest.runtime.unitofwork.ModuleUnitOfWork; -import org.apache.zest.runtime.value.ValueInstance; -import org.apache.zest.spi.PolygeneSPI; -import org.apache.zest.spi.entity.EntityState; - -import static java.lang.reflect.Proxy.getInvocationHandler; -import static org.apache.zest.runtime.composite.TransientInstance.compositeInstanceOf; - -/** - * Incarnation of Polygene. - */ -public final class PolygeneRuntimeImpl - implements PolygeneSPI, PolygeneRuntime -{ - private final ApplicationAssemblyFactory applicationAssemblyFactory; - private final ApplicationModelFactory applicationModelFactory; - - public PolygeneRuntimeImpl() - { - applicationAssemblyFactory = new ApplicationAssemblyFactoryImpl(); - applicationModelFactory = new ApplicationModelFactoryImpl(); - } - - @Override - public ApplicationAssemblyFactory applicationAssemblyFactory() - { - return applicationAssemblyFactory; - } - - @Override - public ApplicationModelFactory applicationModelFactory() - { - return applicationModelFactory; - } - - @Override - public PolygeneAPI api() - { - return this; - } - - @Override - public PolygeneSPI spi() - { - return this; - } - - // API - - @Override - @SuppressWarnings( "unchecked" ) - public <T> T dereference( T composite ) - { - InvocationHandler handler = getInvocationHandler( composite ); - if( handler instanceof ProxyReferenceInvocationHandler ) - { - return (T) ( (ProxyReferenceInvocationHandler) handler ).proxy(); - } - if( handler instanceof CompositeInstance ) - { - return composite; - } - return null; - } - - @Override - public ModuleDescriptor moduleOf( Object compositeOrServiceReferenceOrUow ) - { - if( compositeOrServiceReferenceOrUow instanceof TransientComposite ) - { - TransientComposite composite = (TransientComposite) compositeOrServiceReferenceOrUow; - return TransientInstance.compositeInstanceOf( composite ).module(); - } - else if( compositeOrServiceReferenceOrUow instanceof EntityComposite ) - { - EntityComposite composite = (EntityComposite) compositeOrServiceReferenceOrUow; - return EntityInstance.entityInstanceOf( composite ).module(); - } - else if( compositeOrServiceReferenceOrUow instanceof ValueComposite ) - { - ValueComposite composite = (ValueComposite) compositeOrServiceReferenceOrUow; - return ValueInstance.valueInstanceOf( composite ).module(); - } - else if( compositeOrServiceReferenceOrUow instanceof ServiceComposite ) - { - ServiceComposite composite = (ServiceComposite) compositeOrServiceReferenceOrUow; - InvocationHandler handler = getInvocationHandler( composite ); - if( handler instanceof ServiceInstance ) - { - return ( (ServiceInstance) handler ).module(); - } - return ( (ServiceReferenceInstance.ServiceInvocationHandler) handler ).module(); - } - else if( compositeOrServiceReferenceOrUow instanceof UnitOfWork ) - { - ModuleUnitOfWork unitOfWork = (ModuleUnitOfWork) compositeOrServiceReferenceOrUow; - return unitOfWork.module(); - } - else if( compositeOrServiceReferenceOrUow instanceof ServiceReferenceInstance ) - { - ServiceReferenceInstance<?> reference = (ServiceReferenceInstance<?>) compositeOrServiceReferenceOrUow; - return reference.module(); - } - else if( compositeOrServiceReferenceOrUow instanceof ImportedServiceReferenceInstance ) - { - ImportedServiceReferenceInstance<?> importedServiceReference - = (ImportedServiceReferenceInstance<?>) compositeOrServiceReferenceOrUow; - return importedServiceReference.module(); - } - throw new IllegalArgumentException( "Wrong type. Must be one of " - + Arrays.asList( TransientComposite.class, ValueComposite.class, - ServiceComposite.class, ServiceReference.class, - UnitOfWork.class ) ); - } - - @Override - public ModelDescriptor modelDescriptorFor( Object compositeOrServiceReference ) - { - if( compositeOrServiceReference instanceof TransientComposite ) - { - TransientComposite composite = (TransientComposite) compositeOrServiceReference; - return TransientInstance.compositeInstanceOf( composite ).descriptor(); - } - else if( compositeOrServiceReference instanceof EntityComposite ) - { - EntityComposite composite = (EntityComposite) compositeOrServiceReference; - return EntityInstance.entityInstanceOf( composite ).descriptor(); - } - else if( compositeOrServiceReference instanceof ValueComposite ) - { - ValueComposite composite = (ValueComposite) compositeOrServiceReference; - return ValueInstance.valueInstanceOf( composite ).descriptor(); - } - else if( compositeOrServiceReference instanceof ServiceComposite ) - { - ServiceComposite composite = (ServiceComposite) compositeOrServiceReference; - InvocationHandler handler = getInvocationHandler( composite ); - if( handler instanceof ServiceInstance ) - { - return ( (ServiceInstance) handler ).descriptor(); - } - return ( (ServiceReferenceInstance.ServiceInvocationHandler) handler ).descriptor(); - } - else if( compositeOrServiceReference instanceof ServiceReferenceInstance ) - { - ServiceReferenceInstance<?> reference = (ServiceReferenceInstance<?>) compositeOrServiceReference; - return reference.serviceDescriptor(); - } - else if( compositeOrServiceReference instanceof ImportedServiceReferenceInstance ) - { - ImportedServiceReferenceInstance<?> importedServiceReference - = (ImportedServiceReferenceInstance<?>) compositeOrServiceReference; - return importedServiceReference.serviceDescriptor(); - } - throw new IllegalArgumentException( "Wrong type. Must be one of " - + Arrays.asList( TransientComposite.class, ValueComposite.class, - ServiceComposite.class, ServiceReference.class ) ); - } - - @Override - public CompositeDescriptor compositeDescriptorFor( Object compositeOrServiceReference ) - { - return (CompositeDescriptor) modelDescriptorFor( compositeOrServiceReference ); - } - - // Descriptors - - @Override - public TransientDescriptor transientDescriptorFor( Object transsient ) - { - if( transsient instanceof TransientComposite ) - { - TransientInstance transientInstance = compositeInstanceOf( (Composite) transsient ); - return (TransientDescriptor) transientInstance.descriptor(); - } - throw new IllegalArgumentException( "Wrong type. Must be subtype of " + TransientComposite.class ); - } - - @Override - public StateHolder stateOf( TransientComposite composite ) - { - return TransientInstance.compositeInstanceOf( composite ).state(); - } - - @Override - public EntityDescriptor entityDescriptorFor( Object entity ) - { - if( entity instanceof EntityComposite ) - { - EntityInstance entityInstance = (EntityInstance) getInvocationHandler( entity ); - return entityInstance.entityModel(); - } - throw new IllegalArgumentException( "Wrong type. Must be subtype of " + EntityComposite.class ); - } - - @Override - public AssociationStateHolder stateOf( EntityComposite composite ) - { - return EntityInstance.entityInstanceOf( composite ).state(); - } - - @Override - public ValueDescriptor valueDescriptorFor( Object value ) - { - if( value instanceof ValueComposite ) - { - ValueInstance valueInstance = ValueInstance.valueInstanceOf( (ValueComposite) value ); - return valueInstance.descriptor(); - } - throw new IllegalArgumentException( "Wrong type. Must be subtype of " + ValueComposite.class ); - } - - @Override - public AssociationStateHolder stateOf( ValueComposite composite ) - { - return ValueInstance.valueInstanceOf( composite ).state(); - } - - @Override - public ServiceDescriptor serviceDescriptorFor( Object service ) - { - if( service instanceof ServiceReferenceInstance ) - { - ServiceReferenceInstance<?> ref = (ServiceReferenceInstance<?>) service; - return ref.serviceDescriptor(); - } - if( service instanceof ServiceComposite ) - { - ServiceComposite composite = (ServiceComposite) service; - return (ServiceDescriptor) ServiceInstance.serviceInstanceOf( composite ).descriptor(); - } - throw new IllegalArgumentException( "Wrong type. Must be subtype of " - + ServiceComposite.class + " or " + ServiceReference.class ); - } - - @Override - public PropertyDescriptor propertyDescriptorFor( Property<?> property ) - { - while( property instanceof PropertyWrapper ) - { - property = ( (PropertyWrapper) property ).next(); - } - - return (PropertyDescriptor) ( (PropertyInstance<?>) property ).propertyInfo(); - } - - @Override - public AssociationDescriptor associationDescriptorFor( AbstractAssociation association ) - { - while( association instanceof AssociationWrapper ) - { - association = ( (AssociationWrapper) association ).next(); - } - - while( association instanceof ManyAssociationWrapper ) - { - association = ( (ManyAssociationWrapper) association ).next(); - } - - while( association instanceof NamedAssociationWrapper ) - { - association = ( (NamedAssociationWrapper) association ).next(); - } - - return (AssociationDescriptor) ( (AbstractAssociationInstance) association ).associationInfo(); - } - - // SPI - @Override - public EntityState entityStateOf( EntityComposite composite ) - { - return EntityInstance.entityInstanceOf( composite ).entityState(); - } - - @Override - public EntityReference entityReferenceOf( Association<?> assoc ) - { - return assoc.reference(); - } - - @Override - public Stream<EntityReference> entityReferencesOf( ManyAssociation<?> assoc ) - { - return assoc.references(); - } - - @Override - public Stream<Map.Entry<String, EntityReference>> entityReferencesOf( NamedAssociation<?> assoc ) - { - return assoc.references(); - } -}
