Index: lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java	(revision 1468127)
+++ lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java	(working copy)
@@ -82,6 +82,15 @@
   }
 
   @Override
+  public Query rewrite(Query original) throws IOException {
+    // TODO: use the more sophisticated QueryUtils.check sometimes!
+    QueryUtils.check(original);
+    Query rewritten = super.rewrite(original);
+    QueryUtils.check(rewritten);
+    return rewritten;
+  }
+
+  @Override
   protected Query wrapFilter(Query query, Filter filter) {
     if (random.nextBoolean())
       return super.wrapFilter(query, filter);
Index: lucene/queries/src/test/org/apache/lucene/queries/BoostingQueryTest.java
===================================================================
--- lucene/queries/src/test/org/apache/lucene/queries/BoostingQueryTest.java	(revision 1468127)
+++ lucene/queries/src/test/org/apache/lucene/queries/BoostingQueryTest.java	(working copy)
@@ -22,6 +22,9 @@
 import org.apache.lucene.util.LuceneTestCase;
 
 public class BoostingQueryTest extends LuceneTestCase {
+  // TODO: this suite desperately needs more tests!
+  // ... like ones that actually run the query
+  
   public void testBoostingQueryEquals() {
     TermQuery q1 = new TermQuery(new Term("subject:", "java"));
     TermQuery q2 = new TermQuery(new Term("subject:", "java"));
Index: lucene/queries/src/test/org/apache/lucene/queries/mlt/TestMoreLikeThis.java
===================================================================
--- lucene/queries/src/test/org/apache/lucene/queries/mlt/TestMoreLikeThis.java	(revision 1468127)
+++ lucene/queries/src/test/org/apache/lucene/queries/mlt/TestMoreLikeThis.java	(working copy)
@@ -134,4 +134,6 @@
     mlt.setFieldNames(new String[] {"text", "foobar"});
     mlt.like(new StringReader("this is a test"), "foobar");
   }
+  
+  // TODO: add tests for the MoreLikeThisQuery
 }
Index: lucene/queries/src/java/org/apache/lucene/queries/BoostingQuery.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/BoostingQuery.java	(revision 1468127)
+++ lucene/queries/src/java/org/apache/lucene/queries/BoostingQuery.java	(working copy)
@@ -84,7 +84,7 @@
     @Override
     public int hashCode() {
       final int prime = 31;
-      int result = 1;
+      int result = super.hashCode();
       result = prime * result + Float.floatToIntBits(boost);
       result = prime * result + ((context == null) ? 0 : context.hashCode());
       result = prime * result + ((match == null) ? 0 : match.hashCode());
@@ -102,6 +102,10 @@
       if (getClass() != obj.getClass()) {
         return false;
       }
+      
+      if (!super.equals(obj)) {
+        return false;
+      }
 
       BoostingQuery other = (BoostingQuery) obj;
       if (Float.floatToIntBits(boost) != Float.floatToIntBits(other.boost)) {
Index: lucene/queries/src/java/org/apache/lucene/queries/mlt/MoreLikeThisQuery.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/mlt/MoreLikeThisQuery.java	(revision 1468127)
+++ lucene/queries/src/java/org/apache/lucene/queries/mlt/MoreLikeThisQuery.java	(working copy)
@@ -29,6 +29,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.StringReader;
+import java.util.Arrays;
 import java.util.Set;
 
 /**
@@ -148,4 +149,47 @@
   public void setMinDocFreq(int minDocFreq) {
     this.minDocFreq = minDocFreq;
   }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = super.hashCode();
+    result = prime * result + ((analyzer == null) ? 0 : analyzer.hashCode());
+    result = prime * result + ((fieldName == null) ? 0 : fieldName.hashCode());
+    result = prime * result + ((likeText == null) ? 0 : likeText.hashCode());
+    result = prime * result + maxQueryTerms;
+    result = prime * result + minDocFreq;
+    result = prime * result + minTermFrequency;
+    result = prime * result + Arrays.hashCode(moreLikeFields);
+    result = prime * result + Float.floatToIntBits(percentTermsToMatch);
+    result = prime * result + ((stopWords == null) ? 0 : stopWords.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (!super.equals(obj)) return false;
+    if (getClass() != obj.getClass()) return false;
+    MoreLikeThisQuery other = (MoreLikeThisQuery) obj;
+    if (analyzer == null) {
+      if (other.analyzer != null) return false;
+    } else if (!analyzer.equals(other.analyzer)) return false;
+    if (fieldName == null) {
+      if (other.fieldName != null) return false;
+    } else if (!fieldName.equals(other.fieldName)) return false;
+    if (likeText == null) {
+      if (other.likeText != null) return false;
+    } else if (!likeText.equals(other.likeText)) return false;
+    if (maxQueryTerms != other.maxQueryTerms) return false;
+    if (minDocFreq != other.minDocFreq) return false;
+    if (minTermFrequency != other.minTermFrequency) return false;
+    if (!Arrays.equals(moreLikeFields, other.moreLikeFields)) return false;
+    if (Float.floatToIntBits(percentTermsToMatch) != Float
+        .floatToIntBits(other.percentTermsToMatch)) return false;
+    if (stopWords == null) {
+      if (other.stopWords != null) return false;
+    } else if (!stopWords.equals(other.stopWords)) return false;
+    return true;
+  }
 }
Index: lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java
===================================================================
--- lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java	(revision 1468127)
+++ lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java	(working copy)
@@ -43,6 +43,7 @@
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.MatchAllDocsQuery;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryUtils;
 import org.apache.lucene.search.ScoreDoc;
 import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.search.TopDocs;
@@ -148,6 +149,7 @@
     // Making sure the query yields 25 documents with the facet "a"
     DrillDownQuery q = new DrillDownQuery(defaultParams);
     q.add(new CategoryPath("a"));
+    QueryUtils.check(q);
     TopDocs docs = searcher.search(q, 100);
     assertEquals(25, docs.totalHits);
     
Index: lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java	(revision 1468127)
+++ lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java	(working copy)
@@ -181,7 +181,9 @@
   
   @Override
   public int hashCode() {
-    return query.hashCode();
+    final int prime = 31;
+    int result = super.hashCode();
+    return prime * result + query.hashCode();
   }
   
   @Override
@@ -191,7 +193,7 @@
     }
     
     DrillDownQuery other = (DrillDownQuery) obj;
-    return query.equals(other.query);
+    return query.equals(other.query) && super.equals(other);
   }
   
   @Override
Index: lucene/facet/src/java/org/apache/lucene/facet/search/DrillSidewaysQuery.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/search/DrillSidewaysQuery.java	(revision 1468127)
+++ lucene/facet/src/java/org/apache/lucene/facet/search/DrillSidewaysQuery.java	(working copy)
@@ -157,13 +157,34 @@
     };
   }
 
+  // TODO: these should do "deeper" equals/hash on the 2-D drillDownTerms array
+
   @Override
   public int hashCode() {
-    throw new UnsupportedOperationException();
+    final int prime = 31;
+    int result = super.hashCode();
+    result = prime * result + ((baseQuery == null) ? 0 : baseQuery.hashCode());
+    result = prime * result
+        + ((drillDownCollector == null) ? 0 : drillDownCollector.hashCode());
+    result = prime * result + Arrays.hashCode(drillDownTerms);
+    result = prime * result + Arrays.hashCode(drillSidewaysCollectors);
+    return result;
   }
 
   @Override
   public boolean equals(Object obj) {
-    throw new UnsupportedOperationException();
+    if (this == obj) return true;
+    if (!super.equals(obj)) return false;
+    if (getClass() != obj.getClass()) return false;
+    DrillSidewaysQuery other = (DrillSidewaysQuery) obj;
+    if (baseQuery == null) {
+      if (other.baseQuery != null) return false;
+    } else if (!baseQuery.equals(other.baseQuery)) return false;
+    if (drillDownCollector == null) {
+      if (other.drillDownCollector != null) return false;
+    } else if (!drillDownCollector.equals(other.drillDownCollector)) return false;
+    if (!Arrays.equals(drillDownTerms, other.drillDownTerms)) return false;
+    if (!Arrays.equals(drillSidewaysCollectors, other.drillSidewaysCollectors)) return false;
+    return true;
   }
 }
Index: lucene/core/src/test/org/apache/lucene/index/TestBinaryTerms.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestBinaryTerms.java	(revision 1468127)
+++ lucene/core/src/test/org/apache/lucene/index/TestBinaryTerms.java	(working copy)
@@ -69,4 +69,9 @@
     ir.close();
     dir.close();
   }
+  
+  public void testToString() {
+    Term term = new Term("foo", new BytesRef(new byte[] { (byte) 0xff, (byte) 0xfe }));
+    assertEquals("foo:[ff fe]", term.toString());
+  }
 }
Index: lucene/core/src/java/org/apache/lucene/index/Term.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/Term.java	(revision 1468127)
+++ lucene/core/src/java/org/apache/lucene/index/Term.java	(working copy)
@@ -17,7 +17,13 @@
  * limitations under the License.
  */
 
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+
 import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.IOUtils;
 
 /**
   A Term represents a word from text.  This is the unit of search.  It is
@@ -69,7 +75,23 @@
   /** Returns the text of this term.  In the case of words, this is simply the
     text of the word.  In the case of dates and other types, this is an
     encoding of the object as a string.  */
-  public final String text() { return bytes.utf8ToString(); }
+  public final String text() { 
+    return toString(bytes);
+  }
+  
+  /** Returns human-readable form of the term text. If the term is not unicode,
+   * the raw bytes will be printed instead. */
+  public static final String toString(BytesRef termText) {
+    // the term might not be text, but usually is. so we make a best effort
+    CharsetDecoder decoder = IOUtils.CHARSET_UTF_8.newDecoder()
+        .onMalformedInput(CodingErrorAction.REPORT)
+        .onUnmappableCharacter(CodingErrorAction.REPORT);
+    try {
+      return decoder.decode(ByteBuffer.wrap(termText.bytes, termText.offset, termText.length)).toString();
+    } catch (CharacterCodingException e) {
+      return termText.toString();
+    }
+  }
 
   /** Returns the bytes of this term. */
   public final BytesRef bytes() { return bytes; }
@@ -132,5 +154,5 @@
   }
 
   @Override
-  public final String toString() { return field + ":" + bytes.utf8ToString(); }
+  public final String toString() { return field + ":" + text(); }
 }
Index: lucene/core/src/java/org/apache/lucene/search/TermRangeQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/TermRangeQuery.java	(revision 1468127)
+++ lucene/core/src/java/org/apache/lucene/search/TermRangeQuery.java	(working copy)
@@ -19,6 +19,7 @@
 
 import java.io.IOException;
 
+import org.apache.lucene.index.Term;
 import org.apache.lucene.index.Terms;
 import org.apache.lucene.index.TermsEnum;
 import org.apache.lucene.util.AttributeSource;
@@ -122,9 +123,9 @@
       }
       buffer.append(includeLower ? '[' : '{');
       // TODO: all these toStrings for queries should just output the bytes, it might not be UTF-8!
