package org.apache.lucene.facet.index.streaming; import java.io.IOException; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.facet.index.attributes.CategoryAttribute; import org.apache.lucene.facet.index.attributes.CategoryProperty; import org.apache.lucene.facet.index.attributes.OrdinalProperty; import org.apache.lucene.facet.index.categorypolicy.OrdinalPolicy; import org.apache.lucene.facet.index.categorypolicy.PathPolicy; import org.apache.lucene.facet.index.params.FacetIndexingParams; import org.apache.lucene.facet.taxonomy.CategoryPath; 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. */ /** * This class adds parents to a {@link CategoryAttributesStream}. The parents * are added according to the {@link PathPolicy} and {@link OrdinalPolicy} from * the {@link FacetIndexingParams} given in the constructor.<br> * By default, category properties are removed when creating parents of a * certain category. However, it is possible to retain certain property types * using {@link #addRetainableProperty(Class)}. * * @lucene.experimental */ public class CategoryParentsStream extends TokenFilter { /** * A {@link TaxonomyWriter} for adding categories and retrieving their * ordinals. */ protected TaxonomyWriter taxonomyWriter; /** An attribute containing all data related to the category */ protected CategoryAttribute categoryAttribute; /** A category property containing the category ordinal */ protected OrdinalProperty ordinalProperty; /** * A set of property classes that are to be retained when creating a parent * token. */ private Set<Class<? extends CategoryProperty>> retainableProperties; /** A {@link PathPolicy} for the category's parents' category paths. */ private PathPolicy pathPolicy; /** An {@link OrdinalPolicy} for the category's parents' ordinals. */ private OrdinalPolicy ordinalPolicy; /** * Constructor. * * @param input * The input stream to handle, must be derived from * {@link CategoryAttributesStream}. * @param taxonomyWriter * The taxonomy writer to use for adding categories and * retrieving their ordinals. * @param indexingParams * The indexing params used for filtering parents. */ public CategoryParentsStream(CategoryAttributesStream input, TaxonomyWriter taxonomyWriter, FacetIndexingParams indexingParams) { super(input); this.categoryAttribute = this.addAttribute(CategoryAttribute.class); this.taxonomyWriter = taxonomyWriter; this.pathPolicy = indexingParams.getPathPolicy(); this.ordinalPolicy = indexingParams.getOrdinalPolicy(); this.ordinalPolicy.init(taxonomyWriter); this.ordinalProperty = new OrdinalProperty(); } @Override public final boolean incrementToken() throws IOException { if (this.categoryAttribute.getCategoryPath() != null) { // try adding the parent of the current category to the stream clearCategoryProperties(); boolean added = false; // set the parent's ordinal, if illegal set -1 int ordinal = this.ordinalProperty.getOrdinal(); if (ordinal != -1) { ordinal = this.taxonomyWriter.getParent(ordinal); if (this.ordinalPolicy.shouldAdd(ordinal)) { this.ordinalProperty.setOrdinal(ordinal); try { this.categoryAttribute.addProperty(ordinalProperty); } catch (UnsupportedOperationException e) { throw new IOException(e.getLocalizedMessage()); } added = true; } else { this.ordinalProperty.setOrdinal(-1); } } // set the parent's category path, if illegal set null CategoryPath cp = this.categoryAttribute.getCategoryPath(); if (cp != null) { cp.trim(1); // if ordinal added, must also have category paths if (added || this.pathPolicy.shouldAdd(cp)) { this.categoryAttribute.setCategoryPath(cp); added = true; } else { this.categoryAttribute.clear(); } } if (added) { // a legal parent exists return true; } } // no more parents - get new category if (input.incrementToken()) { int ordinal = taxonomyWriter.addCategory(this.categoryAttribute.getCategoryPath()); this.ordinalProperty.setOrdinal(ordinal); try { this.categoryAttribute.addProperty(this.ordinalProperty); } catch (UnsupportedOperationException e) { throw new IOException(e.getLocalizedMessage()); } return true; } return false; } /** * Clear the properties of the current {@link CategoryAttribute} attribute * before setting the parent attributes. <br> * It is possible to retain properties of certain types the parent tokens, * using {@link #addRetainableProperty(Class)}. */ protected void clearCategoryProperties() { if (this.retainableProperties == null || this.retainableProperties.isEmpty()) { this.categoryAttribute.clearProperties(); } else { List<Class<? extends CategoryProperty>> propertyClassesToRemove = new LinkedList<Class<? extends CategoryProperty>>(); for (Class<? extends CategoryProperty> propertyClass : this.categoryAttribute .getPropertyClasses()) { if (!this.retainableProperties.contains(propertyClass)) { propertyClassesToRemove.add(propertyClass); } } for (Class<? extends CategoryProperty> propertyClass : propertyClassesToRemove) { this.categoryAttribute.remove(propertyClass); } } } /** * Add a {@link CategoryProperty} class which is retained when creating * parent tokens. * * @param toRetain * The property class to retain. */ public void addRetainableProperty(Class<? extends CategoryProperty> toRetain) { if (this.retainableProperties == null) { this.retainableProperties = new HashSet<Class<? extends CategoryProperty>>(); } this.retainableProperties.add(toRetain); } }