Index: src/java/org/apache/lucene/search/DefaultSimilarity.java
===================================================================
--- src/java/org/apache/lucene/search/DefaultSimilarity.java	(revision 882640)
+++ src/java/org/apache/lucene/search/DefaultSimilarity.java	(working copy)
@@ -46,12 +46,6 @@
   public float lengthNorm(String fieldName, int numTerms) {
     return (float)(1.0 / Math.sqrt(numTerms));
   }
-  
-  /** Implemented as <code>1/sqrt(sumOfSquaredWeights)</code>. */
-  @Override
-  public float queryNorm(float sumOfSquaredWeights) {
-    return (float)(1.0 / Math.sqrt(sumOfSquaredWeights));
-  }
 
   /** Implemented as <code>sqrt(freq)</code>. */
   @Override
Index: src/java/org/apache/lucene/search/Query.java
===================================================================
--- src/java/org/apache/lucene/search/Query.java	(revision 882640)
+++ src/java/org/apache/lucene/search/Query.java	(working copy)
@@ -99,8 +99,7 @@
   public Weight weight(Searcher searcher) throws IOException {
     Query query = searcher.rewrite(this);
     Weight weight = query.createWeight(searcher);
-    float sum = weight.sumOfSquaredWeights();
-    float norm = getSimilarity(searcher).queryNorm(sum);
+    float norm = getSimilarity(searcher).queryNorm(weight);
     weight.normalize(norm);
     return weight;
   }
Index: src/java/org/apache/lucene/search/Similarity.java
===================================================================
--- src/java/org/apache/lucene/search/Similarity.java	(revision 882640)
+++ src/java/org/apache/lucene/search/Similarity.java	(working copy)
@@ -527,10 +527,6 @@
  */
 public abstract class Similarity implements Serializable {
   
-  /**
-   * The Similarity implementation used by default.
-   **/
-  private static Similarity defaultImpl = new DefaultSimilarity();
   public static final int NO_DOC_ID_PROVIDED = -1;
 
   /** Set the default Similarity implementation used by indexing and search
@@ -637,8 +633,39 @@
    *
    * @param sumOfSquaredWeights the sum of the squares of query term weights
    * @return a normalization factor for query weights
+   * @deprecated use {@link #queryNorm(Weight)}
+   */
+  public float queryNorm(float sumOfSquaredWeights) {
+    return (float)(1.0 / Math.sqrt(sumOfSquaredWeights));
+  }
+  
+
+  /** Computes the normalization value for a query given the Weight.  This 
+   * value is multiplied into the weight of each query term. While the classic 
+   * query normalization factor is computed as 1/sqrt(sumOfSquaredWeights), 
+   * other implementations might completely ignore sumOfSquaredWeights (ie return 1).
+   *
+   * <p>This does not affect ranking, but the default implementation does make scores
+   * from different queries more comparable than they would be by eliminating the
+   * magnitude of the Query vector as a factor in the score.
+   *
+   * @param weight the Query Weight
+   * @return a normalization factor for query weights
+   * @throws IOException 
+   * @throws IOException 
    */
-  public abstract float queryNorm(float sumOfSquaredWeights);
+  public float queryNorm(Weight weight) throws IOException {
+    if (supportedMethods.overridesQueryNorm) {
+      // if impl overrides old method, call it instead
+      float sum = weight.sumOfSquaredWeights();
+      return queryNorm(sum);
+    } else {
+      // Implemented as <code>1/sqrt(sumOfSquaredWeights)</code>.
+      float sum = weight.sumOfSquaredWeights();
+      return (float) (1.0 / Math.sqrt(sum));
+    }
+  }
+  
 
   /** Encodes a normalization factor for storage in an index.
    *
@@ -836,5 +863,51 @@
   {
     return 1;
   }
+  
+  
+  /** @deprecated Remove this when old API is removed! */
+  private final MethodSupport supportedMethods = getSupportedMethods(this.getClass());
+  
+    /** @deprecated Remove this when old API is removed! */
+  private static final class MethodSupport implements Serializable {
+    final boolean overridesQueryNorm;
+
+    MethodSupport(Class<? extends Similarity> clazz) {
+      overridesQueryNorm = isMethodOverridden(clazz, "queryNorm", QN_METHOD_PARAMS);
+    }
+    
+    private static boolean isMethodOverridden(Class<?> clazz, String name, Class... params) {
+      try {
+        return clazz.getMethod(name, params).getDeclaringClass() != Similarity.class;
+      } catch (NoSuchMethodException e) {
+        // should not happen
+        throw new RuntimeException(e);
+      }
+    }
+    
+    /** @deprecated Remove this when old API is removed! */
+    private static final Class[] QN_METHOD_PARAMS = new Class[]{float.class};
+  }
+  
+  /** @deprecated Remove this when old API is removed! */
+  private static final IdentityHashMap<Class<? extends Similarity>,MethodSupport> knownMethodSupport
+    = new IdentityHashMap<Class<? extends Similarity>,MethodSupport>();
+  
+  /** @deprecated Remove this when old API is removed! */
+  private static MethodSupport getSupportedMethods(Class<? extends Similarity> clazz) {
+    MethodSupport supportedMethods;
+    synchronized(knownMethodSupport) {
+      supportedMethods = (MethodSupport) knownMethodSupport.get(clazz);
+      if (supportedMethods == null) {
+        knownMethodSupport.put(clazz, supportedMethods = new MethodSupport(clazz));
+      }
+    }
+    return supportedMethods;
+  }
+  
+  /** The Similarity implementation used by default. 
+   *  TODO: move back to top when old API is removed! 
+   **/
+  private static Similarity defaultImpl = new DefaultSimilarity();
 
 }
Index: src/java/org/apache/lucene/search/SimilarityDelegator.java
===================================================================
--- src/java/org/apache/lucene/search/SimilarityDelegator.java	(revision 882640)
+++ src/java/org/apache/lucene/search/SimilarityDelegator.java	(working copy)
@@ -1,5 +1,7 @@
 package org.apache.lucene.search;
 
+import java.io.IOException;
+
 import org.apache.lucene.index.FieldInvertState;
 
 /**
@@ -44,10 +46,18 @@
     return delegee.lengthNorm(fieldName, numTerms);
   }
   
+  /**
+  * @deprecated use {@link #queryNorm(Weight)}
+  */
   @Override
   public float queryNorm(float sumOfSquaredWeights) {
     return delegee.queryNorm(sumOfSquaredWeights);
   }
+ 
+  @Override
+  public float queryNorm(Weight weight) throws IOException {
+    return delegee.queryNorm(weight);
+  }
 
   @Override
   public float tf(float freq) {
Index: src/test/org/apache/lucene/search/JustCompileSearch.java
===================================================================
--- src/test/org/apache/lucene/search/JustCompileSearch.java	(revision 882640)
+++ src/test/org/apache/lucene/search/JustCompileSearch.java	(working copy)
@@ -390,7 +390,7 @@
     }
 
     @Override
-    public float queryNorm(float sumOfSquaredWeights) {
+    public float queryNorm(Weight weight) {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
 

