Index: lucene/CHANGES.txt =================================================================== --- lucene/CHANGES.txt (revision 1558306) +++ lucene/CHANGES.txt (working copy) @@ -34,6 +34,9 @@ AnalyzingInfixSuggester but boosts suggestions that matched tokens with lower positions. (Remi Melisson via Mike McCandless) +* LUCENE-5099: QueryNode should have the ability to detach from its node + parent. Added QueryNode.removeFromParent() that allows nodes to be + detached from its parent node. (Adriano Crestani) Build Index: lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/nodes/QueryNode.java =================================================================== --- lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/nodes/QueryNode.java (revision 1558304) +++ lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/nodes/QueryNode.java (working copy) @@ -91,4 +91,8 @@ */ public Map getTagMap(); + /** + * Removes this query node from its parent. + */ + public void removeFromParent(); } Index: lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/nodes/QueryNodeImpl.java =================================================================== --- lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/nodes/QueryNodeImpl.java (revision 1558304) +++ lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/core/nodes/QueryNodeImpl.java (working copy) @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -102,18 +103,19 @@ // reset parent value for (QueryNode child : children) { - - ((QueryNodeImpl) child).setParent(null); - + child.removeFromParent(); } - + + ArrayList existingChildren = new ArrayList(getChildren()); + for (QueryNode existingChild : existingChildren) { + existingChild.removeFromParent(); + } + // allocate new children list allocate(); - + // add new children and set parent - for (QueryNode child : children) { - add(child); - } + add(children); } @Override @@ -154,7 +156,7 @@ if (isLeaf() || this.clauses == null) { return null; } - return this.clauses; + return new ArrayList(this.clauses); } @Override @@ -181,7 +183,10 @@ private QueryNode parent = null; private void setParent(QueryNode parent) { - this.parent = parent; + if (this.parent != parent) { + this.removeFromParent(); + this.parent = parent; + } } @Override @@ -241,5 +246,21 @@ public Map getTagMap() { return (Map) this.tags.clone(); } + + @Override + public void removeFromParent() { + if (this.parent != null) { + List parentChildren = this.parent.getChildren(); + Iterator it = parentChildren.iterator(); + + while (it.hasNext()) { + if (it.next() == this) { + it.remove(); + } + } + + this.parent = null; + } + } } // end class QueryNodeImpl Index: lucene/queryparser/src/test/org/apache/lucene/queryparser/flexible/core/nodes/TestQueryNode.java =================================================================== --- lucene/queryparser/src/test/org/apache/lucene/queryparser/flexible/core/nodes/TestQueryNode.java (revision 1558304) +++ lucene/queryparser/src/test/org/apache/lucene/queryparser/flexible/core/nodes/TestQueryNode.java (working copy) @@ -18,6 +18,7 @@ */ import java.util.Arrays; +import java.util.Collections; import org.apache.lucene.util.LuceneTestCase; @@ -44,4 +45,23 @@ } + /* LUCENE-5099 - QueryNodeProcessorImpl should set parent to null before returning on processing */ + public void testRemoveFromParent() throws Exception { + BooleanQueryNode booleanNode = new BooleanQueryNode(Collections.emptyList()); + FieldQueryNode fieldNode = new FieldQueryNode("foo", "A", 0, 1); + assertNull(fieldNode.getParent()); + + booleanNode.add(fieldNode); + assertNotNull(fieldNode.getParent()); + + fieldNode.removeFromParent(); + assertNull(fieldNode.getParent()); + + booleanNode.add(fieldNode); + assertNotNull(fieldNode.getParent()); + + booleanNode.set(Collections.emptyList()); + assertNull(fieldNode.getParent()); + } + } Index: lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java =================================================================== --- lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java (revision 1558306) +++ lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java (working copy) @@ -1066,6 +1066,7 @@ assertTrue(bq.getClauses()[1].getQuery() instanceof MatchAllDocsQuery); } + @SuppressWarnings("unused") private void assertHits(int expected, String query, IndexSearcher is) throws Exception { String oldDefaultField = getDefaultField(); setDefaultField("date");