pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / facet / src / java / org / apache / lucene / facet / index / CategoryContainer.java
1 package org.apache.lucene.facet.index;
2
3 import java.io.IOException;
4 import java.io.ObjectInputStream;
5 import java.io.ObjectOutputStream;
6 import java.io.Serializable;
7 import java.util.HashMap;
8 import java.util.Iterator;
9 import java.util.Map;
10 import java.util.Set;
11
12 import org.apache.lucene.util.Attribute;
13
14 import org.apache.lucene.facet.FacetException;
15 import org.apache.lucene.facet.index.attributes.CategoryAttribute;
16 import org.apache.lucene.facet.index.attributes.CategoryAttributeImpl;
17 import org.apache.lucene.facet.index.attributes.CategoryProperty;
18 import org.apache.lucene.facet.taxonomy.CategoryPath;
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  * A container to add categories which are to be introduced to
39  * {@link CategoryDocumentBuilder#setCategories(Iterable)}. Categories can be
40  * added with Properties. 
41  * 
42  * @lucene.experimental
43  */
44 public class CategoryContainer implements Iterable<CategoryAttribute>, Serializable {
45
46   protected transient Map<CategoryPath, CategoryAttribute> map;
47
48   /**
49    * Constructor.
50    */
51   public CategoryContainer() {
52     map = new HashMap<CategoryPath, CategoryAttribute>();
53   }
54
55   /**
56    * Add a category.
57    * 
58    * @param categoryPath
59    *            The path of the category.
60    * @return The {@link CategoryAttribute} of the category.
61    */
62   public CategoryAttribute addCategory(CategoryPath categoryPath) {
63     return mapCategoryAttribute(categoryPath);
64   }
65
66   /**
67    * Add a category with a property.
68    * 
69    * @param categoryPath
70    *            The path of the category.
71    * @param property
72    *            The property to associate to the category.
73    * @return The {@link CategoryAttribute} of the category.
74    */
75   public CategoryAttribute addCategory(CategoryPath categoryPath,
76       CategoryProperty property) {
77     /*
78      * This method is a special case of addCategory with multiple
79      * properties, but it is kept here for two reasons: 1) Using the array
80      * version has some performance cost, and 2) it is expected that most
81      * calls will be for this version (single property).
82      */
83     CategoryAttribute ca = mapCategoryAttribute(categoryPath);
84     ca.addProperty(property);
85     return ca;
86   }
87
88   /**
89    * Add a category with multiple properties.
90    * 
91    * @param categoryPath
92    *            The path of the category.
93    * @param properties
94    *            The properties to associate to the category.
95    * @return The {@link CategoryAttribute} of the category.
96    * @throws FacetException
97    *             When the category already has a property of the same type as
98    *             one of the new properties, and merging for this property type
99    *             is prohibited.
100    */
101   public CategoryAttribute addCategory(CategoryPath categoryPath,
102       CategoryProperty... properties) throws FacetException {
103     CategoryAttribute ca = mapCategoryAttribute(categoryPath);
104     for (CategoryProperty attribute : properties) {
105       ca.addProperty(attribute);
106     }
107     return ca;
108   }
109
110   /**
111    * Add an entire {@link CategoryAttribute}.
112    * 
113    * @param categoryAttribute
114    *            The {@link CategoryAttribute} to add.
115    * @return The {@link CategoryAttribute} of the category (could be different
116    *         from the one provided).
117    * @throws FacetException
118    */
119   public CategoryAttribute addCategory(CategoryAttribute categoryAttribute)
120   throws FacetException {
121     CategoryAttribute ca = mapCategoryAttribute(categoryAttribute
122         .getCategoryPath());
123     Set<Class<? extends CategoryProperty>> propertyClasses = categoryAttribute
124     .getPropertyClasses();
125     if (propertyClasses != null) {
126       for (Class<? extends CategoryProperty> propertyClass : propertyClasses) {
127         ca.addProperty(categoryAttribute.getProperty(propertyClass));
128       }
129     }
130     return ca;
131   }
132
133   /**
134    * Get the {@link CategoryAttribute} object for a specific
135    * {@link CategoryPath}, from the map.
136    */
137   private final CategoryAttribute mapCategoryAttribute(
138       CategoryPath categoryPath) {
139     CategoryAttribute ca = map.get(categoryPath);
140     if (ca == null) {
141       ca = new CategoryAttributeImpl(categoryPath);
142       map.put(categoryPath, ca);
143     }
144     return ca;
145   }
146
147   /**
148    * Get the {@link CategoryAttribute} this container has for a certain
149    * category, or {@code null} if the category is not in the container.
150    * 
151    * @param categoryPath
152    *            The category path of the requested category.
153    */
154   public CategoryAttribute getCategoryAttribute(CategoryPath categoryPath) {
155     return map.get(categoryPath);
156   }
157
158   public Iterator<CategoryAttribute> iterator() {
159     return map.values().iterator();
160   }
161
162   /**
163    * Remove all categories.
164    */
165   public void clear() {
166     map.clear();
167   }
168
169   /**
170    * Add the categories from another {@link CategoryContainer} to this one.
171    * 
172    * @param other
173    *            The {@link CategoryContainer} to take categories from.
174    * @throws FacetException
175    *             If any prohibited merge of category properties is attempted.
176    */
177   public void merge(CategoryContainer other) throws FacetException {
178     for (CategoryAttribute categoryAttribute : other.map.values()) {
179       addCategory(categoryAttribute);
180     }
181   }
182
183   /**
184    * Get the number of categories in the container.
185    * 
186    * @return The number of categories in the container.
187    */
188   public int size() {
189     return map.size();
190   }
191
192   @Override
193   public String toString() {
194     StringBuilder builder = new StringBuilder("CategoryContainer");
195     for (CategoryAttribute ca : map.values()) {
196       builder.append('\n');
197       builder.append('\t');
198       builder.append(ca.toString());
199     }
200     return builder.toString();
201   }
202   
203   /**
204    * Serialize object content to given {@link ObjectOutputStream}
205    */
206   private void writeObject(ObjectOutputStream out) throws IOException {
207     out.defaultWriteObject();
208     // write the number of categories
209     out.writeInt(size());
210     // write the category attributes
211     for (CategoryAttribute ca : this) {
212       serializeCategoryAttribute(out, ca);
213     }
214   }
215
216   /**
217    * Serialize each of the {@link CategoryAttribute}s to the given
218    * {@link ObjectOutputStream}.<br>
219    * NOTE: {@link CategoryProperty}s are {@link Serializable}, but do not
220    * assume that Lucene's {@link Attribute}s are as well
221    * @throws IOException 
222    */
223   protected void serializeCategoryAttribute(ObjectOutputStream out,
224       CategoryAttribute ca) throws IOException {
225     out.writeObject(ca.getCategoryPath());
226     Set<Class<? extends CategoryProperty>> propertyClasses = ca.getPropertyClasses();
227     if (propertyClasses != null) {
228       out.writeInt(propertyClasses.size());
229       for (Class<? extends CategoryProperty> clazz : propertyClasses) {
230         out.writeObject(ca.getProperty(clazz));
231       }
232     } else {
233       out.writeInt(0);
234     }
235   }
236   
237   /**
238    * Deserialize object from given {@link ObjectInputStream}
239    */
240   private void readObject(ObjectInputStream in) throws IOException,
241       ClassNotFoundException {
242     in.defaultReadObject();
243     map = new HashMap<CategoryPath, CategoryAttribute>();
244     int size = in.readInt();
245     for (int i = 0; i < size; i++) {
246       deserializeCategoryAttribute(in);
247     }
248   }
249
250   /**
251    * De-Serialize each of the {@link CategoryAttribute}s from the given
252    * {@link ObjectInputStream}.
253    */
254   protected void deserializeCategoryAttribute(ObjectInputStream in)
255       throws IOException, ClassNotFoundException {
256     CategoryPath cp = (CategoryPath) in.readObject();
257     int nProperties = in.readInt();
258     if (nProperties == 0) {
259       addCategory(cp);
260     } else {
261       for (int j = 0; j < nProperties; j++) {
262         CategoryProperty property = (CategoryProperty) in.readObject();
263         addCategory(cp, property);
264       }
265     }
266   }
267   
268   @Override
269   public boolean equals(Object o) {
270     if (! (o instanceof CategoryContainer)) {
271       return false;
272     }
273     
274     CategoryContainer that = (CategoryContainer)o;
275     return this.map.equals(that.map);
276   }
277   
278   @Override
279   public int hashCode() {
280     return map.hashCode();
281   }
282 }