pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / queryparser / src / java / org / apache / lucene / queryParser / core / builders / QueryTreeBuilder.java
diff --git a/lucene-java-3.5.0/lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/builders/QueryTreeBuilder.java b/lucene-java-3.5.0/lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/core/builders/QueryTreeBuilder.java
new file mode 100644 (file)
index 0000000..665ca9a
--- /dev/null
@@ -0,0 +1,230 @@
+package org.apache.lucene.queryParser.core.builders;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.lucene.messages.MessageImpl;
+import org.apache.lucene.queryParser.core.QueryNodeException;
+import org.apache.lucene.queryParser.core.messages.QueryParserMessages;
+import org.apache.lucene.queryParser.core.nodes.FieldableNode;
+import org.apache.lucene.queryParser.core.nodes.QueryNode;
+import org.apache.lucene.queryParser.core.util.StringUtils;
+import org.apache.lucene.queryParser.standard.parser.EscapeQuerySyntaxImpl;
+
+/**
+ * This class should be used when there is a builder for each type of node.
+ * 
+ * The type of node may be defined in 2 different ways: - by the field name,
+ * when the node implements the {@link FieldableNode} interface - by its class,
+ * it keeps checking the class and all the interfaces and classes this class
+ * implements/extends until it finds a builder for that class/interface
+ * 
+ * This class always check if there is a builder for the field name before it
+ * checks for the node class. So, field name builders have precedence over class
+ * builders.
+ * 
+ * When a builder is found for a node, it's called and the node is passed to the
+ * builder. If the returned built object is not <code>null</code>, it's tagged
+ * on the node using the tag {@link QueryTreeBuilder#QUERY_TREE_BUILDER_TAGID}.
+ * 
+ * The children are usually built before the parent node. However, if a builder
+ * associated to a node is an instance of {@link QueryTreeBuilder}, the node is
+ * delegated to this builder and it's responsible to build the node and its
+ * children.
+ * 
+ * @see QueryBuilder
+ */
+public class QueryTreeBuilder implements QueryBuilder {
+
+  /**
+   * This tag is used to tag the nodes in a query tree with the built objects
+   * produced from their own associated builder.
+   */
+  public static final String QUERY_TREE_BUILDER_TAGID = QueryTreeBuilder.class
+      .getName();
+
+  private HashMap<Class<? extends QueryNode>, QueryBuilder> queryNodeBuilders;
+
+  private HashMap<String, QueryBuilder> fieldNameBuilders;
+
+  /**
+   * {@link QueryTreeBuilder} constructor.
+   */
+  public QueryTreeBuilder() {
+    // empty constructor
+  }
+
+  /**
+   * Associates a field name with a builder.
+   * 
+   * @param fieldName the field name
+   * @param builder the builder to be associated
+   */
+  public void setBuilder(String fieldName, QueryBuilder builder) {
+
+    if (this.fieldNameBuilders == null) {
+      this.fieldNameBuilders = new HashMap<String, QueryBuilder>();
+    }
+
+    this.fieldNameBuilders.put(fieldName.toString(), builder);
+
+  }
+
+  /**
+   * Associates a field name with a builder.
+   * 
+   * @param fieldName the field name
+   * @param builder the builder to be associated
+   * 
+   * @deprecated use {@link #setBuilder(String, QueryBuilder)} instead
+   */
+  @Deprecated
+  public void setBuilder(CharSequence fieldName, QueryBuilder builder) {
+    setBuilder(StringUtils.toString(fieldName), builder);
+  }
+
+  /**
+   * Associates a class with a builder
+   * 
+   * @param queryNodeClass the class
+   * @param builder the builder to be associated
+   */
+  public void setBuilder(Class<? extends QueryNode> queryNodeClass,
+      QueryBuilder builder) {
+
+    if (this.queryNodeBuilders == null) {
+      this.queryNodeBuilders = new HashMap<Class<? extends QueryNode>, QueryBuilder>();
+    }
+
+    this.queryNodeBuilders.put(queryNodeClass, builder);
+
+  }
+
+  private void process(QueryNode node) throws QueryNodeException {
+
+    if (node != null) {
+      QueryBuilder builder = getBuilder(node);
+
+      if (!(builder instanceof QueryTreeBuilder)) {
+        List<QueryNode> children = node.getChildren();
+
+        if (children != null) {
+
+          for (QueryNode child : children) {
+            process(child);
+          }
+
+        }
+
+      }
+
+      processNode(node, builder);
+
+    }
+
+  }
+
+  private QueryBuilder getBuilder(QueryNode node) {
+    QueryBuilder builder = null;
+
+    if (this.fieldNameBuilders != null && node instanceof FieldableNode) {
+      builder = this.fieldNameBuilders.get(StringUtils
+          .toString(((FieldableNode) node).getField()));
+    }
+
+    if (builder == null && this.queryNodeBuilders != null) {
+
+      Class<?> clazz = node.getClass();
+
+      do {
+        builder = getQueryBuilder(clazz);
+
+        if (builder == null) {
+          Class<?>[] classes = node.getClass().getInterfaces();
+
+          for (Class<?> actualClass : classes) {
+            builder = getQueryBuilder(actualClass);
+
+            if (builder != null) {
+              break;
+            }
+
+          }
+
+        }
+
+      } while (builder == null && (clazz = clazz.getSuperclass()) != null);
+
+    }
+
+    return builder;
+
+  }
+
+  private void processNode(QueryNode node, QueryBuilder builder)
+      throws QueryNodeException {
+
+    if (builder == null) {
+
+      throw new QueryNodeException(new MessageImpl(
+          QueryParserMessages.LUCENE_QUERY_CONVERSION_ERROR, node
+              .toQueryString(new EscapeQuerySyntaxImpl()), node.getClass()
+              .getName()));
+
+    }
+
+    Object obj = builder.build(node);
+
+    if (obj != null) {
+      node.setTag(QUERY_TREE_BUILDER_TAGID, obj);
+    }
+
+  }
+
+  private QueryBuilder getQueryBuilder(Class<?> clazz) {
+
+    if (QueryNode.class.isAssignableFrom(clazz)) {
+      return this.queryNodeBuilders.get(clazz);
+    }
+
+    return null;
+
+  }
+
+  /**
+   * Builds some kind of object from a query tree. Each node in the query tree
+   * is built using an specific builder associated to it.
+   * 
+   * @param queryNode the query tree root node
+   * 
+   * @return the built object
+   * 
+   * @throws QueryNodeException if some node builder throws a
+   *         {@link QueryNodeException} or if there is a node which had no
+   *         builder associated to it
+   */
+  public Object build(QueryNode queryNode) throws QueryNodeException {
+    process(queryNode);
+
+    return queryNode.getTag(QUERY_TREE_BUILDER_TAGID);
+
+  }
+
+}