-      buffer.append(lowerTerm != null ? ("*".equals(lowerTerm.utf8ToString()) ? "\\*" : lowerTerm.utf8ToString())  : "*");
+      buffer.append(lowerTerm != null ? ("*".equals(Term.toString(lowerTerm)) ? "\\*" : Term.toString(lowerTerm))  : "*");
       buffer.append(" TO ");
-      buffer.append(upperTerm != null ? ("*".equals(upperTerm.utf8ToString()) ? "\\*" : upperTerm.utf8ToString()) : "*");
+      buffer.append(upperTerm != null ? ("*".equals(Term.toString(upperTerm)) ? "\\*" : Term.toString(upperTerm)) : "*");
       buffer.append(includeUpper ? ']' : '}');
       buffer.append(ToStringUtils.boost(getBoost()));
       return buffer.toString();
Index: lucene/sandbox/src/java/org/apache/lucene/sandbox/queries/FuzzyLikeThisQuery.java
===================================================================
--- lucene/sandbox/src/java/org/apache/lucene/sandbox/queries/FuzzyLikeThisQuery.java	(revision 1468127)
+++ lucene/sandbox/src/java/org/apache/lucene/sandbox/queries/FuzzyLikeThisQuery.java	(working copy)
@@ -72,7 +72,7 @@
     @Override
     public int hashCode() {
       final int prime = 31;
-      int result = 1;
+      int result = super.hashCode();
       result = prime * result + ((analyzer == null) ? 0 : analyzer.hashCode());
       result = prime * result
           + ((fieldVals == null) ? 0 : fieldVals.hashCode());
@@ -89,6 +89,9 @@
         return false;
       if (getClass() != obj.getClass())
         return false;
+      if (!super.equals(obj)) {
+        return false;
+      }
       FuzzyLikeThisQuery other = (FuzzyLikeThisQuery) obj;
       if (analyzer == null) {
         if (other.analyzer != null)
Index: lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java	(revision 1468127)
+++ lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java	(working copy)
@@ -339,7 +339,8 @@
       final ToChildBlockJoinQuery other = (ToChildBlockJoinQuery) _other;
       return origParentQuery.equals(other.origParentQuery) &&
         parentsFilter.equals(other.parentsFilter) &&
-        doScores == other.doScores;
+        doScores == other.doScores &&
+        super.equals(other);
     } else {
       return false;
     }
@@ -348,7 +349,7 @@
   @Override
   public int hashCode() {
     final int prime = 31;
-    int hash = 1;
+    int hash = super.hashCode();
     hash = prime * hash + origParentQuery.hashCode();
     hash = prime * hash + new Boolean(doScores).hashCode();
     hash = prime * hash + parentsFilter.hashCode();
Index: lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java	(revision 1468127)
+++ lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java	(working copy)
@@ -446,7 +446,8 @@
       final ToParentBlockJoinQuery other = (ToParentBlockJoinQuery) _other;
       return origChildQuery.equals(other.origChildQuery) &&
         parentsFilter.equals(other.parentsFilter) &&
-        scoreMode == other.scoreMode;
+        scoreMode == other.scoreMode && 
+        super.equals(other);
     } else {
       return false;
     }
@@ -455,17 +456,10 @@
   @Override
   public int hashCode() {
     final int prime = 31;
-    int hash = 1;
+    int hash = super.hashCode();
     hash = prime * hash + origChildQuery.hashCode();
     hash = prime * hash + scoreMode.hashCode();
     hash = prime * hash + parentsFilter.hashCode();
     return hash;
   }
-
-  @Override
-  public ToParentBlockJoinQuery clone() {
-    return new ToParentBlockJoinQuery(origChildQuery.clone(),
-                              parentsFilter,
-                              scoreMode);
-  }
 }
