src/java/com/mhs/qsol/proximity/distribute/BasicDistributable.java
public class BasicDistributable implements Distributable {
  private final static Logger logger = Logger
      .getLogger(BasicDistributable.class.getPackage().getName());
  private SpanQuery query;
  private Distributable parent;

  private BasicDistributable() {
  }

  public BasicDistributable(SpanQuery query) {
    this.query = query;
  }

  public void addConnector(Occur occurType) {
    // no-op
  }

  public void add(Distributable c) {
    // no-op
  }

  public ArrayList<Distributable> getChildren() {
    return null;
  }

  public void remove(Distributable c) {
    // no-op
  }

  public void clear() {
    // no-op
  }

  public String toString() {
    int numParents = 0;
    Distributable distrib = this;

    do {
      distrib = distrib.getParent();

      if (distrib != null) {
        numParents++;
      }
    } while (distrib != null);

    StringBuilder tabs = new StringBuilder();

    for (int i = 0; i < numParents; i++) {
      tabs.append("\t");
    }

    if (query == null) {
      System.out.println("null term found in distrib in toString()");
    }

    return ("\n" + tabs.toString() + "distrib(" + query.toString() + ")");
  }

  public void setParent(Distributable distrib) {
    parent = distrib;
  }

  public Distributable getParent() {
    return parent;
  }

  public void setChildren(ArrayList<Distributable> children) {
    // no-op
  }

  public ArrayList<Occur> getConnectors() {
    // no-op
    return null;
  }

  public void setConnectors(ArrayList<Occur> connectors) {
    // no-op
  }

  public Query distribute(Distributable distrib, ProxInfo proxInfo) {
    BooleanQuery boolQuery = new BooleanQuery();
    List<Distributable> children = distrib.getChildren();

    if (children == null) {
      Query query = distrib.distribute(this.query, proxInfo);

      if (logger.isLoggable(Level.FINE)) {
        logger.fine("return query:" + query);
      }

      return query;
    }

    Query query;
    Query cacheQuery2 = null;
    int size = children.size();
    List<Occur> connectors = distrib.getConnectors();

    for (int i = 0; i < size; i++) {
      Occur con = connectors.get(i);

      // if we have already computed query2 looking for a possible SpanOr
      // use
      if (cacheQuery2 != null) {
        query = cacheQuery2;
      } else {
        query = children.get(i).distribute(this.query, proxInfo);
      }

      // must make sure both clauses are spans and connector is | if you
      // want to optimize to SpanOr
      if (children.size() > (i+1) && (con == Occur.SHOULD) && query instanceof SpanQuery) {
        cacheQuery2 = children.get(i + 1).distribute(this.query, proxInfo);

        if (cacheQuery2 instanceof SpanQuery) {
          if (children.size() == 2) {
            return new SpanOrQuery(new SpanQuery[] { (SpanQuery) query,
                (SpanQuery) cacheQuery2 });
          } else {
            query = new SpanOrQuery(new SpanQuery[] { (SpanQuery) query,
                (SpanQuery) cacheQuery2 });
            cacheQuery2 = null;
            i++;
          }
        } else {
          cacheQuery2 = null;
        }
      }

      boolQuery.add(query, con);
    }

    if (logger.isLoggable(Level.FINE)) {
      logger.fine("distribute(Distributable) - to distrib:" + distrib
          + " and :" + this.query);
      logger.fine("boolquery:" + boolQuery.toString());
    }

    return boolQuery;
  }

  public Query distribute(SpanQuery query, ProxInfo proxInfo) {
    if (logger.isLoggable(Level.FINE)) {
      logger.fine("distribute(Distributable) - to distrib:" + query + " and :"
          + this.query);
    }

    if ((this.query == null) || (query == null)) {
      throw new QsolParseException(
          "A proximity search cannot contain stop words: " + query + " ~"
              + proxInfo.distance + proxInfo.proxType + " " + this.query);
    }

    switch (proxInfo.proxType) {
    case WORD:

      if (logger.isLoggable(Level.FINE)) {
        logger.fine("return:" + "spanQuery(" + query + "," + this.query + ")");
      }

      SpanQuery spanQuery = new SpanNearQuery(new SpanQuery[] { query,
          this.query }, Integer.parseInt(proxInfo.distance), proxInfo.ordered);

      if (proxInfo.fieldBreakMarker != null) {
        SpanTermQuery fieldBreakMarker = new SpanTermQuery(new Term(this.query
            .getField(), proxInfo.fieldBreakMarker));
        spanQuery = new SpanNotQuery(spanQuery, fieldBreakMarker);
      }

      return spanQuery;

    case PARAGRAPH:

      SpanTermQuery paraMarker = new SpanTermQuery(new Term(this.query
          .getField(), proxInfo.paraMarker));
      SpanQuery querySpan = new SpanNearQuery(new SpanQuery[] { query,
          this.query }, 99999, false);

      if (proxInfo.fieldBreakMarker != null) {
        SpanTermQuery fieldBreakMarker = new SpanTermQuery(new Term(this.query
            .getField(), proxInfo.fieldBreakMarker));
        querySpan = new SpanNotQuery(querySpan, fieldBreakMarker);
      }

      return new SpanWithinQuery(querySpan, paraMarker, Integer
          .parseInt(proxInfo.distance));

    case SENTENCE:

      SpanTermQuery sentMarker = new SpanTermQuery(new Term(this.query
          .getField(), proxInfo.sentMarker));
      querySpan = new SpanNearQuery(new SpanQuery[] { query, this.query },
          99999, false);

      if (proxInfo.fieldBreakMarker != null) {
        SpanTermQuery fieldBreakMarker = new SpanTermQuery(new Term(this.query
            .getField(), proxInfo.fieldBreakMarker));
        querySpan = new SpanNotQuery(querySpan, fieldBreakMarker);
      }

      return new SpanWithinQuery(querySpan, sentMarker, Integer
          .parseInt(proxInfo.distance));
    }

    throw new RuntimeException(
        "proximity search was not of type sent/para/word");
  }
}
src/java/com/mhs/qsol/abstractnode/VisitCheckOp3.java
public class VisitCheckOp3 implements VisitCheckOp {
  private CheckOrd3Search n;

  public VisitCheckOp3(CheckOrd3Search n) {
    this.n = n;
  }

  public boolean isF1Present() {
    return n.f1.present();
  }

  public Query visitf0(GJVisitor<Query, Query> visitor, Query query) {
    return n.f0.accept(visitor, query);
  }

  public Query visitf1(GJVisitor<Query, Query> visitor, Query query) {
    return n.f1.accept(visitor, query);
  }
}
src/java/com/mhs/qsol/abstractnode/VisitOrd2.java
public class VisitOrd2 implements VisitOp {
  private Ord2Search n;

  public VisitOrd2(Ord2Search n) {
    this.n = n;
  }

  public boolean isF2Present() {
    return n.f2.present();
  }

  public Query visitf1(GJVisitor<Query, Query> visitor, Query query) {
    return n.f1.accept(visitor, query);
  }

  public Query visitf2(GJVisitor<Query, Query> visitor, Query query) {
    return n.f2.accept(visitor, query);
  }

  public int getOpNum() {
    return 2;
  }

