You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sh...@apache.org on 2011/06/29 13:53:19 UTC

svn commit: r1141060 [14/21] - in /lucene/dev/branches/branch_3x: dev-tools/eclipse/ dev-tools/maven/lucene/contrib/facet/ lucene/contrib/ lucene/contrib/facet/ lucene/contrib/facet/src/ lucene/contrib/facet/src/examples/ lucene/contrib/facet/src/examp...

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/FacetTestBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/FacetTestBase.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/FacetTestBase.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/FacetTestBase.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,319 @@
+package org.apache.lucene.facet;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.lucene.DocumentBuilder.DocumentBuilderException;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.WhitespaceAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.Field.TermVector;
+import org.apache.lucene.index.CorruptIndexException;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.IndexWriterConfig.OpenMode;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+import org.apache.lucene.store.RAMDirectory;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.facet.index.CategoryDocumentBuilder;
+import org.apache.lucene.facet.index.params.CategoryListParams;
+import org.apache.lucene.facet.index.params.DefaultFacetIndexingParams;
+import org.apache.lucene.facet.index.params.FacetIndexingParams;
+import org.apache.lucene.facet.search.params.FacetRequest;
+import org.apache.lucene.facet.search.params.FacetSearchParams;
+import org.apache.lucene.facet.search.results.FacetResult;
+import org.apache.lucene.facet.search.results.FacetResultNode;
+import org.apache.lucene.facet.taxonomy.CategoryPath;
+import org.apache.lucene.facet.taxonomy.TaxonomyReader;
+import org.apache.lucene.facet.taxonomy.TaxonomyWriter;
+import org.apache.lucene.facet.taxonomy.lucene.LuceneTaxonomyReader;
+import org.apache.lucene.facet.taxonomy.lucene.LuceneTaxonomyWriter;
+
+/**
+ * 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.
+ */
+
+/** Base faceted search test. */
+public abstract class FacetTestBase extends LuceneTestCase {
+  
+  /** Documents text field. */
+  protected static final String CONTENT_FIELD = "content";
+  
+  /** Directory for the index */
+  protected Directory indexDir;
+  
+  /** Directory for the taxonomy */
+  protected Directory taxoDir;
+  
+  /** taxonomy Reader for the test. */
+  protected TaxonomyReader taxoReader;
+  
+  /** Index Reader for the test. */
+  protected IndexReader indexReader;
+  
+  /** Searcher for the test. */
+  protected IndexSearcher searcher;
+  
+  /** documents text (for the text field). */
+  private static final String[] DEFAULT_CONTENT = {
+      "the white car is the one I want.",
+      "the white dog does not belong to anyone.",
+  };
+  
+  /** Facets: facets[D][F] == category-path no. F for document no. D. */
+  private static final CategoryPath[][] DEFAULT_CATEGORIES = {
+      { new CategoryPath("root","a","f1"), new CategoryPath("root","a","f2") },
+      { new CategoryPath("root","a","f1"), new CategoryPath("root","a","f3") },
+  };
+  
+  /** categories to be added to specified doc */
+  protected List<CategoryPath> getCategories(int doc) {
+    return Arrays.asList(DEFAULT_CATEGORIES[doc]);
+  }
+  
+  /** Number of documents to index */
+  protected int numDocsToIndex() {
+    return DEFAULT_CONTENT.length;
+  }
+  
+  /** content to be added to specified doc */
+  protected String getContent(int doc) {
+    return DEFAULT_CONTENT[doc];
+  }
+  
+  /** Prepare index (in RAM) with single partition */
+  protected final void initIndex() throws Exception {
+    initIndex(Integer.MAX_VALUE);
+  }
+  
+  /** Prepare index (in RAM) with some documents and some facets */
+  protected final void initIndex(int partitionSize) throws Exception {
+    initIndex(partitionSize, false);
+  }
+
+  /** Prepare index (in RAM/Disk) with some documents and some facets */
+  protected final void initIndex(int partitionSize, boolean onDisk) throws Exception {
+    if (VERBOSE) {
+      System.out.println("Partition Size: " + partitionSize+"  onDisk: "+onDisk);
+    }
+
+    if (onDisk) {
+      File indexFile = new File(TEMP_DIR,"index");
+      indexDir = FSDirectory.open(indexFile);
+      taxoDir = FSDirectory.open(new File(indexFile,"facets"));
+    } else { 
+      indexDir = new RAMDirectory();
+      taxoDir = new RAMDirectory();
+    }
+    
+    IndexWriter iw = new IndexWriter(indexDir, new IndexWriterConfig(TEST_VERSION_CURRENT, getAnalyzer()));
+    TaxonomyWriter taxo = new LuceneTaxonomyWriter(taxoDir, OpenMode.CREATE);
+    
+    populateIndex(iw, taxo, getFacetIndexingParams(partitionSize));
+    
+    // commit changes (taxonomy prior to search index for consistency)
+    taxo.commit();
+    iw.commit();
+    taxo.close();
+    iw.close();
+    
+    // prepare for searching
+    taxoReader = new LuceneTaxonomyReader(taxoDir);
+    indexReader = IndexReader.open(indexDir);
+    searcher = new IndexSearcher(indexReader);
+  }
+
+  /** Returns a default facet indexing params */
+  protected FacetIndexingParams getFacetIndexingParams(final int partSize) {
+    return new DefaultFacetIndexingParams() {
+      @Override
+      protected int fixedPartitionSize() {
+        return partSize;
+      }
+    };
+  }
+  
+  /**
+   * Faceted Search Params for the test.
+   * Sub classes should override in order to test with different faceted search params.
+   */
+  protected FacetSearchParams getFacetedSearchParams() {
+    return getFacetedSearchParams(Integer.MAX_VALUE);
+  }
+
+  /**
+   * Faceted Search Params with specified partition size.
+   * @see #getFacetedSearchParams()
+   */
+  protected FacetSearchParams getFacetedSearchParams(int partitionSize) {
+    FacetSearchParams res = new FacetSearchParams(getFacetIndexingParams(partitionSize));
+    return res; 
+  }
+
+  /**
+   * Populate the test index+taxonomy for this test.
+   * <p>Subclasses can override this to test different scenarios
+   */
+  protected void populateIndex(IndexWriter iw, TaxonomyWriter taxo, FacetIndexingParams iParams)
+      throws IOException, DocumentBuilderException, CorruptIndexException {
+    // add test documents 
+    int numDocsToIndex = numDocsToIndex();
+    for (int doc=0; doc<numDocsToIndex; doc++) {
+      indexDoc(iParams, iw, taxo, getContent(doc), getCategories(doc));
+    }
+    
+    // also add a document that would be deleted, so that all tests are also working against deletions in the index
+    String content4del = "ContentOfDocToDelete";
+    indexDoc(iParams, iw, taxo, content4del, getCategories(0));
+    iw.commit(); // commit it
+    iw.deleteDocuments(new Term(CONTENT_FIELD,content4del)); // now delete the committed doc 
+  }
+  
+  /** Close all indexes */
+  protected void closeAll() throws Exception {
+    // close and nullify everything
+    taxoReader.close();
+    taxoReader = null;
+    indexReader.close();
+    indexReader = null;
+    searcher.close();
+    searcher = null;
+    indexDir = null;
+    taxoDir = null;
+  }
+  
+  /**
+   * Analyzer to use for the test.
+   * Sub classes should override in order to test with different analyzer.
+   */
+  protected Analyzer getAnalyzer() {
+    return new WhitespaceAnalyzer(TEST_VERSION_CURRENT);
+  }
+  
+  /** convenience method: convert sub results to an array */  
+  protected static FacetResultNode[] resultNodesAsArray(FacetResultNode parentRes) {
+    ArrayList<FacetResultNode> a = new ArrayList<FacetResultNode>();
+    for (FacetResultNode frn : parentRes.getSubResults()) {
+      a.add(frn);
+    }
+    return a.toArray(new FacetResultNode[0]);
+  }
+  
+  /** utility Create a dummy document with specified categories and content */
+  protected final void indexDoc(FacetIndexingParams iParams, IndexWriter iw,
+      TaxonomyWriter tw, String content, List<CategoryPath> categories) throws IOException,
+      CorruptIndexException {
+    Document d = new Document();
+    CategoryDocumentBuilder builder = new CategoryDocumentBuilder(tw, iParams);
+    builder.setCategoryPaths(categories);
+    builder.build(d);
+    d.add(new Field("content", content, Store.YES, Index.ANALYZED, TermVector.NO));
+    iw.addDocument(d);
+  }
+  
+  /** Build the "truth" with ALL the facets enumerating indexes content. */
+  protected Map<CategoryPath, Integer> facetCountsTruth() throws IOException {
+    FacetIndexingParams iParams = getFacetIndexingParams(Integer.MAX_VALUE);
+    String delim = String.valueOf(iParams.getFacetDelimChar());
+    Map<CategoryPath, Integer> res = new HashMap<CategoryPath, Integer>();
+    HashSet<Term> handledTerms = new HashSet<Term>();
+    for (CategoryListParams clp : iParams.getAllCategoryListParams()) {
+      Term baseTerm = clp.getTerm().createTerm("");
+      if (!handledTerms.add(baseTerm)) {
+        continue; // already handled this term (for another list) 
+      }
+      TermEnum te = indexReader.terms(baseTerm);
+      while (te.next()) {
+        Term t = te.term();
+        if (!t.field().equals(baseTerm.field())) {
+          break; // hit a different field
+        }
+        TermDocs tp = indexReader.termDocs(t);
+        int cnt = 0;
+        while (tp.next()) {
+          if (!indexReader.isDeleted(tp.doc())) { // ignore deleted docs
+            cnt++;
+          }
+        }
+        res.put(new CategoryPath(t.text().split(delim)), cnt);
+      }
+    }
+    return res;
+  }
+  
+  /** Validate counts for returned facets, and that there are not too many results */
+  protected static void assertCountsAndCardinality(Map<CategoryPath, Integer> facetCountsTruth, List<FacetResult> facetResults) throws Exception {
+    for (FacetResult fr : facetResults) {
+      FacetResultNode topResNode = fr.getFacetResultNode();
+      FacetRequest freq = fr.getFacetRequest();
+      if (VERBOSE) {
+        System.out.println(freq.getCategoryPath().toString()+ "\t\t" + topResNode);
+      }
+      assertCountsAndCardinality(facetCountsTruth, topResNode, freq.getNumResults());
+    }
+  }
+    
+  /** Validate counts for returned facets, and that there are not too many results */
+  private static void assertCountsAndCardinality(Map<CategoryPath,Integer> facetCountsTruth,  FacetResultNode resNode, int reqNumResults) throws Exception {
+    int actualNumResults = resNode.getNumSubResults();
+    if (VERBOSE) {
+      System.out.println("NumResults: " + actualNumResults);
+    }
+    assertTrue("Too many results!", actualNumResults <= reqNumResults);
+    for (FacetResultNode subRes : resNode.getSubResults()) {
+      assertEquals("wrong count for: "+subRes, facetCountsTruth.get(subRes.getLabel()).intValue(), (int)subRes.getValue());
+      assertCountsAndCardinality(facetCountsTruth, subRes, reqNumResults); // recurse into child results
+    }
+  }
+  
+  /** Validate results equality */
+  protected static void assertSameResults(List<FacetResult> expected,
+                                          List<FacetResult> actual) {
+    String expectedResults = resStringValueOnly(expected);
+    String actualResults = resStringValueOnly(actual);
+    if (!expectedResults.equals(actualResults)) {
+      System.err.println("Results are not the same!");
+      System.err.println("Expected:\n" + expectedResults);
+      System.err.println("Actual" + actualResults);
+      fail("Results are not the same!");
+    }
+  }
+  
+  /** exclude the residue and numDecendants because it is incorrect in sampling */
+  private static final String resStringValueOnly(List<FacetResult> results) {
+    StringBuilder sb = new StringBuilder();
+    for (FacetResult facetRes : results) {
+      sb.append(facetRes.toString()).append('\n');
+    }
+    return sb.toString().replaceAll("Residue:.*.0", "").replaceAll("Num valid Descendants.*", "");
+  }
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/FacetTestUtils.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/FacetTestUtils.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/FacetTestUtils.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/FacetTestUtils.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,166 @@
+package org.apache.lucene.facet;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.Field.TermVector;
+import org.apache.lucene.index.CorruptIndexException;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.search.TopScoreDocCollector;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+
+import org.apache.lucene.search.MultiCollector;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.facet.index.CategoryDocumentBuilder;
+import org.apache.lucene.facet.index.params.DefaultFacetIndexingParams;
+import org.apache.lucene.facet.index.params.FacetIndexingParams;
+import org.apache.lucene.facet.search.FacetsCollector;
+import org.apache.lucene.facet.search.params.CountFacetRequest;
+import org.apache.lucene.facet.search.params.FacetRequest;
+import org.apache.lucene.facet.search.params.FacetSearchParams;
+import org.apache.lucene.facet.taxonomy.CategoryPath;
+import org.apache.lucene.facet.taxonomy.TaxonomyReader;
+import org.apache.lucene.facet.taxonomy.TaxonomyWriter;
+import org.apache.lucene.facet.taxonomy.lucene.LuceneTaxonomyReader;
+import org.apache.lucene.facet.taxonomy.lucene.LuceneTaxonomyWriter;
+
+/**
+ * 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.
+ */
+
+public class FacetTestUtils {
+
+  public static Directory[][] createIndexTaxonomyDirs(int number) {
+    Directory[][] dirs = new Directory[number][2];
+    for (int i = 0; i < number; i++) {
+      dirs[i][0] = new RAMDirectory();
+      dirs[i][1] = new RAMDirectory();
+    }
+    return dirs;
+  }
+
+  public static IndexTaxonomyReaderPair[] createIndexTaxonomyReaderPair(
+      Directory[][] dirs) throws IOException {
+    IndexTaxonomyReaderPair[] pairs = new IndexTaxonomyReaderPair[dirs.length];
+    for (int i = 0; i < dirs.length; i++) {
+      IndexTaxonomyReaderPair pair = new IndexTaxonomyReaderPair();
+      pair.indexReader = IndexReader.open(dirs[i][0]);
+      pair.indexSearcher = new IndexSearcher(pair.indexReader);
+      pair.taxReader = new LuceneTaxonomyReader(dirs[i][1]);
+      pairs[i] = pair;
+    }
+    return pairs;
+  }
+
+  public static IndexTaxonomyWriterPair[] createIndexTaxonomyWriterPair(
+      Directory[][] dirs) throws IOException {
+    IndexTaxonomyWriterPair[] pairs = new IndexTaxonomyWriterPair[dirs.length];
+    for (int i = 0; i < dirs.length; i++) {
+      IndexTaxonomyWriterPair pair = new IndexTaxonomyWriterPair();
+      pair.indexWriter = new IndexWriter(dirs[i][0], new IndexWriterConfig(
+          LuceneTestCase.TEST_VERSION_CURRENT, new StandardAnalyzer(
+              LuceneTestCase.TEST_VERSION_CURRENT)));
+      pair.taxWriter = new LuceneTaxonomyWriter(dirs[i][1]);
+      pair.indexWriter.commit();
+      pair.taxWriter.commit();
+      pairs[i] = pair;
+    }
+    return pairs;
+  }
+
+  public static Collector[] search(IndexSearcher searcher,
+      TaxonomyReader taxonomyReader, DefaultFacetIndexingParams iParams,
+      int k, String... facetNames) throws IOException,
+      IllegalAccessException, InstantiationException {
+    
+    Collector[] collectors = new Collector[2];
+    
+    FacetSearchParams facetSearchParams = new FacetSearchParams(iParams);
+    Collection<FacetRequest> fRequests = new ArrayList<FacetRequest>();
+    for (String facetName : facetNames) {
+      CategoryPath cp = new CategoryPath(facetName);
+      FacetRequest fq = new CountFacetRequest(cp, k);
+      facetSearchParams.addFacetRequest(fq);
+      fRequests.add(fq);
+    }
+
+    TopScoreDocCollector topDocsCollector = TopScoreDocCollector.create(
+        searcher.getIndexReader().maxDoc(), true);
+    FacetsCollector facetsCollector = new FacetsCollector(
+        facetSearchParams, searcher.getIndexReader(), taxonomyReader);
+    Collector mColl = MultiCollector.wrap(topDocsCollector, facetsCollector);
+    
+    collectors[0] = topDocsCollector;
+    collectors[1] = facetsCollector;
+
+    searcher.search(new MatchAllDocsQuery(), mColl);
+    return collectors;
+  }
+  
+  public static void add(FacetIndexingParams iParams, IndexWriter iw,
+      TaxonomyWriter tw, String... strings) throws IOException,
+      CorruptIndexException {
+    ArrayList<CategoryPath> cps = new ArrayList<CategoryPath>();
+    CategoryPath cp = new CategoryPath(strings);
+    cps.add(cp);
+    Document d = new Document();
+    new CategoryDocumentBuilder(tw, iParams).setCategoryPaths(cps).build(d);
+    d.add(new Field("content", "alpha", Store.YES, Index.ANALYZED,
+        TermVector.NO));
+    iw.addDocument(d);
+  }
+
+  public static class IndexTaxonomyReaderPair {
+    public IndexReader indexReader;
+    public TaxonomyReader taxReader;
+    public IndexSearcher indexSearcher;
+
+    public void close() throws IOException {
+      indexSearcher.close();
+      indexReader.close();
+      taxReader.close();
+    }
+
+  }
+
+  public static class IndexTaxonomyWriterPair {
+    public IndexWriter indexWriter;
+    public TaxonomyWriter taxWriter;
+
+    public void close() throws IOException {
+      indexWriter.close();
+      taxWriter.close();
+    }
+
+    public void commit() throws IOException {
+      indexWriter.commit();
+      taxWriter.commit();
+    }
+  }
+
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/CategoryEnhancementDummy1.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/CategoryEnhancementDummy1.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/CategoryEnhancementDummy1.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/CategoryEnhancementDummy1.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,70 @@
+package org.apache.lucene.facet.enhancements;
+
+import org.apache.lucene.analysis.TokenStream;
+
+import org.apache.lucene.facet.enhancements.CategoryEnhancement;
+import org.apache.lucene.facet.enhancements.params.EnhancementsIndexingParams;
+import org.apache.lucene.facet.index.attributes.CategoryAttribute;
+import org.apache.lucene.facet.index.attributes.CategoryProperty;
+import org.apache.lucene.facet.index.streaming.CategoryListTokenizer;
+import org.apache.lucene.facet.taxonomy.TaxonomyWriter;
+
+/**
+ * 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.
+ */
+
+public class CategoryEnhancementDummy1 implements CategoryEnhancement {
+
+  public boolean generatesCategoryList() {
+    return false;
+  }
+
+  public String getCategoryListTermText() {
+    return null;
+  }
+
+  public CategoryListTokenizer getCategoryListTokenizer(
+      TokenStream tokenizer, EnhancementsIndexingParams indexingParams,
+      TaxonomyWriter taxonomyWriter) {
+    return null;
+  }
+
+  public byte[] getCategoryTokenBytes(CategoryAttribute categoryAttribute) {
+    return null;
+  }
+
+  public Object extractCategoryTokenData(byte[] buffer, int offset, int length) {
+    return null;
+  }
+
+  public Class<? extends CategoryProperty> getRetainableProperty() {
+    return null;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof CategoryEnhancementDummy1) {
+      return true;
+    }
+    return false;
+  }
+  
+  @Override
+  public int hashCode() {
+    return super.hashCode();
+  }
+  
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/CategoryEnhancementDummy2.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/CategoryEnhancementDummy2.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/CategoryEnhancementDummy2.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/CategoryEnhancementDummy2.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,79 @@
+package org.apache.lucene.facet.enhancements;
+
+import org.apache.lucene.analysis.TokenStream;
+
+import org.apache.lucene.facet.enhancements.CategoryEnhancement;
+import org.apache.lucene.facet.enhancements.params.EnhancementsIndexingParams;
+import org.apache.lucene.facet.index.DummyProperty;
+import org.apache.lucene.facet.index.attributes.CategoryAttribute;
+import org.apache.lucene.facet.index.attributes.CategoryProperty;
+import org.apache.lucene.facet.index.streaming.CategoryListTokenizer;
+import org.apache.lucene.facet.taxonomy.TaxonomyWriter;
+
+/**
+ * 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.
+ */
+
+public class CategoryEnhancementDummy2 implements CategoryEnhancement {
+
+  public static byte[] CATEGORY_TOKEN_BYTES = new byte[] { 3, 0, 7 };
+
+  public boolean generatesCategoryList() {
+    return false;
+  }
+
+  public String getCategoryListTermText() {
+    return null;
+  }
+
+  public CategoryListTokenizer getCategoryListTokenizer(
+      TokenStream tokenizer, EnhancementsIndexingParams indexingParams,
+      TaxonomyWriter taxonomyWriter) {
+    return null;
+  }
+
+  public byte[] getCategoryTokenBytes(CategoryAttribute categoryAttribute) {
+    return CATEGORY_TOKEN_BYTES;
+  }
+
+  public Object extractCategoryTokenData(byte[] buffer, int offset, int length) {
+    if (length != CATEGORY_TOKEN_BYTES.length) {
+      throw new IllegalArgumentException("unexpected data length "
+          + length);
+    }
+    byte[] ret = new byte[length];
+    System.arraycopy(buffer, offset, ret, 0, length);
+    return ret;
+  }
+
+  public Class<? extends CategoryProperty> getRetainableProperty() {
+    return DummyProperty.class;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof CategoryEnhancementDummy2) {
+      return true;
+    }
+    return false;
+  }
+  
+  @Override
+  public int hashCode() {
+    return super.hashCode();
+  }
+  
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/CategoryEnhancementDummy3.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/CategoryEnhancementDummy3.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/CategoryEnhancementDummy3.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/CategoryEnhancementDummy3.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,78 @@
+package org.apache.lucene.facet.enhancements;
+
+import org.apache.lucene.analysis.TokenStream;
+
+import org.apache.lucene.facet.enhancements.CategoryEnhancement;
+import org.apache.lucene.facet.enhancements.params.EnhancementsIndexingParams;
+import org.apache.lucene.facet.index.attributes.CategoryAttribute;
+import org.apache.lucene.facet.index.attributes.CategoryProperty;
+import org.apache.lucene.facet.index.streaming.CategoryListTokenizer;
+import org.apache.lucene.facet.taxonomy.TaxonomyWriter;
+
+/**
+ * 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.
+ */
+
+public class CategoryEnhancementDummy3 implements CategoryEnhancement {
+
+  public static byte[] CATEGORY_TOKEN_BYTES = new byte[] { 5, -1, 33, 8 };
+
+  public boolean generatesCategoryList() {
+    return false;
+  }
+
+  public String getCategoryListTermText() {
+    return null;
+  }
+
+  public CategoryListTokenizer getCategoryListTokenizer(
+      TokenStream tokenizer, EnhancementsIndexingParams indexingParams,
+      TaxonomyWriter taxonomyWriter) {
+    return null;
+  }
+
+  public byte[] getCategoryTokenBytes(CategoryAttribute categoryAttribute) {
+    return CATEGORY_TOKEN_BYTES;
+  }
+
+  public Object extractCategoryTokenData(byte[] buffer, int offset, int length) {
+    if (length != CATEGORY_TOKEN_BYTES.length) {
+      throw new IllegalArgumentException("unexpected data length "
+          + length);
+    }
+    byte[] ret = new byte[length];
+    System.arraycopy(buffer, offset, ret, 0, length);
+    return ret;
+  }
+
+  public Class<? extends CategoryProperty> getRetainableProperty() {
+    return null;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof CategoryEnhancementDummy3) {
+      return true;
+    }
+    return false;
+  }
+  
+  @Override
+  public int hashCode() {
+    return super.hashCode();
+  }
+  
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/EnhancementsPayloadIteratorTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/EnhancementsPayloadIteratorTest.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/EnhancementsPayloadIteratorTest.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/EnhancementsPayloadIteratorTest.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,108 @@
+package org.apache.lucene.facet.enhancements;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.facet.enhancements.EnhancementsPayloadIterator;
+import org.apache.lucene.facet.enhancements.association.AssociationEnhancement;
+import org.apache.lucene.facet.enhancements.params.EnhancementsIndexingParams;
+import org.apache.lucene.facet.example.association.AssociationIndexer;
+import org.apache.lucene.facet.example.association.AssociationUtils;
+import org.apache.lucene.facet.search.DrillDown;
+import org.apache.lucene.facet.taxonomy.CategoryPath;
+
+/**
+ * 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.
+ */
+
+public class EnhancementsPayloadIteratorTest extends LuceneTestCase {
+
+  private static Directory indexDir;
+  private static Directory taxoDir;
+  private static EnhancementsIndexingParams indexingParams;
+  private static AssociationEnhancement associationEnhancement;
+
+  @BeforeClass
+  public static void buildAssociationIndex() throws Exception {
+    // create Directories for the search index and for the taxonomy index
+    indexDir = new RAMDirectory();
+    taxoDir = new RAMDirectory();
+
+    // index the sample documents
+    if (VERBOSE) {
+      System.out.println("index the sample documents...");
+    }
+    AssociationIndexer.index(indexDir, taxoDir);
+
+    indexingParams = AssociationUtils.assocIndexingParams;
+    associationEnhancement = (AssociationEnhancement) indexingParams
+        .getCategoryEnhancements().get(0);
+  }
+
+  @Test
+  public void testFullIterator() throws IOException {
+    IndexReader indexReader = IndexReader.open(indexDir);
+    Term term = DrillDown.term(indexingParams, new CategoryPath("tags", "lucene"));
+    EnhancementsPayloadIterator iterator = new EnhancementsPayloadIterator(
+        indexingParams.getCategoryEnhancements(), indexReader, term);
+    assertTrue("Unexpected failure of init()", iterator.init());
+    assertTrue("Missing instance of tags/lucene in doc 0", iterator.setdoc(0));
+    int assoc = (Integer) iterator.getCategoryData(associationEnhancement);
+    assertEquals("Unexpected association value for tags/lucene in doc 0", 3, assoc, 1E-5);
+    assertTrue("Missing instance of tags/lucene in doc 1", iterator.setdoc(1));
+    assoc = (Integer) iterator.getCategoryData(associationEnhancement);
+    assertEquals("Unexpected association value for tags/lucene in doc 1", 1, assoc, 1E-5);
+  }
+
+  @Test
+  public void testEmptyIterator() throws IOException {
+    IndexReader indexReader = IndexReader.open(indexDir);
+    Term term = DrillDown.term(indexingParams, new CategoryPath("root","a", "f2"));
+    EnhancementsPayloadIterator iterator = new EnhancementsPayloadIterator(
+        indexingParams.getCategoryEnhancements(), indexReader, term);
+    assertTrue("Unexpected failure of init()", iterator.init());
+    assertFalse("Unexpected payload for root/a/f2 in doc 0", iterator.setdoc(0));
+    assertFalse("Unexpected instance of root/a/f2 in doc 1", iterator.setdoc(1));
+  }
+
+  @Test
+  public void testPartialIterator() throws IOException {
+    IndexReader indexReader = IndexReader.open(indexDir);
+    Term term = DrillDown.term(indexingParams, new CategoryPath("genre","software"));
+    EnhancementsPayloadIterator iterator = new EnhancementsPayloadIterator(
+        indexingParams.getCategoryEnhancements(), indexReader, term);
+    assertTrue("Unexpected failure of init()", iterator.init());
+    assertFalse("Unexpected payload for genre/computing in doc 0", iterator.setdoc(0));
+    assertTrue("Missing instance of genre/computing in doc 1", iterator.setdoc(1));
+    float assoc = Float.intBitsToFloat((Integer) iterator
+        .getCategoryData(associationEnhancement));
+    assertEquals("Unexpected association value for genre/computing in doc 1", 0.34f, assoc, 0.001);
+  }
+
+  @AfterClass
+  public static void closeDirectories() throws IOException {
+    indexDir.close();
+    taxoDir.close();
+  }
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/TwoEnhancementsTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/TwoEnhancementsTest.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/TwoEnhancementsTest.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/TwoEnhancementsTest.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,129 @@
+package org.apache.lucene.facet.enhancements;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.lucene.analysis.WhitespaceAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.junit.Test;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.facet.enhancements.EnhancementsDocumentBuilder;
+import org.apache.lucene.facet.enhancements.EnhancementsPayloadIterator;
+import org.apache.lucene.facet.enhancements.params.DefaultEnhancementsIndexingParams;
+import org.apache.lucene.facet.enhancements.params.EnhancementsIndexingParams;
+import org.apache.lucene.facet.search.DrillDown;
+import org.apache.lucene.facet.taxonomy.CategoryPath;
+import org.apache.lucene.facet.taxonomy.TaxonomyWriter;
+import org.apache.lucene.facet.taxonomy.lucene.LuceneTaxonomyWriter;
+
+/**
+ * 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.
+ */
+
+public class TwoEnhancementsTest extends LuceneTestCase {
+
+  @Test
+  public void testTwoEmptyAndNonEmptyByteArrays() throws Exception {
+    Directory indexDir = new RAMDirectory();
+    Directory taxoDir = new RAMDirectory();
+
+    EnhancementsIndexingParams indexingParams = 
+      new DefaultEnhancementsIndexingParams(
+          new CategoryEnhancementDummy1(),
+          new CategoryEnhancementDummy3());
+
+    // add document with a category containing data for both enhancements
+    List<CategoryPath> categoryPaths = new ArrayList<CategoryPath>();
+    categoryPaths.add(new CategoryPath("a", "b"));
+
+    IndexWriter indexWriter = new IndexWriter(indexDir, new IndexWriterConfig(
+        TEST_VERSION_CURRENT, new WhitespaceAnalyzer(TEST_VERSION_CURRENT)));
+    TaxonomyWriter taxo = new LuceneTaxonomyWriter(taxoDir);
+
+    // a category document builder will add the categories to a document
+    // once build() is called
+    Document doc = new Document();
+    indexWriter.addDocument(new EnhancementsDocumentBuilder(taxo,
+        indexingParams).setCategoryPaths(categoryPaths).build(doc));
+
+    indexWriter.close();
+
+    IndexReader indexReader = IndexReader.open(indexDir);
+    Term term = DrillDown.term(indexingParams, new CategoryPath("a","b"));
+    EnhancementsPayloadIterator iterator = new EnhancementsPayloadIterator(
+        indexingParams.getCategoryEnhancements(), indexReader, term);
+
+    assertTrue("EnhancementsPayloadIterator failure", iterator.init());
+    assertTrue("Missing document 0", iterator.setdoc(0));
+    assertNull("Unexpected data for CategoryEnhancementDummy2", iterator
+        .getCategoryData(new CategoryEnhancementDummy1()));
+    byte[] dummy3 = (byte[]) iterator
+        .getCategoryData(new CategoryEnhancementDummy3());
+    assertTrue("Bad array returned for CategoryEnhancementDummy3", Arrays
+        .equals(dummy3, CategoryEnhancementDummy3.CATEGORY_TOKEN_BYTES));
+  }
+
+  @Test
+  public void testTwoNonEmptyByteArrays() throws Exception {
+    // add document with a category containing data for both enhancements
+    Directory indexDir = new RAMDirectory();
+    Directory taxoDir = new RAMDirectory();
+
+    EnhancementsIndexingParams indexingParams = 
+      new DefaultEnhancementsIndexingParams(
+          new CategoryEnhancementDummy2(),
+          new CategoryEnhancementDummy3());
+
+    List<CategoryPath> categoryPaths = new ArrayList<CategoryPath>();
+    categoryPaths.add(new CategoryPath("a", "b"));
+
+    IndexWriter indexWriter = new IndexWriter(indexDir, new IndexWriterConfig(
+        TEST_VERSION_CURRENT, new WhitespaceAnalyzer(TEST_VERSION_CURRENT)));
+    TaxonomyWriter taxo = new LuceneTaxonomyWriter(taxoDir);
+
+    // a category document builder will add the categories to a document
+    // once build() is called
+    Document doc = new Document();
+    indexWriter.addDocument(new EnhancementsDocumentBuilder(taxo,
+        indexingParams).setCategoryPaths(categoryPaths).build(doc));
+
+    indexWriter.close();
+
+    IndexReader indexReader = IndexReader.open(indexDir);
+    Term term = DrillDown.term(indexingParams, new CategoryPath("a","b"));
+    EnhancementsPayloadIterator iterator = new EnhancementsPayloadIterator(
+        indexingParams.getCategoryEnhancements(), indexReader, term);
+
+    assertTrue("EnhancementsPayloadIterator failure", iterator.init());
+    assertTrue("Missing document 0", iterator.setdoc(0));
+    byte[] dummy2 = (byte[]) iterator
+        .getCategoryData(new CategoryEnhancementDummy2());
+    assertTrue("Bad array returned for CategoryEnhancementDummy2", Arrays
+        .equals(dummy2, CategoryEnhancementDummy2.CATEGORY_TOKEN_BYTES));
+    byte[] dummy3 = (byte[]) iterator
+        .getCategoryData(new CategoryEnhancementDummy3());
+    assertTrue("Bad array returned for CategoryEnhancementDummy3", Arrays
+        .equals(dummy3, CategoryEnhancementDummy3.CATEGORY_TOKEN_BYTES));
+  }
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/association/AssociationPropertyTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/association/AssociationPropertyTest.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/association/AssociationPropertyTest.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/association/AssociationPropertyTest.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,65 @@
+package org.apache.lucene.facet.enhancements.association;
+
+import org.junit.Test;
+
+import org.apache.lucene.facet.FacetException;
+import org.apache.lucene.facet.enhancements.association.AssociationFloatProperty;
+import org.apache.lucene.facet.enhancements.association.AssociationIntProperty;
+import org.apache.lucene.facet.enhancements.association.AssociationProperty;
+import org.apache.lucene.util.LuceneTestCase;
+
+/**
+ * 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.
+ */
+
+/** Test {@link AssociationProperty}-ies. */
+public class AssociationPropertyTest extends LuceneTestCase {
+
+  @Test
+  public void testAssociationCountProperty() throws FacetException {
+    AssociationProperty aa1 = new AssociationIntProperty(5);
+    AssociationProperty aa2 = new AssociationIntProperty(3);
+    assertEquals("Wrong association for property", 5, aa1.getAssociation());
+    assertEquals("Wrong association for property", 3, aa2.getAssociation());
+    aa1.merge(aa2);
+    assertEquals("Wrong association for property", 8, aa1.getAssociation());
+  }
+
+  @Test
+  public void testAssociationFloatProperty() throws FacetException {
+    AssociationFloatProperty aa1 = new AssociationFloatProperty(5);
+    AssociationFloatProperty aa2 = new AssociationFloatProperty(3);
+    assertEquals("Wrong association for property", 5.0, aa1.getFloatAssociation(), 0.00001);
+    assertEquals("Wrong association for property", 3.0, aa2.getFloatAssociation(), 0.00001);
+    aa1.merge(aa2);
+    assertEquals("Wrong association for property", 8.0, aa1.getFloatAssociation(), 0.00001);
+  }
+
+  @Test
+  public void testEquals() {
+    AssociationProperty aa1 = new AssociationIntProperty(5);
+    AssociationProperty aa2 = new AssociationIntProperty(5);
+    AssociationProperty aa3 = new AssociationFloatProperty(5);
+    AssociationProperty aa4 = new AssociationFloatProperty(5);
+
+    assertTrue("Should be equal", aa1.equals(aa1));
+    assertTrue("Should be equal", aa1.equals(aa2));
+    assertFalse("Should not be equal", aa1.equals(aa3));
+    assertTrue("Should be equal", aa3.equals(aa3));
+    assertTrue("Should be equal", aa3.equals(aa4));
+  }
+  
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/association/CustomAssociationPropertyTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/association/CustomAssociationPropertyTest.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/association/CustomAssociationPropertyTest.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/association/CustomAssociationPropertyTest.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,97 @@
+package org.apache.lucene.facet.enhancements.association;
+
+import org.apache.lucene.analysis.KeywordAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.junit.Test;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.facet.enhancements.EnhancementsDocumentBuilder;
+import org.apache.lucene.facet.enhancements.params.DefaultEnhancementsIndexingParams;
+import org.apache.lucene.facet.enhancements.params.EnhancementsIndexingParams;
+import org.apache.lucene.facet.index.CategoryContainer;
+import org.apache.lucene.facet.index.attributes.CategoryAttributeImpl;
+import org.apache.lucene.facet.index.attributes.CategoryProperty;
+import org.apache.lucene.facet.taxonomy.CategoryPath;
+import org.apache.lucene.facet.taxonomy.lucene.LuceneTaxonomyReader;
+import org.apache.lucene.facet.taxonomy.lucene.LuceneTaxonomyWriter;
+
+/**
+ * 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.
+ */
+
+public class CustomAssociationPropertyTest extends LuceneTestCase {
+
+  @Test
+  public void testCustomProperty() throws Exception {
+    class CustomProperty extends AssociationIntProperty {
+      public CustomProperty(int value) {
+        super(value);
+      }
+      @Override
+      public void merge(CategoryProperty other) {
+        throw new UnsupportedOperationException();
+      }
+    }
+
+    final int NUM_CATEGORIES = 10;
+    EnhancementsIndexingParams iParams = new DefaultEnhancementsIndexingParams(
+        new AssociationEnhancement());
+
+    Directory iDir = new RAMDirectory();
+    Directory tDir = new RAMDirectory();
+    
+    IndexWriter w = new IndexWriter(iDir, new IndexWriterConfig(TEST_VERSION_CURRENT, new KeywordAnalyzer()));
+    LuceneTaxonomyWriter taxoW = new LuceneTaxonomyWriter(tDir);
+    
+    CategoryContainer cc = new CategoryContainer();
+    EnhancementsDocumentBuilder builder = new EnhancementsDocumentBuilder(taxoW, iParams);
+    for (int i = 1; i <= NUM_CATEGORIES; i++) {
+      CategoryAttributeImpl ca = new CategoryAttributeImpl(new CategoryPath(Integer.toString(i)));
+      ca.addProperty(new CustomProperty(i));
+      
+      cc.addCategory(ca);
+    }
+    builder.setCategories(cc);
+    w.addDocument(builder.build(new Document()));
+    taxoW.close();
+    w.close();
+    
+    IndexReader reader = IndexReader.open(iDir);
+    LuceneTaxonomyReader taxo = new LuceneTaxonomyReader(tDir);
+    String field = iParams.getCategoryListParams(new CategoryPath("0")).getTerm().field();
+    AssociationsPayloadIterator api = new AssociationsPayloadIterator(reader, field);
+
+    api.setNextDoc(0);
+
+    boolean flag = false;
+    for (int i = 1; i <= NUM_CATEGORIES; i++) {
+      int ordinal = taxo.getOrdinal(new CategoryPath(Integer.toString(i)));
+      flag = true;
+      long association = api.getAssociation(ordinal);
+      assertTrue("Association expected for ordinal "+ordinal+" but none was found",
+          association <= Integer.MAX_VALUE);
+      
+      assertEquals("Wrong association value for category '"+ i+"'", i, (int)association);
+    }
+    
+    assertTrue("No categories found for doc #0", flag);
+  }
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/params/DefaultEnhancementsIndexingParamsTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/params/DefaultEnhancementsIndexingParamsTest.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/params/DefaultEnhancementsIndexingParamsTest.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/enhancements/params/DefaultEnhancementsIndexingParamsTest.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,67 @@
+package org.apache.lucene.facet.enhancements.params;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.facet.enhancements.CategoryEnhancement;
+import org.apache.lucene.facet.enhancements.CategoryEnhancementDummy1;
+import org.apache.lucene.facet.enhancements.CategoryEnhancementDummy2;
+import org.apache.lucene.facet.enhancements.params.DefaultEnhancementsIndexingParams;
+import org.apache.lucene.facet.enhancements.params.EnhancementsIndexingParams;
+import org.apache.lucene.facet.index.DummyProperty;
+import org.apache.lucene.facet.index.attributes.CategoryProperty;
+
+/**
+ * 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.
+ */
+
+public class DefaultEnhancementsIndexingParamsTest extends LuceneTestCase {
+
+  @Test
+  public void testCategoryEnhancements() {
+    EnhancementsIndexingParams params = 
+      new DefaultEnhancementsIndexingParams(
+          new CategoryEnhancementDummy1());
+
+    // check retainable properties 
+    List<Class<? extends CategoryProperty>> retainableProps = params
+        .getRetainableProperties();
+    assertNull("Unexpected content in retainable list", retainableProps);
+
+    params.addCategoryEnhancements(new CategoryEnhancementDummy2());
+
+    List<CategoryEnhancement> enhancements = params
+        .getCategoryEnhancements();
+
+    assertEquals("Wrong number of enhancements", 2, enhancements.size());
+
+    assertTrue("Wrong first enhancement",
+        enhancements.get(0) instanceof CategoryEnhancementDummy1);
+    assertTrue("Wrong second enhancement",
+        enhancements.get(1) instanceof CategoryEnhancementDummy2);
+
+    // re-check retainable properties 
+    retainableProps = params.getRetainableProperties();
+    assertNotNull("Unexpected empty retainable list", retainableProps);
+    assertEquals("Unexpected size of retainable list", 1, retainableProps
+        .size());
+    assertEquals("Wrong property in retainable list", DummyProperty.class,
+        retainableProps.get(0));
+
+  }
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestAdaptiveExample.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestAdaptiveExample.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestAdaptiveExample.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestAdaptiveExample.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,40 @@
+package org.apache.lucene.facet.example;
+
+import org.junit.Test;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.facet.example.ExampleResult;
+import org.apache.lucene.facet.example.adaptive.AdaptiveMain;
+
+/**
+ * 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.
+ */
+
+/**
+ * Test that the adaptive example works as expected. This test helps to verify
+ * that examples code is alive!
+ */
+public class TestAdaptiveExample extends LuceneTestCase {
+  
+  @Test
+  public void testAdaptive () throws Exception {
+    ExampleResult res = new AdaptiveMain().runSample();
+    assertNotNull("Null result!", res);
+    assertNotNull("Null facet result!", res.getFacetResults());
+    assertEquals("Wrong number of results!",1, res.getFacetResults().size());
+    assertEquals("Wrong number of facets!",3, res.getFacetResults().get(0).getNumValidDescendants());
+  }
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestAssociationExample.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestAssociationExample.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestAssociationExample.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestAssociationExample.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,55 @@
+package org.apache.lucene.facet.example;
+
+import org.junit.Test;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.facet.example.ExampleResult;
+import org.apache.lucene.facet.example.association.AssociationMain;
+import org.apache.lucene.facet.search.results.FacetResultNode;
+
+/**
+ * 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.
+ */
+
+/**
+ * Test that the association example works as expected. This test helps to
+ * verify that examples code is alive!
+ */
+public class TestAssociationExample extends LuceneTestCase {
+  
+  private static final double[] EXPECTED_INT_SUM_RESULTS = { 4, 2};
+  private static final double[] EXPECTED_FLOAT_SUM_RESULTS = { 1.62, 0.34};
+
+  @Test
+  public void testAssociationExamples() throws Exception {
+    assertExampleResult(new AssociationMain().runSumIntAssociationSample(), EXPECTED_INT_SUM_RESULTS);
+    assertExampleResult(new AssociationMain().runSumFloatAssociationSample(), EXPECTED_FLOAT_SUM_RESULTS);
+  }
+
+  private void assertExampleResult(ExampleResult res, double[] expectedResults) {
+    assertNotNull("Null result!", res);
+    assertNotNull("Null facet result!", res.getFacetResults());
+    assertEquals("Wrong number of results!", 1, res.getFacetResults().size());
+    assertEquals("Wrong number of facets!", 2, res.getFacetResults().get(0).getNumValidDescendants());
+    
+    Iterable<? extends FacetResultNode> it = res.getFacetResults().get(0).getFacetResultNode().getSubResults();
+    int i = 0;
+    for (FacetResultNode fResNode : it) {
+      assertEquals("Wrong result for facet "+fResNode.getLabel(), expectedResults[i++], fResNode.getValue(), 1E-5);
+    }
+  }
+  
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestMultiCLExample.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestMultiCLExample.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestMultiCLExample.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestMultiCLExample.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,90 @@
+package org.apache.lucene.facet.example;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Test;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.facet.example.ExampleResult;
+import org.apache.lucene.facet.example.multiCL.MultiCLMain;
+import org.apache.lucene.facet.search.results.FacetResult;
+import org.apache.lucene.facet.search.results.FacetResultNode;
+
+/**
+ * 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.
+ */
+
+/**
+ * Test that the multi-category list example works as expected. This test helps
+ * to verify that examples code is alive!
+ */
+public class TestMultiCLExample extends LuceneTestCase {
+
+  @Test
+  public void testMulti() throws Exception {
+    ExampleResult res = new MultiCLMain().runSample();
+    assertCorrectMultiResults(res);
+  }
+
+  public static void assertCorrectMultiResults(ExampleResult exampleResults)
+      throws Exception {
+    List<FacetResult> results = exampleResults.getFacetResults();
+    FacetResult result = results.get(0);
+    assertNotNull("Result should not be null", result);
+    assertEquals("Invalid label", "5", result.getFacetResultNode()
+        .getLabel().toString());
+    assertEquals("Invalid value", 2.0, result.getFacetResultNode()
+        .getValue(), 0.0);
+    assertEquals("Invalid # of subresults", 3, result.getFacetResultNode()
+        .getNumSubResults());
+
+    Iterator<? extends FacetResultNode> subResults = result
+        .getFacetResultNode().getSubResults().iterator();
+    FacetResultNode sub = subResults.next();
+    assertEquals("Invalid subresult value", 1.0, sub.getValue(), 0.0);
+    assertEquals("Invalid subresult label", "5/2", sub.getLabel()
+        .toString());
+    sub = subResults.next();
+    assertEquals("Invalid subresult value", 1.0, sub.getValue(), 0.0);
+    assertEquals("Invalid subresult label", "5/7", sub.getLabel()
+        .toString());
+    sub = subResults.next();
+    assertEquals("Invalid subresult value", 1.0, sub.getValue(), 0.0);
+    assertEquals("Invalid subresult label", "5/5", sub.getLabel()
+        .toString());
+
+    result = results.get(1);
+    assertNotNull("Result should not be null", result);
+    assertEquals("Invalid label", "5/5", result.getFacetResultNode()
+        .getLabel().toString());
+    assertEquals("Invalid value", 1,
+        result.getFacetResultNode().getValue(), 0.0);
+    assertEquals("Invalid number of subresults", 0, result
+        .getFacetResultNode().getNumSubResults());
+
+    result = results.get(2);
+    assertNotNull("Result should not be null", result);
+    assertEquals("Invalid label", "6/2", result.getFacetResultNode()
+        .getLabel().toString());
+    assertEquals("Invalid value", 1,
+        result.getFacetResultNode().getValue(), 0.0);
+    assertEquals("Invalid number of subresults", 0, result
+        .getFacetResultNode().getNumSubResults());
+
+  }
+
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestSimpleExample.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestSimpleExample.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestSimpleExample.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/example/TestSimpleExample.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,67 @@
+package org.apache.lucene.facet.example;
+
+import java.util.Iterator;
+
+import org.junit.Test;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.facet.example.ExampleResult;
+import org.apache.lucene.facet.example.simple.SimpleMain;
+import org.apache.lucene.facet.search.results.FacetResult;
+import org.apache.lucene.facet.search.results.FacetResultNode;
+
+/**
+ * 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.
+ */
+
+/**
+ * Test that the simple example works as expected. This test helps to verify
+ * that examples code is alive!
+ */
+public class TestSimpleExample extends LuceneTestCase {
+
+  @Test
+  public void testSimple () throws Exception {
+    ExampleResult res = new SimpleMain().runSimple();
+    assertNotNull("Null result!", res);
+    assertNotNull("Null facet result!", res.getFacetResults());
+    assertEquals("Wrong number of results!",1, res.getFacetResults().size());
+    assertEquals("Wrong number of facets!",3, res.getFacetResults().get(0).getNumValidDescendants());
+  }
+
+  /**
+   * In drill down test we are drilling down to a facet that appears in a single document.
+   * As result, facets that without drill down got count of 2 will now get a count of 1. 
+   */
+  @Test
+  public void testDrillDown () throws Exception {
+    ExampleResult res = new SimpleMain().runDrillDown();
+    assertNotNull("Null result!", res);
+    assertNotNull("Null facet result!", res.getFacetResults());
+    assertEquals("Wrong number of results!",1, res.getFacetResults().size());
+    
+    // drill down facet appears in only 1 doc, and that doc has only 2 facets  
+    FacetResult facetResult = res.getFacetResults().get(0);
+    assertEquals("Wrong number of facets!",2, facetResult.getNumValidDescendants());
+    
+    Iterator<? extends FacetResultNode> resIterator = facetResult.getFacetResultNode().getSubResults().iterator();
+    assertTrue("Too few results", resIterator.hasNext());
+    assertEquals("wrong count for first result out of 2", 1, (int)resIterator.next().getValue());
+    assertTrue("Too few results", resIterator.hasNext());
+    assertEquals("wrong count for second result out of 2", 1, (int)resIterator.next().getValue());
+    assertFalse("Too many results!", resIterator.hasNext());
+  }
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/CategoryContainerTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/CategoryContainerTest.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/CategoryContainerTest.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/CategoryContainerTest.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,244 @@
+package org.apache.lucene.facet.index;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Iterator;
+
+import org.junit.Test;
+
+import org.apache.lucene.facet.FacetException;
+import org.apache.lucene.facet.enhancements.association.AssociationIntProperty;
+import org.apache.lucene.facet.enhancements.association.AssociationProperty;
+import org.apache.lucene.facet.index.CategoryContainer;
+import org.apache.lucene.facet.index.attributes.CategoryAttribute;
+import org.apache.lucene.facet.index.attributes.CategoryAttributeImpl;
+import org.apache.lucene.facet.index.streaming.CategoryAttributesStream;
+import org.apache.lucene.facet.taxonomy.CategoryPath;
+
+/**
+ * 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.
+ */
+
+public class CategoryContainerTest extends CategoryContainerTestBase {
+
+  @Test
+  public void basicTest() {
+    assertEquals("Wrong number of categories in the container", 3,
+        categoryContainer.size());
+
+    categoryContainer.clear();
+    assertEquals("Container should not contain categories after clear", 0,
+        categoryContainer.size());
+  }
+
+  @Test
+  public void testIterator() throws FacetException {
+    Iterator<CategoryAttribute> iterator = categoryContainer.iterator();
+
+    // count the number of tokens
+    int nCategories;
+    for (nCategories = 0; iterator.hasNext(); nCategories++) {
+      iterator.next();
+    }
+    assertEquals("Wrong number of tokens", 3, nCategories);
+  }
+
+  @Test
+  public void testExistingNewCategoryWithProperty() throws FacetException {
+    categoryContainer.addCategory(new CategoryPath("five", "six"),
+        new DummyProperty());
+    Iterator<CategoryAttribute> iterator = categoryContainer.iterator();
+
+    // count the number of tokens, and check there is one DummyAttribute
+    int nCategories;
+    int nProperties = 0;
+    for (nCategories = 0; iterator.hasNext(); nCategories++) {
+      CategoryAttribute attribute = iterator.next();
+      if (attribute.getProperty(DummyProperty.class) != null) {
+        nProperties++;
+      }
+    }
+    assertEquals("Wrong number of tokens", 3, nCategories);
+    assertEquals("Wrong number of tokens with properties", 1, nProperties);
+  }
+
+  @Test
+  public void testMultipleCategoriesWithProperties() throws FacetException {
+    AssociationProperty associationProperty = new AssociationIntProperty(
+        49);
+    categoryContainer.addCategory(new CategoryPath("five", "six"),
+        new DummyProperty(), associationProperty);
+    categoryContainer.addCategory(new CategoryPath("seven", "eight"),
+        new DummyProperty());
+    associationProperty = new AssociationIntProperty(123);
+    categoryContainer.addCategory(new CategoryPath("nine"),
+        associationProperty, new DummyProperty());
+    Iterator<CategoryAttribute> iterator = categoryContainer.iterator();
+
+    // count the number of tokens, and check there is one DummyAttribute
+    int nCategories;
+    int nDummyAttributes = 0;
+    int nAssocAttributes = 0;
+    for (nCategories = 0; iterator.hasNext(); nCategories++) {
+      CategoryAttribute attribute = iterator.next();
+      if (attribute.getProperty(DummyProperty.class) != null) {
+        nDummyAttributes++;
+      }
+      if (attribute.getProperty(AssociationIntProperty.class) != null) {
+        nAssocAttributes++;
+      }
+    }
+    assertEquals("Wrong number of tokens", 5, nCategories);
+    assertEquals("Wrong number of tokens with dummy properties", 3,
+        nDummyAttributes);
+    assertEquals("Wrong number of tokens with association properties", 2,
+        nAssocAttributes);
+  }
+
+  @Test
+  public void testAddNewCategoryWithProperty() throws FacetException {
+    categoryContainer.addCategory(new CategoryPath("seven", "eight"),
+        new DummyProperty());
+    Iterator<CategoryAttribute> iterator = categoryContainer.iterator();
+
+    // count the number of tokens, and check there is one DummyAttribute
+    int nCategories;
+    int nProperties = 0;
+    for (nCategories = 0; iterator.hasNext(); nCategories++) {
+      CategoryAttribute attribute = iterator.next();
+      if (attribute.getProperty(DummyProperty.class) != null) {
+        nProperties++;
+      }
+    }
+    assertEquals("Wrong number of tokens", 4, nCategories);
+    assertEquals("Wrong number of tokens with properties", 1, nProperties);
+  }
+
+  /**
+   * Test addition of {@link CategoryAttribute} object without properties to a
+   * {@link CategoryContainer}.
+   * 
+   * @throws FacetException
+   */
+  @Test
+  public void testAddCategoryAttributeWithoutProperties()
+      throws FacetException {
+    CategoryAttribute newCA = new CategoryAttributeImpl(new CategoryPath(
+        "seven", "eight"));
+    categoryContainer.addCategory(newCA);
+  }
+
+  /**
+   * Test addition of {@link CategoryAttribute} object with property to a
+   * {@link CategoryContainer}.
+   * 
+   * @throws FacetException
+   */
+  @Test
+  public void testAddCategoryAttributeWithProperty() throws FacetException {
+    CategoryAttribute newCA = new CategoryAttributeImpl(new CategoryPath(
+        "seven", "eight"));
+    newCA.addProperty(new DummyProperty());
+    categoryContainer.addCategory(newCA);
+    Iterator<CategoryAttribute> iterator = categoryContainer.iterator();
+
+    // count the number of tokens, and check there is one DummyAttribute
+    int nCategories;
+    int nProperties = 0;
+    for (nCategories = 0; iterator.hasNext(); nCategories++) {
+      CategoryAttribute attribute = iterator.next();
+      if (attribute.getProperty(DummyProperty.class) != null) {
+        nProperties++;
+      }
+    }
+    assertEquals("Wrong number of tokens", 4, nCategories);
+    assertEquals("Wrong number of tokens with properties", 1, nProperties);
+  }
+
+  /**
+   * Verifies that a {@link CategoryAttributesStream} can be constructed from
+   * {@link CategoryContainer} and produce the correct number of tokens.
+   * 
+   * @throws IOException
+   */
+  @Test
+  public void testCategoryAttributesStream() throws IOException {
+    CategoryAttributesStream stream = new CategoryAttributesStream(
+        categoryContainer);
+    // count the number of tokens
+    int nTokens;
+    for (nTokens = 0; stream.incrementToken(); nTokens++) {
+    }
+    assertEquals("Wrong number of tokens", 3, nTokens);
+  }
+
+  /**
+   * Test that {@link CategoryContainer} merges properties.
+   * 
+   * @throws FacetException
+   */
+  @Test
+  public void testCategoryAttributeMerge() throws FacetException {
+    categoryContainer.addCategory(initialCatgeories[0],
+        new AssociationIntProperty(2));
+    categoryContainer.addCategory(initialCatgeories[0],
+        new AssociationIntProperty(15));
+
+    Iterator<CategoryAttribute> iterator = categoryContainer.iterator();
+
+    int nCategories;
+    int nAssociations = 0;
+    for (nCategories = 0; iterator.hasNext(); nCategories++) {
+      CategoryAttribute ca = iterator.next();
+      AssociationProperty aa = (AssociationProperty) ca
+          .getProperty(AssociationIntProperty.class);
+      if (aa != null) {
+        assertEquals("Wrong association value", 17, aa.getAssociation());
+        nAssociations++;
+      }
+    }
+    assertEquals("Wrong number of tokens", 3, nCategories);
+    assertEquals("Wrong number of tokens with associations", 1,
+        nAssociations);
+  }
+  
+  @Test
+  public void testSerialization() throws Exception {
+    AssociationProperty associationProperty = new AssociationIntProperty(
+        49);
+    categoryContainer.addCategory(new CategoryPath("five", "six"),
+        new DummyProperty(), associationProperty);
+    categoryContainer.addCategory(new CategoryPath("seven", "eight"),
+        new DummyProperty());
+    associationProperty = new AssociationIntProperty(123);
+    categoryContainer.addCategory(new CategoryPath("nine"),
+        associationProperty, new DummyProperty());
+    
+    ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
+    ObjectOutputStream out = new ObjectOutputStream(baos);
+    out.writeObject(categoryContainer);
+    out.close();
+    
+    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+    ObjectInputStream in = new ObjectInputStream(bais);
+    assertEquals(
+        "Original and deserialized CategoryContainer are different",
+        categoryContainer, in.readObject());
+  }
+}
\ No newline at end of file

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/CategoryContainerTestBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/CategoryContainerTestBase.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/CategoryContainerTestBase.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/CategoryContainerTestBase.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,45 @@
+package org.apache.lucene.facet.index;
+
+import org.junit.Before;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.facet.index.CategoryContainer;
+import org.apache.lucene.facet.taxonomy.CategoryPath;
+
+/**
+ * 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.
+ */
+
+public abstract class CategoryContainerTestBase extends LuceneTestCase {
+  
+  protected CategoryContainer categoryContainer;
+  protected CategoryPath[] initialCatgeories;
+
+  @Before
+  public void setCategoryContainer() {
+    initialCatgeories = new CategoryPath[3];
+    initialCatgeories[0] = new CategoryPath("one", "two", "three");
+    initialCatgeories[1] = new CategoryPath("four");
+    initialCatgeories[2] = new CategoryPath("five", "six");
+
+    categoryContainer = new CategoryContainer();
+
+    for (int i = 0; i < initialCatgeories.length; i++) {
+      categoryContainer.addCategory(initialCatgeories[i]);
+    }
+  }
+  
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/CategoryListPayloadStreamTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/CategoryListPayloadStreamTest.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/CategoryListPayloadStreamTest.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/CategoryListPayloadStreamTest.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,80 @@
+package org.apache.lucene.facet.index;
+
+import java.io.ByteArrayInputStream;
+
+import org.junit.Test;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.facet.index.CategoryListPayloadStream;
+import org.apache.lucene.util.encoding.DGapIntDecoder;
+import org.apache.lucene.util.encoding.DGapIntEncoder;
+import org.apache.lucene.util.encoding.IntDecoder;
+import org.apache.lucene.util.encoding.NOnesIntDecoder;
+import org.apache.lucene.util.encoding.NOnesIntEncoder;
+import org.apache.lucene.util.encoding.UniqueValuesIntEncoder;
+
+/**
+ * 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.
+ */
+
+public class CategoryListPayloadStreamTest extends LuceneTestCase {
+
+  /**
+   * Verifies that a CategoryListPayloadStream can properly encode values into
+   * a byte stream for later constructing a Payload.
+   */
+  @Test
+  public void testStream() throws Exception {
+
+    CategoryListPayloadStream clps = new CategoryListPayloadStream(
+        new UniqueValuesIntEncoder(new DGapIntEncoder(
+            new NOnesIntEncoder(3))));
+
+    clps.appendIntToStream(1);
+    clps.appendIntToStream(10);
+    clps.appendIntToStream(100);
+    clps.appendIntToStream(1000);
+    clps.appendIntToStream(10000);
+    clps.appendIntToStream(100000);
+    clps.appendIntToStream(1000000);
+    clps.appendIntToStream(10000000);
+    clps.appendIntToStream(100000000);
+    clps.appendIntToStream(1000000000);
+    clps.appendIntToStream(Integer.MAX_VALUE);
+
+    ByteArrayInputStream bais = new ByteArrayInputStream(clps
+        .convertStreamToByteArray());
+    IntDecoder decoder = new DGapIntDecoder(new NOnesIntDecoder(3));
+    decoder.reInit(bais);
+    assertEquals("Wrong value in byte stream", 1, decoder.decode());
+    assertEquals("Wrong value in byte stream", 10, decoder.decode());
+    assertEquals("Wrong value in byte stream", 100, decoder.decode());
+    assertEquals("Wrong value in byte stream", 1000, decoder.decode());
+    assertEquals("Wrong value in byte stream", 10000, decoder.decode());
+    assertEquals("Wrong value in byte stream", 100000, decoder.decode());
+    assertEquals("Wrong value in byte stream", 1000000, decoder.decode());
+    assertEquals("Wrong value in byte stream", 10000000, decoder.decode());
+    assertEquals("Wrong value in byte stream", 100000000, decoder.decode());
+    assertEquals("Wrong value in byte stream", 1000000000, decoder.decode());
+    assertEquals("Wrong value in byte stream", Integer.MAX_VALUE, decoder.decode());
+    assertEquals("End of stream not reached", IntDecoder.EOS, decoder.decode());
+
+    clps.reset();
+    decoder.reInit(bais);
+    assertEquals("End of stream not reached", IntDecoder.EOS, decoder.decode());
+  }
+
+}

Added: lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/DummyProperty.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/DummyProperty.java?rev=1141060&view=auto
==============================================================================
--- lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/DummyProperty.java (added)
+++ lucene/dev/branches/branch_3x/lucene/contrib/facet/src/test/org/apache/lucene/facet/index/DummyProperty.java Wed Jun 29 11:53:10 2011
@@ -0,0 +1,50 @@
+package org.apache.lucene.facet.index;
+
+import org.apache.lucene.facet.index.attributes.CategoryProperty;
+
+/**
+ * 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.
+ */
+
+/**
+ * An empty attribute for testing.
+ */
+public class DummyProperty implements CategoryProperty {
+
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof DummyProperty) {
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode();
+  }
+  
+  public void merge(CategoryProperty other) {
+    throw new UnsupportedOperationException(
+        "Merging dummy attribute is prohibited");
+  }
+  
+  @Override
+  public String toString() {
+    return "I am dummy property";
+  }
+
+}