Index: lucene/contrib/CHANGES.txt
===================================================================
--- lucene/contrib/CHANGES.txt	(revision 1363615)
+++ lucene/contrib/CHANGES.txt	(working copy)
@@ -3,6 +3,14 @@
 For more information on past and future Lucene versions, please see:
 http://s.apache.org/luceneversions
 
+======================= Lucene 3.6.2 ================
+
+Bug Fixes
+
+ * LUCENE-4109: BooleanQueries are not parsed correctly with the 
+   flexible query parser. (Karsten Rauch via Robert Muir)
+
+
 ======================= Lucene 3.6.1 ================
 
 Bug Fixes
Index: lucene/contrib/queryparser/src/test/org/apache/lucene/queryParser/standard/TestQPHelper.java
===================================================================
--- lucene/contrib/queryparser/src/test/org/apache/lucene/queryParser/standard/TestQPHelper.java	(revision 1363615)
+++ lucene/contrib/queryparser/src/test/org/apache/lucene/queryParser/standard/TestQPHelper.java	(working copy)
@@ -469,6 +469,15 @@
     assertQueryEquals(".NET", a, ".NET");
   }
 
+  public void testGroup() throws Exception {
+    assertQueryEquals("!(a AND b) OR c", null, "-(+a +b) c");
+    assertQueryEquals("!(a AND b) AND c", null, "-(+a +b) +c");
+    assertQueryEquals("((a AND b) AND c)", null, "+(+a +b) +c");
+    assertQueryEquals("(a AND b) AND c", null, "+(+a +b) +c");
+    assertQueryEquals("b !(a AND b)", null, "b -(+a +b)");
+    assertQueryEquals("(a AND b)^4 OR c", null, "((+a +b)^4.0) c");
+  }
+
   public void testSlop() throws Exception {
 
     assertQueryEquals("\"term germ\"~2", null, "\"term germ\"~2");
@@ -1284,5 +1293,17 @@
     w.close();
     dir.close();
   }
+  
+  public void testParens() throws Exception {
+    StandardQueryParser qp = new StandardQueryParser(new MockAnalyzer(random));
+    String query = "(field:[1 TO *] AND field:[* TO 2]) AND field2:(z)";
+    BooleanQuery q = new BooleanQuery();
+    BooleanQuery bq = new BooleanQuery();
+    bq.add(new TermRangeQuery("field", "1", null, true, true), BooleanClause.Occur.MUST);
+    bq.add(new TermRangeQuery("field", null, "2", true, true), BooleanClause.Occur.MUST);
+    q.add(bq, BooleanClause.Occur.MUST);
+    q.add(new TermQuery(new Term("field2", "z")), BooleanClause.Occur.MUST);
+    assertEquals(q, qp.parse(query, "foo"));
+  }
 
 }
Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/StandardQueryNodeProcessorPipeline.java
===================================================================
--- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/StandardQueryNodeProcessorPipeline.java	(revision 1363615)
+++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/StandardQueryNodeProcessorPipeline.java	(working copy)
@@ -59,7 +59,7 @@
     add(new AllowLeadingWildcardProcessor());    
     add(new AnalyzerQueryNodeProcessor());
     add(new PhraseSlopQueryNodeProcessor());
