1 package org.apache.lucene.queryParser.core.builders;
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import java.util.HashMap;
21 import java.util.List;
23 import org.apache.lucene.messages.MessageImpl;
24 import org.apache.lucene.queryParser.core.QueryNodeException;
25 import org.apache.lucene.queryParser.core.messages.QueryParserMessages;
26 import org.apache.lucene.queryParser.core.nodes.FieldableNode;
27 import org.apache.lucene.queryParser.core.nodes.QueryNode;
28 import org.apache.lucene.queryParser.core.util.StringUtils;
29 import org.apache.lucene.queryParser.standard.parser.EscapeQuerySyntaxImpl;
32 * This class should be used when there is a builder for each type of node.
34 * The type of node may be defined in 2 different ways: - by the field name,
35 * when the node implements the {@link FieldableNode} interface - by its class,
36 * it keeps checking the class and all the interfaces and classes this class
37 * implements/extends until it finds a builder for that class/interface
39 * This class always check if there is a builder for the field name before it
40 * checks for the node class. So, field name builders have precedence over class
43 * When a builder is found for a node, it's called and the node is passed to the
44 * builder. If the returned built object is not <code>null</code>, it's tagged
45 * on the node using the tag {@link QueryTreeBuilder#QUERY_TREE_BUILDER_TAGID}.
47 * The children are usually built before the parent node. However, if a builder
48 * associated to a node is an instance of {@link QueryTreeBuilder}, the node is
49 * delegated to this builder and it's responsible to build the node and its
54 public class QueryTreeBuilder implements QueryBuilder {
57 * This tag is used to tag the nodes in a query tree with the built objects
58 * produced from their own associated builder.
60 public static final String QUERY_TREE_BUILDER_TAGID = QueryTreeBuilder.class
63 private HashMap<Class<? extends QueryNode>, QueryBuilder> queryNodeBuilders;
65 private HashMap<String, QueryBuilder> fieldNameBuilders;
68 * {@link QueryTreeBuilder} constructor.
70 public QueryTreeBuilder() {
75 * Associates a field name with a builder.
77 * @param fieldName the field name
78 * @param builder the builder to be associated
80 public void setBuilder(String fieldName, QueryBuilder builder) {
82 if (this.fieldNameBuilders == null) {
83 this.fieldNameBuilders = new HashMap<String, QueryBuilder>();
86 this.fieldNameBuilders.put(fieldName.toString(), builder);
91 * Associates a field name with a builder.
93 * @param fieldName the field name
94 * @param builder the builder to be associated
96 * @deprecated use {@link #setBuilder(String, QueryBuilder)} instead
99 public void setBuilder(CharSequence fieldName, QueryBuilder builder) {
100 setBuilder(StringUtils.toString(fieldName), builder);
104 * Associates a class with a builder
106 * @param queryNodeClass the class
107 * @param builder the builder to be associated
109 public void setBuilder(Class<? extends QueryNode> queryNodeClass,
110 QueryBuilder builder) {
112 if (this.queryNodeBuilders == null) {
113 this.queryNodeBuilders = new HashMap<Class<? extends QueryNode>, QueryBuilder>();
116 this.queryNodeBuilders.put(queryNodeClass, builder);
120 private void process(QueryNode node) throws QueryNodeException {
123 QueryBuilder builder = getBuilder(node);
125 if (!(builder instanceof QueryTreeBuilder)) {
126 List<QueryNode> children = node.getChildren();
128 if (children != null) {
130 for (QueryNode child : children) {
138 processNode(node, builder);
144 private QueryBuilder getBuilder(QueryNode node) {
145 QueryBuilder builder = null;
147 if (this.fieldNameBuilders != null && node instanceof FieldableNode) {
148 builder = this.fieldNameBuilders.get(StringUtils
149 .toString(((FieldableNode) node).getField()));
152 if (builder == null && this.queryNodeBuilders != null) {
154 Class<?> clazz = node.getClass();
157 builder = getQueryBuilder(clazz);
159 if (builder == null) {
160 Class<?>[] classes = node.getClass().getInterfaces();
162 for (Class<?> actualClass : classes) {
163 builder = getQueryBuilder(actualClass);
165 if (builder != null) {
173 } while (builder == null && (clazz = clazz.getSuperclass()) != null);
181 private void processNode(QueryNode node, QueryBuilder builder)
182 throws QueryNodeException {
184 if (builder == null) {
186 throw new QueryNodeException(new MessageImpl(
187 QueryParserMessages.LUCENE_QUERY_CONVERSION_ERROR, node
188 .toQueryString(new EscapeQuerySyntaxImpl()), node.getClass()
193 Object obj = builder.build(node);
196 node.setTag(QUERY_TREE_BUILDER_TAGID, obj);
201 private QueryBuilder getQueryBuilder(Class<?> clazz) {
203 if (QueryNode.class.isAssignableFrom(clazz)) {
204 return this.queryNodeBuilders.get(clazz);
212 * Builds some kind of object from a query tree. Each node in the query tree
213 * is built using an specific builder associated to it.
215 * @param queryNode the query tree root node
217 * @return the built object
219 * @throws QueryNodeException if some node builder throws a
220 * {@link QueryNodeException} or if there is a node which had no
221 * builder associated to it
223 public Object build(QueryNode queryNode) throws QueryNodeException {
226 return queryNode.getTag(QUERY_TREE_BUILDER_TAGID);