diff --git a/exist-core/src/main/java/org/exist/xquery/AbstractPragma.java b/exist-core/src/main/java/org/exist/xquery/AbstractPragma.java index bd96758ba10..08057ad3aaa 100644 --- a/exist-core/src/main/java/org/exist/xquery/AbstractPragma.java +++ b/exist-core/src/main/java/org/exist/xquery/AbstractPragma.java @@ -93,6 +93,13 @@ public void resetState(final boolean postOptimization) { @Override public String toString() { - return "(# " + name + ' ' + contents + "#)"; + final StringBuilder builder = new StringBuilder(); + builder.append("(# "); + builder.append(name); + if (contents != null && !contents.isEmpty()) { + builder.append(' ').append(contents); + } + builder.append("#)"); + return builder.toString(); } } diff --git a/exist-core/src/main/java/org/exist/xquery/BasicExpressionVisitor.java b/exist-core/src/main/java/org/exist/xquery/BasicExpressionVisitor.java index 2260aa2596f..2975f22c470 100644 --- a/exist-core/src/main/java/org/exist/xquery/BasicExpressionVisitor.java +++ b/exist-core/src/main/java/org/exist/xquery/BasicExpressionVisitor.java @@ -28,19 +28,18 @@ * Basic implementation of the {@link ExpressionVisitor} interface. * This implementation will traverse a PathExpr object if it wraps * around a single other expression. All other methods are empty. - * - * @author wolf * + * @author wolf */ public class BasicExpressionVisitor implements ExpressionVisitor { @Override - public void visit(Expression expression) { + public void visit(final Expression expression) { processWrappers(expression); } @Override - public void visitCastExpr(CastExpression expression) { + public void visitCastExpr(final CastExpression expression) { //Nothing to do } @@ -50,7 +49,7 @@ public void visitCastExpr(CastExpression expression) { * expression object. */ @Override - public void visitPathExpr(PathExpr expression) { + public void visitPathExpr(final PathExpr expression) { if (expression.getLength() == 1) { final Expression next = expression.getExpression(0); next.accept(this); @@ -58,182 +57,185 @@ public void visitPathExpr(PathExpr expression) { } @Override - public void visitFunctionCall(FunctionCall call) { + public void visitFunctionCall(final FunctionCall call) { // Nothing to do } @Override - public void visitGeneralComparison(GeneralComparison comparison) { + public void visitGeneralComparison(final GeneralComparison comparison) { //Nothing to do } @Override - public void visitUnionExpr(Union union) { + public void visitUnionExpr(final Union union) { //Nothing to do } @Override - public void visitIntersectionExpr(Intersect intersect) { + public void visitIntersectionExpr(final Intersect intersect) { //Nothing to do } @Override - public void visitAndExpr(OpAnd and) { + public void visitAndExpr(final OpAnd and) { //Nothing to do } @Override - public void visitOrExpr(OpOr or) { + public void visitOrExpr(final OpOr or) { //Nothing to do } @Override - public void visitLocationStep(LocationStep locationStep) { + public void visitLocationStep(final LocationStep locationStep) { //Nothing to do } @Override - public void visitFilteredExpr(FilteredExpression filtered) { + public void visitFilteredExpr(final FilteredExpression filtered) { //Nothing to do } @Override - public void visitPredicate(Predicate predicate) { + public void visitPredicate(final Predicate predicate) { //Nothing to do } @Override - public void visitVariableReference(VariableReference ref) { + public void visitVariableReference(final VariableReference ref) { //Nothing to do } @Override - public void visitVariableDeclaration(VariableDeclaration decl) { - // Nothing to do + public void visitVariableDeclaration(final VariableDeclaration decl) { + // Nothing to do } - - protected void processWrappers(Expression expr) { + + protected void processWrappers(final Expression expr) { if (expr instanceof Atomize || expr instanceof DynamicCardinalityCheck || expr instanceof DynamicNameCheck || expr instanceof DynamicTypeCheck || - expr instanceof UntypedValueCheck) { + expr instanceof UntypedValueCheck || + expr instanceof PathExpr) { expr.accept(this); } } - public static LocationStep findFirstStep(Expression expr) { - if (expr instanceof LocationStep) - {return (LocationStep) expr;} + public static LocationStep findFirstStep(final Expression expr) { + if (expr instanceof LocationStep) { + return (LocationStep) expr; + } final FirstStepVisitor visitor = new FirstStepVisitor(); expr.accept(visitor); return visitor.firstStep; } - public static List findLocationSteps(Expression expr) { + public static List findLocationSteps(final Expression expr) { final List steps = new ArrayList<>(5); if (expr instanceof LocationStep) { - steps.add((LocationStep)expr); + steps.add((LocationStep) expr); return steps; } expr.accept( - new BasicExpressionVisitor() { - @Override - public void visitPathExpr(PathExpr expression) { - for (int i = 0; i < expression.getLength(); i++) { - final Expression next = expression.getExpression(i); - next.accept(this); - if (steps.size() - 1 != i) { - steps.add(null); + new BasicExpressionVisitor() { + @Override + public void visitPathExpr(final PathExpr expression) { + for (int i = 0; i < expression.getLength(); i++) { + final Expression next = expression.getExpression(i); + next.accept(this); + if (steps.size() - 1 != i) { + steps.add(null); + } } } + + @Override + public void visitLocationStep(final LocationStep locationStep) { + steps.add(locationStep); + } } - @Override - public void visitLocationStep(LocationStep locationStep) { - steps.add(locationStep); - } - } ); return steps; } - public static VariableReference findVariableRef(Expression expr) { + public static VariableReference findVariableRef(final Expression expr) { final VariableRefVisitor visitor = new VariableRefVisitor(); expr.accept(visitor); return visitor.ref; } @Override - public void visitForExpression(ForExpr forExpr) { + public void visitForExpression(final ForExpr forExpr) { //Nothing to do } @Override - public void visitLetExpression(LetExpr letExpr) { + public void visitLetExpression(final LetExpr letExpr) { //Nothing to do } @Override - public void visitOrderByClause(OrderByClause orderBy) { + public void visitOrderByClause(final OrderByClause orderBy) { // Nothing to do } @Override - public void visitGroupByClause(GroupByClause groupBy) { + public void visitGroupByClause(final GroupByClause groupBy) { // Nothing to do } @Override - public void visitWhereClause(WhereClause where) { + public void visitWhereClause(final WhereClause where) { // Nothing to do } @Override - public void visitBuiltinFunction(Function function) { + public void visitBuiltinFunction(final Function function) { //Nothing to do } @Override - public void visitUserFunction(UserDefinedFunction function) { + public void visitUserFunction(final UserDefinedFunction function) { //Nothing to do } @Override - public void visitConditional(ConditionalExpression conditional) { + public void visitConditional(final ConditionalExpression conditional) { //Nothing to do } @Override - public void visitTryCatch(TryCatchExpression conditional) { + public void visitTryCatch(final TryCatchExpression conditional) { //Nothing to do } @Override - public void visitDocumentConstructor(DocumentConstructor constructor) { - // Nothing to do + public void visitDocumentConstructor(final DocumentConstructor constructor) { + // Nothing to do } - - public void visitElementConstructor(ElementConstructor constructor) { + + public void visitElementConstructor(final ElementConstructor constructor) { //Nothing to do } @Override - public void visitTextConstructor(DynamicTextConstructor constructor) { + public void visitTextConstructor(final DynamicTextConstructor constructor) { //Nothing to do } @Override - public void visitAttribConstructor(AttributeConstructor constructor) { + public void visitAttribConstructor(final AttributeConstructor constructor) { //Nothing to do } @Override - public void visitAttribConstructor(DynamicAttributeConstructor constructor) { + public void visitAttribConstructor(final DynamicAttributeConstructor constructor) { //Nothing to do } @Override - public void visitSimpleMapOperator(OpSimpleMap simpleMap) { + public void visitSimpleMapOperator(final OpSimpleMap simpleMap) { // Nothing to do } @@ -246,7 +248,7 @@ public LocationStep getFirstStep() { } @Override - public void visitLocationStep(LocationStep locationStep) { + public void visitLocationStep(final LocationStep locationStep) { firstStep = locationStep; } } @@ -256,12 +258,12 @@ public static class VariableRefVisitor extends BasicExpressionVisitor { private VariableReference ref = null; @Override - public void visitVariableReference(VariableReference ref) { + public void visitVariableReference(final VariableReference ref) { this.ref = ref; } @Override - public void visitPathExpr(PathExpr expression) { + public void visitPathExpr(final PathExpr expression) { for (int i = 0; i < expression.getLength(); i++) { final Expression next = expression.getExpression(i); next.accept(this); diff --git a/exist-core/src/main/java/org/exist/xquery/ExtensionExpression.java b/exist-core/src/main/java/org/exist/xquery/ExtensionExpression.java index e18dcaf600e..135646e874b 100644 --- a/exist-core/src/main/java/org/exist/xquery/ExtensionExpression.java +++ b/exist-core/src/main/java/org/exist/xquery/ExtensionExpression.java @@ -26,20 +26,20 @@ import org.exist.xquery.value.Item; import org.exist.xquery.value.Sequence; -import java.util.ArrayList; -import java.util.List; +import javax.annotation.Nullable; +import java.util.Arrays; /** * Implements an XQuery extension expression. An extension expression starts with - * a list of pragmas, followed by an expression enclosed in curly braces. For evaluation - * details check {{@link #eval(Sequence, Item)}. + * a one or more pragmas, followed by an expression enclosed in curly braces. For evaluation + * details check {@link #eval(Sequence, Item)}. * * @author wolf */ public class ExtensionExpression extends AbstractExpression { + @Nullable private Pragma[] pragmas = null; private Expression innerExpression; - private final List pragmas = new ArrayList<>(3); public ExtensionExpression(final XQueryContext context) { super(context); @@ -50,7 +50,12 @@ public void setExpression(final Expression inner) { } public void addPragma(final Pragma pragma) { - pragmas.add(pragma); + if (pragmas == null) { + pragmas = new Pragma[1]; + } else { + pragmas = Arrays.copyOf(pragmas, pragmas.length + 1); + } + pragmas[pragmas.length - 1] = pragma; } /** @@ -64,12 +69,14 @@ public void addPragma(final Pragma pragma) { @Override public Sequence eval(final Sequence contextSequence, final Item contextItem) throws XPathException { callBefore(contextSequence); - Sequence result = null; - for (final Pragma pragma : pragmas) { - final Sequence temp = pragma.eval(contextSequence, contextItem); - if (temp != null) { - result = temp; - break; + @Nullable Sequence result = null; + if (pragmas != null) { + for (final Pragma pragma : pragmas) { + final Sequence temp = pragma.eval(contextSequence, contextItem); + if (temp != null) { + result = temp; + break; + } } } if (result == null) { @@ -80,33 +87,48 @@ public Sequence eval(final Sequence contextSequence, final Item contextItem) thr } private void callAfter() throws XPathException { + if (pragmas == null) { + return; + } + for (final Pragma pragma : pragmas) { pragma.after(context, innerExpression); } } private void callBefore(final Sequence contextSequence) throws XPathException { + if (pragmas == null) { + return; + } + for (final Pragma pragma : pragmas) { pragma.before(context, innerExpression, contextSequence); } } + @Override public int returnsType() { return innerExpression.returnsType(); } + @Override public void analyze(final AnalyzeContextInfo contextInfo) throws XPathException { final AnalyzeContextInfo newContext = new AnalyzeContextInfo(contextInfo); - for (final Pragma pragma : pragmas) { - pragma.analyze(newContext); + if (pragmas != null) { + for (final Pragma pragma : pragmas) { + pragma.analyze(newContext); + } } innerExpression.analyze(newContext); } + @Override public void dump(final ExpressionDumper dumper) { - for (final Pragma pragma : pragmas) { - pragma.dump(dumper); - dumper.nl(); + if (pragmas != null) { + for (final Pragma pragma : pragmas) { + pragma.dump(dumper); + dumper.nl(); + } } dumper.display('{'); dumper.startIndent(); @@ -145,8 +167,10 @@ public int getPrimaryAxis() { public void resetState(final boolean postOptimization) { super.resetState(postOptimization); innerExpression.resetState(postOptimization); - for (final Pragma pragma : pragmas) { - pragma.resetState(postOptimization); + if (pragmas != null) { + for (final Pragma pragma : pragmas) { + pragma.resetState(postOptimization); + } } } @@ -154,4 +178,21 @@ public void resetState(final boolean postOptimization) { public void accept(final ExpressionVisitor visitor) { visitor.visit(innerExpression); } + + @Override + public String toString() { + final StringBuilder result = new StringBuilder(); + + if (pragmas != null) { + for (final Pragma pragma : pragmas) { + result.append(pragma.toString()); + } + } + + result.append("{ "); + result.append(innerExpression.toString()); + result.append(" }"); + + return result.toString(); + } } diff --git a/exist-core/src/main/java/org/exist/xquery/Optimizer.java b/exist-core/src/main/java/org/exist/xquery/Optimizer.java index 28b38319bc6..7905e27c6dc 100644 --- a/exist-core/src/main/java/org/exist/xquery/Optimizer.java +++ b/exist-core/src/main/java/org/exist/xquery/Optimizer.java @@ -63,6 +63,8 @@ public class Optimizer extends DefaultExpressionVisitor { private List rewriters; + private final FindOptimizable findOptimizable = new FindOptimizable(); + public Optimizer(XQueryContext context) { this.context = context; final DBBroker broker = context.getBroker(); @@ -100,11 +102,13 @@ public void visitLocationStep(final LocationStep locationStep) { // try to find a predicate containing an expression which is an instance // of Optimizable. for (final Predicate pred : preds) { - final FindOptimizable find = new FindOptimizable(); - pred.accept(find); - final List list = find.getOptimizables(); - if (list.size() > 0 && canOptimize(list)) { + pred.accept(findOptimizable); + @Nullable final Optimizable[] list = findOptimizable.getOptimizables(); + if (canOptimize(list)) { optimize = true; + } + findOptimizable.reset(); + if (optimize) { break; } } @@ -208,19 +212,23 @@ public void visitFilteredExpr(FilteredExpression filtered) { } } - private boolean hasOptimizable(List preds) { + private boolean hasOptimizable(final List preds) { // walk through the predicates attached to the current location step. // try to find a predicate containing an expression which is an instance // of Optimizable. + boolean optimizable = false; for (final Predicate pred : preds) { - final FindOptimizable find = new FindOptimizable(); - pred.accept(find); - final List list = find.getOptimizables(); - if (list.size() > 0 && canOptimize(list)) { - return true; + pred.accept(findOptimizable); + @Nullable final Optimizable[] list = findOptimizable.getOptimizables(); + if (canOptimize(list)) { + optimizable = true; + } + findOptimizable.reset(); + if (optimizable) { + break; } } - return false; + return optimizable; } public void visitAndExpr(OpAnd and) { @@ -337,7 +345,11 @@ public void visitVariableReference(final VariableReference ref) { } } - private boolean canOptimize(List list) { + private boolean canOptimize(@Nullable final Optimizable[] list) { + if (list == null || list.length == 0) { + return false; + } + for (final Optimizable optimizable : list) { final int axis = optimizable.getOptimizeAxis(); if (!(axis == Constants.CHILD_AXIS || axis == Constants.DESCENDANT_AXIS || @@ -378,32 +390,49 @@ private Expression simplifyPath(Expression expression) { */ public static class FindOptimizable extends BasicExpressionVisitor { - List optimizables = new ArrayList<>(); + private @Nullable Optimizable[] optimizables = null; - public List getOptimizables() { + public @Nullable Optimizable[] getOptimizables() { return optimizables; } - public void visitPathExpr(PathExpr expression) { + @Override + public void visitPathExpr(final PathExpr expression) { for (int i = 0; i < expression.getLength(); i++) { final Expression next = expression.getExpression(i); next.accept(this); } } - public void visitGeneralComparison(GeneralComparison comparison) { - optimizables.add(comparison); + @Override + public void visitGeneralComparison(final GeneralComparison comparison) { + addOptimizable(comparison); } - public void visitPredicate(Predicate predicate) { + @Override + public void visitPredicate(final Predicate predicate) { predicate.getExpression(0).accept(this); } - public void visitBuiltinFunction(Function function) { + @Override + public void visitBuiltinFunction(final Function function) { if (function instanceof Optimizable) { - optimizables.add((Optimizable) function); + addOptimizable((Optimizable) function); } } + + private void addOptimizable(final Optimizable optimizable) { + if (optimizables == null) { + optimizables = new Optimizable[1]; + } else { + optimizables = Arrays.copyOf(optimizables, optimizables.length + 1); + } + optimizables[optimizables.length - 1] = optimizable; + } + + public void reset() { + this.optimizables = null; + } } /**