  public String getF0TokenImage() {
    return n.f0.tokenImage;
  }
}
src/java/com/mhs/qsol/abstractnode/VisitOrd3.java
public class VisitOrd3 implements VisitOp {
  private Ord3Search n;

  public VisitOrd3(Ord3Search n) {
    this.n = n;
  }

  public boolean isF2Present() {
    return n.f2.present();
  }

  public Query visitf1(GJVisitor<Query, Query> visitor, Query query) {
    return n.f1.accept(visitor, query);
  }

  public Query visitf2(GJVisitor<Query, Query> visitor, Query query) {
    return n.f2.accept(visitor, query);
  }

  public int getOpNum() {
    return 3;
  }

  public String getF0TokenImage() {
    return n.f0.tokenImage;
  }
}
src/java/com/mhs/qsol/syntaxtree/Ord2Search.java
public class Ord2Search implements Node {
  public NodeToken f0;
  public CheckOrd3Search f1;
  public NodeOptional f2;

  public Ord2Search(NodeToken n0, CheckOrd3Search n1, NodeOptional n2) {
    f0 = n0;
    f1 = n1;
    f2 = n2;
  }

  public Ord2Search(CheckOrd3Search n0, NodeOptional n1) {
    f0 = new NodeToken("op2");
    f1 = n0;
    f2 = n1;
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/syntaxtree/CheckOrd2Search.java
public class CheckOrd2Search implements Node {
  public CheckOrd3Search f0;
  public NodeOptional f1;

  public CheckOrd2Search(CheckOrd3Search n0, NodeOptional n1) {
    f0 = n0;
    f1 = n1;
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/abstractnode/VisitOp.java
public interface VisitOp {
  Query visitf1(GJVisitor<Query, Query> visitor, Query query);

  boolean isF2Present();

  int getOpNum();

  Query visitf2(GJVisitor<Query, Query> visitor, Query query);

  String getF0TokenImage();
}
src/java/com/mhs/qsol/abstractnode/VisitCheckOp4.java
public class VisitCheckOp4 implements VisitCheckOp {
  private CheckOrd4Search n;

  public VisitCheckOp4(CheckOrd4Search n) {
    this.n = n;
  }

  public boolean isF1Present() {
    return n.f1.present();
  }

  public Query visitf0(GJVisitor<Query, Query> visitor, Query query) {
    return n.f0.f0.nodes.elementAt(0).accept(visitor, query);
  }

  public Query visitf1(GJVisitor<Query, Query> visitor, Query query) {
    return n.f1.accept(visitor, query);
  }
}
src/java/com/mhs/qsol/QsolToQueryVisitor.java
public class QsolToQueryVisitor extends GJDepthFirst<Query, Query> {
  public static final Pattern GET_SLOP = Pattern.compile("^\"(.*)\":(\\d+)$");
  public static final Pattern GET_SLOP_AND_BOOST = Pattern
      .compile("^\"(.*)\"(?::(\\d+))?\\^(\\d+(?:\\.\\d+)?)$");

  // private static final Pattern RANGE_EXTRACTOR2 = Pattern.compile(
  // "^(.+)\\srng\\s(.+)$", Pattern.CASE_INSENSITIVE);
  static final Pattern RANGE_EXTRACTOR = Pattern
      .compile("^([\\[{])?(.*?) (TO|RNG|rng) (.*?)([}\\]])?$");
  private static final Pattern BOOST_EXTRACTOR = Pattern
      .compile("^(.*?)\\^(\\d+(?:\\.\\d+)?)$");
  private final static Logger logger = Logger
      .getLogger(QsolToQueryVisitor.class.getPackage().getName());
  private static final Map<Operator, List<Occur>> opToOccur = new HashMap<Operator, List<Occur>>(
      3);
  private static final Map<String, List<Occur>> rfOpToOccur = new HashMap<String, List<Occur>>(
      3);

  static {
    List<Occur> and = new ArrayList<Occur>(2);
    and.add(Occur.MUST);
    and.add(Occur.MUST);

    List<Occur> andNot = new ArrayList<Occur>(2);
    andNot.add(Occur.MUST);
    andNot.add(Occur.MUST_NOT);

    List<Occur> or = new ArrayList<Occur>(2);
    or.add(Occur.SHOULD);
    or.add(Occur.SHOULD);

    opToOccur.put(Operator.AND, and);
    opToOccur.put(Operator.ANDNOT, andNot);
    opToOccur.put(Operator.OR, or);

    List<Occur> rfAndNot = new ArrayList<Occur>(2);
    rfAndNot.add(Occur.MUST);
    rfAndNot.add(Occur.MUST_NOT);

    rfOpToOccur.put("&&", and);
    rfOpToOccur.put("||", or);
    rfOpToOccur.put("!!", rfAndNot);
  }

  private Set<String> dateFields = new HashSet<String>();
  private String field;
  private Analyzer analyzer;
  private Locale locale = Locale.getDefault();
  private boolean[] opChain = new boolean[] { false, false, false, false };
  private int slop = 0; // 0 is the default slop for when phrases become SpanNearQuerys
  private Directory didyoumeanIndex = null;
  private DateParser dateParser;
  private List<Operator> orderOfOps = new ArrayList<Operator>();
  private String paraMarker;
  private String sentMarker;
  private boolean lowercaseExpandedTerms = true;
  private String fieldBreakMarker;
  private float boost = 1;
  private Map<String, String> fieldMap = new HashMap<String, String>(4);

  public void setDateFields(Set<String> dateFields) {
    this.dateFields = dateFields;
  }

  public void setField(String field) {
    this.field = field;
  }

  public void setAnalyzer(Analyzer analyzer) {
    this.analyzer = analyzer;
  }

  /**
   * f0 -> CheckOrd1Search() f1 -> <EOF>
   */
  public Query visit(Search n, Query query) {
    if (analyzer == null) {
      throw new IllegalStateException("analyzer not set");
    }

    Query returnQuery = null;

    if (dateParser == null) {
      dateParser = new DefaultDateParser();
    }

    returnQuery = n.f0.accept(this, query);

    if (logger.isLoggable(Level.FINE)) {
      logger.fine("query:" + returnQuery);
    }

    return returnQuery;
  }

  /**
   * Check for first order operation f0 -> CheckOrd2Search() f1 -> (
   * Ord1Search() )?
   */
  public Query visit(CheckOrd1Search n, Query query) {
    Operator op = orderOfOps.get(0);

    if (op != Operator.PROXIMITY) {
      Query q = visitCheckForBoolean(new VisitCheckOp1(n), query);

      return q;
    }

    return visitCheckForProx(new VisitCheckOp1(n), query);
  }

  /**
   * f0 -> <1st order operator> f1 -> CheckOrd2Search() f2 -> ( Ord1Search() )?
   */
  public Query visit(Ord1Search n, Query query) {
    List<Occur> occurs = null;

    if (n.f0.tokenImage.length() == 2) {
      occurs = rfOpToOccur.get(n.f0.tokenImage.substring(0, 1));
    } else {
      occurs = opToOccur.get(orderOfOps.get(0));
    }

    return visitBooleanOp(new VisitOrd1(n), query, occurs);
  }

  /**
   * Check for second order operation f0 -> CheckOrd3Search() f1 -> (
   * Ord2Search() )?
   */
  public Query visit(CheckOrd2Search n, Query query) {
    Operator op = orderOfOps.get(1);

    if (op != Operator.PROXIMITY) {
      Query q = visitCheckForBoolean(new VisitCheckOp2(n), query);

      return q;
    }

    return visitCheckForProx(new VisitCheckOp2(n), query);
  }

  /**
   * f0 -> <2nd order operator> f1 -> CheckOrd3Search() f2 -> ( Ord2Search() )?
   */
  public Query visit(Ord2Search n, Query query) {
    List<Occur> occurs = null;

    if (n.f0.tokenImage.length() == 2) {
      occurs = rfOpToOccur.get(n.f0.tokenImage.substring(0, 1));
    } else {
      occurs = opToOccur.get(orderOfOps.get(1));
    }

    return visitBooleanOp(new VisitOrd2(n), query, occurs);
  }

  /**
   * Check for 3rd order operator f0 -> CheckOrd4Search() f1 -> ( Ord3Search()
   * )?
   */
  public Query visit(CheckOrd3Search n, Query query) {
    Operator op = orderOfOps.get(2);

    if (op != Operator.PROXIMITY) {
      return visitCheckForBoolean(new VisitCheckOp3(n), query);
    }

    return visitCheckForProx(new VisitCheckOp3(n), query);
  }

  /**
   * f0 -> <3rd order operator> f1 -> CheckOrd4Search() f2 -> ( Ord3Search() )?
   */
  public Query visit(Ord3Search n, Query query) {
    List<Occur> occurs = null;

    if (n.f0.tokenImage.length() == 2) {
      occurs = rfOpToOccur.get(n.f0.tokenImage.substring(0, 1));
    } else {
      occurs = opToOccur.get(orderOfOps.get(2));
    }

    return visitBooleanOp(new VisitOrd3(n), query, occurs);
  }

  /**
   * Check for 4th order operator f0 -> (BasicSearchType())+ f1 -> (
   * Ord4Search() )?
   */
  public Query visit(CheckOrd4Search n, Query query) {
    Operator op = orderOfOps.get(3);

    if (op != Operator.PROXIMITY) {
      return visitCheckForBoolean(new VisitCheckOp4(n), query);
    }

    return visitCheckForProx(new VisitCheckOp4(n), query);
  }

  /**
   * f0 -> <4th order operator> f1 -> BasicSearchType() f2 -> ( Ord4Search() )?
   */
  public Query visit(Ord4Search n, Query query) {
    List<Occur> occurs = null;

    if (n.f0.tokenImage.length() == 2) {
      occurs = rfOpToOccur.get(n.f0.tokenImage);
    } else {
      occurs = opToOccur.get(orderOfOps.get(3));
    }

    return visitBooleanOp(new VisitOrd4(n), query, occurs);
  }

  /**
   * f0 -> <FIELDSTART> f1 -> CheckOrd1Search() f2 -> ")"
   */
  public Query visit(FieldSearch n, Query query) {
    Query returnQuery = null;

    // get comma delimited field list
    String fieldList = n.f0.toString();

    String[] fields = fieldList.split(",");

    BooleanQuery fieldSearch = new BooleanQuery();
    // save the query field
    String oldField = field;
    // apply the search to each field
    for (int x = 0; x < fields.length; x++) {
      field = fields[x];

      String mapTo;

      if ((mapTo = fieldMap.get(field)) != null) {
        field = mapTo;
      }

      // if the field is a registered date field, build date
      if (dateFields.contains(field)) {
        StringBuilder date = new StringBuilder();

        BasicSearch tokens = n.f1.f0.f0.f0.f0;

        // (SearchToken) n.f2.f0.f0.f0.f0.f0.choice;
        int size = tokens.f0.size();

        for (int i = 0; i < size; i++) {
          // NodeChoice choice = (NodeChoice) tokens.f0.elementAt(i);
          // date.append(choice.choice.toString());
          SearchToken choice = (SearchToken) ((BasicSearchType) tokens.f0
              .elementAt(i)).f0.choice;
          date.append(choice.f0.choice);

          if (i != (size - 1)) {
            date.append(" ");
          }
        }

        returnQuery = dateParser.buildDateQuery(field, date.toString(), locale);
      } else {

        returnQuery = n.f1.accept(this, query);

      }

      if (returnQuery != null) {
        fieldSearch.add(returnQuery, BooleanClause.Occur.SHOULD);
      }
    }
    field = oldField;
    if (fieldSearch.getClauses().length == 0) {
      return null;
    }
    return fieldSearch;
  }

  /**
   * f0 -> <MATCHALL> | <QUOTED> | <BOOSTEDQUOTED> | <RANGE> | <WILDCARD> | <FUZZY> | <BOOSTEDSEARCHTOKEN> | <SEARCHTOKEN>
   */
  public Query visit(SearchToken n, Query query) {
    String tokens = null;
    NodeChoice choice = (NodeChoice) n.f0;

    if (choice.which == 0) {
      return new MatchAllDocsQuery();

    } else if (choice.which == 1) {
      Matcher m = GET_SLOP.matcher(choice.choice.toString());

      // check for slop
      if (m.matches()) {
        tokens = m.group(1);
        int holdSlop = slop;
        slop = Integer.parseInt(m.group(2));
        Query returnQuery = tokenToQuery(tokens);
        slop = holdSlop;
        return returnQuery;
      } else {
        String tokensWithQuotes = choice.choice.toString();
        tokens = tokensWithQuotes.substring(1, tokensWithQuotes.length()-1);
        return tokenToQuery(tokens);
      }
    } else if (choice.which == 2) {
      Matcher m = GET_SLOP_AND_BOOST.matcher(choice.choice.toString());

      // check for slop
      if (m.matches()) {
        tokens = m.group(1);
        int holdSlop = slop;

        String phraseSlop = m.group(2);

        if (phraseSlop != null) {
          slop = Integer.parseInt(m.group(2));
        }

        this.boost = Float.parseFloat(m.group(3));

        Query phraseQuery = tokenToQuery(tokens);
        this.boost = 1;
        this.slop = holdSlop;

        return phraseQuery;
      }

      throw new RuntimeException(
          "boosted quoted matched in javacc but not here");
    } else if (choice.which == 3) {
      Matcher m = RANGE_EXTRACTOR.matcher(choice.choice.toString());
      boolean inclusive1 = false;
      boolean inclusive2 = false;

      if (m.matches()) {
        if (m.group(1) != null && m.group(1).equals("[")) {
          inclusive1 = true;
        }

        if (m.group(1) != null && m.group(5).equals("]")) {
          inclusive2 = true;
        }

        String term1 = m.group(2);
        String term2 = m.group(4);

        if (lowercaseExpandedTerms) {
          term1 = term1.toLowerCase();
          term2 = term2.toLowerCase();
        }

        return new TermRangeQuery(field, term1, term2, inclusive1,
            inclusive2);
      } else {
        throw new RuntimeException(
            "Range did not match in QsolToQueryVisitor but did with JavaCC parser");
      }
    } else if (choice.which == 4) {
      // Wildcard
      String term = choice.choice.toString();

      if (lowercaseExpandedTerms) {
        term = term.toLowerCase();
      }

      return new WildcardQuery(new Term(field, term));
    } else if (choice.which == 5) {
      // Fuzzyquery
      String fuzzyString = choice.choice.toString();

      if (lowercaseExpandedTerms) {
        fuzzyString = fuzzyString.toLowerCase();
      }

      return new FuzzyQuery(new Term(field, fuzzyString.substring(0,
          fuzzyString.length() - 1)));
    } else if (choice.which == 6) {
      // boosted term
      Matcher m = BOOST_EXTRACTOR.matcher(choice.choice.toString());

      if (m.matches()) {
        float boost = Float.parseFloat(m.group(2));
        this.boost = boost;

        Query boostQuery = tokenToQuery(m.group(1));
        this.boost = 1;

        return boostQuery;
      } else {
        throw new RuntimeException("Matched boosted in javacc but not here");
      }
    }

    // must be search token
    return tokenToQuery(choice.choice.toString());
  }

  /**
   * f0 -> "(" f1 -> CheckOrd1Search() f2 -> ")"
   */
  public Query visit(ParenthesisSearch n, Query query) {
    Query returnQuery = null;

    returnQuery = n.f1.accept(this, query);

    return returnQuery;
  }

  /**
   * f0 -> ( BasicSearchType() )+
   */
  public Query visit(BasicSearch n, Query query) {
    Query returnQuery = n.f0.elementAt(0).accept(this, query);

    return returnQuery;
  }

  /**
   * f0 -> FieldSearch() | SearchToken() | ParenthesisSearch()
   */
  public Query visit(BasicSearchType n, Query query) {
    Query returnQuery = null;
    returnQuery = n.f0.accept(this, query);

    return returnQuery;
  }

  /**
   * There is no need for a visitProx because check for prox creates a new prox
   * visitor.
   * 
   * @param op
   * @param query
   * @return
   */
  private Query visitCheckForProx(VisitCheckOp op, Query query) {
    Query returnQuery = null;

    ProximityVisitor proxVisitor = new ProximityVisitor(analyzer, field);
    proxVisitor.setOrderOfOps(this.orderOfOps);

    proxVisitor.setParaMarker(paraMarker);
    proxVisitor.setSentMarker(sentMarker);

    proxVisitor.setFieldBreakMarker(fieldBreakMarker);

    if (op.isF1Present()) {
      proxVisitor.startGroup();
      op.visitf0(proxVisitor, null);

      proxVisitor.endGroup();
      proxVisitor.promote();
    } else {
      returnQuery = op.visitf0(this, query);
    }

    if (op.isF1Present()) {
      proxVisitor.startGroup();

      op.visitf1(proxVisitor, null);

      returnQuery = proxVisitor.getQuery();
    }

    return returnQuery;
  }

  /**
   * f0 -> CheckNextOp() f1 -> ( CheckCurrentOp() )?
   */
  private Query visitCheckForBoolean(VisitCheckOp op, Query query) {
    Query returnQuery = null;

    returnQuery = op.visitf0(this, query);

    if (op.isF1Present()) {
      returnQuery = op.visitf1(this, returnQuery);
    }

    return returnQuery;
  }

  /**
   * f0 -> <operator> f1 -> CheckNextOp() f2 -> ( CurrentOp() )?
   */
  private Query visitBooleanOp(VisitOp op, Query query, List<Occur> occurs) {
    // run down right side of OPERATOR
    Query returnQuery = op.visitf1(this, query);

    // check for stop word removal
    if (returnQuery == null) {
      // if another of the same op follows
      if (op.isF2Present()) {

        returnQuery = op.visitf2(this, query);

        return returnQuery;
      } else {

        return query;
      }
    }

    if (query == null) {
      // if another of the same op follows
      if (op.isF2Present()) {
        query = op.visitf2(this, returnQuery);

        return query;
      } else {

        return returnQuery;
      }
    }

    // done stop word check
    BooleanQuery boolQuery = null;

    // if in an op chain i.e. mark & horse & beer : the second & is in an op
    // chain
    if (opChain[op.getOpNum() - 1] && query instanceof BooleanQuery) {
      boolQuery = (BooleanQuery) query;
      boolQuery.add(returnQuery, occurs.get(1));
    } else {
      boolQuery = new BooleanQuery();

      boolQuery.add(query, occurs.get(0));

      boolQuery.add(returnQuery, occurs.get(1));
    }

    // if another of the same op follows set that we are in an op chain
    if (op.isF2Present()) {
      opChain[op.getOpNum() - 1] = true;

      Query nextQuery = op.visitf2(this, boolQuery);
      opChain[op.getOpNum() - 1] = false;

      return nextQuery;
    }

    return boolQuery;
  }

  /**
   * Removes escape characters.
   */
  private String removeEscapeChars(String input) {
    char[] caSource = input.toCharArray();
    char[] caDest = new char[caSource.length];
    int j = 0;

    for (int i = 0; i < caSource.length; i++) {
      if ((caSource[i] != '\\') || ((i > 0) && (caSource[i - 1] == '\\'))) {
        caDest[j++] = caSource[i];
      }
    }

    return new String(caDest, 0, j);
  }

  /**
   * Converts a token, as defined in the qsol.jtb JavaCC file, into an
   * appropriate query.
   * 
   * @param token
   * @return
   */
  protected Query tokenToQuery(String token) {

    token = removeEscapeChars(token);

    TokenStream source = analyzer.tokenStream(field, new StringReader(token));
    ArrayList<Token> v = new ArrayList<Token>();
    Token t;
    int positionCount = 0;
    boolean severalTokensAtSamePosition = false;

    CharTermAttribute charTermAtrib = source.getAttribute(CharTermAttribute.class);
    OffsetAttribute offsetAtrib = source.getAttribute(OffsetAttribute.class);
    PositionIncrementAttribute posIncAtt = source.addAttribute(PositionIncrementAttribute.class);
    
    while (true) {
      try {
        if (!source.incrementToken()) {
          break;
        }
        t = new Token(charTermAtrib.buffer(), 0, charTermAtrib.length(), offsetAtrib.startOffset(), offsetAtrib.endOffset());
        t.setPositionIncrement(posIncAtt.getPositionIncrement());
      } catch (IOException e) {
        t = null;
      }

      if (t == null) {
        break;
      }

      v.add(t);

      if (t.getPositionIncrement() != 0) {
        positionCount += t.getPositionIncrement();
      } else {
        severalTokensAtSamePosition = true;
      }
    }

    try {
      source.close();
    } catch (IOException e) {
      // ignore
    }

    if (v.size() == 0) {
      // null's will get cleaned up in visitBooleanOp
      return null;
    } else if (v.size() == 1) {

      t = v.get(0);

      TermQuery termQuery = new TermQuery(new Term(field, new String(t.buffer(), 0, t.length())));
      termQuery.setBoost(this.boost);

      return termQuery;
    } else {
      if (severalTokensAtSamePosition) {
        if (positionCount == 1) {
          // no phrase query:
          BooleanQuery q = new BooleanQuery(true);

          for (int i = 0; i < v.size(); i++) {
            t = v.get(i);

            TermQuery currentQuery = new TermQuery(
                new Term(field, new String(t.buffer(), 0, t.length())));
            currentQuery.setBoost(this.boost);

            q.add(currentQuery, BooleanClause.Occur.SHOULD);
          }

          return q;
        } else {
            // All the Tokens in each sub-list are positioned at the the same location.
            ArrayList<ArrayList<Token>> identicallyPositionedTokenLists =
                    new ArrayList<ArrayList<Token>>();
            for (int i = 0; i < v.size(); i++) {
              if ((i == 0) || (v.get(i).getPositionIncrement() > 0)) {
                identicallyPositionedTokenLists.add(new ArrayList<Token>());
              }
              ArrayList<Token> curList =
                      identicallyPositionedTokenLists.get(identicallyPositionedTokenLists.size()-1);
              curList.add(v.get(i));
            }

            ArrayList<SpanQuery> spanNearSubclauses = new ArrayList<SpanQuery>();
            for (int listNum = 0; listNum < identicallyPositionedTokenLists.size(); listNum++) {
              ArrayList<Token> curTokens = identicallyPositionedTokenLists.get(listNum);

              ArrayList<SpanTermQuery> curTermQueries = new ArrayList<SpanTermQuery>();
              for (int tokenNum = 0; tokenNum < curTokens.size(); tokenNum++) {
                SpanTermQuery termQuery = new SpanTermQuery(new Term(field, curTokens.get(tokenNum).term()));
                termQuery.setBoost(this.boost);
                curTermQueries.add(termQuery);
              }

              int size = curTermQueries.size();
              if (size <= 0)
                continue;
              else if (size == 1)
                spanNearSubclauses.add(curTermQueries.get(0));
              else
                spanNearSubclauses.add(new SpanOrQuery(curTermQueries.toArray(new SpanQuery[0])));
            }
            
            SpanNearQuery query = new SpanNearQuery((SpanQuery[]) spanNearSubclauses
                    .toArray(new SpanQuery[0]), slop, true);

          return query;
        }
      } else {
        SpanTermQuery[] clauses = new SpanTermQuery[v.size()];

        for (int i = 0; i < v.size(); i++) {
          Token t2 = v.get(i);
          SpanTermQuery spanQuery = new SpanTermQuery(new Term(field, new String(t2.buffer(), 0, t2.length())));
          spanQuery.setBoost(boost);
          clauses[i] = spanQuery;
        }

        // Note: There's a bug here (not by me) that where term offsets are not respected.
        SpanNearQuery query = new SpanNearQuery(clauses, slop, true);

        return query;
      }
    }
  }

  public Locale getLocale() {
    return locale;
  }

  public void setLocale(Locale locale) {
    this.locale = locale;
  }

  /**
   * @return the didyoumeanIndex
   */
  public Directory getDidyoumeanIndex() {
    return didyoumeanIndex;
  }

  /**
   * @param didyoumeanIndex
   *          the didyoumeanIndex to set
   */
  public void setDidyoumeanIndex(Directory didyoumeanIndex) {
    this.didyoumeanIndex = didyoumeanIndex;
  }

  /**
   * @return the dateParser
   */
  public DateParser getDateParser() {
    return dateParser;
  }

  /**
   * @param dateParser
   *          the dateParser to set
   */
  public void setDateParser(DateParser dateParser) {
    this.dateParser = dateParser;
  }

  /**
   * @return the orderOfOps
   */
  public List<Operator> getOrderOfOps() {
    return orderOfOps;
  }

  /**
   * @param orderOfOps
   *          the orderOfOps to set
   */
  public void setOrderOfOps(List<Operator> orderOfOps) {
    this.orderOfOps = orderOfOps;
  }

  /**
   * @return the paraMarker
   */
  public String getParaMarker() {
    return paraMarker;
  }

  /**
   * @param paraMarker
   *          the paraMarker to set
   */
  public void setParaMarker(String paraMarker) {
    this.paraMarker = paraMarker;
  }

  /**
   * @return the sentMarker
   */
  public String getSentMarker() {
    return sentMarker;
  }

  /**
   * @param sentMarker
   *          the sentMarker to set
   */
  public void setSentMarker(String sentMarker) {
    this.sentMarker = sentMarker;
  }

  public void setLowercaseExpandedTerms(boolean lowercaseExpandedTerms) {
    this.lowercaseExpandedTerms = lowercaseExpandedTerms;
  }

  public String getFieldBreakMarker() {
    return fieldBreakMarker;
  }

  public void setFieldBreakMarker(String fieldBreakMarker) {
    this.fieldBreakMarker = fieldBreakMarker;
  }

  public void addFieldMapping(String field2, String mapToField) {
    this.fieldMap.put(field2, mapToField);
  }

  public void addFieldMappings(Map<String, String> fieldMap) {
    this.fieldMap.putAll(fieldMap);
  }
}
src/java/com/mhs/qsol/abstractnode/VisitCheckOp1.java
public class VisitCheckOp1 implements VisitCheckOp {
  private CheckOrd1Search n;

  public VisitCheckOp1(CheckOrd1Search n) {
    this.n = n;
  }

  public boolean isF1Present() {
    return n.f1.present();
  }

  public Query visitf0(GJVisitor<Query, Query> visitor, Query query) {
    return n.f0.accept(visitor, query);
  }

  public Query visitf1(GJVisitor<Query, Query> visitor, Query query) {
    return n.f1.accept(visitor, query);
  }
}
src/java/com/mhs/qsol/syntaxtree/FieldSearch.java
public class FieldSearch implements Node {
  public NodeToken f0;
  public CheckOrd1Search f1;
  public NodeToken f2;

  public FieldSearch(NodeToken n0, CheckOrd1Search n1, NodeToken n2) {
    f0 = n0;
    f1 = n1;
    f2 = n2;
  }

  public FieldSearch(CheckOrd1Search n0) {
    f0 = new NodeToken("(");
    f1 = n0;
    f2 = new NodeToken(")");
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/abstractnode/VisitOrd4.java
public class VisitOrd4 implements VisitOp {
  private Ord4Search n;

  public VisitOrd4(Ord4Search n) {
    this.n = n;
  }

  public boolean isF2Present() {
    return n.f2.present();
  }

  public Query visitf1(GJVisitor<Query, Query> visitor, Query query) {
    return n.f1.accept(visitor, query);
  }

  public Query visitf2(GJVisitor<Query, Query> visitor, Query query) {
    return n.f2.accept(visitor, query);
  }

  public int getOpNum() {
    return 4;
  }

  public String getF0TokenImage() {
    return n.f0.tokenImage;
  }
}
src/java/com/mhs/qsol/syntaxtree/CheckOrd4Search.java
public class CheckOrd4Search implements Node {
  public BasicSearch f0;
  public NodeOptional f1;

  public CheckOrd4Search(BasicSearch n0, NodeOptional n1) {
    f0 = n0;
    f1 = n1;
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/QsolParser.java
public enum Operator {
  AND, OR, PROXIMITY, ANDNOT;
}
src/java/com/mhs/qsol/syntaxtree/ParenthesisSearch.java
public class ParenthesisSearch implements Node {
  public NodeToken f0;
  public CheckOrd1Search f1;
  public NodeToken f2;

  public ParenthesisSearch(NodeToken n0, CheckOrd1Search n1, NodeToken n2) {
    f0 = n0;
    f1 = n1;
    f2 = n2;
  }

  public ParenthesisSearch(CheckOrd1Search n0) {
    f0 = new NodeToken("(");
    f1 = n0;
    f2 = new NodeToken(")");
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/visitor/GJDepthFirst.java
public class GJDepthFirst<R, A> implements GJVisitor<R, A> {
  //
  // Auto class visitors--probably don't need to be overridden.
  //
  public R visit(NodeList n, A argu) {
    R _ret = null;
    int _count = 0;
    for (Enumeration<Node> e = n.elements(); e.hasMoreElements();) {
      e.nextElement().accept(this, argu);
      _count++;
    }
    return _ret;
  }

  public R visit(NodeListOptional n, A argu) {
    if (n.present()) {
      R _ret = null;
      int _count = 0;
      for (Enumeration<Node> e = n.elements(); e.hasMoreElements();) {
        e.nextElement().accept(this, argu);
        _count++;
      }
      return _ret;
    } else
      return null;
  }

  public R visit(NodeOptional n, A argu) {
    if (n.present())
      return n.node.accept(this, argu);
    else
      return null;
  }

  public R visit(NodeSequence n, A argu) {
    R _ret = null;
    int _count = 0;
    for (Enumeration<Node> e = n.elements(); e.hasMoreElements();) {
      e.nextElement().accept(this, argu);
      _count++;
    }
    return _ret;
  }

  public R visit(NodeToken n, A argu) {
    return null;
  }

  //
  // User-generated visitor methods below
  //

  /**
   * f0 -> CheckOrd1Search() f1 -> <EOF>
   */
  public R visit(Search n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    n.f1.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> CheckOrd2Search() f1 -> ( Ord1Search() )?
   */
  public R visit(CheckOrd1Search n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    n.f1.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> <OP1> f1 -> CheckOrd2Search() f2 -> ( Ord1Search() )?
   */
  public R visit(Ord1Search n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    n.f1.accept(this, argu);
    n.f2.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> CheckOrd3Search() f1 -> ( Ord2Search() )?
   */
  public R visit(CheckOrd2Search n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    n.f1.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> <OP2> f1 -> CheckOrd3Search() f2 -> ( Ord2Search() )?
   */
  public R visit(Ord2Search n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    n.f1.accept(this, argu);
    n.f2.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> CheckOrd4Search() f1 -> ( Ord3Search() )?
   */
  public R visit(CheckOrd3Search n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    n.f1.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> <OP3> f1 -> CheckOrd4Search() f2 -> ( Ord3Search() )?
   */
  public R visit(Ord3Search n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    n.f1.accept(this, argu);
    n.f2.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> BasicSearch() f1 -> ( Ord4Search() )?
   */
  public R visit(CheckOrd4Search n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    n.f1.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> <OP4> f1 -> BasicSearch() f2 -> ( Ord4Search() )?
   */
  public R visit(Ord4Search n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    n.f1.accept(this, argu);
    n.f2.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> <FIELDSTART> f1 -> CheckOrd1Search() f2 -> ")"
   */
  public R visit(FieldSearch n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    n.f1.accept(this, argu);
    n.f2.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> <MATCHALL> | <QUOTED> | <BOOSTEDQUOTED> | <RANGE> | <WILDCARD> |
   * <FUZZY> | <BOOSTEDSEARCHTOKEN> | <SEARCHTOKEN>
   */
  public R visit(SearchToken n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> "(" f1 -> CheckOrd1Search() f2 -> ")"
   */
  public R visit(ParenthesisSearch n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    n.f1.accept(this, argu);
    n.f2.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> ( BasicSearchType() )+
   */
  public R visit(BasicSearch n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    return _ret;
  }

  /**
   * f0 -> FieldSearch() | SearchToken() | ParenthesisSearch()
   */
  public R visit(BasicSearchType n, A argu) {
    R _ret = null;
    n.f0.accept(this, argu);
    return _ret;
  }

}
src/java/com/mhs/qsol/syntaxtree/CheckOrd1Search.java
public class CheckOrd1Search implements Node {
  public CheckOrd2Search f0;
  public NodeOptional f1;

  public CheckOrd1Search(CheckOrd2Search n0, NodeOptional n1) {
    f0 = n0;
    f1 = n1;
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/syntaxtree/SearchToken.java
public class SearchToken implements Node {
  public NodeChoice f0;

  public SearchToken(NodeChoice n0) {
    f0 = n0;
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/syntaxtree/Ord3Search.java
public class Ord3Search implements Node {
  public NodeToken f0;
  public CheckOrd4Search f1;
  public NodeOptional f2;

  public Ord3Search(NodeToken n0, CheckOrd4Search n1, NodeOptional n2) {
    f0 = n0;
    f1 = n1;
    f2 = n2;
  }

  public Ord3Search(CheckOrd4Search n0, NodeOptional n1) {
    f0 = new NodeToken("op3");
    f1 = n0;
    f2 = n1;
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/abstractnode/VisitOrd1.java
public class VisitOrd1 implements VisitOp {
  private Ord1Search n;

  public VisitOrd1(Ord1Search n) {
    this.n = n;
  }

  public boolean isF2Present() {
    return n.f2.present();
  }

  public Query visitf1(GJVisitor<Query, Query> visitor, Query query) {
    return n.f1.accept(visitor, query);
  }

  public Query visitf2(GJVisitor<Query, Query> visitor, Query query) {
    return n.f2.accept(visitor, query);
  }

  public int getOpNum() {
    return 1;
  }

  public String getF0TokenImage() {
    return n.f0.tokenImage;
  }
}
src/java/com/mhs/qsol/spans/SpanFuzzyQuery.java
public class SpanFuzzyQuery extends SpanQuery {
  public final static float defaultMinSimilarity = 0.7f;
  public final static int defaultPrefixLength = 0;
  private final Term term;
  private final float minimumSimilarity;
  private final int prefixLength;
  private BooleanQuery rewrittenFuzzyQuery;

  public SpanFuzzyQuery(Term term) {
    this(term, defaultMinSimilarity, defaultPrefixLength);
  }

  public SpanFuzzyQuery(Term term, float minimumSimilarity, int prefixLength) {
    this.term = term;
    this.minimumSimilarity = minimumSimilarity;
    this.prefixLength = prefixLength;

    if (minimumSimilarity >= 1.0f) {
      throw new IllegalArgumentException("minimumSimilarity >= 1");
    } else if (minimumSimilarity < 0.0f) {
      throw new IllegalArgumentException("minimumSimilarity < 0");
    }

    if (prefixLength < 0) {
      throw new IllegalArgumentException("prefixLength < 0");
    }
  }

  public Query rewrite(IndexReader reader) throws IOException {
    FuzzyQuery fuzzyQuery = new FuzzyQuery(term, minimumSimilarity,
        prefixLength);

    rewrittenFuzzyQuery = (BooleanQuery) fuzzyQuery.rewrite(reader);

    BooleanClause[] clauses = rewrittenFuzzyQuery.getClauses();
    SpanQuery[] spanQueries = new SpanQuery[clauses.length];

    for (int i = 0; i < clauses.length; i++) {
      BooleanClause clause = clauses[i];

      TermQuery termQuery = (TermQuery) clause.getQuery();

      spanQueries[i] = new SpanTermQuery(termQuery.getTerm());
      spanQueries[i].setBoost(termQuery.getBoost());
    }

    SpanOrQuery query = new SpanOrQuery(spanQueries);
    query.setBoost(fuzzyQuery.getBoost());

    return query;
  }

  /**
   * Expert: Returns the matches for this query in an index. Used internally to
   * search for spans.
   */
  public Spans getSpans(IndexReader reader) throws IOException {
    throw new UnsupportedOperationException("Query should have been rewritten");
  }

  /** Returns the name of the field matched by this query. */
  public String getField() {
    return term.field();
  }

  /** Returns a collection of all terms matched by this query. */
  public Collection getTerms() {
    if (rewrittenFuzzyQuery == null) {
      throw new RuntimeException(
          "Query must be rewritten prior to calling getTerms()!");
    } else {
      LinkedList<Term> terms = new LinkedList<Term>();
      BooleanClause[] clauses = rewrittenFuzzyQuery.getClauses();

      for (int i = 0; i < clauses.length; i++) {
        BooleanClause clause = clauses[i];
        TermQuery termQuery = (TermQuery) clause.getQuery();
        terms.add(termQuery.getTerm());
      }

      return terms;
    }
  }

  /**
   * Prints a query to a string, with <code>field</code> as the default field
   * for terms.
   * <p>
   * The representation used is one that is supposed to be readable by
   * {@link org.apache.lucene.queryParser.QueryParser QueryParser}. However,
   * there are the following limitations:
   * <ul>
   * <li>If the query was created by the parser, the printed representation may
   * not be exactly what was parsed. For example, characters that need to be
   * escaped will be represented without the required backslash.</li>
   * <li>Some of the more complicated queries (e.g. span queries) don't have a
   * representation that can be parsed by QueryParser.</li>
   * </ul>
   */
  public String toString(String field) {
    return "fuzzy(" + term.toString() + ")";
  }
}
src/java/com/mhs/qsol/syntaxtree/BasicSearchType.java
public class BasicSearchType implements Node {
  public NodeChoice f0;

  public BasicSearchType(NodeChoice n0) {
    f0 = n0;
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/syntaxtree/Ord1Search.java
public class Ord1Search implements Node {
  public NodeToken f0;
  public CheckOrd2Search f1;
  public NodeOptional f2;

  public Ord1Search(NodeToken n0, CheckOrd2Search n1, NodeOptional n2) {
    f0 = n0;
    f1 = n1;
    f2 = n2;
  }

  public Ord1Search(CheckOrd2Search n0, NodeOptional n1) {
    f0 = new NodeToken("op1");
    f1 = n0;
    f2 = n1;
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/syntaxtree/CheckOrd3Search.java
public class CheckOrd3Search implements Node {
  public CheckOrd4Search f0;
  public NodeOptional f1;

  public CheckOrd3Search(CheckOrd4Search n0, NodeOptional n1) {
    f0 = n0;
    f1 = n1;
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/syntaxtree/Search.java
public class Search implements Node {
  public CheckOrd1Search f0;
  public NodeToken f1;

  public Search(CheckOrd1Search n0, NodeToken n1) {
    f0 = n0;
    f1 = n1;
  }

  public Search(CheckOrd1Search n0) {
    f0 = n0;
    f1 = new NodeToken("");
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/abstractnode/VisitCheckOp2.java
public class VisitCheckOp2 implements VisitCheckOp {
  private CheckOrd2Search n;

  public VisitCheckOp2(CheckOrd2Search n) {
    this.n = n;
  }

  public boolean isF1Present() {
    return n.f1.present();
  }

  public Query visitf0(GJVisitor<Query, Query> visitor, Query query) {
    return n.f0.accept(visitor, query);
  }

  public Query visitf1(GJVisitor<Query, Query> visitor, Query query) {
    return n.f1.accept(visitor, query);
  }
}
src/java/com/mhs/qsol/spans/SpanWildcardQuery.java
public class SpanWildcardQuery extends SpanQuery {
  private Term term;

  public SpanWildcardQuery(Term term) {
    this.term = term;
  }

  public Term getTerm() {
    return term;
  }

  public Query rewrite(IndexReader reader) throws IOException {
    WildcardQuery wildQuery = new WildcardQuery(term);
    wildQuery.setRewriteMethod(WildcardQuery.CONSTANT_SCORE_BOOLEAN_QUERY_REWRITE);
    BooleanQuery bq = null;
    bq = (BooleanQuery) ((ConstantScoreQuery) wildQuery.rewrite(reader)).getQuery();
    
    BooleanClause[] clauses = bq.getClauses();
    SpanQuery[] sqs = new SpanQuery[clauses.length];

    for (int i = 0; i < clauses.length; i++) {
      BooleanClause clause = clauses[i];

      // Clauses from RegexQuery.rewrite are always TermQuery's
      TermQuery tq = (TermQuery) clause.getQuery();

      sqs[i] = new SpanTermQuery(tq.getTerm());
      sqs[i].setBoost(tq.getBoost());
    }

    SpanOrQuery query = new SpanOrQuery(sqs);
    query.setBoost(wildQuery.getBoost());

    return query;
  }

  public Spans getSpans(IndexReader reader) throws IOException {
    throw new UnsupportedOperationException("Query should have been rewritten");
  }

  public String getField() {
    return term.field();
  }

  public Collection getTerms() {
    Collection terms = new ArrayList();
    terms.add(term);

    return terms;
  }

  public String toString(String field) {
    StringBuffer buffer = new StringBuffer();
    buffer.append("spanWildcardQuery(");
    buffer.append(term);
    buffer.append(")");

    // buffer.append(ToStringUtils.boost(getBoost()));
    return buffer.toString();
  }
}
src/java/com/mhs/qsol/abstractnode/VisitCheckOp.java
public interface VisitCheckOp {
  Query visitf0(GJVisitor<Query, Query> visitor, Query query);

  Query visitf1(GJVisitor<Query, Query> visitor, Query query);

  boolean isF1Present();
}
src/java/com/mhs/qsol/syntaxtree/Ord4Search.java
public class Ord4Search implements Node {
  public NodeToken f0;
  public BasicSearch f1;
  public NodeOptional f2;

  public Ord4Search(NodeToken n0, BasicSearch n1, NodeOptional n2) {
    f0 = n0;
    f1 = n1;
    f2 = n2;
  }

  public Ord4Search(BasicSearch n0, NodeOptional n1) {
    f0 = new NodeToken("op4");
    f1 = n0;
    f2 = n1;
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    v.visit(this);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return v.visit(this, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return v.visit(this);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    v.visit(this, argu);
  }
}
src/java/com/mhs/qsol/syntaxtree/NodeChoice.java
public class NodeChoice implements Node {
  public NodeChoice(Node node) {
    this(node, -1);
  }

  public NodeChoice(Node node, int whichChoice) {
    choice = node;
    which = whichChoice;
  }

  public void accept(com.mhs.qsol.visitor.Visitor v) {
    choice.accept(v);
  }

  public <R, A> R accept(com.mhs.qsol.visitor.GJVisitor<R, A> v, A argu) {
    return choice.accept(v, argu);
  }

  public <R> R accept(com.mhs.qsol.visitor.GJNoArguVisitor<R> v) {
    return choice.accept(v);
  }

  public <A> void accept(com.mhs.qsol.visitor.GJVoidVisitor<A> v, A argu) {
    choice.accept(v, argu);
  }

  public Node choice;
  public int which;
}
src/java/com/mhs/qsol/QsolParseException.java
public class QsolParseException extends RuntimeException {
  private Throwable cause;

  /**
   * Default constructor.
   */
  public QsolParseException() {
    super();
  }

  /**
   * Constructs with message.
   */
  public QsolParseException(String message) {
    super(message);
  }

  /**
   * Constructs with chained exception.
   */
  public QsolParseException(Throwable cause) {
    super(cause.toString());
    this.cause = cause;
  }

  /**
   * Constructs with message and exception.
   */
  public QsolParseException(String message, Throwable cause) {
    super(message, cause);
  }

  /**
   * Retrieves nested exception.
   */
  public Throwable getException() {
    return cause;
  }

  public void printStackTrace() {
    printStackTrace(System.err);
  }

  public void printStackTrace(PrintStream ps) {
    synchronized (ps) {
      super.printStackTrace(ps);

      if (cause != null) {
        ps.println("--- Nested Exception ---");
        cause.printStackTrace(ps);
      }
    }
  }

  public void printStackTrace(PrintWriter pw) {
    synchronized (pw) {
      super.printStackTrace(pw);

      if (cause != null) {
        pw.println("--- Nested Exception ---");
        cause.printStackTrace(pw);
      }
    }
  }
}


Question: import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Token;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.spans.SpanNearQuery;
import org.apache.lucene.search.spans.SpanOrQuery;
import org.apache.lucene.search.spans.SpanQuery;
import org.apache.lucene.search.spans.SpanTermQuery;
import com.mhs.qsol.QsolParseException;
import com.mhs.qsol.QsolParser.Operator;
import com.mhs.qsol.QsolToQueryVisitor;
import com.mhs.qsol.abstractnode.VisitCheckOp;
import com.mhs.qsol.abstractnode.VisitCheckOp1;
import com.mhs.qsol.abstractnode.VisitCheckOp2;
import com.mhs.qsol.abstractnode.VisitCheckOp3;
import com.mhs.qsol.abstractnode.VisitCheckOp4;
import com.mhs.qsol.abstractnode.VisitOp;
import com.mhs.qsol.abstractnode.VisitOrd1;
import com.mhs.qsol.abstractnode.VisitOrd2;
import com.mhs.qsol.abstractnode.VisitOrd3;
import com.mhs.qsol.abstractnode.VisitOrd4;
import com.mhs.qsol.proximity.distribute.BasicDistributable;
import com.mhs.qsol.spans.SpanFuzzyQuery;
import com.mhs.qsol.spans.SpanWildcardQuery;
import com.mhs.qsol.syntaxtree.BasicSearchType;
import com.mhs.qsol.syntaxtree.CheckOrd1Search;
import com.mhs.qsol.syntaxtree.CheckOrd2Search;
import com.mhs.qsol.syntaxtree.CheckOrd3Search;
import com.mhs.qsol.syntaxtree.CheckOrd4Search;
import com.mhs.qsol.syntaxtree.FieldSearch;
import com.mhs.qsol.syntaxtree.NodeChoice;
import com.mhs.qsol.syntaxtree.Ord1Search;
import com.mhs.qsol.syntaxtree.Ord2Search;
import com.mhs.qsol.syntaxtree.Ord3Search;
import com.mhs.qsol.syntaxtree.Ord4Search;
import com.mhs.qsol.syntaxtree.ParenthesisSearch;
import com.mhs.qsol.syntaxtree.Search;
import com.mhs.qsol.syntaxtree.SearchToken;
import com.mhs.qsol.visitor.GJDepthFirst;
/**
 * Copyright 2006 Mark Miller (markrmiller@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mhs.qsol.proximity;




public class ProximityVisitor extends GJDepthFirst<Query, Query> {
  private static final Pattern BOOST_EXTRACTOR = Pattern
      .compile("^(.*?)\\^(\\d+(?:\\.\\d+)?)$");
  private final static Logger logger = Logger.getLogger(ProximityVisitor.class
      .getPackage().getName());
  private static final Map<Operator, Occur> opToOccur = new HashMap<Operator, Occur>();

  static {
    opToOccur.put(Operator.AND, Occur.MUST);
    opToOccur.put(Operator.ANDNOT, Occur.MUST_NOT);
    opToOccur.put(Operator.OR, Occur.SHOULD);
  }

  private boolean lowercaseExpandedTermsboolean = true;
  ProximityBuilder proxBuilder = new ProximityBuilder();
  private String field;
  private Analyzer analyzer;
  private int slop = 0; // 0 is the default slop for when phrases become SpanNearQuerys
  private List<Operator> orderOfOps = new ArrayList<Operator>();
  private float boost = 1;

  public ProximityVisitor(Analyzer analyzer, String field) {
    this.analyzer = analyzer;
    this.field = field;
  }

  public Query getQuery() {
    return proxBuilder.getQuery();
  }

  /**
   * f0 -> CheckOrd1Search() f1 -> <EOF>
   */
  public Query visit(Search n, Query query) {
    proxBuilder = new ProximityBuilder();

    n.f0.accept(this, null);

    return null;
  }

  /**
   * f0 -> CheckOrd2Search() f1 -> ( Ord1Search() )?
   */
  public Query visit(CheckOrd1Search n, Query query) {
    Operator op = orderOfOps.get(0);

    if (op != Operator.PROXIMITY) {
      visitCheckForBoolean(new VisitCheckOp1(n));

      return null;
    }

    visitCheckForProx(new VisitCheckOp1(n), query);

    return null;
  }

  /**
   * f0 -> <OR> f1 -> CheckOrd2Search() f2 -> ( Ord1Search() )?
   */
  public Query visit(Ord1Search n, Query query) {
    Operator op = orderOfOps.get(0);

    if (op != Operator.PROXIMITY) {
      visitBooleanOp(new VisitOrd1(n), opToOccur.get(op));

      return null;
    }

    visitProxOp(new VisitOrd1(n), query);

    return null;
  }

  /**
   * f0 -> CheckOrd3Search() f1 -> ( Ord2Search() )?
   */
  public Query visit(CheckOrd2Search n, Query query) {
    Operator op = orderOfOps.get(1);

    if (op != Operator.PROXIMITY) {
      visitCheckForBoolean(new VisitCheckOp2(n));

      return null;
    }

    visitCheckForProx(new VisitCheckOp2(n), query);

    return null;
  }

  /**
   * f0 -> <AND> f1 -> CheckOrd3Search() f2 -> ( Ord2Search() )?
   */
  public Query visit(Ord2Search n, Query query) {
    Operator op = orderOfOps.get(1);

    if (op != Operator.PROXIMITY) {
      visitBooleanOp(new VisitOrd2(n), opToOccur.get(op));

      return null;
    }

    visitProxOp(new VisitOrd2(n), query);

    return null;
  }

  /**
   * f0 -> CheckOrd4Search() f1 -> ( Ord3Search() )?
   */
  public Query visit(CheckOrd3Search n, Query query) {
    Operator op = orderOfOps.get(2);

    if (op != Operator.PROXIMITY) {
      visitCheckForBoolean(new VisitCheckOp3(n));

      return null;
    }

    visitCheckForProx(new VisitCheckOp3(n), query);

    return null;
  }

  /**
   * f0 -> <PROXIMITY> f1 -> CheckOrd4Search() f2 -> ( Ord3Search() )?
   */
  public Query visit(Ord3Search n, Query query) {
    Operator op = orderOfOps.get(2);

    if (op != Operator.PROXIMITY) {
Answer:       visitBooleanOp(new VisitOrd3(n), opToOccur.get(op));