pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / facet / src / java / org / apache / lucene / facet / index / streaming / CategoryParentsStream.java
1 package org.apache.lucene.facet.index.streaming;
2
3 import java.io.IOException;
4 import java.util.HashSet;
5 import java.util.LinkedList;
6 import java.util.List;
7 import java.util.Set;
8
9 import org.apache.lucene.analysis.TokenFilter;
10
11 import org.apache.lucene.facet.index.attributes.CategoryAttribute;
12 import org.apache.lucene.facet.index.attributes.CategoryProperty;
13 import org.apache.lucene.facet.index.attributes.OrdinalProperty;
14 import org.apache.lucene.facet.index.categorypolicy.OrdinalPolicy;
15 import org.apache.lucene.facet.index.categorypolicy.PathPolicy;
16 import org.apache.lucene.facet.index.params.FacetIndexingParams;
17 import org.apache.lucene.facet.taxonomy.CategoryPath;
18 import org.apache.lucene.facet.taxonomy.TaxonomyWriter;
19
20 /**
21  * Licensed to the Apache Software Foundation (ASF) under one or more
22  * contributor license agreements.  See the NOTICE file distributed with
23  * this work for additional information regarding copyright ownership.
24  * The ASF licenses this file to You under the Apache License, Version 2.0
25  * (the "License"); you may not use this file except in compliance with
26  * the License.  You may obtain a copy of the License at
27  *
28  *     http://www.apache.org/licenses/LICENSE-2.0
29  *
30  * Unless required by applicable law or agreed to in writing, software
31  * distributed under the License is distributed on an "AS IS" BASIS,
32  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33  * See the License for the specific language governing permissions and
34  * limitations under the License.
35  */
36
37 /**
38  * This class adds parents to a {@link CategoryAttributesStream}. The parents
39  * are added according to the {@link PathPolicy} and {@link OrdinalPolicy} from
40  * the {@link FacetIndexingParams} given in the constructor.<br>
41  * By default, category properties are removed when creating parents of a
42  * certain category. However, it is possible to retain certain property types
43  * using {@link #addRetainableProperty(Class)}.
44  * 
45  * @lucene.experimental
46  */
47 public class CategoryParentsStream extends TokenFilter {
48
49   /**
50    * A {@link TaxonomyWriter} for adding categories and retrieving their
51    * ordinals.
52    */
53   protected TaxonomyWriter taxonomyWriter;
54
55   /** An attribute containing all data related to the category */
56   protected CategoryAttribute categoryAttribute;
57
58   /** A category property containing the category ordinal */
59   protected OrdinalProperty ordinalProperty;
60
61   /**
62    * A set of property classes that are to be retained when creating a parent
63    * token.
64    */
65   private Set<Class<? extends CategoryProperty>> retainableProperties;
66
67   /** A {@link PathPolicy} for the category's parents' category paths. */
68   private PathPolicy pathPolicy;
69
70   /** An {@link OrdinalPolicy} for the category's parents' ordinals. */
71   private OrdinalPolicy ordinalPolicy;
72
73   /**
74    * Constructor.
75    * 
76    * @param input
77    *            The input stream to handle, must be derived from
78    *            {@link CategoryAttributesStream}.
79    * @param taxonomyWriter
80    *            The taxonomy writer to use for adding categories and
81    *            retrieving their ordinals.
82    * @param indexingParams
83    *            The indexing params used for filtering parents.
84    */
85   public CategoryParentsStream(CategoryAttributesStream input,
86       TaxonomyWriter taxonomyWriter, FacetIndexingParams indexingParams) {
87     super(input);
88     this.categoryAttribute = this.addAttribute(CategoryAttribute.class);
89     this.taxonomyWriter = taxonomyWriter;
90     this.pathPolicy = indexingParams.getPathPolicy();
91     this.ordinalPolicy = indexingParams.getOrdinalPolicy();
92     this.ordinalPolicy.init(taxonomyWriter);
93     this.ordinalProperty = new OrdinalProperty();
94     
95   }
96
97   @Override
98   public final boolean incrementToken() throws IOException {
99     if (this.categoryAttribute.getCategoryPath() != null) {
100       // try adding the parent of the current category to the stream
101       clearCategoryProperties();
102       boolean added = false;
103       // set the parent's ordinal, if illegal set -1
104       int ordinal = this.ordinalProperty.getOrdinal();
105       if (ordinal != -1) {
106         ordinal = this.taxonomyWriter.getParent(ordinal);
107         if (this.ordinalPolicy.shouldAdd(ordinal)) {
108           this.ordinalProperty.setOrdinal(ordinal);
109           try {
110             this.categoryAttribute.addProperty(ordinalProperty);
111           } catch (UnsupportedOperationException e) {
112             throw new IOException(e.getLocalizedMessage());
113           }
114           added = true;
115         } else {
116           this.ordinalProperty.setOrdinal(-1);
117         }
118       }
119       // set the parent's category path, if illegal set null
120       CategoryPath cp = this.categoryAttribute.getCategoryPath();
121       if (cp != null) {
122         cp.trim(1);
123         // if ordinal added, must also have category paths
124         if (added || this.pathPolicy.shouldAdd(cp)) {
125           this.categoryAttribute.setCategoryPath(cp);
126           added = true;
127         } else {
128           this.categoryAttribute.clear();
129         }
130       }
131       if (added) {
132         // a legal parent exists
133         return true;
134       }
135     }
136     // no more parents - get new category
137     if (input.incrementToken()) {
138       int ordinal = taxonomyWriter.addCategory(this.categoryAttribute.getCategoryPath());
139       this.ordinalProperty.setOrdinal(ordinal);
140       try {
141         this.categoryAttribute.addProperty(this.ordinalProperty);
142       } catch (UnsupportedOperationException e) {
143         throw new IOException(e.getLocalizedMessage());
144       }
145       return true;
146     }
147     return false;
148   }
149
150   /**
151    * Clear the properties of the current {@link CategoryAttribute} attribute
152    * before setting the parent attributes. <br>
153    * It is possible to retain properties of certain types the parent tokens,
154    * using {@link #addRetainableProperty(Class)}.
155    */
156   protected void clearCategoryProperties() {
157     if (this.retainableProperties == null
158         || this.retainableProperties.isEmpty()) {
159       this.categoryAttribute.clearProperties();
160     } else {
161       List<Class<? extends CategoryProperty>> propertyClassesToRemove = 
162                             new LinkedList<Class<? extends CategoryProperty>>();
163       for (Class<? extends CategoryProperty> propertyClass : this.categoryAttribute
164           .getPropertyClasses()) {
165         if (!this.retainableProperties.contains(propertyClass)) {
166           propertyClassesToRemove.add(propertyClass);
167         }
168       }
169       for (Class<? extends CategoryProperty> propertyClass : propertyClassesToRemove) {
170         this.categoryAttribute.remove(propertyClass);
171       }
172     }
173   }
174
175   /**
176    * Add a {@link CategoryProperty} class which is retained when creating
177    * parent tokens.
178    * 
179    * @param toRetain
180    *            The property class to retain.
181    */
182   public void addRetainableProperty(Class<? extends CategoryProperty> toRetain) {
183     if (this.retainableProperties == null) {
184       this.retainableProperties = new HashSet<Class<? extends CategoryProperty>>();
185     }
186     this.retainableProperties.add(toRetain);
187   }
188
189 }