gt:             if (!test.isWildcardTest() && test.getName() != null) {
context: /*
   *  eXist Open Source Native XML Database
   *  Copyright (C) 2013 The eXist Project
   *  http://exist-db.org
   *
   *  This program is free software; you can redistribute it and/or
   *  modify it under the terms of the GNU Lesser General Public License
   *  as published by the Free Software Foundation; either version 2
   *  of the License, or (at your option) any later version.
   *
   *  This program is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU Lesser General Public License for more details.
   *
   *  You should have received a copy of the GNU Lesser General Public
   *  License along with this library; if not, write to the Free Software
   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
   *
   *  $Id$
   */
  package org.exist.xquery.modules.range;
  
  import org.exist.indexing.range.*;
  import org.exist.storage.NodePath;
  import org.exist.xquery.*;
  
  import java.util.ArrayList;
  import java.util.List;
  
  /**
   * Query rewriter for the range index. May replace path expressions like a[b = "c"] or a[b = "c"][d = "e"]
   * with either a[range:equals(b, "c")] or range:field-equals(...).
   */
  public class RangeQueryRewriter extends QueryRewriter {
  
      public RangeQueryRewriter(XQueryContext context) {
          super(context);
      }
  
      @Override
      public Pragma rewriteLocationStep(LocationStep locationStep) throws XPathException {
          if (locationStep.hasPredicates()) {
              Expression parentExpr = locationStep.getParentExpression();
              if ((parentExpr instanceof RewritableExpression)) {
                  // Step 1: replace all optimizable expressions within predicates with
                  // calls to the range functions. If those functions are used or not will
                  // be decided at run time.
  
                  final List<Predicate> preds = locationStep.getPredicates();
  
                  // will become true if optimizable expression is found
                  boolean canOptimize = false;
                  // get path of path expression before the predicates
                  NodePath contextPath = toNodePath(getPrecedingSteps(locationStep));
                  // process the remaining predicates
                  for (Predicate pred : preds) {
                      if (pred.getLength() != 1) {
                          // can only optimize predicates with one expression
                          break;
                      }
  
                      Expression innerExpr = pred.getExpression(0);
                      List<LocationStep> steps = getStepsToOptimize(innerExpr);
                      if (steps == null || steps.size() == 0) {
                          // no optimizable steps found
                          continue;
                      }
  
                      // check if inner steps are on an axis we can optimize
                      final int axis;
                      if (innerExpr instanceof InternalFunctionCall) {
                          InternalFunctionCall fcall = (InternalFunctionCall) innerExpr;
                          axis = ((Optimizable) fcall.getFunction()).getOptimizeAxis();
                      } else {
                          axis = ((Optimizable) innerExpr).getOptimizeAxis();
                      }
                      if (!(axis == Constants.CHILD_AXIS || axis == Constants.DESCENDANT_AXIS ||
                              axis == Constants.DESCENDANT_SELF_AXIS || axis == Constants.ATTRIBUTE_AXIS ||
                              axis == Constants.DESCENDANT_ATTRIBUTE_AXIS || axis == Constants.SELF_AXIS
                      )) {
                          continue;
                      }
  
                      // compute left hand path
                      NodePath innerPath = toNodePath(steps);
                      if (innerPath == null) {
                          continue;
                      }
                      NodePath path;
                      if (contextPath == null) {
                          path = innerPath;
                      } else {
                          path = new NodePath(contextPath);
                          path.append(innerPath);
                      }
  
                      if (path.length() > 0) {
                          // replace with call to lookup function
                          // collect arguments
                          Lookup func = rewrite(innerExpr, path);
                          // preserve original comparison: may need it for in-memory lookups
                          func.setFallback(innerExpr, axis);
                          func.setLocation(innerExpr.getLine(), innerExpr.getColumn());
  
                          pred.replace(innerExpr, new InternalFunctionCall(func));
                          canOptimize = true;
                      }
                  }
  
                  if (canOptimize) {
                      // Step 2: return an OptimizeFieldPragma to handle field lookups and optimize the entire
                      // path expression. If the pragma can optimize the path expression, the original code will
                      // not be called.
                      return new OptimizeFieldPragma(OptimizeFieldPragma.OPTIMIZE_RANGE_PRAGMA, null, getContext());
                  }
              }
          }
          return null;
      }
  
      protected static Lookup rewrite(Expression expression, NodePath path) throws XPathException {
          ArrayList<Expression> eqArgs = new ArrayList<Expression>(2);
          if (expression instanceof GeneralComparison) {
              GeneralComparison comparison = (GeneralComparison) expression;
              eqArgs.add(comparison.getLeft());
              eqArgs.add(comparison.getRight());
              Lookup func = Lookup.create(comparison.getContext(), getOperator(expression), path);
              func.setArguments(eqArgs);
              return func;
          } else if (expression instanceof InternalFunctionCall) {
              InternalFunctionCall fcall = (InternalFunctionCall) expression;
              Function function = fcall.getFunction();
              if (function instanceof Lookup) {
                  if (function.isCalledAs("matches")) {
                      eqArgs.add(function.getArgument(0));
                      eqArgs.add(function.getArgument(1));
                      Lookup func = Lookup.create(function.getContext(), RangeIndex.Operator.MATCH, path);
                      func.setArguments(eqArgs);
                      return func;
                  }
              }
          }
          return null;
      }
  
      protected static List<LocationStep> getStepsToOptimize(Expression expr) {
          if (expr instanceof GeneralComparison) {
              GeneralComparison comparison = (GeneralComparison) expr;
              return BasicExpressionVisitor.findLocationSteps(comparison.getLeft());
          } else if (expr instanceof InternalFunctionCall) {
              InternalFunctionCall fcall = (InternalFunctionCall) expr;
              Function function = fcall.getFunction();
              if (function instanceof Lookup) {
                  if (function.isCalledAs("matches")) {
                      return BasicExpressionVisitor.findLocationSteps(function.getArgument(0));
                  } else {
                      Expression original = ((Lookup)function).getFallback();
                      return getStepsToOptimize(original);
                  }
              }
          }
          return null;
      }
  
      protected static RangeIndex.Operator getOperator(Expression expr) {
          if (expr instanceof InternalFunctionCall) {
              InternalFunctionCall fcall = (InternalFunctionCall) expr;
              Function function = fcall.getFunction();
              if (function instanceof Lookup) {
                  expr = ((Lookup)function).getFallback();
              }
          }
          RangeIndex.Operator operator = RangeIndex.Operator.EQ;
          if (expr instanceof GeneralComparison) {
              GeneralComparison comparison = (GeneralComparison) expr;
              int relation = comparison.getRelation();
              switch(relation) {
                  case Constants.LT:
                      operator = RangeIndex.Operator.LT;
                      break;
                  case Constants.GT:
                      operator = RangeIndex.Operator.GT;
                      break;
                  case Constants.LTEQ:
                      operator = RangeIndex.Operator.LE;
                      break;
                  case Constants.GTEQ:
                      operator = RangeIndex.Operator.GE;
                      break;
                  case Constants.EQ:
                      switch (comparison.getTruncation()) {
                          case Constants.TRUNC_BOTH:
                              operator = RangeIndex.Operator.CONTAINS;
                              break;
                          case Constants.TRUNC_LEFT:
                              operator = RangeIndex.Operator.ENDS_WITH;
                              break;
                          case Constants.TRUNC_RIGHT:
                              operator = RangeIndex.Operator.STARTS_WITH;
                              break;
                          default:
                              operator = RangeIndex.Operator.EQ;
                              break;
                      }
                      break;
                  case Constants.NEQ:
                      operator = RangeIndex.Operator.NE;
                      break;
  
              }
          } else if (expr instanceof InternalFunctionCall) {
              InternalFunctionCall fcall = (InternalFunctionCall) expr;
              Function function = fcall.getFunction();
              if (function instanceof Lookup && function.isCalledAs("matches")) {
                  operator = RangeIndex.Operator.MATCH;
              }
          } else if (expr instanceof Lookup && ((Function)expr).isCalledAs("matches")) {
              operator = RangeIndex.Operator.MATCH;
          }
          return operator;
      }
  
      protected static NodePath toNodePath(List<LocationStep> steps) {
          NodePath path = new NodePath();
          for (LocationStep step: steps) {
              if (step == null) {
                  return null;
              }
              NodeTest test = step.getTest();
              if (test.isWildcardTest() && step.getAxis() == Constants.SELF_AXIS) {
                  //return path;
                  continue;
              }
  