-    add(new GroupQueryNodeProcessor());
+    add(new BooleanQuery2ModifierNodeProcessor());
     add(new NoChildOptimizationQueryNodeProcessor());
     add(new RemoveDeletedQueryNodesProcessor());
     add(new RemoveEmptyNonLeafQueryNodeProcessor());
Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/BooleanQuery2ModifierNodeProcessor.java
===================================================================
--- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/BooleanQuery2ModifierNodeProcessor.java	(revision 0)
+++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/BooleanQuery2ModifierNodeProcessor.java	(working copy)
@@ -0,0 +1,202 @@
+package org.apache.lucene.queryParser.standard.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.AndQueryNode;
+import org.apache.lucene.queryParser.core.nodes.BooleanQueryNode;
+import org.apache.lucene.queryParser.core.nodes.ModifierQueryNode;
+import org.apache.lucene.queryParser.core.nodes.ModifierQueryNode.Modifier;
+import org.apache.lucene.queryParser.core.nodes.QueryNode;
+import org.apache.lucene.queryParser.core.processors.QueryNodeProcessor;
+import org.apache.lucene.queryParser.precedence.processors.BooleanModifiersQueryNodeProcessor;
+import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler;
+import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys;
+import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.Operator;
+import org.apache.lucene.queryParser.standard.nodes.BooleanModifierNode;
+import org.apache.lucene.queryParser.standard.parser.StandardSyntaxParser;
+
+/**
+ * <p>
+ * This processor is used to apply the correct {@link ModifierQueryNode} to
+ * {@link BooleanQueryNode}s children. This is a variant of
+ * {@link BooleanModifiersQueryNodeProcessor} which ignores precedence.
+ * </p>
+ * <p>
+ * The {@link StandardSyntaxParser} knows the rules of precedence, but lucene
+ * does not. e.g. <code>(A AND B OR C AND D)</code> ist treated like
+ * <code>(+A +B +C +D)</code>.
+ * </p>
+ * <p>
+ * This processor walks through the query node tree looking for
+ * {@link BooleanQueryNode}s. If an {@link AndQueryNode} is found, every child,
+ * which is not a {@link ModifierQueryNode} or the {@link ModifierQueryNode} is
+ * {@link Modifier#MOD_NONE}, becomes a {@link Modifier#MOD_REQ}. For default
+ * {@link BooleanQueryNode}, it checks the default operator is
+ * {@link Operator#AND}, if it is, the same operation when an
+ * {@link AndQueryNode} is found is applied to it. Each {@link BooleanQueryNode}
+ * which direct parent is also a {@link BooleanQueryNode} is removed (to ignore
+ * the rules of precidence).
+ * </p>
+ * 
+ * @see ConfigurationKeys#DEFAULT_OPERATOR
+ * @see BooleanModifiersQueryNodeProcessor
+ */
+public class BooleanQuery2ModifierNodeProcessor implements QueryNodeProcessor {
+  final static String TAG_REMOVE = "remove";
+  final static String TAG_MODIFIER = "wrapWithModifier";
+  final static String TAG_BOOLEAN_ROOT = "booleanRoot";
+  
+  QueryConfigHandler queryConfigHandler;
+  
+  private final ArrayList<QueryNode> childrenBuffer = new ArrayList<QueryNode>();
+  
+  private Boolean usingAnd = false;
+  
+  public BooleanQuery2ModifierNodeProcessor() {
+    // empty constructor
+  }
+  
+  @Override
+  public QueryNode process(QueryNode queryTree) throws QueryNodeException {
+    Operator op = getQueryConfigHandler().get(
+        ConfigurationKeys.DEFAULT_OPERATOR);
+    
+    if (op == null) {
+      throw new IllegalArgumentException(
+          "StandardQueryConfigHandler.ConfigurationKeys.DEFAULT_OPERATOR should be set on the QueryConfigHandler");
+    }
+    
+    this.usingAnd = StandardQueryConfigHandler.Operator.AND == op;
+    
+    return processIteration(queryTree);
+    
+  }
+  
+  protected void processChildren(QueryNode queryTree) throws QueryNodeException {
+    List<QueryNode> children = queryTree.getChildren();
+    if (children != null && children.size() > 0) {
+      for (QueryNode child : children) {
+        child = processIteration(child);
+      }
+    }
+  }
+  
+  private QueryNode processIteration(QueryNode queryTree)
+      throws QueryNodeException {
+    queryTree = preProcessNode(queryTree);
+    
+    processChildren(queryTree);
+    
+    queryTree = postProcessNode(queryTree);
+    
+    return queryTree;
+    
+  }
+  
+  protected void fillChildrenBufferAndApplyModifiery(QueryNode parent) {
+    for (QueryNode node : parent.getChildren()) {
+      if (node.containsTag(TAG_REMOVE)) {
+        fillChildrenBufferAndApplyModifiery(node);
+      } else if (node.containsTag(TAG_MODIFIER)) {
+        childrenBuffer.add(applyModifier(node,
+            (Modifier) node.getTag(TAG_MODIFIER)));
+      } else {
+        childrenBuffer.add(node);
+      }
+    }
+  }
+  
+  protected QueryNode postProcessNode(QueryNode node) throws QueryNodeException {
+    if (node.containsTag(TAG_BOOLEAN_ROOT)) {
+      this.childrenBuffer.clear();
+      fillChildrenBufferAndApplyModifiery(node);
+      node.set(childrenBuffer);
+    }
+    return node;
+    
+  }
+  
+  protected QueryNode preProcessNode(QueryNode node) throws QueryNodeException {
+    QueryNode parent = node.getParent();
+    if (node instanceof BooleanQueryNode) {
+      if (parent instanceof BooleanQueryNode) {
+        node.setTag(TAG_REMOVE, Boolean.TRUE); // no precedence
+      } else {
+        node.setTag(TAG_BOOLEAN_ROOT, Boolean.TRUE);
+      }
+    } else if (parent instanceof BooleanQueryNode) {
+      if ((parent instanceof AndQueryNode)
+          || (usingAnd && isDefaultBooleanQueryNode(parent))) {
+        tagModifierButDoNotOverride(node, ModifierQueryNode.Modifier.MOD_REQ);
+      }
+    }
+    return node;
+  }
+  
+  protected boolean isDefaultBooleanQueryNode(QueryNode toTest) {
+    return toTest != null && BooleanQueryNode.class.equals(toTest.getClass());
+  }
+  
+  private QueryNode applyModifier(QueryNode node, Modifier mod) {
+    
+    // check if modifier is not already defined and is default
+    if (!(node instanceof ModifierQueryNode)) {
+      return new BooleanModifierNode(node, mod);
+      
+    } else {
+      ModifierQueryNode modNode = (ModifierQueryNode) node;
+      
+      if (modNode.getModifier() == Modifier.MOD_NONE) {
+        return new ModifierQueryNode(modNode.getChild(), mod);
+      }
+      
+    }
+    
+    return node;
+    
+  }
+  
+  protected void tagModifierButDoNotOverride(QueryNode node, Modifier mod) {
+    if (node instanceof ModifierQueryNode) {
+      ModifierQueryNode modNode = (ModifierQueryNode) node;
+      if (modNode.getModifier() == Modifier.MOD_NONE) {
+        node.setTag(TAG_MODIFIER, mod);
+      }
+    } else {
+      node.setTag(TAG_MODIFIER, ModifierQueryNode.Modifier.MOD_REQ);
+    }
+  }
+  
+  @Override
+  public void setQueryConfigHandler(QueryConfigHandler queryConfigHandler) {
+    this.queryConfigHandler = queryConfigHandler;
+    
+  }
+  
+  @Override
+  public QueryConfigHandler getQueryConfigHandler() {
+    return queryConfigHandler;
+  }
+  
+}
+

