/** * Flattens the tree by pushing down the field name. For example, if the tree looks like this: * * <pre> * EQ * / \ * VALUE EQ * / \ / \ * TEXT field GLOBAL (N) * </pre> * * Then we will output tree that looks like this: * * <pre> * EQ * / \ * VALUE (N) * / \ * TEXT field * </pre> * * Here <code>(N)</code> is an arbitrary node. We also drop EQ if it is in front of conjunction or * disjunction. We do not drop it for other comparators, as we want parsing to fail for foo < * (1 2). */ private static Tree flatten(Tree tree, Tree restriction) throws QueryTreeException { if (tree.getType() == QueryLexer.VALUE) { return tree; } if (tree.getType() == QueryLexer.HAS || tree.getType() == QueryLexer.EQ) { Tree lhs = tree.getChild(0); if (lhs.getType() == QueryLexer.VALUE) { String myField = lhs.getChild(1).getText(); if (restriction == null) { restriction = lhs; } else { String otherField = restriction.getChild(1).getText(); if (!myField.equals(otherField)) { throw new QueryTreeException( String.format("Restriction on %s and %s", otherField, myField), lhs.getChild(1).getCharPositionInLine()); } } } Tree rhs = tree.getChild(1); Tree flattened = flatten(rhs, restriction); if (flattened.getType() == QueryLexer.HAS || flattened.getType() == QueryLexer.EQ || flattened.getType() == QueryLexer.CONJUNCTION || flattened.getType() == QueryLexer.DISJUNCTION || flattened.getType() == QueryLexer.SEQUENCE) { return flattened; } if (flattened != rhs) { tree.setChild(1, flattened); } if (restriction != lhs) { tree.setChild(0, restriction); } return tree; } for (int i = 0; i < tree.getChildCount(); ++i) { Tree original = tree.getChild(i); Tree flattened = flatten(tree.getChild(i), restriction); if (original != flattened) { tree.setChild(i, flattened); } } return tree; }
public static Tree simplify(Tree tree) { for (int i = 0; i < tree.getChildCount(); ++i) { Tree child = tree.getChild(i); Tree optimized = simplify(child); if (child != optimized) { tree.setChild(i, optimized); } } switch (tree.getType()) { case QueryLexer.CONJUNCTION: case QueryLexer.DISJUNCTION: case QueryLexer.SEQUENCE: if (tree.getChildCount() == 1) { return tree.getChild(0); } break; } return tree; }