com.exasol.adapter.document.documentpath.DocumentPathWalker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of virtual-schema-common-document Show documentation
Show all versions of virtual-schema-common-document Show documentation
Common module of Exasol Virtual Schema Adapters for Document Data Sources.
The newest version!
package com.exasol.adapter.document.documentpath;
import java.util.Optional;
import java.util.function.BiFunction;
import com.exasol.adapter.document.documentnode.DocumentArray;
import com.exasol.adapter.document.documentnode.DocumentNode;
import com.exasol.adapter.document.documentnode.DocumentObject;
/**
* This class walks a given path defined in {@link DocumentPathExpression} through a {@link DocumentNode} structure.
*/
@java.lang.SuppressWarnings("squid:S119") // VisitorType does not fit naming conventions.
public class DocumentPathWalker {
private final DocumentPathExpression pathExpression;
private final PathIterationStateProvider iterationStateProvider;
/**
* Create an instance of {@link DocumentPathWalker}.
*
* @param pathExpression path to walk
* @param iterationStateProvider iteration state for {@link ArrayAllPathSegment}s in the path
*/
public DocumentPathWalker(final DocumentPathExpression pathExpression,
final PathIterationStateProvider iterationStateProvider) {
this.pathExpression = pathExpression;
this.iterationStateProvider = iterationStateProvider;
}
/**
* Walks the path defined in constructor through the given document.
*
* @param rootNode document to walk through
* @return document's attribute described in {@link DocumentPathExpression} or an empty {@link Optional} if the
* defined path does not exist in the given document
*/
public Optional walkThroughDocument(final DocumentNode rootNode) {
return this.performStep(rootNode, 0);
}
private Optional performStep(final DocumentNode thisNode, final int position) {
if (this.pathExpression.size() <= position) {
return Optional.of(thisNode);
}
final BiFunction> stepper = getStepperFor(
this.pathExpression.getSegments().get(position));
return runTraverseStepper(stepper, thisNode, position);
}
private Optional runTraverseStepper(
final BiFunction> traverseStepper,
final DocumentNode thisNode, final int position) {
final Optional nextNode = traverseStepper.apply(thisNode,
this.pathExpression.getSubPath(0, position + 1));
if (nextNode.isEmpty()) {
return Optional.empty();
} else {
return performStep(nextNode.get(), position + 1);
}
}
private BiFunction> getStepperFor(
final PathSegment pathSegment) {
final WalkVisitor visitor = new WalkVisitor();
pathSegment.accept(visitor);
return visitor.getStepper();
}
private class WalkVisitor implements PathSegmentVisitor {
BiFunction> stepper;
@Override
public void visit(final ObjectLookupPathSegment objectLookupPathSegment) {
this.stepper = (thisNode, pathToThisNode) -> {
final String key = objectLookupPathSegment.getLookupKey();
if (!(thisNode instanceof DocumentObject)) {
return Optional.empty();
}
final DocumentObject thisObject = (DocumentObject) thisNode;
if (!thisObject.hasKey(key)) {
return Optional.empty();
}
return Optional.of(thisObject.get(key));
};
}
@Override
public void visit(final ArrayLookupPathSegment arrayLookupPathSegment) {
this.stepper = (thisNode, pathToThisNode) -> {
if (!(thisNode instanceof DocumentArray)) {
return Optional.empty();
}
final DocumentArray thisArray = (DocumentArray) thisNode;
if (thisArray.size() < arrayLookupPathSegment.getLookupIndex()) {
return Optional.empty();
}
return Optional.of(thisArray.getValue(arrayLookupPathSegment.getLookupIndex()));
};
}
@Override
public void visit(final ArrayAllPathSegment arrayAllPathSegment) {
this.stepper = (thisNode, pathToThisNode) -> {
if (!(thisNode instanceof DocumentArray)) {
return Optional.empty();
}
final DocumentArray thisArray = (DocumentArray) thisNode;
final int iterationIndex = DocumentPathWalker.this.iterationStateProvider.getIndexFor(pathToThisNode);
return Optional.of(thisArray.getValue(iterationIndex));
};
}
public BiFunction> getStepper() {
return this.stepper;
}
}
}