Skip to content

Commit

Permalink
[#6] Added callback capability to JsonStateWalker
Browse files Browse the repository at this point in the history
Also reworked terminology so that "walk" now refers to what the walker
does to the tree as a whole or to container nodes within it, while
"visit" is used for what happens to each node included in the walk.

The Disposition class for the result of a visit now includes an optional
Callback metnod instance, which will be invoked just prior to finishing
up the visit. This means that the walk method can do something while
visiting a container node and then undo it after all its children have
been visited.

We'll use that for managing a set of references already encountered in
this walk while visiting an ancestor node, in order to support reference
cycle detection.
  • Loading branch information
andylowry committed Sep 19, 2018
1 parent 5a58c89 commit 6f172ab
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 245 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import com.reprezen.kaizen.normalizer.ReferenceScanner.ScanOp;
import com.reprezen.kaizen.normalizer.util.JsonCopier;
import com.reprezen.kaizen.normalizer.util.JsonStateWalker;
import com.reprezen.kaizen.normalizer.util.JsonStateWalker.AdvancedWalkMethod;
import com.reprezen.kaizen.normalizer.util.JsonStateWalker.AdvancedVisitMethod;
import com.reprezen.kaizen.normalizer.util.JsonStateWalker.Disposition;
import com.reprezen.kaizen.normalizer.util.StateMachine;
import com.reprezen.kaizen.normalizer.util.StateMachine.State;
Expand Down Expand Up @@ -73,19 +73,19 @@ private JsonNode buildNormalizedModel(Content<E> topModel) {

private void copyOtherElements(JsonNode model, ObjectNode result) {
Tracker<E> tracker = machine.tracker(modelState);
OtherElementWalkMethod<E> walkMethod = new OtherElementWalkMethod<E>(result);
new JsonStateWalker<E>(tracker, walkMethod, true, true).walk(model);
OtherElementVisitMethod<E> visitMethod = new OtherElementVisitMethod<E>(result);
new JsonStateWalker<E>(tracker, visitMethod, true, true).walk(model);
}

private static class OtherElementWalkMethod<E extends Enum<E> & Component> implements AdvancedWalkMethod<E> {
private static class OtherElementVisitMethod<E extends Enum<E> & Component> implements AdvancedVisitMethod<E> {
private JsonNode target;

public OtherElementWalkMethod(JsonNode copyTo) {
public OtherElementVisitMethod(JsonNode copyTo) {
this.target = copyTo;
}

@Override
public Disposition walk(JsonNode node, State<E> state, E stateValue, List<Object> path, JsonPointer pointer) {
public Disposition visit(JsonNode node, State<E> state, E stateValue, List<Object> path, JsonPointer pointer) {
if (stateValue == V2State.OFFROAD) {
JsonCopier.copy(node, target, path);
return Disposition.done();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import com.reprezen.kaizen.normalizer.Localizer.LocalizedContent;
import com.reprezen.kaizen.normalizer.Reference.ReferenceTreatment;
import com.reprezen.kaizen.normalizer.util.JsonStateWalker;
import com.reprezen.kaizen.normalizer.util.JsonStateWalker.AdvancedWalkMethod;
import com.reprezen.kaizen.normalizer.util.JsonStateWalker.AdvancedVisitMethod;
import com.reprezen.kaizen.normalizer.util.JsonStateWalker.Disposition;
import com.reprezen.kaizen.normalizer.util.StateMachine;
import com.reprezen.kaizen.normalizer.util.StateMachine.State;
Expand Down Expand Up @@ -51,48 +51,42 @@ public JsonNode scan(E start) {

public JsonNode scan(State<E> startState) {
Tracker<E> tracker = machine.tracker(startState);
Walkers<E> walkers = new Walkers<E>(base, contentManager, options);
AdvancedWalkMethod<E> walkMethod = walkers.getWalkMethod(scanOp);
Optional<JsonNode> newNode = new JsonStateWalker<E>(tracker, walkMethod).walk(tree);
Visitors<E> visitors = new Visitors<E>(base, contentManager, options);
AdvancedVisitMethod<E> visitMethod = visitors.getVisitMethod(scanOp);
Optional<JsonNode> newNode = new JsonStateWalker<E>(tracker, visitMethod).walk(tree);
return newNode.orElse(tree);
}

private static class Walkers<E extends Enum<E> & Component> {
private static class Visitors<E extends Enum<E> & Component> {
private Reference base;
private ContentManager<E> contentManager;
private Options options;

public Walkers(Reference base, ContentManager<E> contentManager, Options options) {
public Visitors(Reference base, ContentManager<E> contentManager, Options options) {
this.base = base;
this.contentManager = contentManager;
this.options = options;
}

public AdvancedWalkMethod<E> getWalkMethod(ScanOp scanOp) {
public AdvancedVisitMethod<E> getVisitMethod(ScanOp scanOp) {
switch (scanOp) {
case LOAD:
return this::loadWalkMethod;
return this::loadVisitMethod;
case COMPONENTS:
return this::componentWalkMethod;
return this::componentVisitMethod;
case POLICY:
return this::policyWalkMethod;
return this::policyVisitMethod;
case NONE:
default:
throw new IllegalArgumentException("There is no walk method for a NONE scan oepration");
throw new IllegalArgumentException("There is no visit method for a NONE scan oepration");
}
}

/**
* Walk method for the LOAD phase, where model files are filled in by having
* Visit method for the LOAD phase, where model files are filled in by having
* their non-conforming references inlined.
* <p>
* Scan Args:
* <ol>
* <li>boolean - whether to fix simple refs</li>
* </ol>
*
*/
public Disposition loadWalkMethod(JsonNode node, State<E> state, E stateValue, List<Object> path,
public Disposition loadVisitMethod(JsonNode node, State<E> state, E stateValue, List<Object> path,
JsonPointer pointer) {
if (Reference.isRefNode(node)) {
Reference ref = new Reference(getRefString(node).get(), base, stateValue);
Expand All @@ -105,33 +99,31 @@ public Disposition loadWalkMethod(JsonNode node, State<E> state, E stateValue, L
// with an adorned ref node
// TODO handle cycles
Content<E> toInline = contentManager.load(ref, state);
return toInline.isValid() ? Disposition.rewalk(toInline.copyTree())
: Disposition.done(toInline.getRef().getRefNode());
return toInline.isValid() ? Disposition.revisit().withReplacement(toInline.copyTree())
: Disposition.done().withReplacement(toInline.getRef().getRefNode());
}
case MERGE:
case INLINE_CONFORMING:
case LOCALIZE:
case RETAIN:
case ERROR:
// all other refs are copied with adornments
return Disposition.done(ref.getRefNode());
return Disposition.done().withReplacement(ref.getRefNode());
}
}
return Disposition.normal();
}

/**
* Walk method for the COMPONENTS phase, where local component definitions are
* Visit method for the COMPONENTS phase, where local component definitions are
* located and added to the localizer for possible inclusion in the final model.
* <p>
* Name collisions are resolved by the localizer. Note that additional component
* definitions may be localized in the POLICY phase, since COMPONENTS phase only
* localizes definitions in their standard containers in top-level source
* models.
* <p>
* Scan args: none
*/
public Disposition componentWalkMethod(JsonNode node, State<E> state, E stateValue, List<Object> path,
public Disposition componentVisitMethod(JsonNode node, State<E> state, E stateValue, List<Object> path,
JsonPointer pointer) {
if (stateValue.isDefiningSite()) {
if (stateValue.hasMergeSemantics()) {
Expand All @@ -157,7 +149,7 @@ public Disposition componentWalkMethod(JsonNode node, State<E> state, E stateVal
}

/**
* Walk method for the POLICY phase, where conforming references are either
* Visit method for the POLICY phase, where conforming references are either
* inlined or retained, accorinding to retention policy in force.
* <p>
* Conforming references are resolved for the first time in this phase, and such
Expand All @@ -176,25 +168,20 @@ public Disposition componentWalkMethod(JsonNode node, State<E> state, E stateVal
* component definitions to the normalized model. Of course conforming
* references contained in newly loaded content may still contribute new
* definitions through localization.
* <p>
* Scan args:
* <ol>
* <li>boolean - whether to fix simple refs when scanning new content inlined or
* localized content</li>
*/
public Disposition policyWalkMethod(JsonNode node, State<E> state, E stateValue, List<Object> path,
public Disposition policyVisitMethod(JsonNode node, State<E> state, E stateValue, List<Object> path,
JsonPointer pointer) {
if (Reference.isRefNode(node) && stateValue.isConformingSite()) {
Reference ref = new Reference(getRefString(node).get(), base, stateValue);
ReferenceTreatment treatment = ref.getTreatment(options);
switch (treatment) {
case INLINE_NONCONFORMING: {
// same behavior as in the LOAD phase walk method
// same behavior as in the LOAD phase visit method
// TODO handle cycles
Content<E> toInline = contentManager.load(ref, state);
toInline.scan(ScanOp.LOAD);
return toInline.isValid() ? Disposition.rewalk(toInline.copyTree())
: Disposition.done(toInline.getRef().getRefNode(false));
return toInline.isValid() ? Disposition.revisit().withReplacement(toInline.copyTree())
: Disposition.done().withReplacement(toInline.getRef().getRefNode(false));
}
case INLINE_CONFORMING: {
// almost the same, but if this reference creates a cycle, we localize it
Expand All @@ -204,8 +191,8 @@ public Disposition policyWalkMethod(JsonNode node, State<E> state, E stateValue,
if (toInline.isValid()) {
toInline.scan(ScanOp.LOAD);
}
return toInline.isValid() ? Disposition.rewalk(toInline.copyTree())
: Disposition.done(toInline.getRef().getRefNode(false));
return toInline.isValid() ? Disposition.revisit().withReplacement(toInline.copyTree())
: Disposition.done().withReplacement(toInline.getRef().getRefNode(false));
}
case LOCALIZE: {
Content<E> toLocalize = contentManager.load(ref, state);
Expand All @@ -214,9 +201,9 @@ public Disposition policyWalkMethod(JsonNode node, State<E> state, E stateValue,
toLocalize.scan(ScanOp.POLICY);
LocalizedContent localized = contentManager.localize(toLocalize.getTree(), stateValue,
ref.getPointer(), ref);
return Disposition.done(localized.getLocalizedRef(ref).getRefNode(false));
return Disposition.done().withReplacement(localized.getLocalizedRef(ref).getRefNode(false));
} else {
return Disposition.done(ref.getRefNode(false));
return Disposition.done().withReplacement(ref.getRefNode(false));
}
}
case MERGE: {
Expand All @@ -230,13 +217,13 @@ public Disposition policyWalkMethod(JsonNode node, State<E> state, E stateValue,
} else {
contentManager.mergeLocalize(ref.getRefNode(false), stateValue, pointer, base);
}
return Disposition.done(ref.getRefNode(false));
return Disposition.done().withReplacement(ref.getRefNode(false));
}
case ERROR:
case RETAIN:
// pass through erroneous and retained references, but remove any adornments
// from prior phases
return Disposition.done(ref.getRefNode(false));
return Disposition.done().withReplacement(ref.getRefNode(false));
default:
break;

Expand Down
Loading

0 comments on commit 6f172ab

Please sign in to comment.