| /* |
| * 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. |
| */ |
| package org.apache.lucene.queryparser.flexible.core.processors; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import org.apache.lucene.queryparser.flexible.core.QueryNodeException; |
| import org.apache.lucene.queryparser.flexible.core.config.QueryConfigHandler; |
| import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; |
| |
| /** |
| * 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>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>Here is an example about how it process the nodes: |
| * |
| * <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.flexible.core.processors.QueryNodeProcessor |
| */ |
| public abstract class QueryNodeProcessorImpl implements QueryNodeProcessor { |
| |
| private ArrayList<ChildrenList> childrenListPool = new ArrayList<>(); |
| |
| private QueryConfigHandler queryConfig; |
| |
| public QueryNodeProcessorImpl() { |
| // empty constructor |
| } |
| |
| public QueryNodeProcessorImpl(QueryConfigHandler queryConfigHandler) { |
| this.queryConfig = queryConfigHandler; |
| } |
| |
| @Override |
| 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 |
| */ |
| @Override |
| 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 |
| */ |
| @Override |
| 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 |
| */ |
| protected abstract 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 |
| */ |
| protected abstract 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 |
| */ |
| protected abstract List<QueryNode> setChildrenOrder(List<QueryNode> children) |
| throws QueryNodeException; |
| |
| private static class ChildrenList extends ArrayList<QueryNode> { |
| |
| boolean beingUsed; |
| } |
| } |