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
1 package org.apache.lucene.queryParser.core.builders;
2
3 /**
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
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  */
19
20 import java.util.HashMap;
21 import java.util.List;
22
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;
30
31 /**
32  * This class should be used when there is a builder for each type of node.
33  * 
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
38  * 
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
41  * builders.
42  * 
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}.
46  * 
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
50  * children.
51  * 
52  * @see QueryBuilder
53  */
54 public class QueryTreeBuilder implements QueryBuilder {
55
56   /**
57    * This tag is used to tag the nodes in a query tree with the built objects
58    * produced from their own associated builder.
59    */
60   public static final String QUERY_TREE_BUILDER_TAGID = QueryTreeBuilder.class
61       .getName();
62
63   private HashMap<Class<? extends QueryNode>, QueryBuilder> queryNodeBuilders;
64
65   private HashMap<String, QueryBuilder> fieldNameBuilders;
66
67   /**
68    * {@link QueryTreeBuilder} constructor.
69    */
70   public QueryTreeBuilder() {
71     // empty constructor
72   }
73
74   /**
75    * Associates a field name with a builder.
76    * 
77    * @param fieldName the field name
78    * @param builder the builder to be associated
79    */
80   public void setBuilder(String fieldName, QueryBuilder builder) {
81
82     if (this.fieldNameBuilders == null) {
83       this.fieldNameBuilders = new HashMap<String, QueryBuilder>();
84     }
85
86     this.fieldNameBuilders.put(fieldName.toString(), builder);
87
88   }
89
90   /**
91    * Associates a field name with a builder.
92    * 
93    * @param fieldName the field name
94    * @param builder the builder to be associated
95    * 
96    * @deprecated use {@link #setBuilder(String, QueryBuilder)} instead
97    */
98   @Deprecated
99   public void setBuilder(CharSequence fieldName, QueryBuilder builder) {
100     setBuilder(StringUtils.toString(fieldName), builder);
101   }
102
103   /**
104    * Associates a class with a builder
105    * 
106    * @param queryNodeClass the class
107    * @param builder the builder to be associated
108    */
109   public void setBuilder(Class<? extends QueryNode> queryNodeClass,
110       QueryBuilder builder) {
111
112     if (this.queryNodeBuilders == null) {
113       this.queryNodeBuilders = new HashMap<Class<? extends QueryNode>, QueryBuilder>();
114     }
115
116     this.queryNodeBuilders.put(queryNodeClass, builder);
117
118   }
119
120   private void process(QueryNode node) throws QueryNodeException {
121
122     if (node != null) {
123       QueryBuilder builder = getBuilder(node);
124
125       if (!(builder instanceof QueryTreeBuilder)) {
126         List<QueryNode> children = node.getChildren();
127
128         if (children != null) {
129
130           for (QueryNode child : children) {
131             process(child);
132           }
133
134         }
135
136       }
137
138       processNode(node, builder);
139
140     }
141
142   }
143
144   private QueryBuilder getBuilder(QueryNode node) {
145     QueryBuilder builder = null;
146
147     if (this.fieldNameBuilders != null && node instanceof FieldableNode) {
148       builder = this.fieldNameBuilders.get(StringUtils
149           .toString(((FieldableNode) node).getField()));
150     }
151
152     if (builder == null && this.queryNodeBuilders != null) {
153
154       Class<?> clazz = node.getClass();
155
156       do {
157         builder = getQueryBuilder(clazz);
158
159         if (builder == null) {
160           Class<?>[] classes = node.getClass().getInterfaces();
161
162           for (Class<?> actualClass : classes) {
163             builder = getQueryBuilder(actualClass);
164
165             if (builder != null) {
166               break;
167             }
168
169           }
170
171         }
172
173       } while (builder == null && (clazz = clazz.getSuperclass()) != null);
174
175     }
176
177     return builder;
178
179   }
180
181   private void processNode(QueryNode node, QueryBuilder builder)
182       throws QueryNodeException {
183
184     if (builder == null) {
185
186       throw new QueryNodeException(new MessageImpl(
187           QueryParserMessages.LUCENE_QUERY_CONVERSION_ERROR, node
188               .toQueryString(new EscapeQuerySyntaxImpl()), node.getClass()
189               .getName()));
190
191     }
192
193     Object obj = builder.build(node);
194
195     if (obj != null) {
196       node.setTag(QUERY_TREE_BUILDER_TAGID, obj);
197     }
198
199   }
200
201   private QueryBuilder getQueryBuilder(Class<?> clazz) {
202
203     if (QueryNode.class.isAssignableFrom(clazz)) {
204       return this.queryNodeBuilders.get(clazz);
205     }
206
207     return null;
208
209   }
210
211   /**
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.
214    * 
215    * @param queryNode the query tree root node
216    * 
217    * @return the built object
218    * 
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
222    */
223   public Object build(QueryNode queryNode) throws QueryNodeException {
224     process(queryNode);
225
226     return queryNode.getTag(QUERY_TREE_BUILDER_TAGID);
227
228   }
229
230 }