/* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed 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. * !# */ package net.ontopia.topicmaps.impl.utils; import java.io.IOException; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import net.ontopia.infoset.fulltext.core.IndexerIF; import net.ontopia.infoset.fulltext.topicmaps.DefaultTopicMapDocumentGenerator; import net.ontopia.infoset.fulltext.topicmaps.TopicMapDocumentGeneratorIF; import net.ontopia.topicmaps.core.OccurrenceIF; import net.ontopia.topicmaps.core.TMObjectIF; import net.ontopia.topicmaps.core.TopicNameIF; import net.ontopia.topicmaps.core.VariantNameIF; import net.ontopia.topicmaps.impl.basic.InMemoryTopicMapStore; import net.ontopia.topicmaps.impl.basic.InMemoryTopicMapTransaction; import net.ontopia.utils.OntopiaRuntimeException; /** * INTERNAL: The indexer manager will keep track of base names, * variants and occurrences that are changed in the topic map. These * fulltext index can later be synchronized through the * synchronizeIndex method. */ public class FulltextIndexManager extends BasicIndex { protected Collection<TMObjectIF> added; protected Collection<String> removed; protected Collection<TMObjectIF> changed; /** * INTERNAL: Registers the fulltext index manager with the event system of the * specified topic map store. * * @param store */ public FulltextIndexManager(InMemoryTopicMapStore store) { EventManagerIF emanager = store.getEventManager(); ObjectTreeManager otree = ((InMemoryTopicMapTransaction) store .getTransaction()).getObjectTreeManager(); // Initialize index maps added = new HashSet<TMObjectIF>(); removed = new HashSet<String>(); changed = new HashSet<TMObjectIF>(); // Initialize object tree event handlers [objects added or removed] EventListenerIF listener_add = new TMObjectIF_added(); EventListenerIF listener_rem = new TMObjectIF_removed(); otree.addListener(listener_add, TopicNameIF.EVENT_ADDED); otree.addListener(listener_rem, TopicNameIF.EVENT_REMOVED); otree.addListener(listener_add, VariantNameIF.EVENT_ADDED); otree.addListener(listener_rem, VariantNameIF.EVENT_REMOVED); otree.addListener(listener_add, OccurrenceIF.EVENT_ADDED); otree.addListener(listener_rem, OccurrenceIF.EVENT_REMOVED); // Initialize object property event handlers EventListenerIF listener_chg = new TMObjectIF_changed(); handlers.put(TopicNameIF.EVENT_SET_VALUE, listener_chg); handlers.put(VariantNameIF.EVENT_SET_VALUE, listener_chg); handlers.put(OccurrenceIF.EVENT_SET_VALUE, listener_chg); // Register dynamic index as event listener Iterator<String> iter = handlers.keySet().iterator(); while (iter.hasNext()) emanager.addListener(this, iter.next()); } /** * INTERNAL: Returns true if index manager has seen changes to the topic map, * so that the index must be updated. * * @return True if index must be updated. */ public boolean needSynchronization() { return !(added.isEmpty() && changed.isEmpty() && removed.isEmpty()); } /** * INTERNAL: Applies all changes made to the topic map to the specified * fulltext indexer. * * @param indexer * The fulltext indexer to synchronize against. * @return True if the index was modified. * @throws IOException */ public synchronized boolean synchronizeIndex(IndexerIF indexer) throws IOException { if (!needSynchronization()) { return false; } boolean changes = false; TopicMapDocumentGeneratorIF docgen = DefaultTopicMapDocumentGenerator.INSTANCE; // delete removed objects if (!removed.isEmpty()) { Iterator<String> iter = removed.iterator(); while (iter.hasNext()) { String oid = iter.next(); indexer.delete("object_id", oid); } removed.clear(); changes = true; } // delete changed objects if (!changed.isEmpty()) { Iterator<TMObjectIF> iter = changed.iterator(); while (iter.hasNext()) { TMObjectIF o = iter.next(); indexer.delete("object_id", o.getObjectId()); } } // add added objects if (!added.isEmpty()) { Iterator<TMObjectIF> iter = added.iterator(); while (iter.hasNext()) { TMObjectIF o = iter.next(); if (o instanceof OccurrenceIF) indexer.index(docgen.generate((OccurrenceIF) o)); else if (o instanceof TopicNameIF) indexer.index(docgen.generate((TopicNameIF) o)); else if (o instanceof VariantNameIF) indexer.index(docgen.generate((VariantNameIF) o)); else throw new OntopiaRuntimeException("Unknown type: " + o); } added.clear(); changes = true; } // add changed objects if (!changed.isEmpty()) { Iterator<TMObjectIF> iter = changed.iterator(); while (iter.hasNext()) { TMObjectIF o = iter.next(); if (o instanceof OccurrenceIF) indexer.index(docgen.generate((OccurrenceIF) o)); else if (o instanceof TopicNameIF) indexer.index(docgen.generate((TopicNameIF) o)); else if (o instanceof VariantNameIF) indexer.index(docgen.generate((VariantNameIF) o)); else throw new OntopiaRuntimeException("Unknown type: " + o); } changed.clear(); changes = true; } return changes; } // ----------------------------------------------------------------------------- // Callbacks // ----------------------------------------------------------------------------- protected synchronized void objectAdded(Object object) { TMObjectIF tmo = (TMObjectIF) object; removed.remove(tmo.getObjectId()); changed.remove(tmo); added.add(tmo); } protected synchronized void objectRemoved(Object object) { TMObjectIF tmo = (TMObjectIF) object; added.remove(tmo); changed.remove(tmo); removed.add(tmo.getObjectId()); } protected synchronized void objectChanged(Object object) { TMObjectIF tmo = (TMObjectIF) object; if (!added.contains(tmo) && !removed.contains(tmo.getObjectId())) changed.add(tmo); } // ----------------------------------------------------------------------------- // Event handlers // ----------------------------------------------------------------------------- /** * EventHandler: TMObjectIF changed */ class TMObjectIF_changed extends EventHandler { public void processEvent(Object object, String event, Object new_value, Object old_value) { objectChanged(object); } } /** * EventHandler: TMObjectIF added to topic map */ class TMObjectIF_added extends EventHandler { public void processEvent(Object object, String event, Object new_value, Object old_value) { objectAdded(new_value); } } /** * EventHandler: TMObjectIF removed from topic map */ class TMObjectIF_removed extends EventHandler { public void processEvent(Object object, String event, Object new_value, Object old_value) { objectRemoved(old_value); } } }