Index: solr/src/java/org/apache/solr/schema/LatLonType.java
===================================================================
--- solr/src/java/org/apache/solr/schema/LatLonType.java	(revision 1082898)
+++ solr/src/java/org/apache/solr/schema/LatLonType.java	(working copy)
@@ -326,7 +326,7 @@
 
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     return this;
   }
 
Index: solr/src/java/org/apache/solr/search/function/FunctionQuery.java
===================================================================
--- solr/src/java/org/apache/solr/search/function/FunctionQuery.java	(revision 1082898)
+++ solr/src/java/org/apache/solr/search/function/FunctionQuery.java	(working copy)
@@ -52,7 +52,7 @@
   }
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     return this;
   }
 
Index: solr/src/java/org/apache/solr/search/function/BoostedQuery.java
===================================================================
--- solr/src/java/org/apache/solr/search/function/BoostedQuery.java	(revision 1082898)
+++ solr/src/java/org/apache/solr/search/function/BoostedQuery.java	(working copy)
@@ -42,8 +42,8 @@
   public ValueSource getValueSource() { return boostVal; }
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
-    Query newQ = q.rewrite(reader);
+    public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
+    Query newQ = rewriter.rewrite(reader, q);
     if (newQ == q) return this;
     BoostedQuery bq = (BoostedQuery)this.clone();
     bq.q = newQ;
Index: solr/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
===================================================================
--- solr/src/java/org/apache/solr/search/SolrConstantScoreQuery.java	(revision 1082898)
+++ solr/src/java/org/apache/solr/search/SolrConstantScoreQuery.java	(working copy)
@@ -47,7 +47,7 @@
   }
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+    public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     return this;
   }
 
Index: solr/src/java/org/apache/solr/handler/component/HighlightComponent.java
===================================================================
--- solr/src/java/org/apache/solr/handler/component/HighlightComponent.java	(revision 1082898)
+++ solr/src/java/org/apache/solr/handler/component/HighlightComponent.java	(working copy)
@@ -18,6 +18,7 @@
 package org.apache.solr.handler.component;
 
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.HighlightParams;
@@ -113,7 +114,7 @@
       
       if(highlightQuery != null) {
         boolean rewrite = !(Boolean.valueOf(req.getParams().get(HighlightParams.USE_PHRASE_HIGHLIGHTER, "true")) && Boolean.valueOf(req.getParams().get(HighlightParams.HIGHLIGHT_MULTI_TERM, "true")));
-        highlightQuery = rewrite ?  highlightQuery.rewrite(req.getSearcher().getIndexReader()) : highlightQuery;
+        highlightQuery = rewrite ?  highlightQuery.rewrite(req.getSearcher().getIndexReader(), new QueryRewriter()) : highlightQuery;
       }
       
       // No highlighting if there is no query -- consider q.alt="*:*
Index: lucene/src/test/org/apache/lucene/search/TestPhraseQuery.java
===================================================================
--- lucene/src/test/org/apache/lucene/search/TestPhraseQuery.java	(revision 1082898)
+++ lucene/src/test/org/apache/lucene/search/TestPhraseQuery.java	(working copy)
@@ -590,7 +590,7 @@
   public void testRewrite() throws IOException {
     PhraseQuery pq = new PhraseQuery();
     pq.add(new Term("foo", "bar"));
-    Query rewritten = pq.rewrite(searcher.getIndexReader());
+    Query rewritten = pq.rewrite(searcher.getIndexReader(), new QueryRewriter());
     assertTrue(rewritten instanceof TermQuery);
   }
 
Index: lucene/src/test/org/apache/lucene/search/spans/TestFieldMaskingSpanQuery.java
===================================================================
--- lucene/src/test/org/apache/lucene/search/spans/TestFieldMaskingSpanQuery.java	(revision 1082898)
+++ lucene/src/test/org/apache/lucene/search/spans/TestFieldMaskingSpanQuery.java	(working copy)
@@ -30,6 +30,7 @@
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.QueryUtils;
+import org.apache.lucene.search.QueryRewriter;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.LuceneTestCase;
 
