/* * #! * 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.entry; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import net.ontopia.utils.CompactHashSet; import net.ontopia.utils.OntopiaRuntimeException; import net.ontopia.topicmaps.core.TopicMapStoreIF; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * INTERNAL: This is the primary implementation of the * TopicMapRepositoryIF interface. This class also implements the * TopicMapSourceIF interface making it a topic map source that * behaves as a facade for the results of multiple topic map * sources.<p> * * The reference keys used are the reference ids retrieved from * <code>reference.getId()</code>. An exception will be thrown if * there are duplicate reference keys.<p> * * The sources that are added to the repository must have unique * ids. If the source id is not specified, the source cannot be looked * up by id.<p> */ public class TopicMapSourceManager implements TopicMapRepositoryIF { // Define a logging category. static Logger log = LoggerFactory.getLogger(TopicMapSourceManager.class.getName()); protected Set<TopicMapSourceIF> sources = new CompactHashSet<TopicMapSourceIF>(); protected Map<String, TopicMapSourceIF> smap = new HashMap<String, TopicMapSourceIF>(); protected boolean refreshed = false; protected Map<String, TopicMapReferenceIF> keyrefs = new HashMap<String, TopicMapReferenceIF>(); // key: ref protected Map<TopicMapReferenceIF, String> refkeys = new HashMap<TopicMapReferenceIF, String>(); // ref: key public TopicMapSourceManager() { super(); } public TopicMapSourceManager(TopicMapSourceIF source) { addSource(source); } public TopicMapSourceManager(Collection<TopicMapSourceIF> sources) { Iterator<TopicMapSourceIF> iter = sources.iterator(); while (iter.hasNext()) addSource(iter.next()); } public synchronized Collection<TopicMapReferenceIF> getReferences() { if (!refreshed) refresh(); return keyrefs.values(); } public synchronized Collection<String> getReferenceKeys() { if (!refreshed) refresh(); return keyrefs.keySet(); } public synchronized TopicMapReferenceIF getReferenceByKey(String key) { if (!refreshed) refresh(); return keyrefs.get(key); } public synchronized String getReferenceKey(TopicMapReferenceIF ref) { return refkeys.get(ref); } public TopicMapStoreIF createStore(String refkey, boolean readonly) { TopicMapReferenceIF ref = getReferenceByKey(refkey); if (ref == null) throw new OntopiaRuntimeException("Topic map reference '" + refkey + "' not found."); try { return ref.createStore(readonly); } catch (java.io.IOException e) { throw new OntopiaRuntimeException(e); } } public synchronized TopicMapSourceIF getSourceById(String source_id) { return smap.get(source_id); } public synchronized Collection<TopicMapSourceIF> getSources() { return Collections.unmodifiableCollection(sources); } public synchronized void addSource(TopicMapSourceIF source) { // Add source to set of sources if (sources.add(source)) { refreshed = false; // Add to source id map, if source has id. String id = source.getId(); if (id != null) { if (!smap.containsKey(id)) smap.put(id, source); else throw new OntopiaRuntimeException("Source with id already exists: " + id); } } } public synchronized void removeSource(TopicMapSourceIF source) { // Remove source from set of sources if (sources.remove(source)) { refreshed = false; // remove from source id map if, if source has id. String id = source.getId(); if (id != null && smap.containsKey(id)) smap.remove(id); } } public synchronized void refresh() { // Clear reference map refreshed = false; keyrefs.clear(); refkeys.clear(); // Refresh sources and rebuild reference map Iterator<TopicMapSourceIF> siter = sources.iterator(); while (siter.hasNext()) { TopicMapSourceIF source = siter.next(); try { // Refresh source source.refresh(); Iterator<TopicMapReferenceIF> riter = source.getReferences().iterator(); while (riter.hasNext()) { TopicMapReferenceIF ref = riter.next(); // Create reference key and update reference map String refkey = createReferenceKey(ref); // ! System.out.println("KEY: " + refkey + " <= " + ref); keyrefs.put(refkey, ref); refkeys.put(ref, refkey); } } catch (Throwable t) { log.error("Could not refresh topic map source " + source + ". Ignoring.", t); } } refreshed = true; } /** * INTERNAL: Creates a unique id. This method is used by the refresh method to * generate unique ids for topic map references. */ protected String createReferenceKey(TopicMapReferenceIF ref) { // Complain if source id contains periods // ! String srcid = source.getId(); // ! if (srcid.indexOf('.') != -1) // ! throw new OntopiaRuntimeException("Source id cannot contain periods." + // srcid); TopicMapSourceIF source = ref.getSource(); if (source == null) throw new OntopiaRuntimeException( "The reference is not attached to a source: " + ref); // Complain if key already used String refkey = ref.getId(); if (keyrefs.containsKey(refkey)) throw new OntopiaRuntimeException("Duplicate reference keys: " + refkey); return refkey; } public synchronized void close() { Iterator<TopicMapReferenceIF> iter = refkeys.keySet().iterator(); while (iter.hasNext()) { TopicMapReferenceIF ref = iter.next(); try { if (ref.isOpen()) ref.close(); } catch (Exception e) { log.warn("Problems occurred when closing reference " + ref, e); } } for (TopicMapSourceIF source : sources) { String message = source.getId() + ": '" + source.getTitle() + "' (" + source + ")"; try { log.debug("Closing source " + message); source.close(); } catch (Exception e) { log.warn("Problems occurred when closing source " + message, e); } } } // -- legacy methods /** * INTERNAL: Gets the reference that has the given id. * * @deprecated replaced by getReferenceByKey(String) */ @Deprecated public TopicMapReferenceIF getReferenceById(String reference_id) { return getReferenceByKey(reference_id); } /** * INTERNAL: Returns true if the manager manages a reference with the given id. * * @deprecated use 'getReferenceByKey(key) != null' instead */ @Deprecated public boolean hasId(String reference_id) { return getReferenceByKey(reference_id) != null; } /** * INTERNAL: Gets the ids of the references managed by this manager. * * @deprecated replaced by getReferenceKeys() */ @Deprecated public Collection<String> getIds() { return getReferenceKeys(); } }