Index: src/test/org/apache/lucene/search/function/TestQueryWrapperValueSource.java
===================================================================
--- src/test/org/apache/lucene/search/function/TestQueryWrapperValueSource.java	(revision 0)
+++ src/test/org/apache/lucene/search/function/TestQueryWrapperValueSource.java	(revision 0)
@@ -0,0 +1,116 @@
+package org.apache.lucene.search.function;
+
+/**
+ * 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.HashMap;
+import java.util.Iterator;
+
+import org.apache.lucene.index.CorruptIndexException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.TopDocs;
+
+/**
+ * Test QueryWrapperValueSource.
+ */
+public class TestQueryWrapperValueSource extends FunctionTestSetup {
+
+  /* @override constructor */
+  public TestQueryWrapperValueSource(String name) {
+    super(name);
+  }
+
+  /* @override */
+  protected void tearDown() throws Exception {
+    super.tearDown();
+  }
+
+  /* @override */
+  protected void setUp() throws Exception {
+    // prepare a small index with just a few documents.  
+    super.setUp();
+  }
+
+  public void testQueryWrapperValueSource () throws CorruptIndexException, Exception {
+    IndexSearcher s = new IndexSearcher(dir);
+    QueryParser qp = new QueryParser(TEXT_FIELD,anlzr); 
+    String qtxt1 = "first aid text"; // from the doc texts in FunctionQuerySetup.
+    String qtxt2 = "longer eBook"; // from the doc texts in FunctionQuerySetup.
+    
+    // regular (boolean) queries.
+    Query q1 = qp.parse(qtxt1); 
+    log(q1);
+    Query q2 = qp.parse(qtxt2); 
+    log(q2);
+    
+    // custom query, that should accept same docs as q1 but score consider q2 in the scoring.
+    float defaultScore = (float) 0.7;
+    ValueSourceQuery vsq = new ValueSourceQuery(new QueryWrapperValueSource(q2,defaultScore ));
+    CustomScoreQuery qCustom = new CustomScoreQuery(q1,vsq);
+    qCustom.setStrict(true);
+    log(qCustom);
+    
+    // do the searches 
+    TopDocs td1 = s.search(q1,null,1000);
+    TopDocs td2 = s.search(q2,null,1000);
+    TopDocs tdCustom = s.search(qCustom,null,1000);
+    
+    // put results in map so we can verify the scores although they have changed
+    HashMap h1 = topDocsToMap(td1);
+    HashMap h2 = topDocsToMap(td2);
+    HashMap hCustom = topDocsToMap(tdCustom);
+    
+    ScoreDoc[] sd1 = td1.scoreDocs;
+    ScoreDoc[] sdCustom = tdCustom.scoreDocs;
+
+    verifyResults(s, defaultScore,
+        h1, h2, hCustom,
+        q1, q2, qCustom);
+  }
+  
+  // verify results are as expected.
+  private void verifyResults(IndexSearcher s, float defaultScore,
+      HashMap h1, HashMap h2, HashMap hCustom,
+      Query q1, Query q2, Query qCustom) throws Exception {
+    
+    // verify numbers of matches
+    log("#hits = "+h1.size());
+    assertEquals(h1.size(),hCustom.size());
+    
+    // verify scores ratios
+    for (Iterator it = h1.keySet().iterator(); it.hasNext();) {
+      Integer x = (Integer) it.next();
+
+      int doc =  x.intValue();
+      log("doc = "+doc);
+
+      float score1 = ((Float)h1.get(x)).floatValue();
+      logResult("score1=", s, q1, doc, score1);
+      
+      float score2 = h2.containsKey(x) ? ((Float)h2.get(x)).floatValue() : defaultScore; // q2 might not find some of the docs
+      logResult("score2=", s, q2, doc, score2);
+      
+      float scoreCustom = ((Float)hCustom.get(x)).floatValue();
+      logResult("scoreCustom=", s, qCustom, doc, scoreCustom);
+      assertEquals("new score for custom mul", score1 * score2, scoreCustom, TEST_SCORE_TOLERANCE_DELTA);
+    }
+  }
+
+}
Index: src/test/org/apache/lucene/search/function/FunctionTestSetup.java
===================================================================
--- src/test/org/apache/lucene/search/function/FunctionTestSetup.java	(revision 767282)
+++ src/test/org/apache/lucene/search/function/FunctionTestSetup.java	(working copy)
@@ -17,12 +17,19 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+import java.util.HashMap;
+
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.standard.StandardAnalyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.Fieldable;
 import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryUtils;
+import org.apache.lucene.search.TopDocs;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.RAMDirectory;
 
@@ -155,4 +162,24 @@
       System.out.println(o.toString());
     }
   }
