From: <lh...@us...> - 2008-11-21 13:17:53
|
Revision: 223 http://tinytim.svn.sourceforge.net/tinytim/?rev=223&view=rev Author: lheuer Date: 2008-11-21 13:17:29 +0000 (Fri, 21 Nov 2008) Log Message: ----------- - Removed version info from XMLWriter - Added Sesame - Added backport of XTM(10|20)TopicMapWriter for 1.5 - Migrated 1.5 org.tinytim.cxtm.Canonicalizer to mio as CXTMTopicMapWriter (implements TopicMapWriter) for 1.5 - Removed datatype normalization from CXTMTopicMapWriter (1.5) Modified Paths: -------------- tinytim-mio/trunk/build-15.xml tinytim-mio/trunk/build.xml tinytim-mio/trunk/src/main/java/org/tinytim/mio/XMLWriter.java Added Paths: ----------- tinytim-mio/trunk/lib/openrdf-sesame-2.2.1-onejar.jar tinytim-mio/trunk/src/main/java/org/tinytim/mio15/ tinytim-mio/trunk/src/main/java/org/tinytim/mio15/AbstractTopicMapWriter.java tinytim-mio/trunk/src/main/java/org/tinytim/mio15/CXTMTopicMapWriter.java tinytim-mio/trunk/src/main/java/org/tinytim/mio15/TinyTimMapInputHandler.java tinytim-mio/trunk/src/main/java/org/tinytim/mio15/XTM10TopicMapWriter.java tinytim-mio/trunk/src/main/java/org/tinytim/mio15/XTM20TopicMapWriter.java Removed Paths: ------------- tinytim-mio/trunk/src/main/java/org/tinytim/mio/TinyTimMapInputHandler-15.txt Modified: tinytim-mio/trunk/build-15.xml =================================================================== --- tinytim-mio/trunk/build-15.xml 2008-11-20 16:55:44 UTC (rev 222) +++ tinytim-mio/trunk/build-15.xml 2008-11-21 13:17:29 UTC (rev 223) @@ -121,11 +121,10 @@ <!-- Compile source files --> <!-- =================================================================== --> <target name="compile" depends="clean, prepare"> - <copy file="${dir.src}/org/tinytim/mio/TinyTimMapInputHandler.java" tofile="${dir.src}/org/tinytim/mio/TinyTimMapInputHandler-20.txt"/> - <copy file="${dir.src}/org/tinytim/mio/TinyTimMapInputHandler-15.txt" tofile="${dir.src}/org/tinytim/mio/TinyTimMapInputHandler.java" overwrite="true"/> <javac destdir="${dir.build.classes}" debug="${debug}" - excludes="org/tinytim/mio/XMLWriter* org/tinytim/mio/Abstract*Writer* org/tinytim/mio/CXTMTopicMapWriter* org/tinytim/mio/XTM*TopicMapWriter*" + excludes="org/tinytim/mio/AbstractTopicMapWriter* org/tinytim/mio/CXTMTopicMapWriter* org/tinytim/mio/XTM*TopicMapWriter*" + includes="org/tinytim/mio15/** org/tinytim/mio/*TopicMapReader* org/tinytim/mio/*TopicMapImporter*" target="1.5"> <classpath> <pathelement location="${lib.tmapi}"/> @@ -135,8 +134,6 @@ </classpath> <src path="${dir.src}"/> </javac> - <copy file="${dir.src}/org/tinytim/mio/TinyTimMapInputHandler-20.txt" tofile="${dir.src}/org/tinytim/mio/TinyTimMapInputHandler.java" overwrite="true"/> - <delete file="${dir.src}/org/tinytim/mio/TinyTimMapInputHandler-20.txt"/> </target> <!-- =================================================================== --> Modified: tinytim-mio/trunk/build.xml =================================================================== --- tinytim-mio/trunk/build.xml 2008-11-20 16:55:44 UTC (rev 222) +++ tinytim-mio/trunk/build.xml 2008-11-21 13:17:29 UTC (rev 223) @@ -123,7 +123,8 @@ <target name="compile" depends="clean, prepare"> <javac destdir="${dir.build.classes}" debug="${debug}" - target="1.5"> + target="1.5" + excludes="org/tinytim/mio15/**"> <classpath> <pathelement location="${lib.tmapi}"/> <pathelement location="${lib.tinytim}"/> Added: tinytim-mio/trunk/lib/openrdf-sesame-2.2.1-onejar.jar =================================================================== (Binary files differ) Property changes on: tinytim-mio/trunk/lib/openrdf-sesame-2.2.1-onejar.jar ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Deleted: tinytim-mio/trunk/src/main/java/org/tinytim/mio/TinyTimMapInputHandler-15.txt =================================================================== --- tinytim-mio/trunk/src/main/java/org/tinytim/mio/TinyTimMapInputHandler-15.txt 2008-11-20 16:55:44 UTC (rev 222) +++ tinytim-mio/trunk/src/main/java/org/tinytim/mio/TinyTimMapInputHandler-15.txt 2008-11-21 13:17:29 UTC (rev 223) @@ -1,590 +0,0 @@ -/* - * Copyright 2008 Lars Heuer (heuer[at]semagia.com) - * - * 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 org.tinytim.mio; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.logging.Logger; - -import org.tinytim.IReifiable; -import org.tinytim.ITyped; -import org.tinytim.TopicMapImpl; -import org.tinytim.TypeInstanceConverter; -import org.tinytim.voc.TMDM; -import org.tmapi.core.Association; -import org.tmapi.core.AssociationRole; -import org.tmapi.core.Locator; -import org.tmapi.core.Occurrence; -import org.tmapi.core.ScopedObject; -import org.tmapi.core.Topic; -import org.tmapi.core.TopicMap; -import org.tmapi.core.TopicMapObject; -import org.tmapi.core.TopicName; -import org.tmapi.core.Variant; - -import com.semagia.mio.IMapHandler; -import com.semagia.mio.IRef; -import com.semagia.mio.MIOException; -import com.semagia.mio.voc.XSD; - -/** - * {@link com.semagia.mio.IMapHandler} implementation. - * - * @author Lars Heuer (heuer[at]semagia.com) <a href="http://www.semagia.com/">Semagia</a> - * @version $Rev: 123 $ - $Date: 2008-08-14 14:05:34 +0200 (Do, 14 Aug 2008) $ - */ -public class TinyTimMapInputHandler implements IMapHandler { - - private enum State { - INITIAL, TOPIC, ASSOCIATION, ROLE, OCCURRENCE, NAME, VARIANT, - SCOPE, THEME, REIFIER, PLAYER, ISA, TYPE; - } - - private static final Logger LOG = Logger.getLogger(TinyTimMapInputHandler.class.getName()); - - private TopicMapImpl _tm; - private List<State> _stateStack; - private List<TopicMapObject> _constructStack; - - public TinyTimMapInputHandler() { - } - - public TinyTimMapInputHandler(TopicMap topicMap) { - this(); - setTopicMap(topicMap); - } - - /** - * Sets the topic map instance to operate on. - * - * @param topicMap The topic map. - */ - public void setTopicMap(TopicMap topicMap) { - if (topicMap == null) { - throw new IllegalArgumentException("The topic map must not be null"); - } - _tm = (TopicMapImpl) topicMap; - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startTopicMap() - */ - public void startTopicMap() throws MIOException { - _constructStack = new ArrayList<TopicMapObject>(); - _stateStack = new ArrayList<State>(); - _enterState(State.INITIAL, _tm); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endTopicMap() - */ - public void endTopicMap() throws MIOException { - TypeInstanceConverter.convertAssociationsToTypes(_tm); - _constructStack = null; - _stateStack = null; - _tm = null; - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startTopic(com.semagia.mio.IRef) - */ - public void startTopic(IRef identity) throws MIOException { - _enterState(State.TOPIC, _createTopic(identity)); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endTopic() - */ - public void endTopic() throws MIOException { - Topic topic = (Topic) _leaveStatePopConstruct(State.TOPIC); - _handleTopic(topic); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startAssociation() - */ - public void startAssociation() throws MIOException { - _enterState(State.ASSOCIATION, _tm.createAssociation()); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endAssociation() - */ - public void endAssociation() throws MIOException { - _leaveStatePopConstruct(State.ASSOCIATION); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startRole() - */ - public void startRole() throws MIOException { - assert _state() == State.ASSOCIATION; - _enterState(State.ROLE, ((Association) _peekConstruct()).createAssociationRole(null, null)); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endRole() - */ - public void endRole() throws MIOException { - _leaveStatePopConstruct(State.ROLE); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startPlayer() - */ - public void startPlayer() throws MIOException { - assert _state() == State.ROLE; - _enterState(State.PLAYER); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endPlayer() - */ - public void endPlayer() throws MIOException { - _leaveState(State.PLAYER); - assert _state() == State.ROLE; - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startOccurrence() - */ - public void startOccurrence() throws MIOException { - _enterState(State.OCCURRENCE, _peekTopic().createOccurrence((Locator) null, null, null)); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endOccurrence() - */ - public void endOccurrence() throws MIOException { - _leaveStatePopConstruct(State.OCCURRENCE); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startName() - */ - public void startName() throws MIOException { - _enterState(State.NAME, _peekTopic().createTopicName(null, null)); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endName() - */ - public void endName() throws MIOException { - TopicName name = (TopicName) _leaveStatePopConstruct(State.NAME); - if (name.getType() == null) { - name.setType(_topicBySubjectIdentifier(TMDM.TOPIC_NAME)); - } - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startVariant() - */ - public void startVariant() throws MIOException { - assert _state() == State.NAME; - _enterState(State.VARIANT, ((TopicName) _peekConstruct()).createVariant((Locator) null, null)); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endVariant() - */ - @SuppressWarnings("unchecked") - public void endVariant() throws MIOException { - Variant variant = (Variant) _leaveStatePopConstruct(State.VARIANT); - Collection<Topic> scope = variant.getScope(); - if (scope.isEmpty() || variant.getTopicName().getScope().equals(scope)) { - throw new MIOException("The variant has no scope"); - } - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startType() - */ - public void startType() throws MIOException { - assert _peekConstruct() instanceof ITyped; - _enterState(State.TYPE); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endType() - */ - public void endType() throws MIOException { - _leaveState(State.TYPE); - assert _peekConstruct() instanceof ITyped; - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startScope() - */ - public void startScope() throws MIOException { - assert _peekConstruct() instanceof ScopedObject; - _enterState(State.SCOPE); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endScope() - */ - public void endScope() throws MIOException { - _leaveState(State.SCOPE); - assert _peekConstruct() instanceof ScopedObject; - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startTheme() - */ - public void startTheme() throws MIOException { - assert _state() == State.SCOPE; - _enterState(State.THEME); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endTheme() - */ - public void endTheme() throws MIOException { - _leaveState(State.THEME); - assert _state() == State.SCOPE; - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#subjectIdentifier(java.lang.String) - */ - public void subjectIdentifier(String subjectIdentifier) throws MIOException { - Locator sid = _tm.createLocator(subjectIdentifier); - Topic topic = _peekTopic(); - Topic existing = _tm.getTopicBySubjectIdentifier(sid); - if (existing != null && !existing.equals(topic)) { - _merge(existing, topic); - } - else { - TopicMapObject tmo = _tm.getObjectByItemIdentifier(sid); - if (tmo != null && tmo instanceof Topic && !tmo.equals(topic)) { - _merge((Topic) tmo, topic); - } - } - topic.addSubjectIdentifier(sid); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#subjectLocator(java.lang.String) - */ - public void subjectLocator(String subjectLocator) throws MIOException { - Locator slo = _tm.createLocator(subjectLocator); - Topic topic = _peekTopic(); - Topic existing = _tm.getTopicBySubjectLocator(slo); - if (existing != null && !existing.equals(topic)) { - _merge(existing, topic); - } - topic.addSubjectLocator(slo); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#itemIdentifier(java.lang.String) - */ - public void itemIdentifier(String itemIdentifier) throws MIOException { - Locator iid = _tm.createLocator(itemIdentifier); - TopicMapObject tmo = _peekConstruct(); - if (_state() == State.TOPIC) { - TopicMapObject existing = _tm.getObjectByItemIdentifier(iid); - if (existing != null && existing instanceof Topic && !existing.equals(tmo)) { - _merge((Topic) existing, (Topic) tmo); - } - else { - Topic topic = _tm.getTopicBySubjectIdentifier(iid); - if (topic != null && !topic.equals(tmo)) { - _merge(topic, (Topic) tmo); - } - } - } - tmo.addSourceLocator(iid); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startIsa() - */ - public void startIsa() throws MIOException { - assert _state() == State.TOPIC; - _enterState(State.ISA); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endIsa() - */ - public void endIsa() throws MIOException { - _leaveState(State.ISA); - assert _state() == State.TOPIC; - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#startReifier() - */ - public void startReifier() throws MIOException { - assert _peekConstruct() instanceof IReifiable; - _enterState(State.REIFIER); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#endReifier() - */ - public void endReifier() throws MIOException { - _leaveState(State.REIFIER); - assert _peekConstruct() instanceof IReifiable; - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#topicRef(com.semagia.mio.IRef) - */ - public void topicRef(IRef identity) throws MIOException { - _handleTopic(_createTopic(identity)); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#value(java.lang.String) - */ - public void value(String value) throws MIOException { - assert _state() == State.NAME; - ((TopicName) _peekConstruct()).setValue(value); - } - - /* (non-Javadoc) - * @see com.semagia.mio.IMapHandler#value(java.lang.String, java.lang.String) - */ - public void value(String value, String datatype) throws MIOException { - boolean isLocator = XSD.ANY_URI.equals(datatype); - if (!isLocator && !XSD.STRING.equals(datatype)) { - LOG.warning("The datatype '" + datatype + "' was converted into xsd:string"); - } - if (_state() == State.OCCURRENCE) { - Occurrence occ = (Occurrence) _peekConstruct(); - if (isLocator) { - occ.setResource(_tm.createLocator(value)); - } - else { - occ.setValue(value); - } - } - else { - assert _state() == State.VARIANT; - Variant variant = (Variant) _peekConstruct(); - if (isLocator) { - variant.setResource(_tm.createLocator(value)); - } - else { - variant.setValue(value); - } - } - } - - /** - * Enters a state. - * - * @param state The state to push ontop of the state stack. - */ - private void _enterState(State state) { - _stateStack.add(state); - } - - /** - * Enters a state and pushes the Topic Maps construct ontop of the construct - * stack. - * - * @param state The state to enter. - * @param tmo The Topic Maps construct which should be pushed to the stack. - */ - private void _enterState(State state, TopicMapObject tmo) { - _enterState(state); - _constructStack.add(tmo); - } - - /** - * Leaves a state. - * - * @param state The state to leave. - * @throws MIOException If the state is not equals to the current state. - */ - private void _leaveState(State state) throws MIOException { - State current = _stateStack.remove(_stateStack.size()-1); - if (state != current) { - _reportError("Unexpected state: " + current + ", expected: " + state); - } - } - - /** - * Leaves a state and removed the Topic Maps construct from the top of the - * construct stack. - * - * @param state The state to leave. - * @return The removed construct. - * @throws MIOException If the state is not equals to the current state. - */ - private TopicMapObject _leaveStatePopConstruct(State state) throws MIOException { - _leaveState(state); - return _constructStack.remove(_constructStack.size()-1); - } - - /** - * Returns the Topic Maps construct on top of the stack. - * - * @return The Topic Maps construct. - */ - private TopicMapObject _peekConstruct() { - return _constructStack.get(_constructStack.size()-1); - } - - /** - * Returns the topic on top of the stack. - * - * @return The topic. - */ - private Topic _peekTopic() { - return (Topic) _peekConstruct(); - } - - /** - * Returns the current state. - * - * @return The current state. - */ - private State _state() { - return _stateStack.get(_stateStack.size()-1); - } - - /** - * Handles the topic dependent on the current state. - * - * @param topic The topic to handle. - */ - private void _handleTopic(Topic topic) { - switch (_state()) { - case ISA: _peekTopic().addType(topic); break; - case TYPE: ((ITyped) _peekConstruct()).setType(topic); break; - case PLAYER: ((AssociationRole) _peekConstruct()).setPlayer(topic); break; - case THEME: ((ScopedObject) _peekConstruct()).addScopingTopic(topic); break; - case REIFIER: ((IReifiable) _peekConstruct()).setReifier(topic); break; - } - } - - /** - * Merges the <tt>source</tt> topic with the <tt>target</tt>. - * - * Further, this method ensures that the construct stack stays valid: If - * the <tt>source</tt> is part of the stack, it is replaced with - * <tt>target</tt>. - * - * @param source The source topic (will be removed). - * @param target The target topic. - */ - private void _merge(Topic source, Topic target) { - int i = _constructStack.indexOf(source); - while (i > -1) { - _constructStack.set(i, target); - i = _constructStack.indexOf(source); - } - target.mergeIn(source); - } - - /** - * Returns either an existing topic with the specified identity or creates - * a topic with the given identity. - * - * @param ref The identity of the topic. - * @return A topic instance. - * @throws MIOException - */ - private Topic _createTopic(IRef ref) throws MIOException { - Locator loc = _tm.createLocator(ref.getIRI()); - switch (ref.getType()) { - case IRef.ITEM_IDENTIFIER: return _topicByItemIdentifier(loc); - case IRef.SUBJECT_IDENTIFIER: return _topicBySubjectIdentifier(loc); - case IRef.SUBJECT_LOCATOR: return _topicBySubjectLocator(loc); - default: _reportError("Unknown reference type " + ref.getType()); - } - // Never returned, an exception was thrown - return null; - } - - /** - * Returns either an existing topic with the specified item identfier, - * or creates a topic with the given item identifier. - * - * @param iid The item identifier of the topic. - * @return A topic instance. - */ - private Topic _topicByItemIdentifier(Locator iid) { - TopicMapObject tmo = _tm.getObjectByItemIdentifier(iid); - Topic topic = (tmo instanceof Topic) ? (Topic) tmo : null; - if (topic == null) { - topic = _tm.getTopicBySubjectIdentifier(iid); - if (topic != null) { - topic.addSourceLocator(iid); - } - } - if (topic == null) { - topic = _tm.createTopic(); - topic.addSourceLocator(iid); - } - return topic; - } - - /** - * Returns either an existing topic with the specified subject identfier, - * or creates a topic with the given subject identifier. - * - * @param sid The subject identifier of the topic. - * @return A topic instance. - */ - private Topic _topicBySubjectIdentifier(Locator sid) { - Topic topic = _tm.getTopicBySubjectIdentifier(sid); - if (topic == null) { - TopicMapObject tmo = _tm.getObjectByItemIdentifier(sid); - if (tmo instanceof Topic) { - topic = (Topic) tmo; - topic.addSubjectIdentifier(sid); - } - } - if (topic == null) { - topic = _tm.createTopic(); - topic.addSubjectIdentifier(sid); - } - return topic; - } - - /** - * Returns either an existing topic with the specified subject locator, - * or creates a topic with the given subject locator. - * - * @param slo The subject locator of the topic. - * @return A topic instance. - */ - private Topic _topicBySubjectLocator(Locator slo) { - Topic topic = _tm.getTopicBySubjectLocator(slo); - if (topic == null) { - topic = _tm.createTopic(); - topic.addSubjectLocator(slo); - } - return topic; - } - - /** - * Reports an error. - * - * @param msg The error message. - * @throws MIOException Thrown in any case. - */ - private static void _reportError(String msg) throws MIOException { - throw new MIOException(msg); - } - -} Modified: tinytim-mio/trunk/src/main/java/org/tinytim/mio/XMLWriter.java =================================================================== --- tinytim-mio/trunk/src/main/java/org/tinytim/mio/XMLWriter.java 2008-11-20 16:55:44 UTC (rev 222) +++ tinytim-mio/trunk/src/main/java/org/tinytim/mio/XMLWriter.java 2008-11-21 13:17:29 UTC (rev 223) @@ -19,7 +19,6 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; -import org.tinytim.Version; import org.xml.sax.Attributes; import org.xml.sax.helpers.AttributesImpl; @@ -58,9 +57,7 @@ _out.write(_encoding); _out.write("\" standalone=\"yes\"?>"); _newline(); - _out.write("<!-- Generated by tinyTiM v"); - _out.write(Version.RELEASE); - _out.write(" - http://tinytim.sourceforge.net/ -->"); + _out.write("<!-- Generated by tinyTiM -- http://tinytim.sourceforge.net/ -->"); _newline(); _depth = 0; } Added: tinytim-mio/trunk/src/main/java/org/tinytim/mio15/AbstractTopicMapWriter.java =================================================================== --- tinytim-mio/trunk/src/main/java/org/tinytim/mio15/AbstractTopicMapWriter.java (rev 0) +++ tinytim-mio/trunk/src/main/java/org/tinytim/mio15/AbstractTopicMapWriter.java 2008-11-21 13:17:29 UTC (rev 223) @@ -0,0 +1,73 @@ +/* + * Copyright 2008 Lars Heuer (heuer[at]semagia.com) + * + * 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 org.tinytim.mio; + +import java.util.Set; + +import org.tmapi.core.Locator; +import org.tmapi.core.Topic; + +/** + * Common, abstract superclass for {@link TopicMapWriter} implementations. + * + * @author Lars Heuer (heuer[at]semagia.com) <a href="http://www.semagia.com/">Semagia</a> + * @version $Rev$ - $Date$ + */ +abstract class AbstractTopicMapWriter implements TopicMapWriter { + + protected final String _baseIRI; + + protected AbstractTopicMapWriter(final String baseIRI) { + _baseIRI = baseIRI; + } + + /** + * Returns an identifier for the topic. + * <p> + * The algorithm tries to avoid to use the internal identifier which may + * cause yet another item identifier. If the topic has an item identifier + * which starts with the specified IRI provided in the constructor, the + * algorithm tries to use the fragment identifier. + * </p> + * + * @param topic The topic to return an identifier for. + * @return An identifier, never <tt>null</tt>. + */ + protected String _getId(final Topic topic) { + String id = null; + @SuppressWarnings("unchecked") + Set<Locator> iids = topic.getSourceLocators(); + for (Locator loc: iids) { + String reference = loc.getReference(); + if (!reference.startsWith(_baseIRI)) { + continue; + } + int fragIdx = reference.indexOf('#'); + if (fragIdx < 0) { + continue; + } + id = reference.substring(fragIdx+1); + if (id.startsWith("id-")) { + id = null; + } + if (id != null) { + break; + } + } + return id != null ? id : "id-" + topic.getObjectId(); + } + +} Property changes on: tinytim-mio/trunk/src/main/java/org/tinytim/mio15/AbstractTopicMapWriter.java ___________________________________________________________________ Added: svn:keywords + Rev Date Id Added: svn:eol-style + native Added: tinytim-mio/trunk/src/main/java/org/tinytim/mio15/CXTMTopicMapWriter.java =================================================================== --- tinytim-mio/trunk/src/main/java/org/tinytim/mio15/CXTMTopicMapWriter.java (rev 0) +++ tinytim-mio/trunk/src/main/java/org/tinytim/mio15/CXTMTopicMapWriter.java 2008-11-21 13:17:29 UTC (rev 223) @@ -0,0 +1,1261 @@ +/* + * Copyright 2008 Lars Heuer (heuer[at]semagia.com) + * + * 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 org.tinytim.mio; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.logging.Logger; + +import org.tinytim.DuplicateRemovalUtils; +import org.tinytim.IConstruct; +import org.tinytim.IDatatypeAwareConstruct; +import org.tinytim.IReifiable; +import org.tinytim.ITyped; +import org.tinytim.TopicMapImpl; +import org.tinytim.index.ITypeInstanceIndex; +import org.tinytim.voc.TMDM; +import org.tmapi.core.Association; +import org.tmapi.core.AssociationRole; +import org.tmapi.core.DuplicateSourceLocatorException; +import org.tmapi.core.Locator; +import org.tmapi.core.MergeException; +import org.tmapi.core.ModelConstraintException; +import org.tmapi.core.Occurrence; +import org.tmapi.core.ScopedObject; +import org.tmapi.core.TMAPIException; +import org.tmapi.core.Topic; +import org.tmapi.core.TopicInUseException; +import org.tmapi.core.TopicMap; +import org.tmapi.core.TopicMapObject; +import org.tmapi.core.TopicName; +import org.tmapi.core.Variant; + +import org.xml.sax.Attributes; +import org.xml.sax.helpers.AttributesImpl; + +/** + * Provides serialization of topic maps into Canonical XTM (CXTM). + * <p> + * CXTM is a format that guarantees that two equivalent Topic Maps Data Model + * instances [ISO/IEC 13250-2] will always produce byte-by-byte identical + * serializations, and that non-equivalent instances will always produce + * different serializations. + * </p> + * <p> + * See <a href="http://www.isotopicmaps.org/cxtm/">http://www.isotopicmaps.org/cxtm/</a> + * for details. + * </p> + * <p> + * <em>CAUTION</em>: This class implements the + * <a href="http://www.isotopicmaps.org/cxtm/">CXTM draft dtd. 2008-05-15</a>, + * the output may change in the future. + * </p> + * <p> + * The canonicalizer IS NOT a generic TMAPI-compatible implementation. It + * requires tinyTiM. The canonicalizer requires that the property + * {@link org.tinytim.Property#XTM10_REIFICATION} is set to <tt>false</tt> and + * that the property {@link org.tinytim.Property#INHERIT_NAME_SCOPE} is enabled + * (set to <tt>true</tt>). + * </p> + * @author Lars Heuer (heuer[at]semagia.com) <a href="http://www.semagia.com/">Semagia</a> + * @version $Rev$ - $Date$ + */ +public final class CXTMTopicMapWriter implements TopicMapWriter { + + private static final Logger LOG = Logger.getLogger(CXTMTopicMapWriter.class.getName()); + + private static final String _XSD_ANY_URI = "http://www.w3.org/2001/XMLSchema#anyURI"; + + private static final AssociationRole[] _EMPTY_ROLES = new AssociationRole[0]; + + private Topic _type; + private Topic _instance; + private Topic _typeInstance; + + private XMLC14NWriter _out; + private final String _normBase; + private Map<TopicMapObject, Integer> _construct2Id; + private Map<Topic, List<AssociationRole>> _topic2Roles; + private Map<String, String> _locator2Norm; + + private Comparator<Topic> _topicComparator; + private Comparator<Association> _assocComparator; + private Comparator<AssociationRole> _roleComparator; + private Comparator<Occurrence> _occComparator; + private Comparator<TopicName> _nameComparator; + private Comparator<Variant> _variantComparator; + private Comparator<Set<Locator>> _locSetComparator; + private Comparator<Locator> _locComparator; + private Comparator<Set<Topic>> _scopeComparator; + + private Map<Association, AssociationRole[]> _assoc2Roles; + + /** + * Creates a canonicalizer. + * + * @param out The stream the CXTM is written onto. + * @param baseLocator The base locator which is used to resolve IRIs against. + * @throws IOException If an error occurs. + */ + public CXTMTopicMapWriter(OutputStream out, String baseLocator) throws IOException { + if (baseLocator == null) { + throw new IllegalArgumentException("The base locator must not be null"); + } + _out = new XMLC14NWriter(out); + _normBase = _normalizeBaseLocator(baseLocator); + _topicComparator = new TopicComparator(); + _assocComparator = new AssociationComparator(); + _roleComparator = new RoleComparator(); + _occComparator = new OccurrenceComparator(); + _nameComparator = new NameComparator(); + _variantComparator = new VariantComparator(); + _locSetComparator = new LocatorSetComparator(); + _locComparator = new LocatorComparator(); + _scopeComparator = new ScopeComparator(); + } + + /** + * Serializes the specified <code>topicMap</code> into the CXTM format. + * <p> + * <em>CAUTION</em>: This method MAY modify the topic map since duplicate + * Topic Maps constructs (if any) are removed in advance. + * </p> + * <p> + * The topic map's base locator + * ({@link org.tmapi.core.TopicMap#getBaseLocator()}) is ignored. + * </p> + * + * @param topicMap The topic map to serialize. + * @throws IOException If an error occurs. + */ + public void write(TopicMap topicMap) throws IOException { + DuplicateRemovalUtils.removeDuplicates(topicMap); + _construct2Id = new IdentityHashMap<TopicMapObject, Integer>(); + _locator2Norm = new HashMap<String, String>(); + _assoc2Roles = new IdentityHashMap<Association, AssociationRole[]>(); + _topic2Roles = new IdentityHashMap<Topic, List<AssociationRole>>(); + ITypeInstanceIndex typeInstanceIndex = ((TopicMapImpl) topicMap).getIndexManager().getTypeInstanceIndex(); + if (!typeInstanceIndex.isAutoUpdated()) { + typeInstanceIndex.reindex(); + } + Topic[] topics = _fetchTopics(topicMap, typeInstanceIndex); + Association[] assocs = _fetchAssociations(topicMap, typeInstanceIndex); + typeInstanceIndex.close(); + _createIndex(topics, assocs); + _out.startDocument(); + AttributesImpl attrs = new AttributesImpl(); + _addReifier(attrs, (IReifiable)topicMap); + _out.startElement("topicMap", attrs); + _out.newline(); + _writeItemIdentifiers(topicMap); + for (Topic topic: topics) { + _writeTopic(topic); + } + for (Association assoc: assocs) { + _writeAssociation(assoc); + } + _out.endElement("topicMap"); + _out.newline(); + _out.endDocument(); + _out = null; + _construct2Id = null; + _locator2Norm = null; + _assoc2Roles = null; + _topic2Roles = null; + } + + /** + * Returns an unsorted array of topics which should be included into + * the output. + * + * This method may return more topics than {@link TopicMap#getTopics()} + * since this method creates virtual topics to model type-instance + * relationships properly. + * + * @param topicMap The topic map from which the topic should be serialized. + * @param idx A (upto date) type instance index. + * @return All topics which must be included into the output. + */ + @SuppressWarnings("unchecked") + private Topic[] _fetchTopics(TopicMap topicMap, ITypeInstanceIndex idx) { + Collection<Topic> types = idx.getTopicTypes(); + if (types.isEmpty()) { + Set<Topic> topics = topicMap.getTopics(); + return topics.toArray(new Topic[topics.size()]); + } + else { + List<Topic> topics = new ArrayList<Topic>(topicMap.getTopics()); + TopicMapImpl tm = (TopicMapImpl) topicMap; + _typeInstance = _getTopicBySubjectIdentifier(tm, topics, TMDM.TYPE_INSTANCE); + _type = _getTopicBySubjectIdentifier(tm, topics, TMDM.TYPE); + _instance = _getTopicBySubjectIdentifier(tm, topics, TMDM.INSTANCE); + return topics.toArray(new Topic[topics.size()]); + } + } + + /** + * Returns a topic by its subject identifier. If the topic is null, a + * {@link TypeInstanceTopic} is created, added to the <code>topics</code> + * and returned. + * + * @param tm The topic map to fetch the topic from. + * @param topics A modifiable collection of topics. + * @param sid The subject identifier. + * @return A topic with the specified subject identifier. + */ + private Topic _getTopicBySubjectIdentifier(TopicMapImpl tm, Collection<Topic> topics, Locator sid) { + Topic topic = tm.getTopicBySubjectIdentifier(sid); + if (topic == null) { + topic = new TypeInstanceTopic(sid); + topics.add(topic); + } + return topic; + } + + /** + * Returns an unsorted array of associations which should be serialized. + * + * This method may return more association than {@link TopicMap#getAssociations()} + * since this method may create virtual associations which are used to + * model type-instance relationships properly. + * + * @param tm The topic map from which the associations should be serialized. + * @param idx A (upto date) type instance index. + * @return An unsorted array of associations which must be included into the output. + */ + @SuppressWarnings("unchecked") + private Association[] _fetchAssociations(TopicMap tm, ITypeInstanceIndex idx) { + Collection<Topic> types = idx.getTopicTypes(); + if (types.isEmpty()) { + Set<Association> assocs = tm.getAssociations(); + return assocs.toArray(new Association[assocs.size()]); + } + else { + List<Association> assocs = new ArrayList<Association>(tm.getAssociations()); + for (Topic type: types) { + for (Topic instance: idx.getTopics(type)) { + assocs.add(new TypeInstanceAssociation(type, instance)); + } + } + return assocs.toArray(new Association[assocs.size()]); + } + } + + /** + * Creates the index on which the canonicalizer operates. + * + * As sideeffect, the provided topic and association arrays get sorted. + * + * @param topics An array of topics. + * @param assocs An array of associations. + */ + @SuppressWarnings("unchecked") + private void _createIndex(Topic[] topics, Association[] assocs) { + Arrays.sort(topics, _topicComparator); + Topic topic = null; + for (int i=0; i < topics.length; i++) { + topic = topics[i]; + _construct2Id.put(topic, Integer.valueOf(i+1)); + } + Arrays.sort(assocs, _assocComparator); + Association assoc = null; + for (int i=0; i < assocs.length; i++) { + assoc = assocs[i]; + _construct2Id.put(assoc, Integer.valueOf(i+1)); + Set<AssociationRole> roles_ = assoc.getAssociationRoles(); + AssociationRole[] roles = roles_.toArray(new AssociationRole[roles_.size()]); + Arrays.sort(roles, _roleComparator); + _assoc2Roles.put(assoc, roles); + for (int j=0; j < roles.length; j++) { + _construct2Id.put(roles[j], Integer.valueOf(j+1)); + } + } + } + + /** + * Returns a sorted array of roles of the provided association. + * + * @param assoc The association to retrieve the roles from. + * @return A (maybe empty) sorted array of roles. + */ + private AssociationRole[] _getRoles(Association assoc) { + AssociationRole[] roles = _assoc2Roles.get(assoc); + return roles != null ? roles : _EMPTY_ROLES; + } + + /** + * Returns a sorted array of names of the provided topic. + * + * @param topic The topic to retrieve the names from. + * @return A (maybe empty) sorted array of names. + */ + @SuppressWarnings("unchecked") + private TopicName[] _getNames(Topic topic) { + Set<TopicName> names_ = topic.getTopicNames(); + TopicName[] names = names_.toArray(new TopicName[names_.size()]); + Arrays.sort(names, _nameComparator); + return names; + } + + /** + * Returs a sorted array of variants of the provided name. + * + * @param name The name to retrieve the variants from. + * @return A (maybe empty) sorted array of variants. + */ + @SuppressWarnings("unchecked") + private Variant[] _getVariants(TopicName name) { + Set<Variant> variants_ = name.getVariants(); + Variant[] variants = variants_.toArray(new Variant[variants_.size()]); + Arrays.sort(variants, _variantComparator); + return variants; + } + + /** + * Returns a sorted array of occurrences of the provided topic. + * + * @param topic The topic to retrieve the occurrences from. + * @return A (maybe emtpy) sorted array of occurrences. + */ + @SuppressWarnings("unchecked") + private Occurrence[] _getOccurrences(Topic topic) { + Set<Occurrence> occs_ = topic.getOccurrences(); + Occurrence[] occs = occs_.toArray(new Occurrence[occs_.size()]); + Arrays.sort(occs, _occComparator); + return occs; + } + + /** + * Returns the index of the provided Topic Maps construct. + * + * The "index" is <cite>"[...] the string encoding of the position of this + * information item in the canonically ordered list of the values from + * that set".</cite> (CXTM 3.20 Constructing the number attribute). + * + * @param tmo The Topic Maps construct to return the index of. + * @return The index of the Topic Maps construct. + */ + private int _indexOf(TopicMapObject tmo) { + return _construct2Id.get(tmo).intValue(); + } + + /** + * Serializes the <code>topic</code>. + * + * @param topic The topic to serialize. + * @throws IOException If an error occurs. + */ + @SuppressWarnings("unchecked") + private void _writeTopic(Topic topic) throws IOException { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "number", null, null, "" +_indexOf(topic)); + _out.startElement("topic", attrs); + _out.newline(); + _writeLocatorSet("subjectIdentifiers", topic.getSubjectIdentifiers()); + _writeLocatorSet("subjectLocators", topic.getSubjectLocators()); + _writeItemIdentifiers(topic); + TopicName[] names = _getNames(topic); + for (int i=0; i < names.length; i++) { + _writeName(names[i], i+1); + } + Occurrence[] occs = _getOccurrences(topic); + for (int i=0; i < occs.length; i++) { + _writeOccurrence(occs[i], i+1); + } + Set<AssociationRole> roles_ = new HashSet<AssociationRole>(topic.getRolesPlayed()); + List<AssociationRole> alienRoles = _topic2Roles.get(topic); + if (alienRoles != null) { + roles_.addAll(alienRoles); + } + AssociationRole[] roles = roles_.toArray(new AssociationRole[roles_.size()]); + Arrays.sort(roles, _roleComparator); + AttributesImpl roleAttrs = new AttributesImpl(); + StringBuilder sb = new StringBuilder(); + for (int i=0; i < roles.length; i++) { + sb.append("association.") + .append(_indexOf(roles[i].getAssociation())) + .append(".role.") + .append(_indexOf(roles[i])); + roleAttrs.addAttribute("", "ref", null, null, sb.toString()); + _out.startElement("rolePlayed", roleAttrs); + _out.endElement("rolePlayed"); + _out.newline(); + sb.setLength(0); + roleAttrs.clear(); + } + _out.endElement("topic"); + _out.newline(); + } + + /** + * Serializes an association. + * + * @param assoc The association to serialize. + * @throws IOException If an error occurs. + */ + @SuppressWarnings("unchecked") + private void _writeAssociation(Association assoc) throws IOException { + _out.startElement("association", _attributes(assoc, _indexOf(assoc))); + _out.newline(); + _writeType((ITyped) assoc); + for (AssociationRole role: _getRoles(assoc)) { + _out.startElement("role", _attributes(role, _indexOf(role))); + _out.newline(); + _out.startElement("player", _topicRef(role.getPlayer())); + _out.endElement("player"); + _out.newline(); + _writeType((ITyped) role); + _writeItemIdentifiers(role); + _out.endElement("role"); + _out.newline(); + } + _writeScope(assoc); + _writeItemIdentifiers(assoc); + _out.endElement("association"); + _out.newline(); + } + + /** + * Serializes an occurrence. + * + * @param occ The occurrence to serialize. + * @throws IOException If an error occurs. + */ + private void _writeOccurrence(Occurrence occ, int pos) throws IOException { + _out.startElement("occurrence", _attributes(occ, pos)); + _out.newline(); + _writeDatatyped((IDatatypeAwareConstruct) occ); + _writeType((ITyped) occ); + _writeScope(occ); + _writeItemIdentifiers(occ); + _out.endElement("occurrence"); + _out.newline(); + } + + /** + * Writes the value/datatype pair of an occurrence or variant. + * + * @param obj The construct to serialize. + * @throws IOException If an error occurs. + */ + private void _writeDatatyped(IDatatypeAwareConstruct obj) throws IOException { + String value = obj.getValue2(); + String datatype = obj.getDatatype().getReference(); + //TODO: Handle xsd:decimal, xsd:integer, xsd:date, xsd:dateTime xsd:anyType(?!?) + if (_XSD_ANY_URI.equals(datatype)) { + value = _normalizeLocator(value); + } + _out.startElement("value"); + _out.characters(value); + _out.endElement("value"); + _out.newline(); + _out.startElement("datatype"); + _out.characters(datatype); + _out.endElement("datatype"); + _out.newline(); + } + + /** + * Serializes a topic name. + * + * @param name The name to serialize. + * @throws IOException If an error occurs. + */ + private void _writeName(TopicName name, int pos) throws IOException { + _out.startElement("name", _attributes(name, pos)); + _out.newline(); + _out.startElement("value"); + _out.characters(name.getValue()); + _out.endElement("value"); + _out.newline(); + _writeType((ITyped) name); + _writeScope(name); + Variant[] variants = _getVariants(name); + Variant variant = null; + for (int i=0; i<variants.length; i++) { + variant = variants[i]; + _out.startElement("variant", _attributes(variant, i+1)); + _out.newline(); + _writeDatatyped((IDatatypeAwareConstruct) variant); + _writeScope(variant); + _writeItemIdentifiers(variant); + _out.endElement("variant"); + _out.newline(); + } + _writeItemIdentifiers(name); + _out.endElement("name"); + _out.newline(); + } + + /** + * Serializes the type of a typed Topic Maps construct. + * + * @param typed The typed Topic Maps construct from which the type should be + * serialized. + * @throws IOException If an error occurs. + */ + private void _writeType(ITyped typed) throws IOException { + Topic type = typed.getType(); + if (type == null) { + _reportInvalid("The type of " + typed + " is null"); + } + _out.startElement("type", _topicRef(typed.getType())); + _out.endElement("type"); + _out.newline(); + } + + /** + * Serializes the scope of a scoped Topic Maps construct. + * + * If the scope is unconstrained, this method does nothing. + * + * @param scoped The scoped Topic Maps construct. + * @throws IOException If an error occurs. + */ + @SuppressWarnings("unchecked") + private void _writeScope(ScopedObject scoped) throws IOException { + Set<Topic> scope = scoped.getScope(); + if (scope.isEmpty()) { + return; + } + _out.startElement("scope"); + _out.newline(); + Topic[] themes = scope.toArray(new Topic[scope.size()]); + Arrays.sort(themes, _topicComparator); + for (int i=0; i < themes.length; i++) { + _out.startElement("scopingTopic", _topicRef(themes[i])); + _out.endElement("scopingTopic"); + _out.newline(); + } + _out.endElement("scope"); + _out.newline(); + } + + /** + * Serializes a locator. + * + * A normalized locator value is created which is serialized. + * + * @param loc The locator to serialize. + * @throws IOException If an error occurs. + */ + private void _writeLocator(Locator loc) throws IOException { + _out.startElement("locator"); + _out.characters(_normalizeLocator(loc.getReference())); + _out.endElement("locator"); + _out.newline(); + } + + /** + * Serializes the item identifiers of the specified Topic Maps construct. + * + * @param tmo The Topic Maps construct to take the item identifiers from. + * @throws IOException If an error occurs. + */ + @SuppressWarnings("unchecked") + private void _writeItemIdentifiers(TopicMapObject tmo) throws IOException { + _writeLocatorSet("itemIdentifiers", tmo.getSourceLocators()); + } + + /** + * Serializes the <code>locators</code> using the <code>localName</code> as + * element name. + * + * If the set of <code>locators</code> is empty, this method does nothing. + * + * @param localName The element's name. + * @param locators The locators to serialize. + * @throws IOException If an error occurs. + */ + private void _writeLocatorSet(String localName, Set<Locator> locators) throws IOException { + if (locators.isEmpty()) { + return; + } + Locator[] locs = locators.toArray(new Locator[locators.size()]); + Arrays.sort(locs, _locComparator); + _out.startElement(localName); + _out.newline(); + for (int i=0; i < locs.length; i++) { + _writeLocator(locs[i]); + } + _out.endElement(localName); + _out.newline(); + } + + /** + * Returns attributes which contains a reference to the provided topic. + * + * @param topic The topic to which the reference should point to. + * @return Attributes with a topic reference. + */ + private Attributes _topicRef(Topic topic) { + if (topic == null) { + _reportInvalid("The topic reference is null"); + return XMLC14NWriter.EMPTY_ATTRS; + } + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "topicref", null, null, ""+_indexOf(topic)); + return attrs; + } + + /** + * Returns attributes which contain the reifier (if any) and the number + * of the provided Topic Maps construct (not a topic). + * + * @param reifiable The Topic Maps construct. + * @return Attributes which contain a reference to the reifier (if any) and + * the number of the provided Topic Maps construct. + */ + private Attributes _attributes(TopicMapObject reifiable, int i) { + AttributesImpl attrs = new AttributesImpl(); + _addReifier(attrs, (IReifiable)reifiable); + attrs.addAttribute("", "number", null, null, "" + i); + return attrs; + } + + /** + * Adds a reference to the reifier of the Topic Maps construct to the + * provided attributes. If the Topic Maps construct has no reifier, the + * provided attributes are not modified. + * + * @param attrs The attributes. + * @param reifiable The reifiable Topic Maps construct. + */ + private void _addReifier(AttributesImpl attrs, IReifiable reifiable) { + Topic reifier = reifiable.getReifier(); + if (reifier != null) { + attrs.addAttribute("", "reifier", null, null, "" + _indexOf(reifier)); + } + } + + /** + * Normalizes the locator according to CXTM 3.19. + * + * @param locator The locator to normalize. + * @return A normalized representation of the locator. + */ + private String _normalizeLocator(String locator) { + String normLoc = _locator2Norm.get(locator); + if (normLoc != null) { + return normLoc; + } + normLoc = locator; + if (locator.startsWith(_normBase)) { + normLoc = locator.substring(_normBase.length()); + } + else { + int i = 0; + int slashPos = -1; + final int max = _normBase.length() < locator.length() ? _normBase.length() + : locator.length(); + while(i < max && _normBase.charAt(i) == locator.charAt(i)) { + if (_normBase.charAt(i) == '/') { + slashPos = i; + } + i++; + } + if (slashPos > -1) { + normLoc = locator.substring(slashPos); + } + } + if (normLoc.startsWith("/")) { + normLoc = normLoc.substring(1); + } + _locator2Norm.put(locator, normLoc); + return normLoc; + } + + /** + * Normalizes the base locator according to the following procedure + * (CXTM 3.19 - 1.): + * <cite>[...] the base locator with any fragment identifier and query + * removed and any trailing "/" character removed.[...]</cite> + * + * @param baseLocator + * @return + */ + private static String _normalizeBaseLocator(String baseLocator) { + String loc = baseLocator; + int i = loc.indexOf('#'); + if (i > 0) { + loc = loc.substring(0, i); + } + i = loc.indexOf('?'); + if (i > 0) { + loc = loc.substring(0, i); + } + if (loc.endsWith("/")) { + loc = loc.substring(0, loc.length()-1); + } + return loc; + } + + /** + * Writes a warning msg to the log. + * + * This method is used to inform the user that the serialized topic map + * is not valid acc. to CXTM. + * + * @param msg The warning message. + */ + private static void _reportInvalid(String msg) { + LOG.warning("Invalid CXTM: '" + msg + "'"); + } + + + /* + * Comparators. + */ + + private final class TopicComparator implements Comparator<Topic> { + + @SuppressWarnings("unchecked") + public int compare(Topic o1, Topic o2) { + if (o1 == o2) { + return 0; + } + if (o1 != null && o2 == null) { + _reportInvalid("Comparing topics where one topic is null"); + return +1; + } + else if (o1 == null && o2 != null) { + _reportInvalid("Comparing topics where one topic is null"); + return -1; + } + int res = _locSetComparator.compare(o1.getSubjectIdentifiers(), o2.getSubjectIdentifiers()); + if (res == 0) { + res = _locSetComparator.compare(o1.getSubjectLocators(), o2.getSubjectLocators()); + if (res == 0) { + res = _locSetComparator.compare(o1.getSourceLocators(), o2.getSourceLocators()); + } + } + return res; + } + } + + /** + * Abstract comparator that provides some utility methods which handle common + * comparisons. + */ + private abstract class AbstractComparator<T> implements Comparator<T> { + int compareString(String o1, String o2) { + if (o1 == null && o2 != null) { + _reportInvalid("The first string value is null"); + return -1; + } + if (o1 != null && o2 == null) { + _reportInvalid("The second string value is null"); + return +1; + } + return o1.compareTo(o2); + } + /** + * Extracts the type of the typed Topic Maps constructs and compares + * the topics. + * + * @param o1 The first typed Topic Maps construct. + * @param o2 The second typed Topic Maps construct. + * @return A negative integer, zero, or a positive integer as the + * first argument is less than, equal to, or greater than the + * second. + */ + int compareType(ITyped o1, ITyped o2) { + return _topicComparator.compare(o1.getType(), o2.getType()); + } + /** + * Extracts the scope of the scoped Topic Maps constructs and compares + * them. + * + * @param o1 The first scoped Topic Maps construct. + * @param o2 The second scoped Topic Maps construct. + * @return A negative integer, zero, or a positive integer as the + * first argument is less than, equal to, or greater than the + * second. + */ + @SuppressWarnings("unchecked") + int compareScope(ScopedObject o1, ScopedObject o2) { + return _scopeComparator.compare(o1.getScope(), o2.getScope()); + } + } + + /** + * Enhances the {@link AbstractComparator} with a method to compare the + * value and datatype of an occurrence or variant. + */ + private abstract class AbstractDatatypeAwareComparator<T> extends AbstractComparator<T> { + /** + * Compares the value and datatype of the occurrences / variants. + * + * @param o1 The first occurrence / variant. + * @param o2 The second occurrence / variant. + * @return A negative integer, zero, or a positive integer as the + * first argument is less than, equal to, or greater than the + * second. + */ + int _compareValueDatatype(IDatatypeAwareConstruct o1, IDatatypeAwareConstruct o2) { + int res = compareString(o1.getValue2(), o2.getValue2()); + if (res == 0) { + res = compareString(o1.getDatatype().getReference(), o2.getDatatype().getReference()); + } + return res; + } + } + + /** + * Canonical sort order: + * 1. [type] + * 2. [roles] + * 3. [scope] + * 4. [parent] + */ + private final class AssociationComparator extends AbstractComparator<Association> { + + private Comparator<Set<AssociationRole>> _roleSetComparator; + + AssociationComparator() { + _roleSetComparator = new RoleSetComparator(); + } + + @SuppressWarnings("unchecked") + public int compare(Association o1, Association o2) { + if (o1 == o2) { + return 0; + } + int res = compareType((ITyped) o1, (ITyped) o2); + if (res == 0) { + res = _roleSetComparator.compare(o1.getAssociationRoles(), o2.getAssociationRoles()); + if (res == 0) { + res = compareScope(o1, o2); + } + } + return res; + } ... [truncated message content] |