private Path<?> getPath(Expression<?> leftHandSide) {
   if (leftHandSide instanceof Path<?>) {
     return (Path<?>) leftHandSide;
   } else if (leftHandSide instanceof Operation<?>) {
     Operation<?> operation = (Operation<?>) leftHandSide;
     if (operation.getOperator() == Ops.LOWER || operation.getOperator() == Ops.UPPER) {
       return (Path<?>) operation.getArg(0);
     }
   }
   throw new IllegalArgumentException("Unable to transform " + leftHandSide + " to path");
 }
 private Query toQuery(Operation<?> operation, QueryMetadata metadata) {
   Operator op = operation.getOperator();
   if (op == Ops.OR) {
     return toTwoHandSidedQuery(operation, Occur.SHOULD, metadata);
   } else if (op == Ops.AND) {
     return toTwoHandSidedQuery(operation, Occur.MUST, metadata);
   } else if (op == Ops.NOT) {
     BooleanQuery bq = new BooleanQuery();
     bq.add(new BooleanClause(toQuery(operation.getArg(0), metadata), Occur.MUST_NOT));
     bq.add(new BooleanClause(new MatchAllDocsQuery(), Occur.MUST));
     return bq;
   } else if (op == Ops.LIKE) {
     return like(operation, metadata);
   } else if (op == Ops.LIKE_IC) {
     throw new IgnoreCaseUnsupportedException();
   } else if (op == Ops.EQ) {
     return eq(operation, metadata, false);
   } else if (op == Ops.EQ_IGNORE_CASE) {
     throw new IgnoreCaseUnsupportedException();
   } else if (op == Ops.NE) {
     return ne(operation, metadata, false);
   } else if (op == Ops.STARTS_WITH) {
     return startsWith(metadata, operation, false);
   } else if (op == Ops.STARTS_WITH_IC) {
     throw new IgnoreCaseUnsupportedException();
   } else if (op == Ops.ENDS_WITH) {
     return endsWith(operation, metadata, false);
   } else if (op == Ops.ENDS_WITH_IC) {
     throw new IgnoreCaseUnsupportedException();
   } else if (op == Ops.STRING_CONTAINS) {
     return stringContains(operation, metadata, false);
   } else if (op == Ops.STRING_CONTAINS_IC) {
     throw new IgnoreCaseUnsupportedException();
   } else if (op == Ops.BETWEEN) {
     return between(operation, metadata);
   } else if (op == Ops.IN) {
     return in(operation, metadata, false);
   } else if (op == Ops.NOT_IN) {
     return notIn(operation, metadata, false);
   } else if (op == Ops.LT) {
     return lt(operation, metadata);
   } else if (op == Ops.GT) {
     return gt(operation, metadata);
   } else if (op == Ops.LOE) {
     return le(operation, metadata);
   } else if (op == Ops.GOE) {
     return ge(operation, metadata);
   } else if (op == LuceneOps.LUCENE_QUERY) {
     @SuppressWarnings("unchecked") // This is the expected type
     Query rv = ((Constant<Query>) operation.getArg(0)).getConstant();
     return rv;
   }
   throw new UnsupportedOperationException("Illegal operation " + operation);
 }
  /**
   * template method
   *
   * @param leftHandSide left hand side
   * @param rightHandSide right hand side
   * @return results
   */
  protected String[] convert(
      Path<?> leftHandSide, Expression<?> rightHandSide, QueryMetadata metadata) {
    if (rightHandSide instanceof Operation) {
      Operation<?> operation = (Operation<?>) rightHandSide;
      if (operation.getOperator() == LuceneOps.PHRASE) {
        return Iterables.toArray(WS_SPLITTER.split(operation.getArg(0).toString()), String.class);
      } else if (operation.getOperator() == LuceneOps.TERM) {
        return new String[] {operation.getArg(0).toString()};
      } else {
        throw new IllegalArgumentException(rightHandSide.toString());
      }
    } else if (rightHandSide instanceof ParamExpression<?>) {
      Object value = metadata.getParams().get(rightHandSide);
      if (value == null) {
        throw new ParamNotSetException((ParamExpression<?>) rightHandSide);
      }
      return convert(leftHandSide, value);

    } else if (rightHandSide instanceof Constant<?>) {
      return convert(leftHandSide, ((Constant<?>) rightHandSide).getConstant());
    } else {
      throw new IllegalArgumentException(rightHandSide.toString());
    }
  }