content: package org.apache.lucene.queryparser.flexible.aqp.processors;
  
  import org.apache.lucene.queryparser.flexible.aqp.nodes.*;
  import org.apache.lucene.queryparser.flexible.aqp.parser.AqpStandardQueryConfigHandler;
  import org.apache.lucene.queryparser.flexible.core.QueryNodeException;
  import org.apache.lucene.queryparser.flexible.core.config.QueryConfigHandler;
  import org.apache.lucene.queryparser.flexible.core.messages.QueryParserMessages;
  import org.apache.lucene.queryparser.flexible.core.nodes.AndQueryNode;
  import org.apache.lucene.queryparser.flexible.core.nodes.OrQueryNode;
  import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode;
  import org.apache.lucene.queryparser.flexible.messages.MessageImpl;
  import org.apache.lucene.queryparser.flexible.standard.config.StandardQueryConfigHandler;
  
  /**
   * This processor operates on OPERATOR nodes, these are the nodes with labels
   * AND, OR, NOT, DEFOP, NEAR and possibly others.
   *
   * <p>
   * <p>
   * I have decided to create special QueryNodes for each type of the operator,
   * because the default implementation ({@link AndQueryNode}, {@link OrQueryNode}
   * ) was confusing.
   *
   * <pre>
   *   AND
   *   	- creates {@link AqpAndQueryNode}
   *   OR
   *   	- creates {@link AqpOrQueryNode}
   *   NOT
   *   	- creates {@link AqpNotQueryNode}
   *   NEAR
   *   	- creates {@link AqpNearQueryNode}
   *   WITH
   *   	- not implemented yet
   *   PARAGRAPH
   *   	- not implemented yet
   * </pre>
   * <p>
   * This processor should run after {@link AqpDEFOPProcessor}, and also after
   * {@link AqpMODIFIERProcessor} because modifiers should have precedence over
   * operators. Like in the query: "this OR +that"
   */
  public class AqpOPERATORProcessor extends AqpQProcessorPost {
  
      @Override
      public boolean nodeIsWanted(AqpANTLRNode node) {
          return node.getTokenName().equals("OPERATOR");
      }
  
      @Override
      public QueryNode createQNode(AqpANTLRNode node) throws QueryNodeException {
  
          String label = node.getTokenLabel();
  
          if (label.equals("DEFOP")) {
              return new AqpDefopQueryNode(node.getChildren(), getDefaultOperator());
          } else if (label.equals("AND")) {
              return new AqpAndQueryNode(node.getChildren());
          } else if (label.equals("OR")) {
              return new AqpOrQueryNode(node.getChildren());
          } else if (label.equals("NOT")) {
              return new AqpNotQueryNode(node.getChildren());
          } else if (label.toUpperCase().contains("NEAR")) {
              if (label.length() <= 4) {
                  return new AqpNearQueryNode(node.getChildren(),
                          getDefaultProximityValue());
              } else {
                  int distance = Integer.parseInt(label.substring(4));
                  if (isProximityValueAllowed(distance)) {
                      return new AqpNearQueryNode(node.getChildren(), distance);
                  } else {
                      throw new QueryNodeException(new MessageImpl(
                              QueryParserMessages.INVALID_SYNTAX,
                              "Proximity is only allowed in a range: " + getRange()[0] + "-" + getRange()[1]));
                  }
              }
  
          } else {
              throw new QueryNodeException(new MessageImpl(
                      QueryParserMessages.INVALID_SYNTAX, "Unknown operator " + label));
          }
  
      }
  
      private Integer getDefaultProximityValue() throws QueryNodeException {
          QueryConfigHandler queryConfig = getQueryConfigHandler();
          if (queryConfig == null
                  || !queryConfig
                  .has(AqpStandardQueryConfigHandler.ConfigurationKeys.DEFAULT_PROXIMITY)) {
              throw new QueryNodeException(new MessageImpl(
                      QueryParserMessages.LUCENE_QUERY_CONVERSION_ERROR,
                      "Configuration error: " + "DefaultProximity value is missing"));
          }
          return queryConfig
                  .get(AqpStandardQueryConfigHandler.ConfigurationKeys.DEFAULT_PROXIMITY);
      }
  
      private int[] getRange() {
          QueryConfigHandler queryConfig = getQueryConfigHandler();
          return queryConfig
                  .get(AqpStandardQueryConfigHandler.ConfigurationKeys.ALLOWED_PROXIMITY_RANGE);
  
      }
  
      private boolean isProximityValueAllowed(int userValue) {
          int[] range = getRange();
          return userValue >= range[0] && userValue <= range[1];
      }
  
      protected StandardQueryConfigHandler.Operator getDefaultOperator()
              throws QueryNodeException {
          QueryConfigHandler queryConfig = getQueryConfigHandler();
  
          if (queryConfig != null) {
              if (queryConfig
                      .has(StandardQueryConfigHandler.ConfigurationKeys.DEFAULT_OPERATOR)) {
                  return queryConfig
                          .get(StandardQueryConfigHandler.ConfigurationKeys.DEFAULT_OPERATOR);
              }
          }
          throw new QueryNodeException(new MessageImpl(
                  QueryParserMessages.LUCENE_QUERY_CONVERSION_ERROR,
                  "Configuration error: "
                          + StandardQueryConfigHandler.ConfigurationKeys.class
                          + " is missing"));
      }
  
  }
  