Property changes on: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/BooleanQuery2ModifierNodeProcessor.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/ParametricRangeQueryNodeProcessor.java
===================================================================
--- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/ParametricRangeQueryNodeProcessor.java	(revision 1363615)
+++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/processors/ParametricRangeQueryNodeProcessor.java	(working copy)
@@ -35,6 +35,7 @@
 import org.apache.lucene.queryParser.core.nodes.QueryNode;
 import org.apache.lucene.queryParser.core.nodes.ParametricQueryNode.CompareOperator;
 import org.apache.lucene.queryParser.core.processors.QueryNodeProcessorImpl;
+import org.apache.lucene.queryParser.core.util.UnescapedCharSequence;
 import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.ConfigurationKeys;
 import org.apache.lucene.queryParser.standard.nodes.RangeQueryNode;
 
@@ -60,6 +61,7 @@
  * @see ParametricRangeQueryNode
  */
 public class ParametricRangeQueryNodeProcessor extends QueryNodeProcessorImpl {
+  final public static String OPEN_RANGE_TOKEN = "*";
   
   public ParametricRangeQueryNodeProcessor() {
   // empty constructor
@@ -103,6 +105,8 @@
         inclusive = true;
       }
 
+      boolean dateRangeQuery = false;
+      
       String part1 = lower.getTextAsString();
       String part2 = upper.getTextAsString();
 
@@ -130,17 +134,36 @@
           // pre-1.9 Lucene versions.
           part1 = DateField.dateToString(d1);
           part2 = DateField.dateToString(d2);
-
+	  dateRangeQuery = true;
         } else {
           part1 = DateTools.dateToString(d1, dateRes);
           part2 = DateTools.dateToString(d2, dateRes);
+	  dateRangeQuery = true;
         }
       } catch (Exception e) {
         // do nothing
       }