+  
+  // since custom scoring modifies the order of docs, map results 
+  // by doc ids so that we can later compare/verify them 
+  protected HashMap topDocsToMap(TopDocs td) {
+    HashMap h = new HashMap(); 
+    for (int i=0; i<td.totalHits; i++) {
+      h.put(new Integer(td.scoreDocs[i].doc), new Float(td.scoreDocs[i].score));
+    }
+    return h;
+  }
+
+  // internal test util: print a result
+  protected void logResult(String msg, IndexSearcher s, Query q, int doc, float score) throws IOException {
+    QueryUtils.check(q,s);
+    log(msg+" "+score);
+    log("Explain by: "+q);
+    log(s.explain(q,doc));
+  }
+
+
 }
Index: src/test/org/apache/lucene/search/function/TestCustomScoreQuery.java
===================================================================
--- src/test/org/apache/lucene/search/function/TestCustomScoreQuery.java	(revision 767282)
+++ src/test/org/apache/lucene/search/function/TestCustomScoreQuery.java	(working copy)
@@ -243,21 +243,4 @@
     }
   }
 
-  private void logResult(String msg, IndexSearcher s, Query q, int doc, float score1) throws IOException {
-    QueryUtils.check(q,s);
-    log(msg+" "+score1);
-    log("Explain by: "+q);
-    log(s.explain(q,doc));
-  }
-
-  // since custom scoring modifies the order of docs, map results 
-  // by doc ids so that we can later compare/verify them 
-  private HashMap topDocsToMap(TopDocs td) {
-    HashMap h = new HashMap(); 
-    for (int i=0; i<td.totalHits; i++) {
-      h.put(new Integer(td.scoreDocs[i].doc), new Float(td.scoreDocs[i].score));
-    }
-    return h;
-  }
-
 }
Index: src/java/org/apache/lucene/search/function/QueryWrapperValueSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/QueryWrapperValueSource.java	(revision 0)
+++ src/java/org/apache/lucene/search/function/QueryWrapperValueSource.java	(revision 0)
@@ -0,0 +1,116 @@
+package org.apache.lucene.search.function;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Searcher;
+
+/**
+ * Wrapper of a regular {@link Query} as a {@link ValueSource}, allowing 
+ * to create a {@link ValueSourceQuery} out of that regular {@link Query}.
+ * 
+ * <p><font color="#FF0000">
+ * WARNING: The status of the <b>search.function</b> package is experimental. 
+ * The APIs introduced here might change in the future and will not be 
+ * supported anymore in such a case.</font>
+ */
+public class QueryWrapperValueSource extends ValueSource {
+
+  Query query;
+  float defaultScore;
+  
+  public QueryWrapperValueSource (Query query, float defaultScore) {
+    if (query == null) 
+      throw new IllegalArgumentException("input query must not be null!");
+    this.query = query;
+    this.defaultScore = defaultScore;
+  }
+  
+  // @Override
+  public String description() {
+    return query.toString();
+  }
+
+  // @Override
+  public boolean equals(Object o) {
+    if (o == this) return true;
+    if (!(o instanceof QueryWrapperValueSource)) return false;
+    QueryWrapperValueSource other = (QueryWrapperValueSource) o;
+    return this.query.equals(other.query);
+  }
+
+  // @Override
+  public DocValues getValues(IndexReader reader) throws IOException {
+    Searcher searcher = new IndexSearcher(reader); //TODO quick & dirty: do not commit this code! 
+    Scorer scorer = query.weight(searcher).scorer(reader);
+    return new QueryDocValues(scorer);
+  }
+
+  // @Override
+  public int hashCode() {
+    return query.hashCode();
+  }
+
+  private class QueryDocValues extends DocValues {
+    Scorer scorer;
+    int currDoc = -1;
+    float currScore = -1;
+    boolean more = true;
+    
+    private QueryDocValues (Scorer scorer) {
+      this.scorer = scorer;
+    }
+    
+    // @Override
+    public float floatVal(int doc) {
+      if (doc<currDoc) {
+        return defaultScore;
+      }
+      if (doc==currDoc) {
+        return currScore;
+      }
+      if (!more) {
+        return defaultScore;
+      }
+      try {
+        if (scorer.skipTo(doc)) {
+          currDoc = scorer.doc();
+          currScore = scorer.score();
+        } else {
+          more = false;
+          return defaultScore;
+        }
+      } catch (IOException e) {
+        // TODO we should better throw a declared exception
+        throw new RuntimeException(e);
+      }
+      return doc==currDoc ? currScore : defaultScore;
+    }
+
+    // @Override
+    public String toString(int doc) {
+      return description() + '=' + intVal(doc); 
+    }
+    
+  }
+}
