You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by ni...@apache.org on 2015/07/30 21:48:06 UTC
[07/80] [partial] zest-java git commit: First round of changes to
move to org.apache.zest namespace.
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookup.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookup.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookup.java
new file mode 100644
index 0000000..0ebfbbb
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookup.java
@@ -0,0 +1,629 @@
+/*
+ * Copyright (c) 2008-2012, Rickard Öberg.
+ * Copyright (c) 2008-2012, Niclas Hedhman.
+ * Copyright (c) 2012, Paul Merlin.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zest.runtime.structure;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.zest.api.composite.AmbiguousTypeException;
+import org.apache.zest.api.composite.ModelDescriptor;
+import org.apache.zest.api.service.NoSuchServiceException;
+import org.apache.zest.api.service.ServiceReference;
+import org.apache.zest.functional.Function;
+import org.apache.zest.functional.Specification;
+import org.apache.zest.functional.Specifications;
+import org.apache.zest.runtime.composite.TransientModel;
+import org.apache.zest.runtime.entity.EntityModel;
+import org.apache.zest.runtime.object.ObjectModel;
+import org.apache.zest.runtime.value.ValueModel;
+import org.apache.zest.spi.module.ModelModule;
+
+import static org.apache.zest.api.common.Visibility.application;
+import static org.apache.zest.api.common.Visibility.layer;
+import static org.apache.zest.api.common.Visibility.module;
+import static org.apache.zest.api.util.Classes.RAW_CLASS;
+import static org.apache.zest.api.util.Classes.interfacesOf;
+import static org.apache.zest.functional.Iterables.cast;
+import static org.apache.zest.functional.Iterables.filter;
+import static org.apache.zest.functional.Iterables.first;
+import static org.apache.zest.functional.Iterables.flatten;
+import static org.apache.zest.functional.Iterables.flattenIterables;
+import static org.apache.zest.functional.Iterables.iterable;
+import static org.apache.zest.functional.Iterables.toList;
+import static org.apache.zest.functional.Iterables.unique;
+
+/**
+ * Central place for Composite Type lookups.
+ */
+public class TypeLookup
+{
+
+ // Constructor parameters
+ private final ModuleInstance moduleInstance;
+ // Eager instance objects
+ private final Map<Class<?>, ModelModule<ObjectModel>> objectModels;
+ private final Map<Class<?>, ModelModule<TransientModel>> transientModels;
+ private final Map<Class<?>, ModelModule<ValueModel>> valueModels;
+ private final Map<Class<?>, Iterable<ModelModule<EntityModel>>> allEntityModels;
+ private final Map<Class<?>, ModelModule<EntityModel>> unambiguousEntityModels;
+ private final Map<Type, ServiceReference<?>> serviceReferences;
+ private final Map<Type, Iterable<ServiceReference<?>>> servicesReferences;
+
+ /**
+ * Create a new TypeLookup bound to the given ModuleInstance.
+ *
+ * @param moduleInstance ModuleInstance bound to this TypeLookup
+ */
+ /* package */ TypeLookup( ModuleInstance moduleInstance )
+ {
+ // Constructor parameters
+ this.moduleInstance = moduleInstance;
+
+ // Eager instance objects
+ objectModels = new ConcurrentHashMap<>();
+ transientModels = new ConcurrentHashMap<>();
+ valueModels = new ConcurrentHashMap<>();
+ allEntityModels = new ConcurrentHashMap<>();
+ unambiguousEntityModels = new ConcurrentHashMap<>();
+ serviceReferences = new ConcurrentHashMap<>();
+ servicesReferences = new ConcurrentHashMap<>();
+ }
+
+ /**
+ * Lookup first Object Model matching the given Type.
+ *
+ * <p>First, if Object Models exactly match the given type, the closest one (Visibility then Assembly order) is returned.
+ * Multiple <b>exact</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+ *
+ * <p>Second, if Object Models match a type assignable to the given type, the closest one (Visibility then Assembly order) is returned.
+ * Multiple <b>assignable</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+ *
+ * <p>Type lookup is done lazily and cached.</p>
+ *
+ * @param type Looked up Type
+ *
+ * @return First matching Object Model
+ */
+ @SuppressWarnings( { "raw", "unchecked" } )
+ /* package */ ModelModule<ObjectModel> lookupObjectModel( final Class type )
+ {
+ ModelModule<ObjectModel> model = objectModels.get( type );
+
+ if( model == null )
+ {
+ // Unambiguously and lazily resolve ObjectModel
+ Iterable<ModelModule<ObjectModel>> flatten = flatten(
+ ambiguousTypeCheck( type,
+ findModels( new ExactTypeLookupSpecification( type ),
+ moduleInstance.visibleObjects( module ),
+ moduleInstance.layerInstance().visibleObjects( layer ),
+ moduleInstance.layerInstance().visibleObjects( application ),
+ moduleInstance.layerInstance()
+ .usedLayersInstance()
+ .visibleObjects() ) ),
+ ambiguousTypeCheck( type,
+ findModels( new AssignableTypeLookupSpecification( type ),
+ moduleInstance.visibleObjects( module ),
+ moduleInstance.layerInstance().visibleObjects( layer ),
+ moduleInstance.layerInstance().visibleObjects( application ),
+ moduleInstance.layerInstance()
+ .usedLayersInstance()
+ .visibleObjects() ) ) );
+
+ model = first( flatten );
+
+ if( model != null )
+ {
+ objectModels.put( type, model );
+ }
+ }
+
+ return model;
+ }
+
+ /**
+ * Lookup first Transient Model matching the given Type.
+ *
+ * <p>First, if Transient Models exactly match the given type, the closest one (Visibility then Assembly order) is returned.
+ * Multiple <b>exact</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+ *
+ * <p>Second, if Transient Models match a type assignable to the given type, the closest one (Visibility then Assembly order) is returned.
+ * Multiple <b>assignable</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+ *
+ * <p>Type lookup is done lazily and cached.</p>
+ *
+ * @param type Looked up Type
+ *
+ * @return First matching Transient Model
+ */
+ @SuppressWarnings( { "raw", "unchecked" } )
+ /* package */ ModelModule<TransientModel> lookupTransientModel( final Class type )
+ {
+ ModelModule<TransientModel> model = transientModels.get( type );
+
+ if( model == null )
+ {
+ // Unambiguously and lazily resolve TransientModel
+ Iterable<ModelModule<TransientModel>> allModels = flatten(
+ ambiguousTypeCheck( type,
+ findModels( new ExactTypeLookupSpecification( type ),
+ moduleInstance.visibleTransients( module ),
+ moduleInstance.layerInstance().visibleTransients( layer ),
+ moduleInstance.layerInstance().visibleTransients( application ),
+ moduleInstance.layerInstance().usedLayersInstance().visibleTransients()
+ )
+ ),
+
+ ambiguousTypeCheck( type,
+ findModels( new AssignableTypeLookupSpecification( type ),
+ moduleInstance.visibleTransients( module ),
+ moduleInstance.layerInstance().visibleTransients( layer ),
+ moduleInstance.layerInstance().visibleTransients( application ),
+ moduleInstance.layerInstance().usedLayersInstance().visibleTransients()
+ )
+ )
+ );
+ model = first( allModels );
+
+ if( model != null )
+ {
+ transientModels.put( type, model );
+ }
+ }
+
+ return model;
+ }
+
+ /**
+ * Lookup first Value Model matching the given Type.
+ *
+ * <p>First, if Value Models exactly match the given type, the closest one (Visibility then Assembly order) is returned.
+ * Multiple <b>exact</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+ *
+ * <p>Second, if Value Models match a type assignable to the given type, the closest one (Visibility then Assembly order) is returned.
+ * Multiple <b>assignable</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+ *
+ * <p>Type lookup is done lazily and cached.</p>
+ *
+ * @param type Looked up Type
+ *
+ * @return First matching Value Model
+ */
+ @SuppressWarnings( { "raw", "unchecked" } )
+ public ModelModule<ValueModel> lookupValueModel( final Class type )
+ {
+ ModelModule<ValueModel> model = valueModels.get( type );
+
+ if( model == null )
+ {
+ // Unambiguously and lazily resolve ValueModel
+ Iterable<ModelModule<ValueModel>> flatten = flatten(
+ ambiguousTypeCheck( type,
+ findModels( new ExactTypeLookupSpecification( type ),
+ moduleInstance.visibleValues( module ),
+ moduleInstance.layerInstance().visibleValues( layer ),
+ moduleInstance.layerInstance().visibleValues( application ),
+ moduleInstance.layerInstance().usedLayersInstance().visibleValues() ) ),
+ ambiguousTypeCheck( type,
+ findModels( new AssignableTypeLookupSpecification( type ),
+ moduleInstance.visibleValues( module ),
+ moduleInstance.layerInstance().visibleValues( layer ),
+ moduleInstance.layerInstance().visibleValues( application ),
+ moduleInstance.layerInstance().usedLayersInstance().visibleValues()
+ )
+ )
+ );
+
+ model = first( flatten );
+
+ if( model != null )
+ {
+ valueModels.put( type, model );
+ }
+ }
+
+ return model;
+ }
+
+ /**
+ * Lookup first Entity Model matching the given Type.
+ *
+ * <p>First, if Entity Models exactly match the given type, the closest one (Visibility then Assembly order) is returned.
+ * Multiple <b>exact</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+ *
+ * <p>Second, if Entity Models match a type assignable to the given type, the closest one (Visibility then Assembly order) is returned.
+ * Multiple <b>assignable</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+ *
+ * <p>Type lookup is done lazily and cached.</p>
+ *
+ * <p><b>Should be used for creational use cases only.</b> For non-creational use cases see
+ * {@link #lookupEntityModels(java.lang.Class)}.</p>
+ *
+ * @param type Looked up Type
+ *
+ * @return First matching Entity Model
+ */
+ @SuppressWarnings( { "raw", "unchecked" } )
+ /* package */ ModelModule<EntityModel> lookupEntityModel( final Class type )
+ {
+ ModelModule<EntityModel> model = unambiguousEntityModels.get( type );
+
+ if( model == null )
+ {
+ // Unambiguously and lazily resolve EntityModels
+ Iterable<ModelModule<EntityModel>> allModels = flatten(
+ ambiguousTypeCheck( type,
+ findModels( new ExactTypeLookupSpecification( type ),
+ moduleInstance.visibleEntities( module ),
+ moduleInstance.layerInstance().visibleEntities( layer ),
+ moduleInstance.layerInstance().visibleEntities( application ),
+ moduleInstance.layerInstance()
+ .usedLayersInstance()
+ .visibleEntities() ) ),
+ ambiguousTypeCheck( type,
+ findModels( new AssignableTypeLookupSpecification( type ),
+ moduleInstance.visibleEntities( module ),
+ moduleInstance.layerInstance().visibleEntities( layer ),
+ moduleInstance.layerInstance().visibleEntities( application ),
+ moduleInstance.layerInstance().usedLayersInstance().visibleEntities()
+ )
+ )
+ );
+
+ model = first( allModels );
+
+ if( model != null )
+ {
+ unambiguousEntityModels.put( type, model );
+ }
+ }
+
+ return model;
+ }
+
+ /**
+ * Lookup all Entity Models matching the given Type.
+ *
+ * <p>Returned Iterable contains, in order, Entity Models that: </p>
+ *
+ * <ul>
+ * <li>exactly match the given type, in Visibility then Assembly order ;</li>
+ * <li>match a type assignable to the given type, in Visibility then Assembly order.</li>
+ * </ul>
+ *
+ * <p>Multiple <b>exact</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+ * <p>Multiple <b>assignable</b> matches are <b>allowed</b> to enable polymorphic fetches and queries.</p>
+ *
+ * <p>Type lookup is done lazily and cached.</p>
+ *
+ * <p><b>Should be used for non-creational use cases only.</b> For creational use cases see
+ * {@link #lookupEntityModel(java.lang.Class)}.</p>
+ *
+ * @param type Looked up Type
+ *
+ * @return All matching Entity Models
+ */
+ @SuppressWarnings( { "raw", "unchecked" } )
+ /* package */ Iterable<ModelModule<EntityModel>> lookupEntityModels( final Class type )
+ {
+ Iterable<ModelModule<EntityModel>> models = allEntityModels.get( type );
+ if( models == null )
+ {
+ // Ambiguously and lasily resolve EntityModels
+ Iterable<ModelModule<EntityModel>> matchingEntityModels = flatten(
+ ambiguousTypeCheck( type,
+ findModels( new ExactTypeLookupSpecification( type ),
+ moduleInstance.visibleEntities( module ),
+ moduleInstance.layerInstance().visibleEntities( layer ),
+ moduleInstance.layerInstance().visibleEntities( application ),
+ moduleInstance.layerInstance().usedLayersInstance().visibleEntities()
+ )
+ ),
+ findModels( new AssignableTypeLookupSpecification( type ),
+ moduleInstance.visibleEntities( module ),
+ moduleInstance.layerInstance().visibleEntities( layer ),
+ moduleInstance.layerInstance().visibleEntities( application ),
+ moduleInstance.layerInstance().usedLayersInstance().visibleEntities() ) );
+
+ // Don't return the same EntityModel multiple times
+ matchingEntityModels = unique( matchingEntityModels );
+
+ models = toList( matchingEntityModels );
+ allEntityModels.put( type, models );
+ }
+ return models;
+ }
+
+ /**
+ * Lookup first ServiceReference matching the given Type.
+ *
+ * <p>Type lookup is done lazily and cached.</p>
+ *
+ * <p>See {@link #lookupServiceReferences(java.lang.reflect.Type)}.</p>
+ *
+ * @param <T> Service Type
+ * @param serviceType Looked up Type
+ *
+ * @return First matching ServiceReference
+ */
+ /* package */
+ @SuppressWarnings( "unchecked" )
+ <T> ServiceReference<T> lookupServiceReference( Type serviceType )
+ {
+ ServiceReference<?> serviceReference = serviceReferences.get( serviceType );
+ if( serviceReference == null )
+ {
+ // Lazily resolve ServiceReference
+ serviceReference = first( lookupServiceReferences( serviceType ) );
+ if( serviceReference != null )
+ {
+ serviceReferences.put( serviceType, serviceReference );
+ }
+ }
+
+ if( serviceReference == null )
+ {
+ throw new NoSuchServiceException( RAW_CLASS.map( serviceType ).getName(), moduleInstance.name() );
+ }
+
+ return (ServiceReference<T>) serviceReference;
+ }
+
+ /**
+ * Lookup all ServiceReferences matching the given Type.
+ *
+ * <p>Returned Iterable contains, in order, ServiceReferences that: </p>
+ *
+ * <ul>
+ * <li>exactly match the given type, in Visibility then Assembly order ;</li>
+ * <li>match a type assignable to the given type, in Visibility then Assembly order.</li>
+ * </ul>
+ *
+ * <p>Multiple <b>exact</b> matches with the same Visibility are <b>allowed</b> to enable polymorphic lookup/injection.</p>
+ * <p>Multiple <b>assignable</b> matches with the same Visibility are <b>allowed</b> for the very same reason.</p>
+ *
+ * <p>Type lookup is done lazily and cached.</p>
+ *
+ * @param <T> Service Type
+ * @param serviceType Looked up Type
+ *
+ * @return All matching ServiceReferences
+ */
+ @SuppressWarnings( "unchecked" )
+ /* package */ <T> Iterable<ServiceReference<T>> lookupServiceReferences( final Type serviceType )
+ {
+ Iterable<ServiceReference<?>> serviceRefs = servicesReferences.get( serviceType );
+ if( serviceRefs == null )
+ {
+ // Lazily resolve ServicesReferences
+ Iterable<ServiceReference<?>> matchingServices = flatten(
+ findServiceReferences( new ExactTypeLookupSpecification( serviceType ),
+ moduleInstance.visibleServices( module ),
+ moduleInstance.layerInstance().visibleServices( layer ),
+ moduleInstance.layerInstance().visibleServices( application ),
+ moduleInstance.layerInstance().usedLayersInstance().visibleServices() ),
+ findServiceReferences( new AssignableTypeLookupSpecification( serviceType ),
+ moduleInstance.visibleServices( module ),
+ moduleInstance.layerInstance().visibleServices( layer ),
+ moduleInstance.layerInstance().visibleServices( application ),
+ moduleInstance.layerInstance().usedLayersInstance().visibleServices() ) );
+
+ // Don't return the same ServiceReference multiple times
+ matchingServices = unique( matchingServices );
+
+ serviceRefs = toList( matchingServices );
+ servicesReferences.put( serviceType, serviceRefs );
+ }
+
+ return cast( serviceRefs );
+ }
+
+ @SuppressWarnings( { "raw", "unchecked" } )
+ private static <T extends ModelDescriptor> Iterable<ModelModule<T>> findModels( Specification<Iterable<Class<?>>> specification,
+ Iterable<ModelModule<T>>... models
+ )
+ {
+ Specification<ModelModule<T>> spec = Specifications.translate( new ModelModuleTypesFunction(), specification );
+ Iterable<ModelModule<T>> flattened = flattenIterables( iterable( models ) );
+ return filter( spec, flattened );
+ }
+
+ @SuppressWarnings( { "raw", "unchecked" } )
+ private static Iterable<ServiceReference<?>> findServiceReferences( Specification<Iterable<Class<?>>> specification,
+ Iterable<ServiceReference<?>>... references
+ )
+ {
+ Specification<ServiceReference<?>> spec = Specifications.translate( new ServiceReferenceTypesFunction(), specification );
+ Iterable<ServiceReference<?>> flattened = flattenIterables( iterable( references ) );
+ return filter( spec, flattened );
+ }
+
+ /**
+ * Check if the list of models contains several ones with the same visibility. If yes, then
+ * throw an AmbiguousTypeException
+ */
+ @SuppressWarnings( "raw" )
+ private static <T extends ModelDescriptor> Iterable<ModelModule<T>> ambiguousTypeCheck( final Class type,
+ final Iterable<ModelModule<T>> models
+ )
+ {
+ return new Iterable<ModelModule<T>>()
+ {
+
+ @Override
+ public Iterator<ModelModule<T>> iterator()
+ {
+ ModelModule<T> current = null;
+ List<ModelModule<T>> ambiguous = null;
+ List<ModelModule<T>> results = new ArrayList<>();
+ for( ModelModule<T> model : models )
+ {
+ if( current != null && !model.equals( current ) )
+ {
+ if( model.model().visibility() == current.model().visibility() )
+ {
+ if( ambiguous == null )
+ {
+ ambiguous = new ArrayList<>();
+ }
+ ambiguous.add( model );
+ }
+ }
+ else
+ {
+ current = model;
+ }
+ results.add( model );
+ }
+ if( ambiguous != null )
+ {
+ // Check if we had any ambiguities
+ ambiguous.add( current );
+ throw new AmbiguousTypeException( "More than one type matches " + type.getName() + ":" + ambiguous );
+ }
+ // Ambiguity check done, and no ambiguities found. Return results
+ return results.iterator();
+ }
+ };
+ }
+
+ private static class ModelModuleTypesFunction<T extends ModelDescriptor>
+ implements Function<ModelModule<T>, Iterable<Class<?>>>
+ {
+
+ @Override
+ public Iterable<Class<?>> map( ModelModule<T> modelModule )
+ {
+ return modelModule.model().types();
+ }
+ }
+
+ private static class ServiceReferenceTypesFunction
+ implements Function<ServiceReference<?>, Iterable<Class<?>>>
+ {
+
+ @Override
+ public Iterable<Class<?>> map( ServiceReference<?> serviceReference )
+ {
+ return serviceReference.types();
+ }
+ }
+
+ private static abstract class AbstractTypeLookupSpecification
+ implements Specification<Iterable<Class<?>>>
+ {
+
+ protected final Type lookedUpType;
+
+ private AbstractTypeLookupSpecification( Type lookedUpType )
+ {
+ this.lookedUpType = lookedUpType;
+ }
+
+ @Override
+ public final boolean satisfiedBy( Iterable<Class<?>> types )
+ {
+ if( lookedUpType instanceof Class )
+ {
+ // Straight class assignability check
+ return checkClassMatch( types, (Class) lookedUpType );
+ }
+ else
+ {
+ if( lookedUpType instanceof ParameterizedType )
+ {
+ // Foo<Bar> check
+ // First check Foo
+ ParameterizedType parameterizedType = (ParameterizedType) lookedUpType;
+ if( !checkClassMatch( types, (Class) parameterizedType.getRawType() ) )
+ {
+ return false;
+ }
+ // Then check Bar
+ for( Type intf : interfacesOf( types ) )
+ {
+ if( intf.equals( lookedUpType ) )
+ {
+ // All parameters are the same - ok!
+ return true;
+ }
+ }
+ return false;
+ }
+ else if( lookedUpType instanceof WildcardType )
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private boolean checkClassMatch( Iterable<Class<?>> candidates, Class<?> lookedUpType )
+ {
+ for( Class<?> candidate : candidates )
+ {
+ if( checkClassMatch( candidate, lookedUpType ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected abstract boolean checkClassMatch( Class<?> candidate, Class<?> lookedUpType );
+ }
+
+ private static final class ExactTypeLookupSpecification
+ extends AbstractTypeLookupSpecification
+ {
+
+ private ExactTypeLookupSpecification( Type lookedupType )
+ {
+ super( lookedupType );
+ }
+
+ @Override
+ protected boolean checkClassMatch( Class<?> candidate, Class<?> lookedUpType )
+ {
+ return candidate.equals( lookedUpType );
+ }
+ }
+
+ private static final class AssignableTypeLookupSpecification
+ extends AbstractTypeLookupSpecification
+ {
+
+ private AssignableTypeLookupSpecification( Type lookedupType )
+ {
+ super( lookedupType );
+ }
+
+ @Override
+ protected boolean checkClassMatch( Class<?> candidate, Class<?> lookedUpType )
+ {
+ return !candidate.equals( lookedUpType ) && lookedUpType.isAssignableFrom( candidate );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersInstance.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersInstance.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersInstance.java
new file mode 100644
index 0000000..12893cf
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersInstance.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2008, Rickard Öberg. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.zest.runtime.structure;
+
+import java.util.List;
+import org.apache.zest.api.common.Visibility;
+import org.apache.zest.api.composite.TransientDescriptor;
+import org.apache.zest.api.entity.EntityDescriptor;
+import org.apache.zest.api.object.ObjectDescriptor;
+import org.apache.zest.api.service.ServiceReference;
+import org.apache.zest.api.value.ValueDescriptor;
+import org.apache.zest.functional.Function;
+import org.apache.zest.spi.module.ModelModule;
+
+import static org.apache.zest.functional.Iterables.*;
+
+/**
+ * JAVADOC
+ */
+public final class UsedLayersInstance
+{
+ private final List<LayerInstance> usedLayerInstances;
+
+ public UsedLayersInstance( List<LayerInstance> usedLayerInstances )
+ {
+ this.usedLayerInstances = usedLayerInstances;
+ }
+
+ /* package */ Iterable<ModelModule<ObjectDescriptor>> visibleObjects()
+ {
+ return flattenIterables( map( new Function<LayerInstance, Iterable<ModelModule<ObjectDescriptor>>>()
+ {
+ @Override
+ public Iterable<ModelModule<ObjectDescriptor>> map( LayerInstance layerInstance )
+ {
+ return layerInstance.visibleObjects( Visibility.application );
+ }
+ }, usedLayerInstances ) );
+ }
+
+ /* package */ Iterable<ModelModule<TransientDescriptor>> visibleTransients()
+ {
+ return flattenIterables( map( new Function<LayerInstance, Iterable<ModelModule<TransientDescriptor>>>()
+ {
+ @Override
+ public Iterable<ModelModule<TransientDescriptor>> map( LayerInstance layerInstance )
+ {
+ return layerInstance.visibleTransients( Visibility.application );
+ }
+ }, usedLayerInstances ) );
+ }
+
+ /* package */ Iterable<ModelModule<EntityDescriptor>> visibleEntities()
+ {
+ return flattenIterables( map( new Function<LayerInstance, Iterable<ModelModule<EntityDescriptor>>>()
+ {
+ @Override
+ public Iterable<ModelModule<EntityDescriptor>> map( LayerInstance layerInstance )
+ {
+ return layerInstance.visibleEntities( Visibility.application );
+ }
+ }, usedLayerInstances ) );
+ }
+
+ /* package */ Iterable<ModelModule<ValueDescriptor>> visibleValues()
+ {
+ return flattenIterables( map( new Function<LayerInstance, Iterable<ModelModule<ValueDescriptor>>>()
+ {
+ @Override
+ public Iterable<ModelModule<ValueDescriptor>> map( LayerInstance layerInstance )
+ {
+ return layerInstance.visibleValues( Visibility.application );
+ }
+ }, usedLayerInstances ) );
+ }
+
+ /* package */ Iterable<ServiceReference<?>> visibleServices()
+ {
+ return flattenIterables( map( new Function<LayerInstance, Iterable<ServiceReference<?>>>()
+ {
+ @Override
+ public Iterable<ServiceReference<?>> map( LayerInstance layerInstance )
+ {
+ return layerInstance.visibleServices( Visibility.application );
+ }
+ }, usedLayerInstances ) );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersModel.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersModel.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersModel.java
new file mode 100644
index 0000000..1f098a9
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersModel.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2008, Rickard Öberg. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.zest.runtime.structure;
+
+import java.util.List;
+import org.apache.zest.api.structure.UsedLayersDescriptor;
+import org.apache.zest.functional.HierarchicalVisitor;
+import org.apache.zest.functional.VisitableHierarchy;
+
+/**
+ * JAVADOC
+ */
+public final class UsedLayersModel
+ implements UsedLayersDescriptor, VisitableHierarchy<Object, Object>
+{
+ private final List<LayerModel> usedLayers;
+
+ public UsedLayersModel( List<LayerModel> usedLayers )
+ {
+ this.usedLayers = usedLayers;
+ }
+
+ @Override
+ public Iterable<LayerModel> layers()
+ {
+ return usedLayers;
+ }
+
+ @Override
+ public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> visitor )
+ throws ThrowableType
+ {
+ if( visitor.visitEnter( this ) )
+ {
+ for( LayerModel usedLayer : usedLayers )
+ {
+ if( !usedLayer.accept( visitor ) )
+ {
+ break;
+ }
+ }
+ }
+
+ return visitor.visitLeave( this );
+ }
+
+ public UsedLayersInstance newInstance( List<LayerInstance> usedLayerInstances )
+ {
+ return new UsedLayersInstance( usedLayerInstances );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/VisibilitySpecification.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/VisibilitySpecification.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/VisibilitySpecification.java
new file mode 100644
index 0000000..bb646eb
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/VisibilitySpecification.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.zest.runtime.structure;
+
+import org.apache.zest.api.common.Visibility;
+import org.apache.zest.api.composite.ModelDescriptor;
+import org.apache.zest.functional.Specification;
+
+/**
+ * TODO
+ */
+public class VisibilitySpecification
+ implements Specification<ModelDescriptor>
+{
+ public static final Specification<ModelDescriptor> MODULE = new VisibilitySpecification( Visibility.module );
+ public static final Specification<ModelDescriptor> LAYER = new VisibilitySpecification( Visibility.layer );
+ public static final Specification<ModelDescriptor> APPLICATION = new VisibilitySpecification( Visibility.application );
+
+ private final Visibility visibility;
+
+ public VisibilitySpecification( Visibility visibility )
+ {
+ this.visibility = visibility;
+ }
+
+ @Override
+ public boolean satisfiedBy( ModelDescriptor item )
+ {
+ return item.visibility().ordinal() >= visibility.ordinal();
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/types/ValueTypeFactory.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/types/ValueTypeFactory.java b/core/runtime/src/main/java/org/apache/zest/runtime/types/ValueTypeFactory.java
new file mode 100644
index 0000000..2c7951c
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/types/ValueTypeFactory.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2009 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zest.runtime.types;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import org.apache.zest.api.common.InvalidApplicationException;
+import org.apache.zest.api.common.MetaInfo;
+import org.apache.zest.api.common.Visibility;
+import org.apache.zest.api.type.CollectionType;
+import org.apache.zest.api.type.EnumType;
+import org.apache.zest.api.type.MapType;
+import org.apache.zest.api.type.Serialization;
+import org.apache.zest.api.type.ValueCompositeType;
+import org.apache.zest.api.type.ValueType;
+import org.apache.zest.api.util.Classes;
+import org.apache.zest.api.value.ValueComposite;
+import org.apache.zest.functional.HierarchicalVisitorAdapter;
+import org.apache.zest.functional.Iterables;
+import org.apache.zest.functional.Specifications;
+import org.apache.zest.runtime.association.AssociationsModel;
+import org.apache.zest.runtime.association.ManyAssociationsModel;
+import org.apache.zest.runtime.association.NamedAssociationsModel;
+import org.apache.zest.runtime.composite.CompositeMethodsModel;
+import org.apache.zest.runtime.composite.MixinsModel;
+import org.apache.zest.runtime.property.PropertiesModel;
+import org.apache.zest.runtime.structure.LayerModel;
+import org.apache.zest.runtime.structure.ModuleModel;
+import org.apache.zest.runtime.structure.UsedLayersModel;
+import org.apache.zest.runtime.value.ValueModel;
+import org.apache.zest.runtime.value.ValueStateModel;
+import org.apache.zest.runtime.value.ValuesModel;
+
+public class ValueTypeFactory
+{
+ private static final ValueTypeFactory instance = new ValueTypeFactory();
+
+ public static ValueTypeFactory instance()
+ {
+ return instance;
+ }
+
+ @SuppressWarnings( {"raw", "unchecked"} )
+ public ValueType newValueType( Type type,
+ Class declaringClass,
+ Class compositeType,
+ LayerModel layer,
+ ModuleModel module,
+ Serialization.Variant variant
+ )
+ {
+ ValueType valueType = null;
+ if( CollectionType.isCollection( type ) )
+ {
+ if( type instanceof ParameterizedType )
+ {
+ ParameterizedType pt = (ParameterizedType) type;
+ Type collectionType = pt.getActualTypeArguments()[ 0 ];
+ if( collectionType instanceof TypeVariable )
+ {
+ TypeVariable collectionTypeVariable = (TypeVariable) collectionType;
+ collectionType = Classes.resolveTypeVariable( collectionTypeVariable, declaringClass, compositeType );
+ }
+ ValueType collectedType = newValueType( collectionType, declaringClass, compositeType, layer, module, variant );
+ valueType = new CollectionType( Classes.RAW_CLASS.map( type ), collectedType );
+ }
+ else
+ {
+ ValueType collectedType = newValueType( Object.class, declaringClass, compositeType, layer, module, variant );
+ valueType = new CollectionType( Classes.RAW_CLASS.map( type ), collectedType );
+ }
+ }
+ else if( MapType.isMap( type ) )
+ {
+ if( type instanceof ParameterizedType )
+ {
+ ParameterizedType pt = (ParameterizedType) type;
+ Type keyType = pt.getActualTypeArguments()[ 0 ];
+ if( keyType instanceof TypeVariable )
+ {
+ TypeVariable keyTypeVariable = (TypeVariable) keyType;
+ keyType = Classes.resolveTypeVariable( keyTypeVariable, declaringClass, compositeType );
+ }
+ ValueType keyedType = newValueType( keyType, declaringClass, compositeType, layer, module, variant );
+ Type valType = pt.getActualTypeArguments()[ 1 ];
+ if( valType instanceof TypeVariable )
+ {
+ TypeVariable valueTypeVariable = (TypeVariable) valType;
+ valType = Classes.resolveTypeVariable( valueTypeVariable, declaringClass, compositeType );
+ }
+ ValueType valuedType = newValueType( valType, declaringClass, compositeType, layer, module, variant );
+ valueType = new MapType( Classes.RAW_CLASS.map( type ), keyedType, valuedType, variant );
+ }
+ else
+ {
+ ValueType keyType = newValueType( Object.class, declaringClass, compositeType, layer, module, variant );
+ ValueType valuesType = newValueType( Object.class, declaringClass, compositeType, layer, module, variant );
+ valueType = new MapType( Classes.RAW_CLASS.map( type ), keyType, valuesType, variant );
+ }
+ }
+ else if( ValueCompositeType.isValueComposite( type ) )
+ {
+ // Find ValueModel in module/layer/used layers
+ ValueModel model = new ValueFinder( layer, module, Classes.RAW_CLASS.map( type ) ).getFoundModel();
+
+ if( model == null )
+ {
+ if( type.equals( ValueComposite.class ) )
+ {
+ // Create default model
+ MixinsModel mixinsModel = new MixinsModel();
+ Iterable valueComposite = (Iterable) Iterables.iterable( ValueComposite.class );
+ ValueStateModel valueStateModel = new ValueStateModel( new PropertiesModel(),
+ new AssociationsModel(),
+ new ManyAssociationsModel(),
+ new NamedAssociationsModel() );
+ model = new ValueModel( valueComposite, Visibility.application, new MetaInfo(),
+ mixinsModel, valueStateModel, new CompositeMethodsModel( mixinsModel ) );
+ }
+ else
+ {
+ throw new InvalidApplicationException( "[" + module.name() + "] Could not find ValueComposite of type " + type );
+ }
+ }
+
+ return model.valueType();
+ }
+ else if( EnumType.isEnum( type ) )
+ {
+ valueType = new EnumType( Classes.RAW_CLASS.map( type ) );
+ }
+ else
+ {
+ valueType = new ValueType( Classes.RAW_CLASS.map( type ) );
+ }
+
+ return valueType;
+ }
+
+ @SuppressWarnings( "raw" )
+ private static class ValueFinder
+ extends HierarchicalVisitorAdapter<Object, Object, RuntimeException>
+ {
+ private Class type;
+ private ValueModel foundModel;
+ private Visibility visibility;
+
+ private ValueFinder( LayerModel layer, ModuleModel module, Class type )
+ {
+ this.type = type;
+
+ visibility = Visibility.module;
+ module.accept( this );
+
+ if( foundModel == null )
+ {
+ visibility = Visibility.layer;
+ layer.accept( this );
+
+ if( foundModel == null )
+ {
+ visibility = Visibility.application;
+ layer.usedLayers().accept( this );
+ }
+ }
+ }
+
+ public ValueModel getFoundModel()
+ {
+ return foundModel;
+ }
+
+ @Override
+ public boolean visitEnter( Object visited )
+ throws RuntimeException
+ {
+ if( visited instanceof ValuesModel )
+ {
+ return true;
+ }
+ else if( visited instanceof ModuleModel )
+ {
+ return true;
+ }
+ else if (visited instanceof LayerModel )
+ {
+ return true;
+ }
+ else if (visited instanceof UsedLayersModel )
+ {
+ return true;
+ }
+ else if( visited instanceof ValueModel )
+ {
+ ValueModel valueModel = (ValueModel) visited;
+ boolean typeEquality = Specifications.in( valueModel.types() ).satisfiedBy( type );
+ if( typeEquality && valueModel.visibility().ordinal() >= visibility.ordinal() )
+ {
+ foundModel = valueModel;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean visitLeave( Object visited )
+ throws RuntimeException
+ {
+ return foundModel == null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderEntityState.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderEntityState.java b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderEntityState.java
new file mode 100644
index 0000000..a390874
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderEntityState.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2009-2011, Rickard Öberg. All Rights Reserved.
+ * Copyright (c) 2009-2013, Niclas Hedhman. All Rights Reserved.
+ * Copyright (c) 2014, Paul Merlin. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zest.runtime.unitofwork;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.zest.api.common.QualifiedName;
+import org.apache.zest.api.entity.EntityDescriptor;
+import org.apache.zest.api.entity.EntityReference;
+import org.apache.zest.api.util.Classes;
+import org.apache.zest.spi.entity.EntityState;
+import org.apache.zest.spi.entity.EntityStatus;
+import org.apache.zest.spi.entity.ManyAssociationState;
+import org.apache.zest.spi.entity.NamedAssociationState;
+
+/**
+ * Implementation of EntityState for use through EntityBuilder.
+ */
+public final class BuilderEntityState
+ implements EntityState
+{
+ private final EntityDescriptor entityType;
+ private final EntityReference reference;
+ private final Map<QualifiedName, Object> properties = new HashMap<>();
+ private final Map<QualifiedName, EntityReference> associations = new HashMap<>();
+ private final Map<QualifiedName, ManyAssociationState> manyAssociations = new HashMap<>();
+ private final Map<QualifiedName, NamedAssociationState> namedAssociations = new HashMap<>();
+
+ public BuilderEntityState( EntityDescriptor type, EntityReference reference )
+ {
+ this.entityType = type;
+ this.reference = reference;
+ }
+
+ @Override
+ public EntityReference identity()
+ {
+ return reference;
+ }
+
+ @Override
+ public String version()
+ {
+ return "";
+ }
+
+ @Override
+ public long lastModified()
+ {
+ return 0;
+ }
+
+ @Override
+ public void remove()
+ {
+ }
+
+ @Override
+ public EntityStatus status()
+ {
+ return EntityStatus.NEW;
+ }
+
+ @Override
+ public boolean isAssignableTo( Class<?> type )
+ {
+ return Classes.exactTypeSpecification( type ).satisfiedBy( entityType );
+ }
+
+ @Override
+ public EntityDescriptor entityDescriptor()
+ {
+ return entityType;
+ }
+
+ @Override
+ public Object propertyValueOf( QualifiedName stateName )
+ {
+ return properties.get( stateName );
+ }
+
+ @Override
+ public EntityReference associationValueOf( QualifiedName stateName )
+ {
+ return associations.get( stateName );
+ }
+
+ @Override
+ public void setPropertyValue( QualifiedName stateName, Object newValue )
+ {
+ properties.put( stateName, newValue );
+ }
+
+ @Override
+ public void setAssociationValue( QualifiedName stateName, EntityReference newEntity )
+ {
+ associations.put( stateName, newEntity );
+ }
+
+ @Override
+ public ManyAssociationState manyAssociationValueOf( QualifiedName stateName )
+ {
+ ManyAssociationState state = manyAssociations.get( stateName );
+ if( state == null )
+ {
+ state = new BuilderManyAssociationState();
+ manyAssociations.put( stateName, state );
+ }
+ return state;
+ }
+
+ @Override
+ public NamedAssociationState namedAssociationValueOf( QualifiedName stateName )
+ {
+ NamedAssociationState state = namedAssociations.get( stateName );
+ if( state == null )
+ {
+ state = new BuilderNamedAssociationState();
+ namedAssociations.put( stateName, state );
+ }
+ return state;
+ }
+
+ public void copyTo( EntityState newEntityState )
+ {
+ for( Map.Entry<QualifiedName, Object> fromPropertyEntry : properties.entrySet() )
+ {
+ newEntityState.setPropertyValue( fromPropertyEntry.getKey(), fromPropertyEntry.getValue() );
+ }
+ for( Map.Entry<QualifiedName, EntityReference> fromAssociationEntry : associations.entrySet() )
+ {
+ newEntityState.setAssociationValue( fromAssociationEntry.getKey(), fromAssociationEntry.getValue() );
+ }
+ for( Map.Entry<QualifiedName, ManyAssociationState> fromManyAssociationEntry : manyAssociations.entrySet() )
+ {
+ QualifiedName qName = fromManyAssociationEntry.getKey();
+ ManyAssociationState fromManyAssoc = fromManyAssociationEntry.getValue();
+ ManyAssociationState toManyAssoc = newEntityState.manyAssociationValueOf( qName );
+ for( EntityReference entityReference : fromManyAssoc )
+ {
+ toManyAssoc.add( 0, entityReference );
+ }
+ }
+ for( Map.Entry<QualifiedName, NamedAssociationState> fromNamedAssociationEntry : namedAssociations.entrySet() )
+ {
+ QualifiedName qName = fromNamedAssociationEntry.getKey();
+ NamedAssociationState fromNamedAssoc = fromNamedAssociationEntry.getValue();
+ NamedAssociationState toNamedAssoc = newEntityState.namedAssociationValueOf( qName );
+ for( String name : fromNamedAssoc )
+ {
+ toNamedAssoc.put( name, fromNamedAssoc.get( name ) );
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderManyAssociationState.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderManyAssociationState.java b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderManyAssociationState.java
new file mode 100644
index 0000000..d549c2a
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderManyAssociationState.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009, Rickard Öberg. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.zest.runtime.unitofwork;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.zest.api.entity.EntityReference;
+import org.apache.zest.spi.entity.ManyAssociationState;
+
+/**
+ * Default implementation of ManyAssociationState that also
+ * keeps a list of changes that can be extracted at any time.
+ */
+public final class BuilderManyAssociationState
+ implements ManyAssociationState
+{
+ private List<EntityReference> references;
+
+ public BuilderManyAssociationState()
+ {
+ references = new ArrayList<EntityReference>();
+ }
+
+ @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 entityReference )
+ {
+ return references.remove( entityReference );
+ }
+
+ @Override
+ public EntityReference get( int i )
+ {
+ return references.get( i );
+ }
+
+ @Override
+ public Iterator<EntityReference> iterator()
+ {
+ return references.iterator();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderNamedAssociationState.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderNamedAssociationState.java b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderNamedAssociationState.java
new file mode 100644
index 0000000..8ec7a4b
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderNamedAssociationState.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2011-2013, Niclas Hedhman. All Rights Reserved.
+ * Copyright (c) 2014, Paul Merlin. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zest.runtime.unitofwork;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import org.apache.zest.api.entity.EntityReference;
+import org.apache.zest.spi.entity.NamedAssociationState;
+
+/**
+ * Default implementation of NamedAssociationState that also
+ * keeps a list of changes that can be extracted at any time.
+ */
+public final class BuilderNamedAssociationState
+ implements NamedAssociationState
+{
+ private final Map<String, EntityReference> references;
+
+ public BuilderNamedAssociationState()
+ {
+ references = new HashMap<>();
+ }
+
+ @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/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityBuilderInstance.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityBuilderInstance.java b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityBuilderInstance.java
new file mode 100644
index 0000000..014c28c
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityBuilderInstance.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2007-2009, Rickard Öberg. All Rights Reserved.
+ * Copyright (c) 2008, Alin Dreghiciu. All Rights Reserved.
+ * Copyright (c) 2008, Edward Yakop. All Rights Reserved.
+ * Copyright (c) 2014-2015, Paul Merlin. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.zest.runtime.unitofwork;
+
+import org.apache.zest.api.common.QualifiedName;
+import org.apache.zest.api.entity.EntityBuilder;
+import org.apache.zest.api.entity.EntityReference;
+import org.apache.zest.api.entity.Identity;
+import org.apache.zest.api.entity.LifecycleException;
+import org.apache.zest.runtime.composite.FunctionStateResolver;
+import org.apache.zest.runtime.entity.EntityInstance;
+import org.apache.zest.runtime.entity.EntityModel;
+import org.apache.zest.spi.module.ModelModule;
+import org.apache.zest.runtime.structure.ModuleUnitOfWork;
+import org.apache.zest.spi.entity.EntityState;
+import org.apache.zest.spi.entitystore.EntityStoreUnitOfWork;
+
+/**
+ * Implementation of EntityBuilder. Maintains an instance of the entity which
+ * will not have its state validated until it is created by calling newInstance().
+ */
+public final class EntityBuilderInstance<T>
+ implements EntityBuilder<T>
+{
+ private static final QualifiedName IDENTITY_STATE_NAME;
+
+ private final ModelModule<EntityModel> model;
+ private final ModuleUnitOfWork uow;
+ private final EntityStoreUnitOfWork store;
+ private String identity;
+
+ private final BuilderEntityState entityState;
+ private final EntityInstance prototypeInstance;
+
+ static
+ {
+ try
+ {
+ IDENTITY_STATE_NAME = QualifiedName.fromAccessor( Identity.class.getMethod( "identity" ) );
+ }
+ catch( NoSuchMethodException e )
+ {
+ throw new InternalError( "Zest Core Runtime codebase is corrupted. Contact Zest team: EntityBuilderInstance" );
+ }
+ }
+
+ public EntityBuilderInstance(
+ ModelModule<EntityModel> model,
+ ModuleUnitOfWork uow,
+ EntityStoreUnitOfWork store,
+ String identity
+ )
+ {
+ this( model, uow, store, identity, null );
+ }
+
+ public EntityBuilderInstance(
+ ModelModule<EntityModel> model,
+ ModuleUnitOfWork uow,
+ EntityStoreUnitOfWork store,
+ String identity,
+ FunctionStateResolver stateResolver
+ )
+ {
+ this.model = model;
+ this.uow = uow;
+ this.store = store;
+ this.identity = identity;
+ EntityReference reference = new EntityReference( identity );
+ entityState = new BuilderEntityState( model.model(), reference );
+ model.model().initState( model.module(), entityState );
+ if( stateResolver != null )
+ {
+ stateResolver.populateState( model.model(), entityState );
+ }
+ entityState.setPropertyValue( IDENTITY_STATE_NAME, identity );
+ prototypeInstance = model.model().newInstance( uow, model.module(), entityState );
+ }
+
+ @SuppressWarnings( "unchecked" )
+ @Override
+ public T instance()
+ {
+ checkValid();
+ return prototypeInstance.<T>proxy();
+ }
+
+ @Override
+ public <K> K instanceFor( Class<K> mixinType )
+ {
+ checkValid();
+ return prototypeInstance.newProxy( mixinType );
+ }
+
+ @Override
+ @SuppressWarnings( "unchecked" )
+ public T newInstance()
+ throws LifecycleException
+ {
+ checkValid();
+
+ String identity;
+
+ // Figure out whether to use given or generated identity
+ identity = (String) entityState.propertyValueOf( IDENTITY_STATE_NAME );
+ EntityState newEntityState = model.model().newEntityState( store, uow.module(),
+ EntityReference.parseEntityReference( identity ) );
+
+ prototypeInstance.invokeCreate();
+
+ // Check constraints
+ prototypeInstance.checkConstraints();
+
+ entityState.copyTo( newEntityState );
+
+ EntityInstance instance = model.model().newInstance( uow, model.module(), newEntityState );
+
+ Object proxy = instance.proxy();
+
+ // Add entity in UOW
+ uow.addEntity( instance );
+
+ // Invalidate builder
+ this.identity = null;
+
+ return (T) proxy;
+ }
+
+ private void checkValid()
+ throws IllegalStateException
+ {
+ if( identity == null )
+ {
+ throw new IllegalStateException( "EntityBuilder is not valid after call to newInstance()" );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityStateStore.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityStateStore.java b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityStateStore.java
new file mode 100644
index 0000000..6afe68e
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityStateStore.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2009, Rickard Öberg. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.zest.runtime.unitofwork;
+
+import org.apache.zest.api.association.AssociationStateHolder;
+import org.apache.zest.spi.entity.EntityState;
+
+/**
+ * JAVADOC
+ */
+final class EntityStateStore
+{
+ AssociationStateHolder stateHolder;
+ EntityState state;
+
+ @Override
+ public String toString()
+ {
+ return state.identity().toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/UnitOfWorkInstance.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/UnitOfWorkInstance.java b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/UnitOfWorkInstance.java
new file mode 100644
index 0000000..2136528
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/UnitOfWorkInstance.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 2007-2013, Niclas Hedhman. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zest.runtime.unitofwork;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Stack;
+import java.util.concurrent.TimeUnit;
+import org.apache.zest.api.common.MetaInfo;
+import org.apache.zest.api.entity.EntityComposite;
+import org.apache.zest.api.entity.EntityReference;
+import org.apache.zest.api.metrics.MetricsCounter;
+import org.apache.zest.api.metrics.MetricsCounterFactory;
+import org.apache.zest.api.metrics.MetricsProvider;
+import org.apache.zest.api.metrics.MetricsTimer;
+import org.apache.zest.api.metrics.MetricsTimerFactory;
+import org.apache.zest.api.unitofwork.ConcurrentEntityModificationException;
+import org.apache.zest.api.unitofwork.EntityTypeNotFoundException;
+import org.apache.zest.api.unitofwork.NoSuchEntityException;
+import org.apache.zest.api.unitofwork.UnitOfWorkCallback;
+import org.apache.zest.api.unitofwork.UnitOfWorkCompletionException;
+import org.apache.zest.api.unitofwork.UnitOfWorkException;
+import org.apache.zest.api.unitofwork.UnitOfWorkOptions;
+import org.apache.zest.api.usecase.Usecase;
+import org.apache.zest.runtime.entity.EntityInstance;
+import org.apache.zest.runtime.entity.EntityModel;
+import org.apache.zest.runtime.structure.ModuleUnitOfWork;
+import org.apache.zest.spi.entity.EntityState;
+import org.apache.zest.spi.entity.EntityStatus;
+import org.apache.zest.spi.entitystore.ConcurrentEntityStateModificationException;
+import org.apache.zest.spi.entitystore.EntityNotFoundException;
+import org.apache.zest.spi.entitystore.EntityStore;
+import org.apache.zest.spi.entitystore.EntityStoreUnitOfWork;
+import org.apache.zest.spi.entitystore.StateCommitter;
+import org.apache.zest.spi.metrics.DefaultMetric;
+import org.apache.zest.spi.module.ModelModule;
+import org.apache.zest.spi.module.ModuleSpi;
+
+import static org.apache.zest.api.unitofwork.UnitOfWorkCallback.UnitOfWorkStatus.COMPLETED;
+import static org.apache.zest.api.unitofwork.UnitOfWorkCallback.UnitOfWorkStatus.DISCARDED;
+import static org.apache.zest.functional.Iterables.map;
+
+public final class UnitOfWorkInstance
+{
+ private static final ThreadLocal<Stack<UnitOfWorkInstance>> current = new ThreadLocal<Stack<UnitOfWorkInstance>>()
+ {
+ @Override
+ protected Stack<UnitOfWorkInstance> initialValue()
+ {
+ return new Stack<>();
+ }
+ };
+ private MetricsTimer.Context metricsTimer;
+
+ public static Stack<UnitOfWorkInstance> getCurrent()
+ {
+ return current.get();
+ }
+
+ private long currentTime;
+ private MetricsProvider metrics;
+ final HashMap<EntityReference, EntityInstance> instanceCache;
+ final HashMap<EntityStore, EntityStoreUnitOfWork> storeUnitOfWork;
+
+ private boolean open;
+
+ private boolean paused;
+
+ /**
+ * Lazy query builder factory.
+ */
+ private Usecase usecase;
+
+ private MetaInfo metaInfo;
+
+ private List<UnitOfWorkCallback> callbacks;
+
+ public UnitOfWorkInstance( Usecase usecase, long currentTime, MetricsProvider metrics )
+ {
+ this.currentTime = currentTime;
+ this.open = true;
+ instanceCache = new HashMap<>();
+ storeUnitOfWork = new HashMap<>();
+ getCurrent().push( this );
+ paused = false;
+ this.usecase = usecase;
+ startCapture( metrics );
+ }
+
+ public long currentTime()
+ {
+ return currentTime;
+ }
+
+ public EntityStoreUnitOfWork getEntityStoreUnitOfWork( EntityStore store, ModuleSpi module )
+ {
+ EntityStoreUnitOfWork uow = storeUnitOfWork.get( store );
+ if( uow == null )
+ {
+ uow = store.newUnitOfWork( usecase, module, currentTime );
+ storeUnitOfWork.put( store, uow );
+ }
+ return uow;
+ }
+
+ public <T> T get( EntityReference identity,
+ ModuleUnitOfWork uow,
+ Iterable<ModelModule<EntityModel>> potentialModels,
+ Class<T> mixinType
+ )
+ throws EntityTypeNotFoundException, NoSuchEntityException
+ {
+ checkOpen();
+
+ EntityInstance entityInstance = instanceCache.get( identity );
+ 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;
+ ModuleSpi module = null;
+ // Figure out what EntityStore to use
+ for( ModelModule<EntityModel> potentialModel : potentialModels )
+ {
+ EntityStore store = potentialModel.module().entityStore();
+ EntityStoreUnitOfWork storeUow = getEntityStoreUnitOfWork( store, potentialModel.module() );
+ try
+ {
+ entityState = storeUow.entityStateOf( potentialModel.module(), identity );
+ }
+ 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( identity, mixinType, usecase );
+ }
+ else
+ {
+ throw new EntityTypeNotFoundException( mixinType.getName(),
+ module.name(),
+ map( ModelModule.toStringFunction,
+ module.findVisibleEntityTypes()
+ ) );
+ }
+ }
+
+ // Create instance
+ entityInstance = new EntityInstance( uow, module, model, entityState );
+
+ instanceCache.put( identity, entityInstance );
+ }
+ else
+ {
+ // Check if it has been removed
+ if( entityInstance.status() == EntityStatus.REMOVED )
+ {
+ throw new NoSuchEntityException( identity, 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.identity() );
+ }
+ }
+ if( prunedInstances != null )
+ {
+ for( EntityReference prunedInstance : prunedInstances )
+ {
+ instanceCache.remove( prunedInstance );
+ }
+ }
+ }
+ }
+ }
+ 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
+ for( StateCommitter committer : committers )
+ {
+ committer.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 );
+
+ for( EntityStoreUnitOfWork entityStoreUnitOfWork : storeUnitOfWork.values() )
+ {
+ 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.identity(), 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
+ for( StateCommitter committer : committers )
+ {
+ committer.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();
+ Collection<EntityComposite> modifiedEntities = new ArrayList<>();
+ for( EntityReference modifiedEntityIdentity : modifiedEntityIdentities )
+ {
+ Collection<EntityInstance> instances = instanceCache.values();
+ for( EntityInstance instance : instances )
+ {
+ if( instance.identity().equals( modifiedEntityIdentity ) )
+ {
+ modifiedEntities.add( instance.<EntityComposite>proxy() );
+ }
+ }
+ }
+ throw new ConcurrentEntityModificationException( modifiedEntities );
+ }
+ else
+ {
+ throw new UnitOfWorkCompletionException( e );
+ }
+ }
+ }
+ return committers;
+ }
+
+ private void notifyBeforeCompletion( List<UnitOfWorkCallback> callbacks )
+ throws UnitOfWorkCompletionException
+ {
+ // Notify explicitly registered callbacks
+ if( callbacks != null )
+ {
+ for( UnitOfWorkCallback callback : callbacks )
+ {
+ callback.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 incrementCount()
+ {
+ MetricsCounter counter = getCounter();
+ counter.increment();
+ }
+
+ private void decrementCount()
+ {
+ MetricsCounter counter = getCounter();
+ counter.decrement();
+ }
+
+ private MetricsCounter getCounter()
+ {
+ if( metrics != null )
+ {
+ MetricsCounterFactory metricsFactory = metrics.createFactory( MetricsCounterFactory.class );
+ return metricsFactory.createCounter( getClass(), "UnitOfWork Counter" );
+ }
+ return new DefaultMetric();
+ }
+
+ private void endCapture()
+ {
+ decrementCount();
+ metricsTimer.stop();
+ }
+
+ private void startCapture( MetricsProvider metrics )
+ {
+ this.metrics = metrics;
+ incrementCount();
+ startTimer( metrics );
+ }
+
+ private void startTimer( MetricsProvider metrics )
+ {
+ MetricsTimerFactory metricsFactory = metrics.createFactory( MetricsTimerFactory.class );
+ String name = "UnitOfWork Timer";
+ MetricsTimer timer = metricsFactory.createTimer( getClass(), name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS );
+ metricsTimer = timer.start();
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/value/ManyAssociationValueState.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/value/ManyAssociationValueState.java b/core/runtime/src/main/java/org/apache/zest/runtime/value/ManyAssociationValueState.java
new file mode 100644
index 0000000..6056d9b
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/value/ManyAssociationValueState.java
@@ -0,0 +1,105 @@
+/*
+ * 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.value;
+
+import java.util.Iterator;
+import java.util.List;
+import org.apache.zest.api.entity.EntityReference;
+import org.apache.zest.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();
+ }
+ };
+ }
+}