package org.apache.lucene.queryParser.core.processors; /** * 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.ArrayList; import java.util.List; import org.apache.lucene.queryParser.core.QueryNodeException; import org.apache.lucene.queryParser.core.config.QueryConfigHandler; import org.apache.lucene.queryParser.core.nodes.QueryNode; /** * <p> * This is a default implementation for the {@link QueryNodeProcessor} * interface, it's an abstract class, so it should be extended by classes that * want to process a {@link QueryNode} tree. * </p> * <p> * This class process {@link QueryNode}s from left to right in the tree. While * it's walking down the tree, for every node, * {@link #preProcessNode(QueryNode)} is invoked. After a node's children are * processed, {@link #postProcessNode(QueryNode)} is invoked for that node. * {@link #setChildrenOrder(List)} is invoked before * {@link #postProcessNode(QueryNode)} only if the node has at least one child, * in {@link #setChildrenOrder(List)} the implementor might redefine the * children order or remove any children from the children list. * </p> * <p> * Here is an example about how it process the nodes: * </p> * * <pre> * a * / \ * b e * / \ * c d * </pre> * * Here is the order the methods would be invoked for the tree described above: * * <pre> * preProcessNode( a ); * preProcessNode( b ); * preProcessNode( c ); * postProcessNode( c ); * preProcessNode( d ); * postProcessNode( d ); * setChildrenOrder( bChildrenList ); * postProcessNode( b ); * preProcessNode( e ); * postProcessNode( e ); * setChildrenOrder( aChildrenList ); * postProcessNode( a ) * </pre> * * @see org.apache.lucene.queryParser.core.processors.QueryNodeProcessor */ public abstract class QueryNodeProcessorImpl implements QueryNodeProcessor { private ArrayList<ChildrenList> childrenListPool = new ArrayList<ChildrenList>(); private QueryConfigHandler queryConfig; public QueryNodeProcessorImpl() { // empty constructor } public QueryNodeProcessorImpl(QueryConfigHandler queryConfigHandler) { this.queryConfig = queryConfigHandler; } public QueryNode process(QueryNode queryTree) throws QueryNodeException { return processIteration(queryTree); } private QueryNode processIteration(QueryNode queryTree) throws QueryNodeException { queryTree = preProcessNode(queryTree); processChildren(queryTree); queryTree = postProcessNode(queryTree); return queryTree; } /** * This method is called every time a child is processed. * * @param queryTree * the query node child to be processed * @throws QueryNodeException * if something goes wrong during the query node processing */ protected void processChildren(QueryNode queryTree) throws QueryNodeException { List<QueryNode> children = queryTree.getChildren(); ChildrenList newChildren; if (children != null && children.size() > 0) { newChildren = allocateChildrenList(); try { for (QueryNode child : children) { child = processIteration(child); if (child == null) { throw new NullPointerException(); } newChildren.add(child); } List<QueryNode> orderedChildrenList = setChildrenOrder(newChildren); queryTree.set(orderedChildrenList); } finally { newChildren.beingUsed = false; } } } private ChildrenList allocateChildrenList() { ChildrenList list = null; for (ChildrenList auxList : this.childrenListPool) { if (!auxList.beingUsed) { list = auxList; list.clear(); break; } } if (list == null) { list = new ChildrenList(); this.childrenListPool.add(list); } list.beingUsed = true; return list; } /** * For reference about this method check: * {@link QueryNodeProcessor#setQueryConfigHandler(QueryConfigHandler)}. * * @param queryConfigHandler * the query configuration handler to be set. * * @see QueryNodeProcessor#getQueryConfigHandler() * @see QueryConfigHandler */ public void setQueryConfigHandler(QueryConfigHandler queryConfigHandler) { this.queryConfig = queryConfigHandler; } /** * For reference about this method check: * {@link QueryNodeProcessor#getQueryConfigHandler()}. * * @return QueryConfigHandler the query configuration handler to be set. * * @see QueryNodeProcessor#setQueryConfigHandler(QueryConfigHandler) * @see QueryConfigHandler */ public QueryConfigHandler getQueryConfigHandler() { return this.queryConfig; } /** * This method is invoked for every node when walking down the tree. * * @param node * the query node to be pre-processed * * @return a query node * * @throws QueryNodeException * if something goes wrong during the query node processing */ abstract protected QueryNode preProcessNode(QueryNode node) throws QueryNodeException; /** * This method is invoked for every node when walking up the tree. * * @param node * node the query node to be post-processed * * @return a query node * * @throws QueryNodeException * if something goes wrong during the query node processing */ abstract protected QueryNode postProcessNode(QueryNode node) throws QueryNodeException; /** * This method is invoked for every node that has at least on child. It's * invoked right before {@link #postProcessNode(QueryNode)} is invoked. * * @param children * the list containing all current node's children * * @return a new list containing all children that should be set to the * current node * * @throws QueryNodeException * if something goes wrong during the query node processing */ abstract protected List<QueryNode> setChildrenOrder(List<QueryNode> children) throws QueryNodeException; private static class ChildrenList extends ArrayList<QueryNode> { private static final long serialVersionUID = -2613518456949297135L; boolean beingUsed; } }