From 61d58a967d480acc27c525cad9430713754c4e9f Mon Sep 17 00:00:00 2001 From: "ah.jo" Date: Sat, 30 Nov 2024 15:45:00 +0900 Subject: [PATCH] Refactor isolating NodeResolver --- .../fixturemonkey/FixtureMonkeyBuilder.java | 20 ++- .../customizer/ArbitraryManipulator.java | 8 -- .../fixturemonkey/customizer/InnerSpec.java | 117 ++++++++---------- .../customizer/InnerSpecState.java | 61 +++++---- .../customizer/MonkeyManipulatorFactory.java | 70 ++++++----- .../NodeSetDecomposedValueManipulator.java | 5 +- .../expression/ArbitraryExpression.java | 33 ++++- .../expression/MonkeyExpression.java | 6 +- .../tree/ApplyStrictModeResolver.java | 12 +- .../tree/CompositeNodeResolver.java | 17 +-- .../tree/DefaultNodeResolver.java | 74 ----------- .../tree/IdentityNodeResolver.java | 70 ----------- .../tree/NodePredicateResolver.java | 111 +++++++++++++++++ .../fixturemonkey/tree/NodeResolver.java | 10 +- .../tree/StaticNodeResolver.java | 47 +++++++ 15 files changed, 355 insertions(+), 306 deletions(-) delete mode 100644 fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/DefaultNodeResolver.java delete mode 100644 fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/IdentityNodeResolver.java create mode 100644 fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/NodePredicateResolver.java create mode 100644 fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/StaticNodeResolver.java diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java index d3fa6b80a..b577677fe 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java @@ -58,10 +58,13 @@ import com.navercorp.fixturemonkey.buildergroup.ArbitraryBuilderGroup; import com.navercorp.fixturemonkey.customizer.MonkeyManipulatorFactory; import com.navercorp.fixturemonkey.expression.ArbitraryExpressionFactory; +import com.navercorp.fixturemonkey.expression.MonkeyExpression; import com.navercorp.fixturemonkey.expression.MonkeyExpressionFactory; import com.navercorp.fixturemonkey.resolver.ManipulatorOptimizer; import com.navercorp.fixturemonkey.resolver.NoneManipulatorOptimizer; import com.navercorp.fixturemonkey.tree.ApplyStrictModeResolver; +import com.navercorp.fixturemonkey.tree.NextNodePredicate; +import com.navercorp.fixturemonkey.tree.NodeResolver; @SuppressWarnings("unused") @API(since = "0.4.0", status = Status.MAINTAINED) @@ -443,8 +446,21 @@ public FixtureMonkeyBuilder enableLoggingFail(boolean enableLoggingFail) { } public FixtureMonkeyBuilder useExpressionStrictMode() { - this.monkeyExpressionFactory = expression -> - () -> new ApplyStrictModeResolver(new ArbitraryExpressionFactory().from(expression).toNodeResolver()); + this.monkeyExpressionFactory = expression -> { + MonkeyExpression monkeyExpression = new ArbitraryExpressionFactory().from(expression); + + return new MonkeyExpression() { + @Override + public NodeResolver toNodeResolver() { + return new ApplyStrictModeResolver(monkeyExpression.toNodeResolver()); + } + + @Override + public List toNextNodePredicate() { // TODO: + return monkeyExpression.toNextNodePredicate(); + } + }; + }; return this; } diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/ArbitraryManipulator.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/ArbitraryManipulator.java index 4beac85f6..6a8c9e15d 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/ArbitraryManipulator.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/ArbitraryManipulator.java @@ -21,7 +21,6 @@ import org.apiguardian.api.API; import org.apiguardian.api.API.Status; -import com.navercorp.fixturemonkey.tree.CompositeNodeResolver; import com.navercorp.fixturemonkey.tree.NodeResolver; import com.navercorp.fixturemonkey.tree.ObjectTree; @@ -35,13 +34,6 @@ public final class ArbitraryManipulator { this.nodeManipulator = nodeManipulator; } - ArbitraryManipulator withPrependNodeResolver(NodeResolver nodeResolver) { - return new ArbitraryManipulator( - new CompositeNodeResolver(nodeResolver, this.nodeResolver), - this.nodeManipulator - ); - } - public NodeResolver getNodeResolver() { return nodeResolver; } diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/InnerSpec.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/InnerSpec.java index 6f1b3212b..8585d3b76 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/InnerSpec.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/InnerSpec.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; @@ -42,16 +43,14 @@ import com.navercorp.fixturemonkey.customizer.InnerSpecState.FilterHolder; import com.navercorp.fixturemonkey.customizer.InnerSpecState.ManipulatorHolderSet; import com.navercorp.fixturemonkey.customizer.InnerSpecState.NodeResolverObjectHolder; -import com.navercorp.fixturemonkey.tree.CompositeNodeResolver; import com.navercorp.fixturemonkey.tree.ContainerElementPredicate; -import com.navercorp.fixturemonkey.tree.DefaultNodeResolver; -import com.navercorp.fixturemonkey.tree.IdentityNodeResolver; +import com.navercorp.fixturemonkey.tree.NextNodePredicate; import com.navercorp.fixturemonkey.tree.NodeAllElementPredicate; import com.navercorp.fixturemonkey.tree.NodeElementPredicate; import com.navercorp.fixturemonkey.tree.NodeKeyPredicate; -import com.navercorp.fixturemonkey.tree.NodeResolver; import com.navercorp.fixturemonkey.tree.NodeValuePredicate; import com.navercorp.fixturemonkey.tree.PropertyNameNodePredicate; +import com.navercorp.fixturemonkey.tree.StartNodePredicate; /** * A type-independent specification for configuring nested properties. @@ -69,7 +68,7 @@ public final class InnerSpec { private static final int FIRST_MANIPULATOR_SEQUENCE = 0; private final int sequence; - private final NodeResolver treePathResolver; + private final List nextNodePredicates; private final InnerSpecState state; private final List innerSpecs; @@ -83,12 +82,22 @@ public final class InnerSpec { *

*/ public InnerSpec() { - this(FIRST_MANIPULATOR_SEQUENCE, IdentityNodeResolver.INSTANCE, new InnerSpecState(), new ArrayList<>()); + this( + FIRST_MANIPULATOR_SEQUENCE, + Collections.singletonList(StartNodePredicate.INSTANCE), + new InnerSpecState(), + new ArrayList<>() + ); } - private InnerSpec(int sequence, NodeResolver treePathResolver, InnerSpecState state, List innerSpecs) { + private InnerSpec( + int sequence, + List nextNodePredicates, + InnerSpecState state, + List innerSpecs + ) { this.sequence = sequence; - this.treePathResolver = treePathResolver; + this.nextNodePredicates = new ArrayList<>(nextNodePredicates); this.state = state; this.innerSpecs = innerSpecs; } @@ -101,7 +110,7 @@ private InnerSpec(int sequence, NodeResolver treePathResolver, InnerSpecState st * to be applied to the currently referred property. */ public InnerSpec inner(InnerSpec innerSpec) { - InnerSpec appendInnerSpec = newAppendNodeResolver(innerSpec, this.treePathResolver); + InnerSpec appendInnerSpec = newAppendNextNodePredicate(innerSpec, this.nextNodePredicates); this.innerSpecs.add(appendInnerSpec); return this; } @@ -119,7 +128,7 @@ public InnerSpec size(int minSize, int maxSize) { } this.state.setContainerInfoHolder( - new ContainerInfoHolder(this.sequence + manipulateSize, this.treePathResolver, minSize, maxSize) + new ContainerInfoHolder(this.sequence + manipulateSize, this.nextNodePredicates, minSize, maxSize) ); manipulateSize++; return this; @@ -342,7 +351,7 @@ public InnerSpec valueLazy(Supplier supplier) { * Sets an entry in the currently referred map property with a key and value * obtained lazily from the given suppliers. * - * @param keySupplier a supplier function that provides the value of the map key to set. + * @param keySupplier a supplier function that provides the value of the map key to set. * @param valueSupplier a function that provides the value of the map value to set. */ public InnerSpec entryLazy(Supplier keySupplier, Supplier valueSupplier) { @@ -462,7 +471,7 @@ public InnerSpec listElement(int index, Consumer consumer) { /** * Sets every element within the currently referred container property. * - * @param value value of the elements to set + * @param value value of the elements to set */ public InnerSpec allListElement(@Nullable Object value) { return listElement(NO_OR_ALL_INDEX_INTEGER_VALUE, value); @@ -506,11 +515,11 @@ public InnerSpec property(String propertyName, Consumer consumer) { /** * Sets the post-condition for the currently referred property. * - * @param type type of the property to set - * @param filter a predicate function that determines the post-condition of the property + * @param type type of the property to set + * @param filter a predicate function that determines the post-condition of the property */ public InnerSpec postCondition(Class type, Predicate filter) { - this.state.setFilterHolder(new FilterHolder(manipulateSize++, this.treePathResolver, type, filter)); + this.state.setFilterHolder(new FilterHolder(manipulateSize++, this.nextNodePredicates, type, filter)); return this; } @@ -519,16 +528,16 @@ public ManipulatorSet getManipulatorSet(MonkeyManipulatorFactory monkeyManipulat return monkeyManipulatorFactory.newManipulatorSet(manipulatorHolderSet); } - private InnerSpec newAppendNodeResolver(InnerSpec innerSpec, NodeResolver nodeResolver) { + private InnerSpec newAppendNextNodePredicate(InnerSpec innerSpec, List nextNodePredicates) { InnerSpec newSpec = new InnerSpec( innerSpec.sequence, - innerSpec.treePathResolver, - innerSpec.state.withPrefix(nodeResolver), + innerSpec.nextNodePredicates, + innerSpec.state.withPrefix(nextNodePredicates), new ArrayList<>() ); for (InnerSpec spec : innerSpec.innerSpecs) { - newSpec.innerSpecs.add(newAppendNodeResolver(spec, nodeResolver)); + newSpec.innerSpecs.add(newAppendNextNodePredicate(spec, nextNodePredicates)); } return newSpec; } @@ -540,13 +549,11 @@ private void setMapKey(Object mapKey) { ); } - NodeResolver nextKeyNodeResolver = new CompositeNodeResolver( - this.treePathResolver, - new DefaultNodeResolver(new NodeElementPredicate(entrySize - 1)), - new DefaultNodeResolver(new NodeKeyPredicate()) - ); + List nextKeyNodeNodePredicate = new ArrayList<>(this.nextNodePredicates); + nextKeyNodeNodePredicate.add(new NodeElementPredicate(entrySize - 1)); + nextKeyNodeNodePredicate.add(new NodeKeyPredicate()); - setValue(nextKeyNodeResolver, mapKey); + setValue(nextKeyNodeNodePredicate, mapKey); } private void setMapAllKey(Object mapKey) { @@ -556,33 +563,25 @@ private void setMapAllKey(Object mapKey) { ); } - NodeResolver nextKeyNodeResolver = new CompositeNodeResolver( - this.treePathResolver, - new DefaultNodeResolver(new NodeAllElementPredicate()), - new DefaultNodeResolver(new NodeKeyPredicate()) - ); + List nextKeyNodePredicate = new ArrayList<>(this.nextNodePredicates); + nextKeyNodePredicate.add(new NodeAllElementPredicate()); + nextKeyNodePredicate.add(new NodeKeyPredicate()); - setValue(nextKeyNodeResolver, mapKey); + setValue(nextKeyNodePredicate, mapKey); } private void setMapValue(@Nullable Object mapValue) { - NodeResolver nextValueNodeResolver = new CompositeNodeResolver( - this.treePathResolver, - new DefaultNodeResolver(new NodeElementPredicate(entrySize - 1)), - new DefaultNodeResolver(new NodeValuePredicate()) - ); - - setValue(nextValueNodeResolver, mapValue); + List nextValueNodePredicate = new ArrayList<>(this.nextNodePredicates); + nextValueNodePredicate.add(new NodeElementPredicate(entrySize - 1)); + nextValueNodePredicate.add(new NodeValuePredicate()); + setValue(nextValueNodePredicate, mapValue); } private void setMapAllValue(@Nullable Object mapValue) { - NodeResolver nextValueNodeResolver = new CompositeNodeResolver( - this.treePathResolver, - new DefaultNodeResolver(new NodeAllElementPredicate()), - new DefaultNodeResolver(new NodeValuePredicate()) - ); - - setValue(nextValueNodeResolver, mapValue); + List nextValueNodePredicate = new ArrayList<>(this.nextNodePredicates); + nextValueNodePredicate.add(new NodeAllElementPredicate()); + nextValueNodePredicate.add(new NodeValuePredicate()); + setValue(nextValueNodePredicate, mapValue); } private void setMapEntry(Object key, @Nullable Object value) { @@ -591,31 +590,25 @@ private void setMapEntry(Object key, @Nullable Object value) { } private void setPropertyValue(String propertyName, @Nullable Object value) { - CompositeNodeResolver nextNodeResolver = new CompositeNodeResolver( - this.treePathResolver, - new DefaultNodeResolver(new PropertyNameNodePredicate(propertyName)) - ); - - setValue(nextNodeResolver, value); + List nextNodePredicates = new ArrayList<>(this.nextNodePredicates); + nextNodePredicates.add(new PropertyNameNodePredicate(propertyName)); + setValue(nextNodePredicates, value); } private void setListElement(int index, @Nullable Object value) { - CompositeNodeResolver nextNodeResolver = new CompositeNodeResolver( - this.treePathResolver, - new DefaultNodeResolver(new ContainerElementPredicate(index)) - ); - - setValue(nextNodeResolver, value); + List nextNodePredicates = new ArrayList<>(this.nextNodePredicates); + nextNodePredicates.add(new ContainerElementPredicate(index)); + setValue(nextNodePredicates, value); } @SuppressWarnings("unchecked") - private void setValue(NodeResolver nextNodeResolver, @Nullable Object nextValue) { + private void setValue(List nextNodePredicates, @Nullable Object nextValue) { int nextSequence = this.sequence + manipulateSize; if (nextValue instanceof InnerSpec) { InnerSpec prefix = new InnerSpec( nextSequence, - nextNodeResolver, + nextNodePredicates, new InnerSpecState(), new ArrayList<>() ); @@ -628,7 +621,7 @@ private void setValue(NodeResolver nextNodeResolver, @Nullable Object nextValue) Consumer consumer = (Consumer)nextValue; InnerSpec nextInnerSpec = new InnerSpec( nextSequence, - nextNodeResolver, + nextNodePredicates, new InnerSpecState(), new ArrayList<>() ); @@ -642,14 +635,14 @@ private void setValue(NodeResolver nextNodeResolver, @Nullable Object nextValue) nextInnerSpecState.setObjectHolder( new NodeResolverObjectHolder( nextSequence, - nextNodeResolver, + nextNodePredicates, nextValue ) ); InnerSpec nextInnerSpec = new InnerSpec( nextSequence, - nextNodeResolver, + nextNodePredicates, nextInnerSpecState, new ArrayList<>() ); diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/InnerSpecState.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/InnerSpecState.java index f1516c71a..786c14932 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/InnerSpecState.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/InnerSpecState.java @@ -18,6 +18,7 @@ package com.navercorp.fixturemonkey.customizer; +import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; @@ -26,8 +27,7 @@ import org.apiguardian.api.API; import org.apiguardian.api.API.Status; -import com.navercorp.fixturemonkey.tree.CompositeNodeResolver; -import com.navercorp.fixturemonkey.tree.NodeResolver; +import com.navercorp.fixturemonkey.tree.NextNodePredicate; @API(since = "0.5.0", status = Status.MAINTAINED) final class InnerSpecState { @@ -65,33 +65,36 @@ FilterHolder getFilterHolder() { return filterHolder; } - InnerSpecState withPrefix(NodeResolver nodeResolver) { + InnerSpecState withPrefix(List nextNodePredicates) { InnerSpecState newState = new InnerSpecState(); if (this.objectHolder != null) { + List concat = new ArrayList<>(nextNodePredicates); + concat.addAll(this.objectHolder.nextNodePredicates); newState.objectHolder = new NodeResolverObjectHolder( this.objectHolder.sequence, - new CompositeNodeResolver( - nodeResolver, - this.objectHolder.nodeResolver - ), + concat, this.objectHolder.value ); } if (this.filterHolder != null) { + List concat = new ArrayList<>(nextNodePredicates); + concat.addAll(this.filterHolder.nextNodePredicates); newState.filterHolder = new FilterHolder( this.filterHolder.sequence, - new CompositeNodeResolver(nodeResolver, this.filterHolder.nodeResolver), + concat, this.filterHolder.type, this.filterHolder.predicate ); } if (this.containerInfoHolder != null) { + List concat = new ArrayList<>(nextNodePredicates); + concat.addAll(this.containerInfoHolder.nextNodePredicates); newState.containerInfoHolder = new ContainerInfoHolder( this.containerInfoHolder.sequence, - new CompositeNodeResolver(nodeResolver, this.containerInfoHolder.nodeResolver), + concat, this.containerInfoHolder.elementMinSize, this.containerInfoHolder.elementMaxSize ); @@ -102,13 +105,18 @@ InnerSpecState withPrefix(NodeResolver nodeResolver) { public static class ContainerInfoHolder { private final int sequence; - private final NodeResolver nodeResolver; + private final List nextNodePredicates; private final int elementMinSize; private final int elementMaxSize; - public ContainerInfoHolder(int sequence, NodeResolver nodeResolver, int elementMinSize, int elementMaxSize) { + public ContainerInfoHolder( + int sequence, + List nextNodePredicates, + int elementMinSize, + int elementMaxSize + ) { this.sequence = sequence; - this.nodeResolver = nodeResolver; + this.nextNodePredicates = nextNodePredicates; this.elementMinSize = elementMinSize; this.elementMaxSize = elementMaxSize; } @@ -117,8 +125,8 @@ int getSequence() { return sequence; } - NodeResolver getNodeResolver() { - return nodeResolver; + List getNextNodePredicates() { + return this.nextNodePredicates; } int getElementMinSize() { @@ -132,13 +140,18 @@ int getElementMaxSize() { public static class FilterHolder { private final int sequence; - private final NodeResolver nodeResolver; + private final List nextNodePredicates; private final Class type; private final Predicate predicate; - public FilterHolder(int sequence, NodeResolver nodeResolver, Class type, Predicate predicate) { + public FilterHolder( + int sequence, + List nextNodePredicates, + Class type, + Predicate predicate + ) { this.sequence = sequence; - this.nodeResolver = nodeResolver; + this.nextNodePredicates = nextNodePredicates; this.type = type; this.predicate = predicate; } @@ -147,8 +160,8 @@ public int getSequence() { return sequence; } - NodeResolver getNodeResolver() { - return nodeResolver; + List getNextNodePredicates() { + return this.nextNodePredicates; } Class getType() { @@ -162,17 +175,17 @@ Predicate getPredicate() { public static class NodeResolverObjectHolder { private final int sequence; - private final NodeResolver nodeResolver; + private final List nextNodePredicates; private final Object value; - public NodeResolverObjectHolder(int sequence, NodeResolver nodeResolver, Object value) { + public NodeResolverObjectHolder(int sequence, List nextNodePredicates, Object value) { this.sequence = sequence; - this.nodeResolver = nodeResolver; + this.nextNodePredicates = nextNodePredicates; this.value = value; } - NodeResolver getNodeResolver() { - return nodeResolver; + List getNextNodePredicates() { + return this.nextNodePredicates; } Object getValue() { diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/MonkeyManipulatorFactory.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/MonkeyManipulatorFactory.java index f375685ea..dd22932ab 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/MonkeyManipulatorFactory.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/MonkeyManipulatorFactory.java @@ -19,7 +19,6 @@ package com.navercorp.fixturemonkey.customizer; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -50,10 +49,11 @@ import com.navercorp.fixturemonkey.customizer.Values.Just; import com.navercorp.fixturemonkey.customizer.Values.Unique; import com.navercorp.fixturemonkey.expression.MonkeyExpressionFactory; -import com.navercorp.fixturemonkey.tree.NextNodePredicate; +import com.navercorp.fixturemonkey.tree.CompositeNodeResolver; +import com.navercorp.fixturemonkey.tree.NodePredicateResolver; import com.navercorp.fixturemonkey.tree.NodeResolver; import com.navercorp.fixturemonkey.tree.ObjectNode; -import com.navercorp.fixturemonkey.tree.PropertyPredicate; +import com.navercorp.fixturemonkey.tree.StaticNodeResolver; @API(since = "0.4.10", status = Status.MAINTAINED) public final class MonkeyManipulatorFactory { @@ -129,7 +129,7 @@ public ContainerInfoManipulator newContainerInfoManipulator( int newSequence = sequence.getAndIncrement(); return new ContainerInfoManipulator( - monkeyExpressionFactory.from(expression).toNodeResolver().toNextNodePredicate(), + monkeyExpressionFactory.from(expression).toNextNodePredicate(), new ArbitraryContainerInfo( min, max @@ -162,7 +162,12 @@ public List newRegisteredArbitraryManipulators( ArbitraryBuilderContext context = registeredArbitraryBuilder.getContext(); List arbitraryManipulators = context.getManipulators().stream() - .map(it -> it.withPrependNodeResolver(prependPropertyNodeResolver(property, objectNodes))) + .map( + it -> new ArbitraryManipulator( + new CompositeNodeResolver(new StaticNodeResolver(objectNodes), it.getNodeResolver()), + it.getNodeManipulator() + ) + ) .collect(Collectors.toList()); manipulators.addAll(arbitraryManipulators); @@ -177,19 +182,36 @@ public ManipulatorSet newManipulatorSet(ManipulatorHolderSet manipulatorHolderSe List setArbitraryManipulators = manipulatorHolderSet.getNodeResolverObjectHolders() .stream() - .map(it -> new ArbitraryManipulator( - it.getNodeResolver(), - convertToNodeManipulator(baseSequence + it.getSequence(), it.getValue()) - ) + .map( + it -> { + List nextNodeResolvers = it.getNextNodePredicates().stream() + .map(NodePredicateResolver::new) + .collect(Collectors.toList()); + + CompositeNodeResolver compositeNodeResolver = new CompositeNodeResolver(nextNodeResolvers); + return new ArbitraryManipulator( + compositeNodeResolver, + convertToNodeManipulator(baseSequence + it.getSequence(), it.getValue()) + ); + } ) .collect(Collectors.toList()); List filterArbitraryManipulators = manipulatorHolderSet.getPostConditionManipulators() .stream() - .map(it -> new ArbitraryManipulator( - it.getNodeResolver(), - new NodeFilterManipulator(it.getType(), it.getPredicate()) - )) + .map( + it -> { + List nextNodeResolvers = it.getNextNodePredicates().stream() + .map(NodePredicateResolver::new) + .collect(Collectors.toList()); + + CompositeNodeResolver compositeNodeResolver = new CompositeNodeResolver(nextNodeResolvers); + return new ArbitraryManipulator( + compositeNodeResolver, + new NodeFilterManipulator(it.getType(), it.getPredicate()) + ); + } + ) .collect(Collectors.toList()); arbitraryManipulators.addAll(setArbitraryManipulators); arbitraryManipulators.addAll(filterArbitraryManipulators); @@ -197,7 +219,7 @@ public ManipulatorSet newManipulatorSet(ManipulatorHolderSet manipulatorHolderSe List containerInfoManipulators = manipulatorHolderSet.getContainerInfoManipulators() .stream() .map(it -> new ContainerInfoManipulator( - it.getNodeResolver().toNextNodePredicate(), + it.getNextNodePredicates(), new ArbitraryContainerInfo( it.getElementMinSize(), it.getElementMaxSize() @@ -269,24 +291,4 @@ private NodeManipulator convertToNodeManipulator(int sequence, @Nullable Object ); } } - - private NodeResolver prependPropertyNodeResolver(Property property, List objectNodes) { - return new NodeResolver() { - @Override - public List resolve(ObjectNode objectNode) { - for (ObjectNode node : objectNodes) { - ObjectNode parent = node.getParent(); - while (parent != null) { - parent = parent.getParent(); - } - } - return objectNodes; - } - - @Override - public List toNextNodePredicate() { - return Collections.singletonList(new PropertyPredicate(property)); - } - }; - } } diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/NodeSetDecomposedValueManipulator.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/NodeSetDecomposedValueManipulator.java index 1991f3928..4758972fb 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/NodeSetDecomposedValueManipulator.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/NodeSetDecomposedValueManipulator.java @@ -23,6 +23,7 @@ import static com.navercorp.fixturemonkey.api.type.Types.isAssignable; import static com.navercorp.fixturemonkey.api.type.Types.nullSafe; +import java.util.Collections; import java.util.List; import java.util.function.Function; @@ -41,8 +42,8 @@ import com.navercorp.fixturemonkey.api.tree.TreeNodeManipulator; import com.navercorp.fixturemonkey.api.type.Types; import com.navercorp.fixturemonkey.tree.GenerateFixtureContext; -import com.navercorp.fixturemonkey.tree.IdentityNodeResolver; import com.navercorp.fixturemonkey.tree.ObjectNode; +import com.navercorp.fixturemonkey.tree.StartNodePredicate; @API(since = "0.4.0", status = Status.MAINTAINED) public final class NodeSetDecomposedValueManipulator implements NodeManipulator { @@ -119,7 +120,7 @@ private void setValue(ObjectNode objectNode, @Nullable Object value) { new ArbitraryContainerInfo(decomposedContainerSize, decomposedContainerSize); objectNode.addTreeNodeManipulator( new ContainerInfoManipulator( - IdentityNodeResolver.INSTANCE.toNextNodePredicate(), + Collections.singletonList(StartNodePredicate.INSTANCE), containerInfo, sequence ) diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/expression/ArbitraryExpression.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/expression/ArbitraryExpression.java index 390413ae0..2445c0e31 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/expression/ArbitraryExpression.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/expression/ArbitraryExpression.java @@ -34,10 +34,11 @@ import com.navercorp.fixturemonkey.tree.CompositeNodeResolver; import com.navercorp.fixturemonkey.tree.ContainerElementPredicate; -import com.navercorp.fixturemonkey.tree.DefaultNodeResolver; -import com.navercorp.fixturemonkey.tree.IdentityNodeResolver; +import com.navercorp.fixturemonkey.tree.NextNodePredicate; +import com.navercorp.fixturemonkey.tree.NodePredicateResolver; import com.navercorp.fixturemonkey.tree.NodeResolver; import com.navercorp.fixturemonkey.tree.PropertyNameNodePredicate; +import com.navercorp.fixturemonkey.tree.StartNodePredicate; public final class ArbitraryExpression implements MonkeyExpression, Comparable { private final List expList; @@ -142,6 +143,16 @@ public NodeResolver toNodeResolver() { return nodeResolver; } + @Override + public List toNextNodePredicate() { + List nextNodePredicates = new ArrayList<>(); + nextNodePredicates.add(StartNodePredicate.INSTANCE); + for (Exp exp : expList) { + nextNodePredicates.addAll(exp.toNextNodePredicates()); + } + return nextNodePredicates; + } + private static final class ExpIndex implements Comparable { public static final ExpIndex ALL_INDEX_EXP_INDEX = new ExpIndex(NO_OR_ALL_INDEX_INTEGER_VALUE); @@ -225,24 +236,36 @@ public Exp(String expression) { } public NodeResolver toNodeResolver() { - NodeResolver nodeResolver = IdentityNodeResolver.INSTANCE; + NodeResolver nodeResolver = new NodePredicateResolver(StartNodePredicate.INSTANCE); if (!HEAD_NAME.equals(name)) { nodeResolver = new CompositeNodeResolver( nodeResolver, - new DefaultNodeResolver(new PropertyNameNodePredicate(name)) + new NodePredicateResolver(new PropertyNameNodePredicate(name)) ); } for (ExpIndex index : indices) { nodeResolver = new CompositeNodeResolver( nodeResolver, - new DefaultNodeResolver(new ContainerElementPredicate(index.getIndex())) + new NodePredicateResolver(new ContainerElementPredicate(index.getIndex())) ); } return nodeResolver; } + public List toNextNodePredicates() { + List nextNodePredicates = new ArrayList<>(); + if (!HEAD_NAME.equals(name)) { + nextNodePredicates.add(new PropertyNameNodePredicate(name)); + } + + for (ExpIndex index : indices) { + nextNodePredicates.add(new ContainerElementPredicate(index.getIndex())); + } + return nextNodePredicates; + } + public String getName() { return name; } diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/expression/MonkeyExpression.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/expression/MonkeyExpression.java index 8d834211e..658c9df08 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/expression/MonkeyExpression.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/expression/MonkeyExpression.java @@ -18,13 +18,17 @@ package com.navercorp.fixturemonkey.expression; +import java.util.List; + import org.apiguardian.api.API; import org.apiguardian.api.API.Status; +import com.navercorp.fixturemonkey.tree.NextNodePredicate; import com.navercorp.fixturemonkey.tree.NodeResolver; @API(since = "0.4.0", status = Status.MAINTAINED) -@FunctionalInterface public interface MonkeyExpression { NodeResolver toNodeResolver(); + + List toNextNodePredicate(); } diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/ApplyStrictModeResolver.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/ApplyStrictModeResolver.java index bdb6381b6..6f5e1ee69 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/ApplyStrictModeResolver.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/ApplyStrictModeResolver.java @@ -23,6 +23,9 @@ import org.apiguardian.api.API; import org.apiguardian.api.API.Status; +/** + * A {@link NodeResolver} that applies strict mode. + */ @API(since = "0.4.0", status = Status.MAINTAINED) public final class ApplyStrictModeResolver implements NodeResolver { private final NodeResolver nodeResolver; @@ -32,17 +35,12 @@ public ApplyStrictModeResolver(NodeResolver nodeResolver) { } @Override - public List resolve(ObjectNode objectNode) { - List selectedNodes = nodeResolver.resolve(objectNode); + public List resolve(ObjectNode nextNode) { + List selectedNodes = nodeResolver.resolve(nextNode); if (selectedNodes.isEmpty()) { throw new IllegalArgumentException("No matching results for given NodeResolvers."); } return selectedNodes; } - - @Override - public List toNextNodePredicate() { - return nodeResolver.toNextNodePredicate(); - } } diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/CompositeNodeResolver.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/CompositeNodeResolver.java index 482397d5d..df7ab63d1 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/CompositeNodeResolver.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/CompositeNodeResolver.java @@ -24,7 +24,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; import org.apiguardian.api.API; import org.apiguardian.api.API.Status; @@ -42,9 +41,9 @@ public CompositeNodeResolver(List nodeResolvers) { } @Override - public List resolve(ObjectNode objectNode) { + public List resolve(ObjectNode nextNode) { LinkedList nextNodes = new LinkedList<>(); - nextNodes.add(objectNode); + nextNodes.add(nextNode); for (NodeResolver nodeResolver : nodeResolvers) { List resolvedNodes = new LinkedList<>(); while (!nextNodes.isEmpty()) { @@ -83,9 +82,7 @@ private List distinct(List resolvers) { NodeResolver resolver = resolvers.get(i); if (resolver instanceof CompositeNodeResolver) { CompositeNodeResolver compositeNodeResolver = (CompositeNodeResolver)resolver; - List componentNodeResolvers = compositeNodeResolver.flatten().stream() - .filter(it -> !(it instanceof IdentityNodeResolver)) - .collect(Collectors.toList()); + List componentNodeResolvers = compositeNodeResolver.flatten(); result.add(new CompositeNodeResolver(componentNodeResolvers)); } else { @@ -111,12 +108,4 @@ public boolean equals(Object obj) { public int hashCode() { return Objects.hash(nodeResolvers); } - - @Override - public List toNextNodePredicate() { - return flatten().stream() - .flatMap(it -> it.toNextNodePredicate().stream()) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - } } diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/DefaultNodeResolver.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/DefaultNodeResolver.java deleted file mode 100644 index 56f995619..000000000 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/DefaultNodeResolver.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Fixture Monkey - * - * Copyright (c) 2021-present NAVER Corp. - * - * 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 com.navercorp.fixturemonkey.tree; - -import static com.navercorp.fixturemonkey.api.generator.DefaultNullInjectGenerator.NOT_NULL_INJECT; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.apiguardian.api.API; -import org.apiguardian.api.API.Status; - -@API(since = "0.4.0", status = Status.MAINTAINED) -public final class DefaultNodeResolver implements NodeResolver { - private final NextNodePredicate nextNodePredicate; - - public DefaultNodeResolver(NextNodePredicate nextNodePredicate) { - this.nextNodePredicate = nextNodePredicate; - } - - @Override - public List resolve(ObjectNode objectNode) { - objectNode.expand(); - List resolved = objectNode.getChildren().asList().stream() - .filter(it -> nextNodePredicate.test(it.getArbitraryProperty().getObjectProperty())) - .collect(Collectors.toList()); - - objectNode.setNullInject(NOT_NULL_INJECT); - for (ObjectNode node : resolved) { - node.setNullInject(NOT_NULL_INJECT); - } - - return resolved; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - DefaultNodeResolver that = (DefaultNodeResolver)obj; - return nextNodePredicate.equals(that.nextNodePredicate); - } - - @Override - public int hashCode() { - return Objects.hash(nextNodePredicate); - } - - @Override - public List toNextNodePredicate() { - return Collections.singletonList(nextNodePredicate); - } -} diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/IdentityNodeResolver.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/IdentityNodeResolver.java deleted file mode 100644 index 9c23b8b20..000000000 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/IdentityNodeResolver.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Fixture Monkey - * - * Copyright (c) 2021-present NAVER Corp. - * - * 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 com.navercorp.fixturemonkey.tree; - -import static com.navercorp.fixturemonkey.api.generator.DefaultNullInjectGenerator.NOT_NULL_INJECT; -import static com.navercorp.fixturemonkey.api.type.Types.nullSafe; - -import java.util.Collections; -import java.util.List; - -import org.apiguardian.api.API; -import org.apiguardian.api.API.Status; - -import com.navercorp.fixturemonkey.api.property.SingleElementProperty; - -@API(since = "0.4.0", status = Status.MAINTAINED) -public final class IdentityNodeResolver implements NodeResolver { - public static final IdentityNodeResolver INSTANCE = new IdentityNodeResolver(); - - private IdentityNodeResolver() { - } - - @Override - public List resolve(ObjectNode objectNode) { - ObjectNode resultNode = getChildNodeIfWrapped(objectNode); - resultNode.setNullInject(NOT_NULL_INJECT); - - return Collections.singletonList(resultNode); - } - - private ObjectNode getChildNodeIfWrapped(ObjectNode objectNode) { - ObjectNode searchNode = objectNode; - - while (isWrappedNode(searchNode)) { - searchNode.expand(); - searchNode = nullSafe(searchNode.getChildren()).asList().get(0); - } - - return searchNode; - } - - private boolean isWrappedNode(ObjectNode searchNode) { - searchNode.expand(); - List children = nullSafe(searchNode.getChildren()).asList(); - - return children.size() == 1 - && children.get(0).getResolvedProperty() instanceof SingleElementProperty; - } - - @Override - public List toNextNodePredicate() { - return Collections.singletonList(StartNodePredicate.INSTANCE); - } -} diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/NodePredicateResolver.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/NodePredicateResolver.java new file mode 100644 index 000000000..3cfe31bbf --- /dev/null +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/NodePredicateResolver.java @@ -0,0 +1,111 @@ +/* + * Fixture Monkey + * + * Copyright (c) 2021-present NAVER Corp. + * + * 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 com.navercorp.fixturemonkey.tree; + +import static com.navercorp.fixturemonkey.api.generator.DefaultNullInjectGenerator.NOT_NULL_INJECT; +import static com.navercorp.fixturemonkey.api.type.Types.nullSafe; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.apiguardian.api.API; +import org.apiguardian.api.API.Status; + +import com.navercorp.fixturemonkey.api.property.SingleElementProperty; + +/** + * A {@link NodeResolver} that resolves the next nodes by the given {@link NextNodePredicate}. + */ +@API(since = "1.1.4", status = Status.EXPERIMENTAL) +public final class NodePredicateResolver implements NodeResolver { + private final NextNodePredicate nextNodePredicate; + + public NodePredicateResolver(NextNodePredicate nextNodePredicate) { + this.nextNodePredicate = nextNodePredicate; + } + + /** + * Resolves the next nodes by the given {@link NextNodePredicate}. + * If the {@code nextNodePredicate} is {@link StartNodePredicate}, it resolves the given {@code nextNode}. + * + * @param nextNode it may be the root node or the parent node resolved by the previous NodeResolver + * @return the next nodes that satisfy the given {@link NextNodePredicate}, + * or an empty list if there are no such nodes. {@link StartNodePredicate} always returns given {@code nextNode}. + */ + @Override + public List resolve(ObjectNode nextNode) { + if (nextNodePredicate == StartNodePredicate.INSTANCE) { + return Collections.singletonList(resolveStartNode(nextNode)); + } + + nextNode.expand(); + List resolved = nextNode.getChildren().asList().stream() + .filter(it -> nextNodePredicate.test(it.getArbitraryProperty().getObjectProperty())) + .collect(Collectors.toList()); + + nextNode.setNullInject(NOT_NULL_INJECT); + for (ObjectNode node : resolved) { + node.setNullInject(NOT_NULL_INJECT); + } + + return resolved; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + NodePredicateResolver that = (NodePredicateResolver)obj; + return nextNodePredicate.equals(that.nextNodePredicate); + } + + @Override + public int hashCode() { + return Objects.hash(nextNodePredicate); + } + + public ObjectNode resolveStartNode(ObjectNode startNode) { + ObjectNode resultNode = getChildNodeIfWrapped(startNode); + resultNode.setNullInject(NOT_NULL_INJECT); + + return resultNode; + } + + private ObjectNode getChildNodeIfWrapped(ObjectNode objectNode) { + ObjectNode searchNode = objectNode; + + searchNode.expand(); + while (isWrappedNode(searchNode)) { + searchNode = nullSafe(searchNode.getChildren()).asList().get(0); + } + + return searchNode; + } + + private boolean isWrappedNode(ObjectNode searchNode) { + List children = nullSafe(searchNode.getChildren()).asList(); + + return children.size() == 1 && children.get(0).getResolvedProperty() instanceof SingleElementProperty; + } +} diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/NodeResolver.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/NodeResolver.java index 3134f6360..e6a96fe85 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/NodeResolver.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/NodeResolver.java @@ -25,7 +25,11 @@ @API(since = "0.4.0", status = Status.MAINTAINED) public interface NodeResolver { - List resolve(ObjectNode objectNode); - - List toNextNodePredicate(); + /** + * Resolves the next nodes. The nextNode can be omitted if it cannot be resolved from traversal. + * + * @param nextNode it may be the root node or the parent node resolved by the previous {@link NodeResolver} + * @return the next nodes + */ + List resolve(ObjectNode nextNode); } diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/StaticNodeResolver.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/StaticNodeResolver.java new file mode 100644 index 000000000..4cad5d99c --- /dev/null +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/tree/StaticNodeResolver.java @@ -0,0 +1,47 @@ +/* + * Fixture Monkey + * + * Copyright (c) 2021-present NAVER Corp. + * + * 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 com.navercorp.fixturemonkey.tree; + +import java.util.List; + +import org.apiguardian.api.API; +import org.apiguardian.api.API.Status; + +/** + * A {@link NodeResolver} that resolves the provided nodes statically. + */ +@API(since = "1.1.4", status = Status.EXPERIMENTAL) +public final class StaticNodeResolver implements NodeResolver { + private final List nodes; + + public StaticNodeResolver(List nodes) { + this.nodes = nodes; + } + + /** + * Resolves the next nodes statically. It always returns the provided nodes. + * + * @param nextNode it may be the root node or the parent node resolved by the previous {@link NodeResolver} + * @return the provided nodes statically + */ + @Override + public List resolve(ObjectNode nextNode) { + return nodes; + } +}