+      if(dateRangeQuery){ 
+        lower.setText(part1);
+        upper.setText(part2);
+      } else {  //LUCENE-3338: in Version 4.X there is a Class OpenRangeQueryNodeProcessor for this purpose
+        CharSequence lowerText = lower.getText();
+        CharSequence upperText = upper.getText();
+        if (OPEN_RANGE_TOKEN.equals(upper.getTextAsString())
+            && (!(upperText instanceof UnescapedCharSequence) || !((UnescapedCharSequence) upperText)
+                .wasEscaped(0))) {
+          upperText = null;
+        }
+        
+        if (OPEN_RANGE_TOKEN.equals(lower.getTextAsString())
+            && (!(lowerText instanceof UnescapedCharSequence) || !((UnescapedCharSequence) lowerText)
+                .wasEscaped(0))) {
+          lowerText = null;
+        }
+        lower.setText(lowerText);
+        upper.setText(upperText);
+      }
 
-      lower.setText(part1);
-      upper.setText(part2);
 
       return new RangeQueryNode(lower, upper, collator);
 
Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/BooleanModifierNode.java
===================================================================
--- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/BooleanModifierNode.java	(revision 1363615)
+++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/standard/nodes/BooleanModifierNode.java	(working copy)
@@ -19,12 +19,14 @@
 
 import org.apache.lucene.queryParser.core.nodes.ModifierQueryNode;
 import org.apache.lucene.queryParser.core.nodes.QueryNode;
+import org.apache.lucene.queryParser.standard.processors.BooleanQuery2ModifierNodeProcessor;
 import org.apache.lucene.queryParser.standard.processors.GroupQueryNodeProcessor;
 
 /**
  * A {@link BooleanModifierNode} has the same behaviour as
  * {@link ModifierQueryNode}, it only indicates that this modifier was added by
- * {@link GroupQueryNodeProcessor} and not by the user. <br/>
+ * {@link GroupQueryNodeProcessor}  or {@link BooleanQuery2ModifierNodeProcessor}
+ * and not by the user. <br/>
  * 
  * @see ModifierQueryNode
  */
Index: lucene/test-framework/src/java/org/apache/lucene/queryParser/QueryParserTestBase.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/queryParser/QueryParserTestBase.java	(revision 1363615)
+++ lucene/test-framework/src/java/org/apache/lucene/queryParser/QueryParserTestBase.java	(working copy)
@@ -1144,4 +1144,16 @@
       // expected
     }
   }
+  
+  public void testFoo() throws Exception {
+    QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "key", new MockAnalyzer(random));
+    String query = "(field:[1 TO *] AND field:[* TO 2]) AND field2:(z)";
+    BooleanQuery q = new BooleanQuery();
+    BooleanQuery bq = new BooleanQuery();
+    bq.add(new TermRangeQuery("field", "1", null, true, true), BooleanClause.Occur.MUST);
+    bq.add(new TermRangeQuery("field", null, "2", true, true), BooleanClause.Occur.MUST);
+    q.add(bq, BooleanClause.Occur.MUST);
+    q.add(new TermQuery(new Term("field2", "z")), BooleanClause.Occur.MUST);
+    assertEquals(q, qp.parse(query));
+  }
 }
