QI-413 UnitOfWork::newEntityBuilderWithState()
Project: http://git-wip-us.apache.org/repos/asf/zest-qi4j/repo Commit: http://git-wip-us.apache.org/repos/asf/zest-qi4j/commit/355ea47d Tree: http://git-wip-us.apache.org/repos/asf/zest-qi4j/tree/355ea47d Diff: http://git-wip-us.apache.org/repos/asf/zest-qi4j/diff/355ea47d Branch: refs/heads/develop Commit: 355ea47da44f7d4e22133f06cd742f1fe4a12f39 Parents: 118928a Author: Paul Merlin <[email protected]> Authored: Wed Mar 25 16:02:13 2015 +0100 Committer: Paul Merlin <[email protected]> Committed: Wed Mar 25 16:02:13 2015 +0100 ---------------------------------------------------------------------- .../org/qi4j/api/unitofwork/UnitOfWork.java | 60 +++++++- .../composite/FunctionStateResolver.java | 71 +++++++++ .../qi4j/runtime/composite/StateResolver.java | 36 +++++ .../qi4j/runtime/entity/EntityStateModel.java | 2 +- .../qi4j/runtime/structure/ModuleInstance.java | 87 ++++------- .../runtime/structure/ModuleUnitOfWork.java | 109 ++++++++++++-- .../unitofwork/EntityBuilderInstance.java | 50 ++++++- .../runtime/value/ValueBuilderInstance.java | 3 +- .../runtime/value/ValueBuilderWithState.java | 19 ++- .../qi4j/runtime/value/ValueStateInstance.java | 4 +- .../org/qi4j/runtime/value/ValueStateModel.java | 21 +-- .../entity/EntityBuilderWithStateTest.java | 148 +++++++++++++++++++ 12 files changed, 510 insertions(+), 100 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/355ea47d/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWork.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWork.java b/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWork.java index a0ea959..e17bc07 100644 --- a/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWork.java +++ b/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWork.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2007-2011, Rickard Ãberg. All Rights Reserved. * Copyright (c) 2007-2012, Niclas Hedhman. All Rights Reserved. - * Copyright (c) 2013-2014, Paul Merlin. All Rights Reserved. + * Copyright (c) 2013-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. @@ -15,13 +15,18 @@ */ package org.qi4j.api.unitofwork; +import java.util.Map; +import org.qi4j.api.association.AssociationDescriptor; import org.qi4j.api.composite.AmbiguousTypeException; import org.qi4j.api.entity.EntityBuilder; +import org.qi4j.api.entity.EntityReference; import org.qi4j.api.entity.LifecycleException; +import org.qi4j.api.property.PropertyDescriptor; import org.qi4j.api.query.Query; import org.qi4j.api.query.QueryBuilder; import org.qi4j.api.structure.MetaInfoHolder; import org.qi4j.api.usecase.Usecase; +import org.qi4j.functional.Function; /** * All operations on entities goes through an UnitOfWork. @@ -178,6 +183,59 @@ public interface UnitOfWork extends MetaInfoHolder, AutoCloseable throws EntityTypeNotFoundException, AmbiguousTypeException; /** + * Create a new EntityBuilder for an EntityComposite wich implements the given mixin type starting with the given + * state. + * <p> + * An EntityComposite will be chosen according to what has been registered and the visibility rules for Modules and + * Layers will be considered. + * + * @param <T> Entity type + * @param type Entity type + * @param propertyFunction a function providing the state of properties + * @param associationFunction a function providing the state of associations + * @param manyAssociationFunction a function providing the state of many associations + * @param namedAssociationFunction a function providing the state of named associations + * + * @return a new EntityBuilder starting with the given state + * + * @throws EntityTypeNotFoundException if no EntityComposite type of the given mixin type has been registered + * @throws AmbiguousTypeException If several mixins implement the given type + */ + <T> EntityBuilder<T> newEntityBuilderWithState( Class<T> type, + Function<PropertyDescriptor, Object> propertyFunction, + Function<AssociationDescriptor, EntityReference> associationFunction, + Function<AssociationDescriptor, Iterable<EntityReference>> manyAssociationFunction, + Function<AssociationDescriptor, Map<String, EntityReference>> namedAssociationFunction ) + throws EntityTypeNotFoundException, AmbiguousTypeException; + + /** + * Create a new EntityBuilder for an EntityComposite wich implements the given mixin type starting with the given + * state. + * <p> + * An EntityComposite will be chosen according to what has been registered and the visibility rules for Modules and + * Layers will be considered. + * + * @param <T> Entity type + * @param type Entity type + * @param identity the identity of the new Entity + * @param propertyFunction a function providing the state of properties + * @param associationFunction a function providing the state of associations + * @param manyAssociationFunction a function providing the state of many associations + * @param namedAssociationFunction a function providing the state of named associations + * + * @return a new EntityBuilder starting with the given state + * + * @throws EntityTypeNotFoundException If no mixins implements the given type + * @throws AmbiguousTypeException If several mixins implement the given type + */ + <T> EntityBuilder<T> newEntityBuilderWithState( Class<T> type, String identity, + Function<PropertyDescriptor, Object> propertyFunction, + Function<AssociationDescriptor, EntityReference> associationFunction, + Function<AssociationDescriptor, Iterable<EntityReference>> manyAssociationFunction, + Function<AssociationDescriptor, Map<String, EntityReference>> namedAssociationFunction ) + throws EntityTypeNotFoundException, AmbiguousTypeException; + + /** * Find an Entity of the given mixin type with the give identity. This * method verifies that it exists by asking the underlying EntityStore. * http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/355ea47d/core/runtime/src/main/java/org/qi4j/runtime/composite/FunctionStateResolver.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/composite/FunctionStateResolver.java b/core/runtime/src/main/java/org/qi4j/runtime/composite/FunctionStateResolver.java new file mode 100644 index 0000000..284929d --- /dev/null +++ b/core/runtime/src/main/java/org/qi4j/runtime/composite/FunctionStateResolver.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2012, Kent Sølvsten. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.qi4j.runtime.composite; + +import java.util.List; +import java.util.Map; +import org.qi4j.api.association.AssociationDescriptor; +import org.qi4j.api.entity.EntityReference; +import org.qi4j.api.property.PropertyDescriptor; +import org.qi4j.functional.Function; +import org.qi4j.functional.Iterables; + +/** + * Function based StateResolver. + */ +public class FunctionStateResolver + implements StateResolver +{ + final Function<PropertyDescriptor, Object> propertyFunction; + final Function<AssociationDescriptor, EntityReference> associationFunction; + final Function<AssociationDescriptor, Iterable<EntityReference>> manyAssociationFunction; + final Function<AssociationDescriptor, Map<String, EntityReference>> namedAssociationFunction; + + public FunctionStateResolver( Function<PropertyDescriptor, Object> propertyFunction, + Function<AssociationDescriptor, EntityReference> associationFunction, + Function<AssociationDescriptor, Iterable<EntityReference>> manyAssociationFunction, + Function<AssociationDescriptor, Map<String, EntityReference>> namedAssociationFunction ) + { + this.propertyFunction = propertyFunction; + this.associationFunction = associationFunction; + this.manyAssociationFunction = manyAssociationFunction; + this.namedAssociationFunction = namedAssociationFunction; + } + + @Override + public Object getPropertyState( PropertyDescriptor propertyDescriptor ) + { + return propertyFunction.map( propertyDescriptor ); + } + + @Override + public EntityReference getAssociationState( AssociationDescriptor associationDescriptor ) + { + return associationFunction.map( associationDescriptor ); + } + + @Override + public List<EntityReference> getManyAssociationState( AssociationDescriptor associationDescriptor ) + { + return Iterables.toList( manyAssociationFunction.map( associationDescriptor ) ); + } + + @Override + public Map<String, EntityReference> getNamedAssociationState( AssociationDescriptor associationDescriptor ) + { + return namedAssociationFunction.map( associationDescriptor ); + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/355ea47d/core/runtime/src/main/java/org/qi4j/runtime/composite/StateResolver.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/composite/StateResolver.java b/core/runtime/src/main/java/org/qi4j/runtime/composite/StateResolver.java new file mode 100644 index 0000000..1f7072a --- /dev/null +++ b/core/runtime/src/main/java/org/qi4j/runtime/composite/StateResolver.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2012, Kent Sølvsten. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.qi4j.runtime.composite; + +import java.util.List; +import java.util.Map; +import org.qi4j.api.association.AssociationDescriptor; +import org.qi4j.api.entity.EntityReference; +import org.qi4j.api.property.PropertyDescriptor; + +/** + * StateResolver. + */ +public interface StateResolver +{ + Object getPropertyState( PropertyDescriptor propertyDescriptor ); + + EntityReference getAssociationState( AssociationDescriptor associationDescriptor ); + + List<EntityReference> getManyAssociationState( AssociationDescriptor associationDescriptor ); + + Map<String, EntityReference> getNamedAssociationState( AssociationDescriptor associationDescriptor ); +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/355ea47d/core/runtime/src/main/java/org/qi4j/runtime/entity/EntityStateModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/entity/EntityStateModel.java b/core/runtime/src/main/java/org/qi4j/runtime/entity/EntityStateModel.java index 20d397e..5fae1a2 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/entity/EntityStateModel.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/entity/EntityStateModel.java @@ -129,7 +129,7 @@ public final class EntityStateModel } @Override - public Iterable<? extends AssociationDescriptor> namedAssociations() + public Iterable<NamedAssociationModel> namedAssociations() { return namedAssociationsModel.namedAssociations(); } http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/355ea47d/core/runtime/src/main/java/org/qi4j/runtime/structure/ModuleInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/structure/ModuleInstance.java b/core/runtime/src/main/java/org/qi4j/runtime/structure/ModuleInstance.java index 551999e..c009c75 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/structure/ModuleInstance.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/structure/ModuleInstance.java @@ -1,7 +1,8 @@ /* * Copyright (c) 2008-2012, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2012, Kent Sølvsten. All Rights Reserved. * Copyright (c) 2008-2013, Niclas Hedhman. All Rights Reserved. - * Copyright (c) 2012-2014, Paul Merlin. All Rights Reserved. + * Copyright (c) 2012-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. @@ -67,6 +68,7 @@ import org.qi4j.functional.Function2; import org.qi4j.functional.Specification; import org.qi4j.functional.Specifications; import org.qi4j.runtime.activation.ActivationDelegate; +import org.qi4j.runtime.composite.FunctionStateResolver; import org.qi4j.runtime.composite.TransientBuilderInstance; import org.qi4j.runtime.composite.TransientModel; import org.qi4j.runtime.composite.TransientStateInstance; @@ -86,12 +88,12 @@ import org.qi4j.runtime.service.ImportedServicesModel; import org.qi4j.runtime.service.ServicesInstance; import org.qi4j.runtime.service.ServicesModel; import org.qi4j.runtime.unitofwork.UnitOfWorkInstance; +import org.qi4j.runtime.composite.StateResolver; import org.qi4j.runtime.value.ValueBuilderInstance; import org.qi4j.runtime.value.ValueBuilderWithPrototype; import org.qi4j.runtime.value.ValueBuilderWithState; import org.qi4j.runtime.value.ValueInstance; import org.qi4j.runtime.value.ValueModel; -import org.qi4j.runtime.value.ValueStateModel; import org.qi4j.runtime.value.ValuesModel; import org.qi4j.spi.entitystore.EntityStore; import org.qi4j.spi.metrics.MetricsProviderAdapter; @@ -113,7 +115,6 @@ import static org.qi4j.functional.Iterables.toList; public class ModuleInstance implements Module, Activation { - // Constructor parameters private final ModuleModel model; private final LayerInstance layer; @@ -350,7 +351,7 @@ public class ModuleInstance throw new NoSuchValueException( mixinType.getName(), name() ); } - ValueStateModel.StateResolver stateResolver = new InitialStateResolver( compositeModelModule.module() ); + StateResolver stateResolver = new InitialStateResolver( compositeModelModule.module() ); return new ValueBuilderInstance<>( compositeModelModule, this, stateResolver ); } @@ -373,14 +374,15 @@ public class ModuleInstance throw new NoSuchValueException( mixinType.getName(), name() ); } - ValueStateModel.StateResolver stateResolver = new FunctionStateResolver( propertyFunction, associationFunction, manyAssociationFunction, namedAssociationFunction ); + StateResolver stateResolver = new FunctionStateResolver( + propertyFunction, associationFunction, manyAssociationFunction, namedAssociationFunction + ); return new ValueBuilderWithState<>( compositeModelModule, this, stateResolver ); } private static class InitialStateResolver - implements ValueStateModel.StateResolver + implements StateResolver { - private final ModuleInstance module; private InitialStateResolver( ModuleInstance module ) @@ -411,53 +413,6 @@ public class ModuleInstance { return new HashMap<>(); } - - } - - private static class FunctionStateResolver - implements ValueStateModel.StateResolver - { - - private final Function<PropertyDescriptor, Object> propertyFunction; - private final Function<AssociationDescriptor, EntityReference> associationFunction; - private final Function<AssociationDescriptor, Iterable<EntityReference>> manyAssociationFunction; - private final Function<AssociationDescriptor, Map<String, EntityReference>> namedAssociationFunction; - - private FunctionStateResolver( Function<PropertyDescriptor, Object> propertyFunction, - Function<AssociationDescriptor, EntityReference> associationFunction, - Function<AssociationDescriptor, Iterable<EntityReference>> manyAssociationFunction, - Function<AssociationDescriptor, Map<String, EntityReference>> namedAssociationFunction ) - { - this.propertyFunction = propertyFunction; - this.associationFunction = associationFunction; - this.manyAssociationFunction = manyAssociationFunction; - this.namedAssociationFunction = namedAssociationFunction; - } - - @Override - public Object getPropertyState( PropertyDescriptor propertyDescriptor ) - { - return propertyFunction.map( propertyDescriptor ); - } - - @Override - public EntityReference getAssociationState( AssociationDescriptor associationDescriptor ) - { - return associationFunction.map( associationDescriptor ); - } - - @Override - public List<EntityReference> getManyAssociationState( AssociationDescriptor associationDescriptor ) - { - return toList( manyAssociationFunction.map( associationDescriptor ) ); - } - - @Override - public Map<String, EntityReference> getNamedAssociationState( AssociationDescriptor associationDescriptor ) - { - return namedAssociationFunction.map( associationDescriptor ); - } - } @Override @@ -792,8 +747,12 @@ public class ModuleInstance if( iter.hasNext() ) { // Ambiguous exception - throw new ClassNotFoundException( name, new AmbiguousTypeException( - "More than one model matches the classname " + name + ":" + toList( moduleModels ) ) ); + throw new ClassNotFoundException( + name, + new AmbiguousTypeException( + "More than one model matches the classname " + name + ":" + toList( moduleModels ) + ) + ); } } } @@ -819,8 +778,11 @@ public class ModuleInstance if( iter.hasNext() ) { // Ambiguous exception - throw new ClassNotFoundException( name, new AmbiguousTypeException( - "More than one model matches the classname " + name + ":" + toList( layerModels ) ) ); + throw new ClassNotFoundException( + name, + new AmbiguousTypeException( + "More than one model matches the classname " + name + ":" + toList( layerModels ) ) + ); } } } @@ -842,8 +804,12 @@ public class ModuleInstance if( iter.hasNext() ) { // Ambiguous exception - throw new ClassNotFoundException( name, new AmbiguousTypeException( - "More than one model matches the classname " + name + ":" + toList( usedLayersModels ) ) ); + throw new ClassNotFoundException( + name, + new AmbiguousTypeException( + "More than one model matches the classname " + name + ":" + toList( usedLayersModels ) + ) + ); } } } @@ -858,5 +824,4 @@ public class ModuleInstance return clazz; } } - } http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/355ea47d/core/runtime/src/main/java/org/qi4j/runtime/structure/ModuleUnitOfWork.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/structure/ModuleUnitOfWork.java b/core/runtime/src/main/java/org/qi4j/runtime/structure/ModuleUnitOfWork.java index bdadb75..dd1a830 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/structure/ModuleUnitOfWork.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/structure/ModuleUnitOfWork.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2009, Rickard Ãberg. All Rights Reserved. * Copyright (c) 2013, Niclas Hedhman. All Rights Reserved. - * Copyright (c) 2013, Paul Merlin. All Rights Reserved. + * Copyright (c) 2013-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. @@ -18,12 +18,16 @@ package org.qi4j.runtime.structure; import java.util.Collections; import java.util.Iterator; import java.util.Map; +import org.qi4j.api.association.AssociationDescriptor; +import org.qi4j.api.common.QualifiedName; import org.qi4j.api.composite.Composite; import org.qi4j.api.entity.EntityBuilder; import org.qi4j.api.entity.EntityComposite; import org.qi4j.api.entity.EntityReference; +import org.qi4j.api.entity.Identity; import org.qi4j.api.entity.IdentityGenerator; import org.qi4j.api.entity.LifecycleException; +import org.qi4j.api.property.PropertyDescriptor; import org.qi4j.api.query.Query; import org.qi4j.api.query.QueryBuilder; import org.qi4j.api.query.QueryExecutionException; @@ -37,12 +41,18 @@ import org.qi4j.api.unitofwork.UnitOfWorkCallback; import org.qi4j.api.unitofwork.UnitOfWorkCompletionException; import org.qi4j.api.unitofwork.UnitOfWorkFactory; import org.qi4j.api.usecase.Usecase; +import org.qi4j.api.util.NullArgumentException; +import org.qi4j.functional.Function; import org.qi4j.functional.Iterables; import org.qi4j.functional.Specification; +import org.qi4j.runtime.composite.FunctionStateResolver; import org.qi4j.runtime.entity.EntityInstance; import org.qi4j.runtime.entity.EntityModel; +import org.qi4j.runtime.property.PropertyModel; import org.qi4j.runtime.unitofwork.EntityBuilderInstance; import org.qi4j.runtime.unitofwork.UnitOfWorkInstance; +import org.qi4j.runtime.composite.StateResolver; +import org.qi4j.runtime.value.ValueStateModel; import org.qi4j.spi.entity.EntityStatus; import org.qi4j.spi.entitystore.EntityStore; import org.qi4j.spi.query.EntityFinder; @@ -59,20 +69,20 @@ import static org.qi4j.functional.Iterables.first; public class ModuleUnitOfWork implements UnitOfWork { -// private static final QualifiedName IDENTITY_STATE_NAME; -// -// static -// { -// try -// { -// IDENTITY_STATE_NAME = QualifiedName.fromAccessor( Identity.class.getMethod( "identity" ) ); -// } -// catch( NoSuchMethodException e ) -// { -// throw new InternalError( "Qi4j Core Runtime codebase is corrupted. Contact Qi4j team: ModuleUnitOfWork" ); -// } -// } -// + private static final QualifiedName IDENTITY_STATE_NAME; + + static + { + try + { + IDENTITY_STATE_NAME = QualifiedName.fromAccessor( Identity.class.getMethod( "identity" ) ); + } + catch( NoSuchMethodException e ) + { + throw new InternalError( "Qi4j Core Runtime codebase is corrupted. Contact Qi4j team: ModuleUnitOfWork" ); + } + } + private final UnitOfWorkInstance uow; private final ModuleInstance moduleInstance; @@ -185,6 +195,75 @@ public class ModuleUnitOfWork } @Override + public <T> EntityBuilder<T> newEntityBuilderWithState( + Class<T> type, + Function<PropertyDescriptor, Object> propertyFunction, + Function<AssociationDescriptor, EntityReference> associationFunction, + Function<AssociationDescriptor, Iterable<EntityReference>> manyAssociationFunction, + Function<AssociationDescriptor, Map<String, EntityReference>> namedAssociationFunction + ) + throws EntityTypeNotFoundException + { + return newEntityBuilderWithState( type, null, + propertyFunction, + associationFunction, + manyAssociationFunction, + namedAssociationFunction ); + } + + @Override + public <T> EntityBuilder<T> newEntityBuilderWithState( + Class<T> type, String identity, + Function<PropertyDescriptor, Object> propertyFunction, + Function<AssociationDescriptor, EntityReference> associationFunction, + Function<AssociationDescriptor, Iterable<EntityReference>> manyAssociationFunction, + Function<AssociationDescriptor, Map<String, EntityReference>> namedAssociationFunction + ) + throws EntityTypeNotFoundException + { + NullArgumentException.validateNotNull( "propertyFunction", propertyFunction ); + NullArgumentException.validateNotNull( "associationFunction", associationFunction ); + NullArgumentException.validateNotNull( "manyAssociationFunction", manyAssociationFunction ); + NullArgumentException.validateNotNull( "namedAssociationFunction", namedAssociationFunction ); + + ModelModule<EntityModel> model = moduleInstance.typeLookup().lookupEntityModel( type ); + + if( model == null ) + { + throw new EntityTypeNotFoundException( type.getName() ); + } + + EntityStore entityStore = model.module().entityStore(); + + StateResolver stateResolver = new FunctionStateResolver( + propertyFunction, associationFunction, manyAssociationFunction, namedAssociationFunction + ); + + if( identity == null ) + { + // Use identity from StateResolver if available + PropertyModel identityModel = model.model().state().findPropertyModelByQualifiedName( IDENTITY_STATE_NAME ); + identity = (String) stateResolver.getPropertyState( identityModel ); + if( identity == null ) + { + // Generate identity + IdentityGenerator idGen = model.module().identityGenerator(); + if( idGen == null ) + { + throw new NoSuchServiceException( IdentityGenerator.class.getName(), model.module().name() ); + } + identity = idGen.generate( first( model.model().types() ) ); + } + } + + return new EntityBuilderInstance<>( model, + this, + uow.getEntityStoreUnitOfWork( entityStore, moduleInstance ), + identity, + stateResolver ); + } + + @Override public <T> T get( Class<T> type, String identity ) throws EntityTypeNotFoundException, NoSuchEntityException { http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/355ea47d/core/runtime/src/main/java/org/qi4j/runtime/unitofwork/EntityBuilderInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/unitofwork/EntityBuilderInstance.java b/core/runtime/src/main/java/org/qi4j/runtime/unitofwork/EntityBuilderInstance.java index 74d1cf2..8bb0ffd 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/unitofwork/EntityBuilderInstance.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/unitofwork/EntityBuilderInstance.java @@ -1,5 +1,8 @@ /* - * Copyright (c) 2007, Rickard Ãberg. All Rights Reserved. + * 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. @@ -13,15 +16,22 @@ */ package org.qi4j.runtime.unitofwork; +import java.util.Map; +import org.qi4j.api.association.AssociationDescriptor; import org.qi4j.api.common.QualifiedName; import org.qi4j.api.entity.EntityBuilder; import org.qi4j.api.entity.EntityReference; import org.qi4j.api.entity.Identity; import org.qi4j.api.entity.LifecycleException; +import org.qi4j.api.property.PropertyDescriptor; +import org.qi4j.runtime.association.ManyAssociationModel; +import org.qi4j.runtime.association.NamedAssociationModel; import org.qi4j.runtime.entity.EntityInstance; import org.qi4j.runtime.entity.EntityModel; import org.qi4j.runtime.structure.ModelModule; import org.qi4j.runtime.structure.ModuleUnitOfWork; +import org.qi4j.runtime.composite.StateResolver; +import org.qi4j.runtime.value.ValueStateModel; import org.qi4j.spi.entity.EntityState; import org.qi4j.spi.entitystore.EntityStoreUnitOfWork; @@ -61,6 +71,17 @@ public final class EntityBuilderInstance<T> String identity ) { + this( model, uow, store, identity, null ); + } + + public EntityBuilderInstance( + ModelModule<EntityModel> model, + ModuleUnitOfWork uow, + EntityStoreUnitOfWork store, + String identity, + StateResolver stateResolver + ) + { this.model = model; this.uow = uow; this.store = store; @@ -68,6 +89,33 @@ public final class EntityBuilderInstance<T> EntityReference reference = new EntityReference( identity ); entityState = new BuilderEntityState( model.model(), reference ); model.model().initState( model.module(), entityState ); + if( stateResolver != null ) + { + for( PropertyDescriptor propDesc : model.model().state().properties() ) + { + Object value = stateResolver.getPropertyState( propDesc ); + entityState.setPropertyValue( propDesc.qualifiedName(), value ); + } + for( AssociationDescriptor assDesc : model.model().state().associations() ) + { + EntityReference ref = stateResolver.getAssociationState( assDesc ); + entityState.setAssociationValue( assDesc.qualifiedName(), ref ); + } + for( ManyAssociationModel manyAssDesc : model.model().state().manyAssociations() ) + { + for( EntityReference ref : stateResolver.getManyAssociationState( manyAssDesc ) ) + { + entityState.manyAssociationValueOf( manyAssDesc.qualifiedName() ).add( 0, ref ); + } + } + for( NamedAssociationModel namedAssDesc : model.model().state().namedAssociations() ) + { + for( Map.Entry<String, EntityReference> entry : stateResolver.getNamedAssociationState( namedAssDesc ).entrySet() ) + { + entityState.namedAssociationValueOf( namedAssDesc.qualifiedName() ).put( entry.getKey(), entry.getValue() ); + } + } + } entityState.setPropertyValue( IDENTITY_STATE_NAME, identity ); prototypeInstance = model.model().newInstance( uow, model.module(), entityState ); } http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/355ea47d/core/runtime/src/main/java/org/qi4j/runtime/value/ValueBuilderInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/value/ValueBuilderInstance.java b/core/runtime/src/main/java/org/qi4j/runtime/value/ValueBuilderInstance.java index f1f59de..0d70463 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/value/ValueBuilderInstance.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/value/ValueBuilderInstance.java @@ -18,6 +18,7 @@ import org.qi4j.api.common.ConstructionException; import org.qi4j.api.composite.Composite; import org.qi4j.api.value.NoSuchValueException; import org.qi4j.api.value.ValueBuilder; +import org.qi4j.runtime.composite.StateResolver; import org.qi4j.runtime.structure.ModelModule; import org.qi4j.runtime.structure.ModuleInstance; @@ -33,7 +34,7 @@ public final class ValueBuilderInstance<T> private final ModuleInstance currentModule; private final ValueInstance prototypeInstance; - public ValueBuilderInstance( ModelModule<ValueModel> compositeModelModule, ModuleInstance currentModule, ValueStateModel.StateResolver stateResolver ) + public ValueBuilderInstance( ModelModule<ValueModel> compositeModelModule, ModuleInstance currentModule, StateResolver stateResolver ) { ValueStateInstance state = new ValueStateInstance( compositeModelModule, currentModule, stateResolver ); prototypeInstance = compositeModelModule.model().newValueInstance( compositeModelModule.module(), state ); http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/355ea47d/core/runtime/src/main/java/org/qi4j/runtime/value/ValueBuilderWithState.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/value/ValueBuilderWithState.java b/core/runtime/src/main/java/org/qi4j/runtime/value/ValueBuilderWithState.java index d5ed565..ec1f7b3 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/value/ValueBuilderWithState.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/value/ValueBuilderWithState.java @@ -1,8 +1,22 @@ +/* + * Copyright (c) 2012, Kent Sølvsten. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ package org.qi4j.runtime.value; import org.qi4j.api.association.AssociationStateHolder; import org.qi4j.api.common.ConstructionException; import org.qi4j.api.value.ValueBuilder; +import org.qi4j.runtime.composite.StateResolver; import org.qi4j.runtime.structure.ModelModule; import org.qi4j.runtime.structure.ModuleInstance; @@ -11,7 +25,10 @@ public class ValueBuilderWithState<T> implements ValueBuilder<T> private final ModelModule<ValueModel> model; private ValueInstance prototypeInstance; - public ValueBuilderWithState( ModelModule<ValueModel> compositeModelModule, ModuleInstance currentModule, ValueStateModel.StateResolver stateResolver ) { + public ValueBuilderWithState( ModelModule<ValueModel> compositeModelModule, + ModuleInstance currentModule, + StateResolver stateResolver ) + { ValueStateInstance state = new ValueStateInstance( compositeModelModule, currentModule, stateResolver ); ValueInstance instance = compositeModelModule.model().newValueInstance( compositeModelModule.module(), state ); instance.prepareToBuild(); http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/355ea47d/core/runtime/src/main/java/org/qi4j/runtime/value/ValueStateInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/value/ValueStateInstance.java b/core/runtime/src/main/java/org/qi4j/runtime/value/ValueStateInstance.java index 660e2e2..68dd5c7 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/value/ValueStateInstance.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/value/ValueStateInstance.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2008-2011, Rickard Ãberg. All Rights Reserved. * Copyright (c) 2008-2013, Niclas Hedhman. All Rights Reserved. + * Copyright (c) 2012, Kent Sølvsten. 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. @@ -34,6 +35,7 @@ import org.qi4j.runtime.association.ManyAssociationInstance; import org.qi4j.runtime.association.ManyAssociationModel; import org.qi4j.runtime.association.NamedAssociationInstance; import org.qi4j.runtime.association.NamedAssociationModel; +import org.qi4j.runtime.composite.StateResolver; import org.qi4j.runtime.property.PropertyInfo; import org.qi4j.runtime.property.PropertyInstance; import org.qi4j.runtime.property.PropertyModel; @@ -65,7 +67,7 @@ public final class ValueStateInstance public ValueStateInstance( ModelModule<ValueModel> compositeModelModule, ModuleInstance currentModule, - ValueStateModel.StateResolver stateResolver ) + StateResolver stateResolver ) { ValueModel valueModel = compositeModelModule.model(); this.properties = new LinkedHashMap<>(); http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/355ea47d/core/runtime/src/main/java/org/qi4j/runtime/value/ValueStateModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/value/ValueStateModel.java b/core/runtime/src/main/java/org/qi4j/runtime/value/ValueStateModel.java index 8c5ecf7..d3f8060 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/value/ValueStateModel.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/value/ValueStateModel.java @@ -1,5 +1,7 @@ /* - * Copyright (c) 2008-2011, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2008-2011, Rickard Ãberg. + * Copyright (c) 2012, Kent Sølvsten. + * Copyright (c) 2014-2015, Paul Merlin. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,13 +19,9 @@ */ package org.qi4j.runtime.value; -import java.util.List; -import java.util.Map; import org.qi4j.api.association.AssociationDescriptor; import org.qi4j.api.association.AssociationStateDescriptor; import org.qi4j.api.common.QualifiedName; -import org.qi4j.api.entity.EntityReference; -import org.qi4j.api.property.PropertyDescriptor; import org.qi4j.functional.HierarchicalVisitor; import org.qi4j.functional.VisitableHierarchy; import org.qi4j.runtime.association.AssociationModel; @@ -129,19 +127,6 @@ public final class ValueStateModel } } } - return visitor.visitLeave( this ); } - - public interface StateResolver - { - public Object getPropertyState( PropertyDescriptor propertyDescriptor ); - - public EntityReference getAssociationState( AssociationDescriptor associationDescriptor ); - - public List<EntityReference> getManyAssociationState( AssociationDescriptor associationDescriptor ); - - public Map<String, EntityReference> getNamedAssociationState( AssociationDescriptor associationDescriptor ); - } - } http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/355ea47d/core/runtime/src/test/java/org/qi4j/runtime/entity/EntityBuilderWithStateTest.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/test/java/org/qi4j/runtime/entity/EntityBuilderWithStateTest.java b/core/runtime/src/test/java/org/qi4j/runtime/entity/EntityBuilderWithStateTest.java new file mode 100644 index 0000000..a949419 --- /dev/null +++ b/core/runtime/src/test/java/org/qi4j/runtime/entity/EntityBuilderWithStateTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2015 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.qi4j.runtime.entity; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import org.junit.Test; +import org.qi4j.api.association.Association; +import org.qi4j.api.association.AssociationDescriptor; +import org.qi4j.api.association.ManyAssociation; +import org.qi4j.api.association.NamedAssociation; +import org.qi4j.api.common.Optional; +import org.qi4j.api.entity.EntityBuilder; +import org.qi4j.api.entity.EntityReference; +import org.qi4j.api.entity.Identity; +import org.qi4j.api.property.Property; +import org.qi4j.api.property.PropertyDescriptor; +import org.qi4j.api.unitofwork.UnitOfWork; +import org.qi4j.api.unitofwork.UnitOfWorkCompletionException; +import org.qi4j.bootstrap.AssemblyException; +import org.qi4j.bootstrap.ModuleAssembly; +import org.qi4j.functional.Function; +import org.qi4j.test.AbstractQi4jTest; +import org.qi4j.test.EntityTestAssembler; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * EntityBuilder With State Test. + */ +public class EntityBuilderWithStateTest + extends AbstractQi4jTest +{ + @Override + public void assemble( ModuleAssembly module ) + throws AssemblyException + { + new EntityTestAssembler().assemble( module ); + module.entities( SomeEntity.class ); + } + + @Test + public void test() + throws UnitOfWorkCompletionException + { + final String associatedIdentity; + try( UnitOfWork uow = module.newUnitOfWork() ) + { + EntityBuilder<SomeEntity> builder = uow.newEntityBuilder( SomeEntity.class ); + builder.instance().prop().set( "Associated" ); + SomeEntity entity = builder.newInstance(); + associatedIdentity = entity.identity().get(); + uow.complete(); + } + try( UnitOfWork uow = module.newUnitOfWork() ) + { + SomeEntity entity = uow.newEntityBuilderWithState( + SomeEntity.class, + new Function<PropertyDescriptor, Object>() + { + @Override + public Object map( PropertyDescriptor descriptor ) + { + if( "prop".equals( descriptor.qualifiedName().name() ) ) + { + return "Foo"; + } + return null; + } + }, + new Function<AssociationDescriptor, EntityReference>() + { + @Override + public EntityReference map( AssociationDescriptor descriptor ) + { + if( "ass".equals( descriptor.qualifiedName().name() ) ) + { + return EntityReference.parseEntityReference( associatedIdentity ); + } + return null; + } + }, + new Function<AssociationDescriptor, Iterable<EntityReference>>() + { + @Override + public Iterable<EntityReference> map( AssociationDescriptor descriptor ) + { + if( "manyAss".equals( descriptor.qualifiedName().name() ) ) + { + return Arrays.asList( EntityReference.parseEntityReference( associatedIdentity ) ); + } + return null; + } + }, + new Function<AssociationDescriptor, Map<String, EntityReference>>() + { + @Override + public Map<String, EntityReference> map( AssociationDescriptor descriptor ) + { + if( "namedAss".equals( descriptor.qualifiedName().name() ) ) + { + return Collections.singletonMap( + "foo", + EntityReference.parseEntityReference( associatedIdentity ) + ); + } + return null; + } + } + ).newInstance(); + assertThat( entity.prop().get(), equalTo( "Foo" ) ); + assertThat( entity.ass().get().identity().get(), equalTo( associatedIdentity ) ); + assertThat( entity.manyAss().get( 0 ).identity().get(), equalTo( associatedIdentity ) ); + assertThat( entity.namedAss().get( "foo" ).identity().get(), equalTo( associatedIdentity ) ); + uow.complete(); + } + } + + public interface SomeEntity + extends Identity + { + Property<String> prop(); + + @Optional + Association<SomeEntity> ass(); + + @Optional + ManyAssociation<SomeEntity> manyAss(); + + @Optional + NamedAssociation<SomeEntity> namedAss(); + } +}