@@ -145,7 +146,7 @@
     SpanQuery q = new FieldMaskingSpanQuery
       (new SpanTermQuery(new Term("last", "sally")) {
           @Override
-          public Query rewrite(IndexReader reader) {
+          public Query rewrite(IndexReader reader, QueryRewriter rewriter) {
             return new SpanOrQuery(new SpanTermQuery(new Term("first", "sally")),
                 new SpanTermQuery(new Term("first", "james")));
           }
Index: lucene/src/test/org/apache/lucene/search/function/TestCustomScoreQuery.java
===================================================================
--- lucene/src/test/org/apache/lucene/search/function/TestCustomScoreQuery.java	(revision 1082898)
+++ lucene/src/test/org/apache/lucene/search/function/TestCustomScoreQuery.java	(working copy)
@@ -210,14 +210,14 @@
 
     Query q = new TermQuery(new Term(TEXT_FIELD, "first"));
     CustomScoreQuery original = new CustomScoreQuery(q);
-    CustomScoreQuery rewritten = (CustomScoreQuery) original.rewrite(s.getIndexReader());
+    CustomScoreQuery rewritten = (CustomScoreQuery) original.rewrite(s.getIndexReader(), new QueryRewriter());
     assertTrue("rewritten query should be identical, as TermQuery does not rewrite", original == rewritten);
     assertTrue("no hits for query", s.search(rewritten,1).totalHits > 0);
     assertEquals(s.search(q,1).totalHits, s.search(rewritten,1).totalHits);
 
     q = new TermRangeQuery(TEXT_FIELD, null, null, true, true); // everything
     original = new CustomScoreQuery(q);
-    rewritten = (CustomScoreQuery) original.rewrite(s.getIndexReader());
+    rewritten = (CustomScoreQuery) original.rewrite(s.getIndexReader(), new QueryRewriter());
     assertTrue("rewritten query should not be identical, as TermRangeQuery rewrites", original != rewritten);
     assertTrue("no hits for query", s.search(rewritten,1).totalHits > 0);
     assertEquals(s.search(q,1).totalHits, s.search(original,1).totalHits);
Index: lucene/src/java/org/apache/lucene/search/MultiTermQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/MultiTermQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/MultiTermQuery.java	(working copy)
@@ -302,7 +302,7 @@
   }
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     return rewriteMethod.rewrite(reader, this);
   }
 
Index: lucene/src/java/org/apache/lucene/search/ConstantScoreQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/ConstantScoreQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/ConstantScoreQuery.java	(working copy)
@@ -73,9 +73,9 @@
   }
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     if (query != null) {
-      Query rewritten = query.rewrite(reader);
+      Query rewritten = rewriter.rewrite(reader, query);
       if (rewritten != query) {
         rewritten = new ConstantScoreQuery(rewritten);
         rewritten.setBoost(this.getBoost());
Index: lucene/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java	(working copy)
@@ -185,11 +185,11 @@
    * @param reader the IndexReader we query
    * @return an optimized copy of us (which may not be a copy if there is nothing to optimize) */
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     int numDisjunctions = disjuncts.size();
     if (numDisjunctions == 1) {
       Query singleton = disjuncts.get(0);
-      Query result = singleton.rewrite(reader);
+      Query result = rewriter.rewrite(reader, singleton);
       if (getBoost() != 1.0f) {
         if (result == singleton) result = (Query)result.clone();
         result.setBoost(getBoost() * result.getBoost());
@@ -199,7 +199,7 @@
     DisjunctionMaxQuery clone = null;
     for (int i = 0 ; i < numDisjunctions; i++) {
       Query clause = disjuncts.get(i);
-      Query rewrite = clause.rewrite(reader);
+      Query rewrite = rewriter.rewrite(reader, clause);
       if (rewrite != clause) {
         if (clone == null) clone = (DisjunctionMaxQuery)this.clone();
         clone.disjuncts.set(i, rewrite);
Index: lucene/src/java/org/apache/lucene/search/spans/FieldMaskingSpanQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/spans/FieldMaskingSpanQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/spans/FieldMaskingSpanQuery.java	(working copy)
@@ -24,6 +24,7 @@
 import org.apache.lucene.index.IndexReader.AtomicReaderContext;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.util.ToStringUtils;
@@ -107,10 +108,10 @@
   }
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     FieldMaskingSpanQuery clone = null;
 
-    SpanQuery rewritten = (SpanQuery) maskedQuery.rewrite(reader);
+    SpanQuery rewritten = (SpanQuery) rewriter.rewrite(reader, maskedQuery);
     if (rewritten != maskedQuery) {
       clone = (FieldMaskingSpanQuery) this.clone();
       clone.maskedQuery = rewritten;
Index: lucene/src/java/org/apache/lucene/search/spans/SpanNotQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/spans/SpanNotQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/spans/SpanNotQuery.java	(working copy)
@@ -21,6 +21,7 @@
 import org.apache.lucene.index.IndexReader.AtomicReaderContext;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 import org.apache.lucene.util.ToStringUtils;
 
 import java.io.IOException;
@@ -167,15 +168,15 @@
   }
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     SpanNotQuery clone = null;
 
-    SpanQuery rewrittenInclude = (SpanQuery) include.rewrite(reader);
+    SpanQuery rewrittenInclude = (SpanQuery) rewriter.rewrite(reader, include);
     if (rewrittenInclude != include) {
       clone = (SpanNotQuery) this.clone();
       clone.include = rewrittenInclude;
     }
-    SpanQuery rewrittenExclude = (SpanQuery) exclude.rewrite(reader);
+    SpanQuery rewrittenExclude = (SpanQuery) rewriter.rewrite(reader, exclude);
     if (rewrittenExclude != exclude) {
       if (clone == null) clone = (SpanNotQuery) this.clone();
       clone.exclude = rewrittenExclude;
Index: lucene/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java	(working copy)
@@ -21,6 +21,7 @@
 import org.apache.lucene.index.IndexReader.AtomicReaderContext;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -87,10 +88,10 @@
 
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     SpanPositionCheckQuery clone = null;
 
-    SpanQuery rewritten = (SpanQuery) match.rewrite(reader);
+    SpanQuery rewritten = (SpanQuery) rewriter.rewrite(reader, match);
     if (rewritten != match) {
       clone = (SpanPositionCheckQuery) this.clone();
       clone.match = rewritten;
Index: lucene/src/java/org/apache/lucene/search/spans/SpanOrQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/spans/SpanOrQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/spans/SpanOrQuery.java	(working copy)
@@ -31,6 +31,7 @@
 import org.apache.lucene.util.PriorityQueue;
 import org.apache.lucene.util.ToStringUtils;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 
 /** Matches the union of its clauses.*/
 public class SpanOrQuery extends SpanQuery implements Cloneable {
@@ -86,11 +87,11 @@
   }
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     SpanOrQuery clone = null;
     for (int i = 0 ; i < clauses.size(); i++) {
       SpanQuery c = clauses.get(i);
-      SpanQuery query = (SpanQuery) c.rewrite(reader);
+      SpanQuery query = (SpanQuery) rewriter.rewrite(reader, c);
       if (query != c) {                     // clause rewrote: must clone
         if (clone == null)
           clone = (SpanOrQuery) this.clone();
Index: lucene/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java	(working copy)
@@ -24,6 +24,7 @@
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.MultiTermQuery;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 import org.apache.lucene.search.TopTermsRewrite;
 import org.apache.lucene.search.ScoringRewrite;
 import org.apache.lucene.search.BooleanClause.Occur; // javadocs only
@@ -108,8 +109,8 @@
   }
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
-    final Query q = query.rewrite(reader);
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
+    final Query q = rewriter.rewrite(reader, query);
     if (!(q instanceof SpanQuery))
       throw new UnsupportedOperationException("You can only use SpanMultiTermQueryWrapper with a suitable SpanRewriteMethod.");
     return q;
Index: lucene/src/java/org/apache/lucene/search/spans/SpanNearQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/spans/SpanNearQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/spans/SpanNearQuery.java	(working copy)
@@ -30,6 +30,7 @@
 import org.apache.lucene.index.IndexReader.AtomicReaderContext;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 import org.apache.lucene.util.ToStringUtils;
 
 /** Matches spans which are near one another.  One can specify <i>slop</i>, the
@@ -130,11 +131,11 @@
   }
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     SpanNearQuery clone = null;
     for (int i = 0 ; i < clauses.size(); i++) {
       SpanQuery c = clauses.get(i);
-      SpanQuery query = (SpanQuery) c.rewrite(reader);
+      SpanQuery query = (SpanQuery) rewriter.rewrite(reader, c);
       if (query != c) {                     // clause rewrote: must clone
         if (clone == null)
           clone = (SpanNearQuery) this.clone();
Index: lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java	(working copy)
@@ -314,7 +314,7 @@
   }
 
   @Override
-  public Query rewrite(IndexReader reader) {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) {
     if (termArrays.size() == 1) {                 // optimize one-term case
       Term[] terms = termArrays.get(0);
       BooleanQuery boq = new BooleanQuery(true);
Index: lucene/src/java/org/apache/lucene/search/FilteredQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/FilteredQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/FilteredQuery.java	(working copy)
@@ -169,8 +169,8 @@
 
   /** Rewrites the wrapped query. */
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
-    Query rewritten = query.rewrite(reader);
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
+    Query rewritten = rewriter.rewrite(reader, query);
     if (rewritten != query) {
       FilteredQuery clone = (FilteredQuery)this.clone();
       clone.query = rewritten;
Index: lucene/src/java/org/apache/lucene/search/function/ValueSourceQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/function/ValueSourceQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/function/ValueSourceQuery.java	(working copy)
@@ -53,7 +53,7 @@
 
   /*(non-Javadoc) @see org.apache.lucene.search.Query#rewrite(org.apache.lucene.index.IndexReader) */
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     return this;
   }
 
Index: lucene/src/java/org/apache/lucene/search/function/CustomScoreQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/function/CustomScoreQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/function/CustomScoreQuery.java	(working copy)
@@ -27,6 +27,7 @@
 import org.apache.lucene.search.ComplexExplanation;
 import org.apache.lucene.search.Explanation;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.IndexSearcher;
@@ -88,17 +89,17 @@
 
   /*(non-Javadoc) @see org.apache.lucene.search.Query#rewrite(org.apache.lucene.index.IndexReader) */
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     CustomScoreQuery clone = null;
     
-    final Query sq = subQuery.rewrite(reader);
+    final Query sq = rewriter.rewrite(reader, subQuery);
     if (sq != subQuery) {
       clone = (CustomScoreQuery) clone();
       clone.subQuery = sq;
     }
 
     for(int i = 0; i < valSrcQueries.length; i++) {
-      final ValueSourceQuery v = (ValueSourceQuery) valSrcQueries[i].rewrite(reader);
+      final ValueSourceQuery v = (ValueSourceQuery) rewriter.rewrite(reader, valSrcQueries[i]);
       if (v != valSrcQueries[i]) {
         if (clone == null) clone = (CustomScoreQuery) clone();
         clone.valSrcQueries[i] = v;
Index: lucene/src/java/org/apache/lucene/search/BooleanQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/BooleanQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/BooleanQuery.java	(working copy)
@@ -359,12 +359,12 @@
   }
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     if (minNrShouldMatch == 0 && clauses.size() == 1) {                    // optimize 1-clause queries
       BooleanClause c = clauses.get(0);
       if (!c.isProhibited()) {			  // just return clause
 
-        Query query = c.getQuery().rewrite(reader);    // rewrite first
+        Query query = rewriter.rewrite(reader, c.getQuery());    // rewrite first
 
         if (getBoost() != 1.0f) {                 // incorporate boost
           if (query == c.getQuery())                   // if rewrite was no-op
@@ -379,7 +379,7 @@
     BooleanQuery clone = null;                    // recursively rewrite
     for (int i = 0 ; i < clauses.size(); i++) {
       BooleanClause c = clauses.get(i);
-      Query query = c.getQuery().rewrite(reader);
+      Query query =  rewriter.rewrite(reader, c.getQuery());
       if (query != c.getQuery()) {                     // clause rewrote: must clone
         if (clone == null)
           clone = (BooleanQuery)this.clone();
Index: lucene/src/java/org/apache/lucene/search/Query.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/Query.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/Query.java	(working copy)
@@ -109,8 +109,9 @@
   /** Expert: called to re-write queries into primitive queries. For example,
    * a PrefixQuery will be rewritten into a BooleanQuery that consists
    * of TermQuerys.
+   * @param rewriter TODO
    */
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     return this;
   }
   
Index: lucene/src/java/org/apache/lucene/search/PhraseQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/PhraseQuery.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/PhraseQuery.java	(working copy)
@@ -111,13 +111,13 @@
   }
 
   @Override
-  public Query rewrite(IndexReader reader) throws IOException {
+  public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
     if (terms.size() == 1) {
       TermQuery tq = new TermQuery(terms.get(0));
       tq.setBoost(getBoost());
       return tq;
     } else
-      return super.rewrite(reader);
+      return super.rewrite(reader, rewriter);
   }
 
   static class PostingsAndFreq implements Comparable<PostingsAndFreq> {
Index: lucene/src/java/org/apache/lucene/search/IndexSearcher.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/IndexSearcher.java	(revision 1082898)
+++ lucene/src/java/org/apache/lucene/search/IndexSearcher.java	(working copy)
@@ -596,13 +596,13 @@
    * @throws BooleanQuery.TooManyClauses
    */
   public Query rewrite(Query original) throws IOException {
-    Query query = original;
-    for (Query rewrittenQuery = query.rewrite(reader); rewrittenQuery != query;
-         rewrittenQuery = query.rewrite(reader)) {
-      query = rewrittenQuery;
-    }
-    return query;
+    QueryRewriter rewriter = new QueryRewriter(); // nocommit make this pluggable - this should be passed to Query#weight(IS, QueryRewriter) for each query
+    return rewrite(original, rewriter);
   }
+  
+  public Query rewrite(Query original, QueryRewriter rewriter) throws IOException {
+    return rewriter.rewrite(reader, original);
+  }
 
   /** Returns an Explanation that describes how <code>doc</code> scored against
    * <code>query</code>.
Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/DistanceQuery.java
===================================================================
--- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/DistanceQuery.java	(revision 1082898)
+++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/DistanceQuery.java	(working copy)
@@ -24,6 +24,7 @@
 
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 import org.apache.lucene.search.spans.SpanNearQuery;
 import org.apache.lucene.search.spans.SpanQuery;
 
@@ -81,7 +82,7 @@
       }
       
       @Override
-      public Query rewrite(IndexReader reader) throws IOException {
+      public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
         return getSpanNearQuery(reader, fieldName, getBoost(), qf);
       }
       
Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/SimpleTerm.java
===================================================================
--- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/SimpleTerm.java	(revision 1082898)
+++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/surround/query/SimpleTerm.java	(working copy)
@@ -24,6 +24,7 @@
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 
 public abstract class SimpleTerm
   extends SrndQuery
@@ -82,7 +83,7 @@
       }
       
       @Override
-      public Query rewrite(IndexReader reader) throws IOException {
+      public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
         final List<Query> luceneSubQueries = new ArrayList<Query>();
         visitMatchingTerms( reader, fieldName,
             new MatchingTermVisitor() {
Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/complexPhrase/ComplexPhraseQueryParser.java
===================================================================
--- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/complexPhrase/ComplexPhraseQueryParser.java	(revision 1082898)
+++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/complexPhrase/ComplexPhraseQueryParser.java	(working copy)
@@ -31,6 +31,7 @@
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.MultiTermQuery;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.search.TermRangeQuery;
 import org.apache.lucene.search.spans.SpanNearQuery;
@@ -225,7 +226,7 @@
     }
 
     @Override
-    public Query rewrite(IndexReader reader) throws IOException {
+    public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
       // ArrayList spanClauses = new ArrayList();
       if (contents instanceof TermQuery) {
         return contents;
@@ -248,7 +249,7 @@
         // HashSet bclauseterms=new HashSet();
         Query qc = bclauses[i].getQuery();
         // Rewrite this clause e.g one* becomes (one OR onerous)
-        qc = qc.rewrite(reader);
+        qc = qc.rewrite(reader, new QueryRewriter());
         if (bclauses[i].getOccur().equals(BooleanClause.Occur.MUST_NOT)) {
           numNegatives++;
         }
Index: lucene/contrib/xml-query-parser/src/test/org/apache/lucene/xmlparser/TestParser.java
===================================================================
--- lucene/contrib/xml-query-parser/src/test/org/apache/lucene/xmlparser/TestParser.java	(revision 1082898)
+++ lucene/contrib/xml-query-parser/src/test/org/apache/lucene/xmlparser/TestParser.java	(working copy)
@@ -15,6 +15,7 @@
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 import org.apache.lucene.search.ScoreDoc;
 import org.apache.lucene.search.TopDocs;
 import org.apache.lucene.store.Directory;
@@ -141,7 +142,7 @@
 			//show rewritten fuzzyLikeThisQuery - see what is being matched on
 			if(VERBOSE)
 			{
-				System.out.println(q.rewrite(reader));
+				System.out.println(q.rewrite(reader, new QueryRewriter()));
 			}
 			dumpResults("FuzzyLikeThis", q, 5);
 	}
Index: lucene/contrib/highlighter/src/test/org/apache/lucene/search/highlight/HighlighterTest.java
===================================================================
--- lucene/contrib/highlighter/src/test/org/apache/lucene/search/highlight/HighlighterTest.java	(revision 1082898)
+++ lucene/contrib/highlighter/src/test/org/apache/lucene/search/highlight/HighlighterTest.java	(working copy)
@@ -62,6 +62,7 @@
 import org.apache.lucene.search.NumericRangeQuery;
 import org.apache.lucene.search.PhraseQuery;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.search.TermRangeFilter;
 import org.apache.lucene.search.TopDocs;
@@ -1614,7 +1615,7 @@
     searcher = new IndexSearcher(ramDir, true);
     // for any multi-term queries to work (prefix, wildcard, range,fuzzy etc)
     // you must use a rewritten query!
-    query = unReWrittenQuery.rewrite(reader);
+    query = unReWrittenQuery.rewrite(reader, new QueryRewriter());
     if (VERBOSE) System.out.println("Searching for: " + query.toString(FIELD_NAME));
     hits = searcher.search(query, null, 1000);
   }
Index: lucene/contrib/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java
===================================================================
--- lucene/contrib/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java	(revision 1082898)
+++ lucene/contrib/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java	(working copy)
@@ -151,7 +151,7 @@
       }
       if (mtq.getField() != null) {
         IndexReader ir = getLeafContextForField(mtq.getField()).reader;
-        extract(query.rewrite(ir), terms);
+        extract(query.rewrite(ir, new QueryRewriter()), terms);
       }
     } else if (query instanceof MultiPhraseQuery) {
       final MultiPhraseQuery mpq = (MultiPhraseQuery) query;
@@ -235,7 +235,7 @@
     final boolean mustRewriteQuery = mustRewriteQuery(spanQuery);
     if (mustRewriteQuery) {
       for (final String field : fieldNames) {
-        final SpanQuery rewrittenQuery = (SpanQuery) spanQuery.rewrite(getLeafContextForField(field).reader);
+	  final SpanQuery rewrittenQuery = (SpanQuery) spanQuery.rewrite(getLeafContextForField(field).reader, new QueryRewriter());
         queries.put(field, rewrittenQuery);
         rewrittenQuery.extractTerms(nonWeightedTerms);
       }
Index: lucene/contrib/queries/src/test/org/apache/lucene/search/FuzzyLikeThisQueryTest.java
===================================================================
--- lucene/contrib/queries/src/test/org/apache/lucene/search/FuzzyLikeThisQueryTest.java	(revision 1082898)
+++ lucene/contrib/queries/src/test/org/apache/lucene/search/FuzzyLikeThisQueryTest.java	(working copy)
@@ -76,7 +76,7 @@
 	{
 		FuzzyLikeThisQuery flt=new FuzzyLikeThisQuery(10,analyzer);
 		flt.addTerms("smith", "name", 0.3f, 1);
-		Query q=flt.rewrite(searcher.getIndexReader());
+		Query q=flt.rewrite(searcher.getIndexReader(), new QueryRewriter());
 		HashSet<Term> queryTerms=new HashSet<Term>();
 		q.extractTerms(queryTerms);
 		assertTrue("Should have variant smythe",queryTerms.contains(new Term("name","smythe")));
@@ -93,7 +93,7 @@
 	{
 		FuzzyLikeThisQuery flt=new FuzzyLikeThisQuery(10,analyzer);
 		flt.addTerms("jonathin smoth", "name", 0.3f, 1);
-		Query q=flt.rewrite(searcher.getIndexReader());
+		Query q=flt.rewrite(searcher.getIndexReader(), new QueryRewriter());
 		HashSet<Term> queryTerms=new HashSet<Term>();
 		q.extractTerms(queryTerms);
 		assertTrue("Should have variant jonathan",queryTerms.contains(new Term("name","jonathan")));
@@ -109,7 +109,7 @@
 	{
 		FuzzyLikeThisQuery flt=new FuzzyLikeThisQuery(10,analyzer);
 		flt.addTerms("fernando smith", "name", 0.3f, 1);
-		Query q=flt.rewrite(searcher.getIndexReader());
+		Query q=flt.rewrite(searcher.getIndexReader(), new QueryRewriter());
 		HashSet<Term> queryTerms=new HashSet<Term>();
 		q.extractTerms(queryTerms);
 		assertTrue("Should have variant smith",queryTerms.contains(new Term("name","smith")));
Index: lucene/contrib/queries/src/java/org/apache/lucene/search/similar/MoreLikeThisQuery.java
===================================================================
--- lucene/contrib/queries/src/java/org/apache/lucene/search/similar/MoreLikeThisQuery.java	(revision 1082898)
+++ lucene/contrib/queries/src/java/org/apache/lucene/search/similar/MoreLikeThisQuery.java	(working copy)
@@ -29,6 +29,7 @@
 import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryRewriter;
 import org.apache.lucene.search.similar.MoreLikeThis;
 
 /**
@@ -61,7 +62,7 @@
     }
     
     @Override
-    public Query rewrite(IndexReader reader) throws IOException
+    public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException
     {
         MoreLikeThis mlt=new MoreLikeThis(reader);
         
Index: lucene/contrib/queries/src/java/org/apache/lucene/search/BoostingQuery.java
===================================================================
--- lucene/contrib/queries/src/java/org/apache/lucene/search/BoostingQuery.java	(revision 1082898)
+++ lucene/contrib/queries/src/java/org/apache/lucene/search/BoostingQuery.java	(working copy)
@@ -53,7 +53,7 @@
     }
 
     @Override
-    public Query rewrite(IndexReader reader) throws IOException {
+    public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException {
       BooleanQuery result = new BooleanQuery() {
         @Override
         public Weight createWeight(IndexSearcher searcher) throws IOException {
Index: lucene/contrib/queries/src/java/org/apache/lucene/search/FuzzyLikeThisQuery.java
===================================================================
--- lucene/contrib/queries/src/java/org/apache/lucene/search/FuzzyLikeThisQuery.java	(revision 1082898)
+++ lucene/contrib/queries/src/java/org/apache/lucene/search/FuzzyLikeThisQuery.java	(working copy)
@@ -250,7 +250,7 @@
     }
             
     @Override
-    public Query rewrite(IndexReader reader) throws IOException
+    public Query rewrite(IndexReader reader, QueryRewriter rewriter) throws IOException
     {
         if(rewrittenQuery!=null)
         {
Index: lucene/src/java/org/apache/lucene/search/QueryRewriter.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/QueryRewriter.java	(revision 0)
+++ lucene/src/java/org/apache/lucene/search/QueryRewriter.java	(revision 0)
@@ -0,0 +1,62 @@
+package org.apache.lucene.search;
+
+/**
+ * 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.io.IOException;
+import java.util.IdentityHashMap;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * 
+ *  A rewriting visitor that walk the Query "AST" and rewrites the query
+ *  @rough! :)
+ */
+public class QueryRewriter {
+
+  public Query rewrite(IndexReader reader, Query query) throws IOException {
+    for (Query rewrittenQuery = query.rewrite(reader, this); rewrittenQuery != query; rewrittenQuery = query
+        .rewrite(reader, this)) {
+      query = rewrittenQuery;
+    }
+    return query;
+  }
+
+  
+  /*
+   * a simple impl that could caches MTQ rewrites since they are costly
+   */
+  public static class MTQCachingRewriter extends QueryRewriter {
+
+    // nocommit maybe clean this map after done - need a QueryRewriter#done() ?
+    private IdentityHashMap<MultiTermQuery, Query> mtqRewriteCache = new IdentityHashMap<MultiTermQuery, Query>();
+
+    public Query rewrite(IndexReader reader, Query query) throws IOException {
+      if (query instanceof MultiTermQuery) {
+        Query newQuery = mtqRewriteCache.get(query); 
+        if (newQuery != null) {
+          return newQuery;
+        }
+        newQuery = super.rewrite(reader, query);
+        mtqRewriteCache.put((MultiTermQuery)query, newQuery);
+        return newQuery;
+      }
+      return super.rewrite(reader, query);
+    }
+
+  }
+}
