|
From: <lh...@us...> - 2009-03-09 13:57:43
|
Revision: 282
http://tinytim.svn.sourceforge.net/tinytim/?rev=282&view=rev
Author: lheuer
Date: 2009-03-09 13:57:29 +0000 (Mon, 09 Mar 2009)
Log Message:
-----------
- Removed default "xsd" prefix
- Added support for user defined prefixes
- Added support for item identifier suppression
- Topics are now grouped by their type
- Java docs
Modified Paths:
--------------
tinytim-mio/trunk/src/main/java/org/tinytim/mio/CTMTopicMapWriter.java
Modified: tinytim-mio/trunk/src/main/java/org/tinytim/mio/CTMTopicMapWriter.java
===================================================================
--- tinytim-mio/trunk/src/main/java/org/tinytim/mio/CTMTopicMapWriter.java 2009-03-08 14:46:50 UTC (rev 281)
+++ tinytim-mio/trunk/src/main/java/org/tinytim/mio/CTMTopicMapWriter.java 2009-03-09 13:57:29 UTC (rev 282)
@@ -37,7 +37,6 @@
import org.tinytim.internal.api.IScope;
import org.tinytim.internal.api.IScoped;
import org.tinytim.internal.api.IVariant;
-import org.tinytim.voc.Namespace;
import org.tinytim.voc.TMDM;
import org.tinytim.voc.XSD;
@@ -53,6 +52,7 @@
import org.tmapi.core.TopicMap;
import org.tmapi.core.Typed;
import org.tmapi.core.Variant;
+import org.tmapi.index.ScopedIndex;
import org.tmapi.index.TypeInstanceIndex;
/**
@@ -68,38 +68,76 @@
private static final Logger LOG = Logger.getLogger(CTMTopicMapWriter.class.getName());
+ //TODO: Unicode ranges in patterns
private static final Pattern _ID_PATTERN = Pattern.compile("[A-Za-z_](\\.*[\\-A-Za-z_0-9])*");
+ private static final Pattern _LOCAL_PATTERN = Pattern.compile("([0-9]*\\.*[\\-A-Za-z_0-9])*");
+ private static final char[] _TRIPLE_QUOTES = new char[] { '"', '"', '"' };
+ private static final Reference[] _EMPTY_REFERENCE_ARRAY = new Reference[0];
+ private static final Reference _UNTYPED_REFERENCE = Reference.createId("[untyped]");
+
private final Writer _out;
private final String _baseIRI;
private final String _encoding;
private Topic _defaultNameType;
- //TODO: Add setters/getters
- private boolean _exportIIDs = true;
- private boolean _prettify = true;
+ private boolean _exportIIDs;
+ private boolean _keepAbsoluteIIDs;
private String _title;
private String _author;
private String _license;
private String _comment;
+ private char[] _indent;
private final Comparator<Topic> _topicComparator;
+ private final Comparator<Topic> _topicIdComparator;
private final Comparator<Association> _assocComparator;
private final Comparator<Occurrence> _occComparator;
private final Comparator<Name> _nameComparator;
private final Comparator<Set<Topic>> _scopeComparator;
- private final Comparator<Locator> _locComparator;
- private final Comparator<Set<Locator>> _locSetComparator;
private final Comparator<Role> _roleComparator;
private final Comparator<Variant> _variantComparator;
- private final Map<Topic, TopicReference> _topic2Reference;
+ private final Map<Topic, Reference> _topic2Reference; //TODO: LRU?
+ private final Map<String, String> _prefixes;
+ private Reference _lastReference;
-
+ /**
+ * Constructs a new instance using "utf-8" encoding.
+ * <p>
+ * The base IRI is used to abbreviate IRIs. IRIs with a fragment identifier
+ * (like <tt>#my-topic</tt>) are written in an abbreviated from iff they
+ * start with the provided base IRI.
+ * </p>
+ *
+ * @param out The stream to write onto.
+ * @param baseIRI The base IRI to resolve locators against.
+ * @throws IOException In case of an error.
+ */
public CTMTopicMapWriter(final OutputStream out, final String baseIRI) throws IOException {
this(out, baseIRI, "utf-8");
}
+ /**
+ * Constructs a new instance with the specified encoding.
+ * <p>
+ * The base IRI is used to abbreviate IRIs. IRIs with a fragment identifier
+ * (like <tt>#my-topic</tt>) are written in an abbreviated from iff they
+ * start with the provided base IRI.
+ * </p>
+ *
+ * @param out The stream to write onto.
+ * @param baseIRI The base IRI to resolve locators against.
+ * @param encoding The encoding to use.
+ * @throws IOException In case of an error, i.e. if the encoding is unsupported.
+ */
public CTMTopicMapWriter(final OutputStream out, final String baseIRI, final String encoding) throws IOException {
this(new OutputStreamWriter(out, encoding), baseIRI, encoding);
}
+ /**
+ * Constructs a new instance.
+ *
+ * @param writer The writer to use.
+ * @param baseIRI The base IRI to resolve locators against.
+ * @param encoding The encoding to use.
+ */
private CTMTopicMapWriter(final Writer writer, final String baseIRI, final String encoding) {
_out = writer;
if (baseIRI == null) {
@@ -110,20 +148,23 @@
throw new IllegalArgumentException("The encoding must not be null");
}
_encoding = encoding;
- _topic2Reference = new HashMap<Topic, TopicReference>(200);
+ _topic2Reference = new HashMap<Topic, Reference>(200);
+ _topicComparator = new TopicComparator();
_scopeComparator = new ScopeComparator();
- _locSetComparator = new LocatorSetComparator();
- _locComparator = new LocatorComparator();
- _topicComparator = new TopicComparator();
+ _topicIdComparator = new TopicIdComparator();
_assocComparator = new AssociationComparator();
_occComparator = new OccurrenceComparator();
_nameComparator = new NameComparator();
_roleComparator = new RoleComparator();
_variantComparator = new VariantComparator();
+ _prefixes = new HashMap<String, String>();
+ setIdentation(4);
+ setExportItemIdentifiers(false);
}
/**
- * Sets the title of the topic map which appears at the top of the file.
+ * Sets the title of the topic map which appears in the header comment of
+ * the file.
*
* @param title The title of the topic map.
*/
@@ -141,7 +182,7 @@
}
/**
- * Sets the author which appears at the top of the file.
+ * Sets the author which appears in the header comment of the file.
*
* @param author The author.
*/
@@ -159,7 +200,7 @@
}
/**
- * Sets the license which should appear on top of the file.
+ * Sets the license which should appear in the header comment of the file.
* <p>
* The license of the topic map. This could be a name or an IRI or both, i.e.
* "Creative Commons-License <http://creativecommons.org/licenses/by-nc-sa/3.0/>".
@@ -181,7 +222,7 @@
}
/**
- * Sets a file comment.
+ * The an additional comment which appears in the header comment of the file.
* <p>
* The comment could describe the topic map, or provide an additional
* copyright notice, or SVN/CVS keywords etc.
@@ -202,6 +243,105 @@
return _comment;
}
+ /**
+ * Adds a prefix to the writer.
+ * <p>
+ * The writer converts all locators (item identifiers, subject identifiers,
+ * subject locators) into QNames which start with the provided
+ * <tt>reference</tt>.
+ * </p>
+ * <p>
+ * I.e. if a prefix "wp" is set to "http://en.wikipedia.org/wiki", a
+ * subject identifier like "http://en.wikipedia.org/wiki/John_Lennon" is
+ * converted into a QName "wp:John_Lennon".
+ * </p>
+ *
+ * @param prefix The prefix to add, an existing prefix with the same name
+ * will be overridden.
+ * @param reference The IRI to which the prefix should be assigned to.
+ */
+ public void addPrefix(String prefix, String reference) {
+ _prefixes.put(prefix, reference);
+ }
+
+ /**
+ * Removes a prefix mapping.
+ *
+ * @param prefix The prefix to remove.
+ */
+ public void removePrefix(String prefix) {
+ _prefixes.remove(prefix);
+ }
+
+ /**
+ * Sets the identation level, by default the identation level is set to 4
+ * which means four whitespace characters are written.
+ * <p>
+ * If the size is set to <tt>0</tt>, no identation will be done.
+ * </p>
+ * <p>
+ * The identation level indicates how many whitespaces are written in front
+ * of a statement within a topic block.
+ * </p>
+ * <p>Example (identation level = 4):
+ * <pre>
+ * john isa person;
+ * - "John".
+ * </pre>
+ * </p>
+ * <p>Example (identation level = 0):
+ * <pre>
+ * paul isa person;
+ * - "Paul".
+ * </pre>
+ * </p>
+ *
+ * @param level The identation level.
+ */
+ public void setIdentation(int level) {
+ if (_indent == null || _indent.length != level) {
+ _indent = new char[level];
+ Arrays.fill(_indent, ' ');
+ }
+ }
+
+ /**
+ * Returns the identation level.
+ *
+ * @return The number of whitespaces which are written in front of a
+ * statement within a topic block.
+ */
+ public int getIdentation() {
+ return _indent.length;
+ }
+
+ /**
+ * Indicates if the item identifiers of the topics should be exported.
+ * <p>
+ * By default, this feature is disabled.
+ * </p>
+ *
+ * @param export <tt>true</tt> to export item identifiers, otherwise <tt>false</tt>.
+ */
+ public void setExportItemIdentifiers(boolean export) {
+ setExportItemIdentifiers(export, export);
+ }
+
+ /**
+ * Indicates if the item identifiers of a topic are exported.
+ *
+ * @return <tt>true</tt> if the item identifiers are exported, otherwise <tt>false</tt>.
+ */
+ public boolean getExportItemIdentifiers() {
+ return _exportIIDs;
+ }
+
+ // Unsure if this feature should be exposed, keep it private currently
+ private void setExportItemIdentifiers(boolean export, boolean keepAbsoluteItemIdentifiers) {
+ _exportIIDs = export;
+ _keepAbsoluteIIDs = keepAbsoluteItemIdentifiers;
+ }
+
/* (non-Javadoc)
* @see org.tinytim.mio.TopicMapWriter#write(org.tmapi.core.TopicMap)
*/
@@ -212,13 +352,9 @@
_newline();
_out.write("%version 1.0");
_writeFileHeader();
- _out.write("%prefix xsd <" + Namespace.XSD + "> # Default prefix");
+ _writePrefixes();
_newline();
Collection<Topic> topics = new ArrayList<Topic>(topicMap.getTopics());
- final boolean removeDefaultNameType = _shouldStandardTopicExported(_defaultNameType);
- if (removeDefaultNameType) {
- topics.remove(_defaultNameType);
- }
if (topicMap.getReifier() != null) {
// Special handling of the tm reifier to avoid an additional
// whitespace character in front of the ~
@@ -227,7 +363,7 @@
_out.write("~ ");
_writeTopicRef(reifier);
_newline();
- _writeTopic(reifier);
+ _writeTopic(reifier, false);
topics.remove(reifier);
}
TypeInstanceIndex tiIdx = ((IIndexManagerAware) topicMap).getIndexManager().getTypeInstanceIndex();
@@ -235,16 +371,43 @@
tiIdx.reindex();
}
_writeSection("ONTOLOGY");
- _writeOntologyTypes(tiIdx.getTopicTypes(), topics, "Topic Types");
- _writeOntologyTypes(tiIdx.getAssociationTypes(), topics, "Association Types");
- _writeOntologyTypes(tiIdx.getRoleTypes(), topics, "Role Types");
- _writeOntologyTypes(tiIdx.getOccurrenceTypes(), topics, "Occurrence Types");
- Collection<Topic> nameTypes = new ArrayList<Topic>(tiIdx.getNameTypes());
- if (removeDefaultNameType) {
- nameTypes.remove(_defaultNameType);
+ _writeOntologySection(tiIdx.getTopicTypes(), topics, "Topic Types");
+ Collection<Topic> types = tiIdx.getAssociationTypes();
+ final Topic typeInstance = topicMap.getTopicBySubjectIdentifier(TMDM.TYPE_INSTANCE);
+ if (_omitTopic(typeInstance)) {
+ types.remove(typeInstance);
+ topics.remove(typeInstance);
}
- _writeOntologyTypes(nameTypes, topics, "Name Types");
+ _writeOntologySection(types, topics, "Association Types");
+ types = tiIdx.getRoleTypes();
+ final Topic type = topicMap.getTopicBySubjectIdentifier(TMDM.TYPE);
+ final Topic instance = topicMap.getTopicBySubjectIdentifier(TMDM.INSTANCE);
+ if (_omitTopic(type)) {
+ types.remove(type);
+ topics.remove(type);
+ }
+ if (_omitTopic(instance)) {
+ types.remove(instance);
+ topics.remove(instance);
+ }
+ _writeOntologySection(types, topics, "Role Types");
+ _writeOntologySection(tiIdx.getOccurrenceTypes(), topics, "Occurrence Types");
+ types = new ArrayList<Topic>(tiIdx.getNameTypes());
+ if (_omitTopic(_defaultNameType)) {
+ types.remove(_defaultNameType);
+ topics.remove(_defaultNameType);
+ }
+ _writeOntologySection(types, topics, "Name Types");
tiIdx.close();
+ ScopedIndex scopeIdx = ((IIndexManagerAware) topicMap).getIndexManager().getScopedIndex();
+ if (!scopeIdx.isAutoUpdated()) {
+ scopeIdx.reindex();
+ }
+ _writeOntologySection(scopeIdx.getAssociationThemes(), topics, "Association Themes");
+ _writeOntologySection(scopeIdx.getOccurrenceThemes(), topics, "Occurrence Themes");
+ _writeOntologySection(scopeIdx.getNameThemes(), topics, "Name Themes");
+ _writeOntologySection(scopeIdx.getVariantThemes(), topics, "Variant Themes");
+ scopeIdx.close();
_newline();
_writeSection("INSTANCES");
_writeSection("Topics");
@@ -265,14 +428,22 @@
_topic2Reference.clear();
}
- private boolean _shouldStandardTopicExported(Topic topic) {
+ /**
+ * Indicates if the provided <tt>topic</tt> has just one subject identifier
+ * and provides no further properties.
+ *
+ * @param topic The topic to check.
+ * @return <tt>true</tt> if the topic should be omitted, otherwise <tt>false</tt>.
+ */
+ private boolean _omitTopic(Topic topic) {
return topic != null
&& topic.getSubjectIdentifiers().size() == 1
- && topic.getSubjectLocators().size() == 0
- && topic.getTypes().size() == 0
- && topic.getNames().size() == 0
- && topic.getOccurrences().size() == 0
- && topic.getRolesPlayed().size() == 0
+ && topic.getSubjectLocators().isEmpty()
+ && (!_exportIIDs || topic.getItemIdentifiers().isEmpty())
+ && topic.getTypes().isEmpty()
+ && topic.getNames().isEmpty()
+ && topic.getOccurrences().isEmpty()
+ && topic.getRolesPlayed().isEmpty()
&& topic.getReified() == null;
}
@@ -309,11 +480,27 @@
_newline();
_newline();
_out.write(")#");
- _newline();
- _newline();
}
/**
+ * Writes the registered prefixes.
+ *
+ * @throws IOException In case of an error.
+ */
+ private void _writePrefixes() throws IOException {
+ if (_prefixes.isEmpty()) {
+ return;
+ }
+ _writeSection("Prefixes");
+ String[] keys = _prefixes.keySet().toArray(new String[0]);
+ Arrays.sort(keys);
+ for (String ident: keys) {
+ _out.write("%prefix " + ident + " <" + _prefixes.get(ident) + ">");
+ _newline();
+ }
+ }
+
+ /**
* If <tt>topics</tt> is not empty, the topics will be removed from
* <tt>allTopics</tt> and written out under the specified section <tt>title</tt>.
*
@@ -322,7 +509,7 @@
* @param title The title of the ontology section.
* @throws IOException In case of an error.
*/
- private void _writeOntologyTypes(Collection<Topic> topics, Collection<Topic> allTopics, String title) throws IOException {
+ private void _writeOntologySection(Collection<Topic> topics, Collection<Topic> allTopics, String title) throws IOException {
if (topics.isEmpty()) {
return;
}
@@ -338,10 +525,11 @@
* @throws IOException In case of an error.
*/
private void _writeTopics(Collection<Topic> topics) throws IOException {
+ _lastReference = null;
Topic[] topicArray = topics.toArray(new Topic[topics.size()]);
Arrays.sort(topicArray, _topicComparator);
for (Topic topic: topicArray) {
- _writeTopic(topic);
+ _writeTopic(topic, true);
}
}
@@ -351,16 +539,34 @@
* @param topic The topic to serialize.
* @throws IOException In case of an error.
*/
- private void _writeTopic(Topic topic) throws IOException {
+ private void _writeTopic(Topic topic, boolean topicTypeHeader) throws IOException {
+ final Reference mainIdentity = _getTopicReference(topic);
+ Topic[] types = _getTypes(topic);
+ if (topicTypeHeader) {
+ if (types.length > 0) {
+ Reference ref = _getTopicReference(types[0]);
+ if (!ref.equals(_lastReference)) {
+ _writeSection("TT: " + ref);
+ _lastReference = ref;
+ }
+ }
+ else if (_UNTYPED_REFERENCE != _lastReference) {
+ _writeSection("TT: " + _UNTYPED_REFERENCE);
+ _lastReference = _UNTYPED_REFERENCE;
+ }
+ }
+ boolean wantSemicolon = false;
_newline();
- boolean wantSemicolon = false;
- final TopicReference mainIdentity = _getTopicReference(topic);
_writeTopicRef(mainIdentity);
_out.write(' ');
- for (Topic type: topic.getTypes()) {
+ for (Topic type: types) {
_writeTypeInstance(type, wantSemicolon);
wantSemicolon = true;
}
+ for (Topic supertype: _getSupertypes(topic)) {
+ this._writeSupertypeSubtype(supertype, wantSemicolon);
+ wantSemicolon = true;
+ }
for (Name name: _getNames(topic)) {
_writeName((IName) name, wantSemicolon);
wantSemicolon = true;
@@ -369,74 +575,143 @@
_writeOccurrence((IOccurrence) occ, wantSemicolon);
wantSemicolon = true;
}
- for (TopicReference sid: _getSubjectIdentifiers(topic)) {
+ for (Reference sid: _getSubjectIdentifiers(topic)) {
_writeTopicRef(sid, wantSemicolon);
wantSemicolon = true;
}
- for (TopicReference slo: _getSubjectLocators(topic)) {
+ for (Reference slo: _getSubjectLocators(topic)) {
_writeTopicRef(slo, wantSemicolon);
wantSemicolon = true;
}
if (_exportIIDs) {
- TopicReference[] iids = _getItemIdentifiers(topic);
- if ((mainIdentity.type == TopicReference.ID
- || mainIdentity.type == TopicReference.IID)
- && iids.length == 1) {
- //TODO
+ for (Reference iid: _getItemIdentifiers(topic)) {
+ _writeTopicRef(iid, wantSemicolon);
+ wantSemicolon = true;
}
- else {
- for (TopicReference iid: iids) {
- _writeTopicRef(iid, wantSemicolon);
- wantSemicolon = true;
- }
- }
}
- if (wantSemicolon) {
- _out.write(' ');
- }
_out.write('.');
_newline();
}
- private TopicReference[] _getSubjectIdentifiers(Topic topic) {
- return _getLocators(topic, TopicReference.SID, topic.getSubjectIdentifiers());
+ /**
+ * Returns a sorted array of subject identifiers for the specified topic.
+ * <p>
+ * The main identity (the one which starts the topic block) is removed
+ * is not part of the array iff the main identity is a subject identifier.
+ * </p>
+ *
+ * @param topic The topic to retrieve the subject identifiers from.
+ * @return A (maybe empty) sorted array of subject identifiers.
+ */
+ private Reference[] _getSubjectIdentifiers(Topic topic) {
+ return _getLocators(topic, Reference.SID);
}
- private TopicReference[] _getSubjectLocators(Topic topic) {
- return _getLocators(topic, TopicReference.SLO, topic.getSubjectLocators());
+ /**
+ * Returns a sorted array of subject locators for the specified topic.
+ * <p>
+ * The main identity (the one which starts the topic block) is removed
+ * is not part of the array iff the main identity is a subject locator.
+ * </p>
+ *
+ * @param topic The topic to retrieve the subject locators from.
+ * @return A (maybe empty) sorted array of subject locators.
+ */
+ private Reference[] _getSubjectLocators(Topic topic) {
+ return _getLocators(topic, Reference.SLO);
}
- private TopicReference[] _getItemIdentifiers(Topic topic) {
- return _getLocators(topic, TopicReference.IID, topic.getItemIdentifiers());
+ /**
+ * Returns a sorted array of item identifiers for the specified topic.
+ * <p>
+ * The main identity (the one which starts the topic block) is removed
+ * is not part of the array iff the main identity is an item identifier.
+ * </p>
+ *
+ * @param topic The topic to retrieve the item identifiers from.
+ * @return A (maybe empty) sorted array of item identifiers.
+ */
+ private Reference[] _getItemIdentifiers(Topic topic) {
+ Collection<Locator> iids = topic.getItemIdentifiers();
+ if (iids.isEmpty()) {
+ return _EMPTY_REFERENCE_ARRAY;
+ }
+ Collection<Reference> refs = new ArrayList<Reference>();
+ for (Locator iid: iids) {
+ refs.add(Reference.createItemIdentifier(iid));
+ }
+ Reference mainIdentity = _getTopicReference(topic);
+ if (!refs.remove(mainIdentity)
+ && mainIdentity.type == Reference.ID) {
+ String iri = _baseIRI + "#" + mainIdentity.reference;
+ for (Reference r: refs) {
+ if (r.reference.equals(iri)) {
+ refs.remove(r);
+ break;
+ }
+ }
+ }
+ Reference[] refArray = refs.toArray(new Reference[refs.size()]);
+ Arrays.sort(refArray);
+ return refArray;
}
- private TopicReference[] _getLocators(Topic topic, int kind, Set<Locator> locs) {
+ /**
+ * Returns a sorted array of {@link Reference}s which represent the
+ * provided locators.
+ * <p>
+ * The main identity is not part of the array.
+ * </p>
+ *
+ * @param topic The topic.
+ * @param kind Either {@link Reference#SID} or {@link Reference#SLO}.
+ * @return A (maybe empty) sorted array.
+ */
+ private Reference[] _getLocators(Topic topic, int kind) {
+ Set<Locator> locs = kind == Reference.SID ? topic.getSubjectIdentifiers()
+ : topic.getSubjectLocators();
if (locs.isEmpty()) {
- return new TopicReference[0];
+ return _EMPTY_REFERENCE_ARRAY;
}
- Collection<TopicReference> refs = new ArrayList<TopicReference>(locs.size());
- if (kind == TopicReference.SID) {
- for (Locator loc: locs) {
- refs.add(TopicReference.createSubjectIdentifier(loc.toExternalForm()));
- }
+ Collection<Reference> refs = new ArrayList<Reference>(locs.size());
+ for (Locator loc: locs) {
+ refs.add(new Reference(kind, loc));
}
- else if (kind == TopicReference.IID) {
- for (Locator loc: locs) {
- refs.add(TopicReference.createItemIdentifier(loc.toExternalForm()));
- }
- }
- else if (kind == TopicReference.SLO) {
- for (Locator loc: locs) {
- refs.add(TopicReference.createSubjectLocator(loc.toExternalForm()));
- }
- }
- refs.remove(_getTopicReference(topic));
- TopicReference[] refArray = refs.toArray(new TopicReference[refs.size()]);
- //Arrays.sort(locArray, _locComparator);
+ refs.remove(_getTopicReference(topic));
+ Reference[] refArray = refs.toArray(new Reference[refs.size()]);
+ Arrays.sort(refArray);
return refArray;
}
/**
+ * Returns a sorted array of types for the specified topic.
+ *
+ * @param topic The topic to retrieve the types from.
+ * @return A sorted array of types.
+ */
+ private Topic[] _getTypes(Topic topic) {
+ Set<Topic> types_ = topic.getTypes();
+ Topic[] types = types_.toArray(new Topic[types_.size()]);
+ Arrays.sort(types, _topicIdComparator);
+ return types;
+ }
+
+ /**
+ * Returns a sorted array of supertypes for the specified topic.
+ *
+ * @param topic The topic to retrieve the supertypes from.
+ * @return A sorted array of supertypes.
+ */
+ private Topic[] _getSupertypes(Topic topic) {
+ //FIXME
+ return new Topic[0];
+// Set<Topic> supertypes_ = TypeInstanceUtils.getSupertypes(topic);
+// Topic[] supertypes = supertypes_.toArray(new Topic[supertypes_.size()]);
+// Arrays.sort(supertypes, _topicIdComparator);
+// return supertypes;
+ }
+
+ /**
* Returns a sorted array of names for the specified topic.
*
* @param topic The topic to retrieve the names from.
@@ -463,9 +738,9 @@
}
/**
- *
+ * Writes a type-instance relationship via "isa".
*
- * @param type
+ * @param type The type to write.
* @param wantSemicolon Indicates if a semicolon should be written.
* @throws IOException In case of an error.
*/
@@ -475,11 +750,18 @@
_writeTopicRef(type);
}
-// private void _writeSupertypeSubtype(Topic supertype, boolean wantSemicolon) throws IOException {
-// _writeSemicolon(wantSemicolon);
-// _out.write("ako ");
-// _writeTopicRef(supertype);
-// }
+ /**
+ * Writes a supertype-subtype relationship via "isa".
+ *
+ * @param supertype The supertype to write.
+ * @param wantSemicolon Indicates if a semicolon should be written.
+ * @throws IOException In case of an error.
+ */
+ private void _writeSupertypeSubtype(Topic supertype, boolean wantSemicolon) throws IOException {
+ _writeSemicolon(wantSemicolon);
+ _out.write("ako ");
+ _writeTopicRef(supertype);
+ }
/**
* Serializes the specified occurrence.
@@ -548,19 +830,10 @@
_out.write('(');
Role[] roles = assoc.getRoles().toArray(new Role[0]);
Arrays.sort(roles, _roleComparator);
- boolean wantComma = false;
- for (Role role: roles) {
- if (wantComma) {
- _out.write(", ");
- }
- _writeTopicRef(role.getType());
- _out.write(": ");
- _writeTopicRef(role.getPlayer());
- if (role.getReifier() != null) {
- _writeReifier(role);
- _out.write(" #( Great, you found a reason why a role should be reified, please tell us about it :) )# ");
- }
- wantComma = true;
+ _writeRole(roles[0]);
+ for (int i=1; i<roles.length; i++) {
+ _out.write(", ");
+ _writeRole(roles[i]);
}
_out.write(')');
_writeScope((IScoped) assoc);
@@ -569,6 +842,22 @@
}
/**
+ * Serializes the specified association role.
+ *
+ * @param role The association role to serialize.
+ * @throws IOException In case of an error.
+ */
+ private void _writeRole(Role role) throws IOException {
+ _writeTopicRef(role.getType());
+ _out.write(": ");
+ _writeTopicRef(role.getPlayer());
+ if (role.getReifier() != null) {
+ _writeReifier(role);
+ _out.write(" #( Great, you found a reason why a role should be reified, please tell us about it :) )# ");
+ }
+ }
+
+ /**
* Writes a semicolon and a newline character iff <tt>wantSemicolon</tt> is
* <tt>true</tt>.
* <p>
@@ -583,9 +872,7 @@
if (wantSemicolon) {
_out.write(';');
_newline();
- if (_prettify) {
- _out.write(" ");
- }
+ _out.write(_indent);
}
}
@@ -600,21 +887,18 @@
IScope scope = scoped.getScopeObject();
if (!scope.isUnconstrained()) {
Topic[] themes = scope.asSet().toArray(new Topic[scope.size()]);
- Arrays.sort(themes, _topicComparator);
+ Arrays.sort(themes, _topicIdComparator);
_out.write(" @");
- boolean wantComma = false;
- for (Topic theme: themes) {
- if (wantComma) {
- _out.write(", ");
- }
- _writeTopicRef(theme);
- wantComma = true;
+ _writeTopicRef(themes[0]);
+ for (int i=1; i<themes.length; i++) {
+ _out.write(", ");
+ _writeTopicRef(themes[i]);
}
}
}
/**
- * Writes the reifier if <tt>reifiable</tt> is reified.
+ * Writes the reifier iff <tt>reifiable</tt> is reified.
*
* @param reifiable The reifiable construct.
* @throws IOException If an error occurs.
@@ -653,21 +937,14 @@
else {
_writeString(value);
_out.write("^^");
- String datatypeIRI = datatype.toExternalForm();
- if (datatypeIRI.startsWith(Namespace.XSD)) {
- _out.write("xsd:");
- _out.write(datatypeIRI.substring(datatypeIRI.lastIndexOf('#')+1));
- }
- else {
- _writeLocator(datatypeIRI);
- }
+ _writeLocator(datatype);
}
}
/**
- *
+ * Writes a topic reference for the specified topic.
*
- * @param topic
+ * @param topic The topic to serialize.
* @throws IOException In case of an error.
*/
private void _writeTopicRef(Topic topic) throws IOException {
@@ -675,35 +952,35 @@
}
/**
- *
+ * Writes the specied topic reference without whitespaces in front.
*
- * @param topicRef
+ * @param topicRef The topic reference to serialize.
* @throws IOException In case of an error.
*/
- private void _writeTopicRef(TopicReference topicRef) throws IOException {
+ private void _writeTopicRef(Reference topicRef) throws IOException {
_writeTopicRef(topicRef, false);
}
/**
- *
+ * Writes the specified topic reference.
*
- * @param topicRef
+ * @param topicRef The topic reference to write.
* @param wantSemicolon Indicates if a semicolon should be written.
* @throws IOException In case of an error.
*/
- private void _writeTopicRef(TopicReference topicRef, boolean wantSemicolon) throws IOException {
+ private void _writeTopicRef(Reference topicRef, boolean wantSemicolon) throws IOException {
_writeSemicolon(wantSemicolon);
switch (topicRef.type) {
- case TopicReference.ID:
+ case Reference.ID:
_out.write(topicRef.reference);
return;
- case TopicReference.IID:
+ case Reference.IID:
_out.write('^');
break;
- case TopicReference.SLO:
+ case Reference.SLO:
_out.write("= ");
break;
- case TopicReference.SID:
+ case Reference.SID:
break;
default:
throw new RuntimeException("Internal error: Cannot match topic reference type " + topicRef.type);
@@ -712,85 +989,188 @@
}
/**
- *
+ * Serializes the provided <tt>string</tt>.
+ * <p>
+ * This method recognizes characters which have to be escaped.
+ * </p>
*
- * @param string
- * @throws IOException
+ * @param string The string to write.
+ * @throws IOException In case of an error.
*/
private void _writeString(String string) throws IOException {
- _out.write('"');
- char[] ch = string.toCharArray();
- for (int i=0; i<ch.length; i++) {
- switch (ch[i]) {
- case '"':
- case '\\':
- _out.write("\\");
- default:
- _out.write(ch[i]);
+ // Avoid escaping of "
+ if (string.indexOf('"') > 0 && !string.endsWith("\"")) {
+ _out.write(_TRIPLE_QUOTES);
+ char[] ch = string.toCharArray();
+ for (int i=0; i<ch.length; i++) {
+ switch (ch[i]) {
+ case '\\':
+ _out.write("\\");
+ default:
+ _out.write(ch[i]);
+ }
}
+ _out.write(_TRIPLE_QUOTES);
}
- _out.write('"');
+ else {
+ // Either the string ends with a " or the string is a 'normal' string
+ _out.write('"');
+ char[] ch = string.toCharArray();
+ for (int i=0; i<ch.length; i++) {
+ switch (ch[i]) {
+ case '"':
+ case '\\':
+ _out.write("\\");
+ default:
+ _out.write(ch[i]);
+ }
+ }
+ _out.write('"');
+ }
}
- private void _writeLocator(String reference) throws IOException {
- _out.write("<" + reference + ">");
+ /**
+ * Writes the specified locator (maybe abbreviated as QName).
+ *
+ * @param loc The locator to write.
+ * @throws IOException In case of an error.
+ */
+ private void _writeLocator(final Locator loc) throws IOException {
+ _writeLocator(loc.toExternalForm());
}
- private TopicReference _getTopicReference(Topic topic) {
- TopicReference ref = _topic2Reference.get(topic);
- if (ref == null) {
- final boolean hasIIds = !(_exportIIDs || topic.getItemIdentifiers().isEmpty());
- final boolean hasSids = !topic.getSubjectIdentifiers().isEmpty();
- final boolean hasSlos = !topic.getSubjectLocators().isEmpty();
- if (!hasIIds) {
- if (hasSids) {
- ref = hasSlos ? null : TopicReference.createSubjectIdentifier(topic.getSubjectIdentifiers().iterator().next().toExternalForm());
- }
- else if (hasSlos) {
- ref = TopicReference.createSubjectLocator(topic.getSubjectLocators().iterator().next().toExternalForm());
- }
+ /**
+ * Writes the specified locator <tt>reference</tt> which has been
+ * externalized..
+ * <p>
+ * If the reference starts with the base IRI followed by a hash ('#'), the
+ * reference is abbreviated.
+ * </p>
+ *
+ * @param reference The reference to write.
+ * @throws IOException In case of an error.
+ */
+ private void _writeLocator(String reference) throws IOException {
+ // If the reference starts with the base IRI and is followed by a #
+ // a relative reference is written
+ if (reference.startsWith(_baseIRI)) {
+ String tmp = reference.substring(_baseIRI.length());
+ if (tmp.charAt(0) == '#') {
+ _out.write('<' + tmp + '>');
+ return;
}
- if (ref == null) {
- if (topic.getItemIdentifiers().size() == 1) {
- final String iid = topic.getItemIdentifiers().iterator().next().toExternalForm();
- int idx = !iid.startsWith(_baseIRI) ? -1 : iid.lastIndexOf('#');
- if (idx > 0) {
- String id = iid.substring(idx + 1);
- ref = _isValidId(id) ? TopicReference.createId(id)
- : TopicReference.createItemIdentifier("#" + id);
- }
- else {
- ref = TopicReference.createItemIdentifier(iid);
- }
+ }
+ // If no relative IRI was written, check the registered prefixes and
+ // write a QName if a prefix matches
+ for (Map.Entry<String, String> entry: _prefixes.entrySet()) {
+ String iri = entry.getValue();
+ if (reference.startsWith(iri)) {
+ String localPart = reference.substring(iri.length());
+ if (_isValidLocalPart(localPart)) {
+ _out.write(entry.getKey());
+ _out.write(':');
+ _out.write(localPart);
+ return;
}
}
- if (ref == null) {
- String iri = null;
- for (Locator iid: topic.getItemIdentifiers()) {
- String addr = iid.getReference();
- int idx = addr.lastIndexOf('#');
- if (idx < 0) {
- continue;
+ }
+ // No relative IRI and no QName was written, write the reference as it is
+ _out.write('<' + reference + '>');
+ }
+
+ /**
+ * Returns a reference to the provided topic.
+ * <p>
+ * The reference to the topic stays stable during the serialization of
+ * the topic map.
+ * </p>
+ *
+ * @param topic The topic to retrieve a reference for.
+ * @return A reference to the specified topic.
+ */
+ private Reference _getTopicReference(Topic topic) {
+ Reference ref = _topic2Reference.get(topic);
+ if (ref == null) {
+ ref = _generateTopicReference(topic);
+ _topic2Reference.put(topic, ref);
+ }
+ return ref;
+ }
+
+ /**
+ * Returns a reference to the specified <tt>topic</tt>.
+ *
+ * @param topic The topic to generate a reference for.
+ * @return A reference to the specified topic.
+ */
+ private Reference _generateTopicReference(final Topic topic) {
+ Reference ref = null;
+ Collection<Reference> refs = new ArrayList<Reference>();
+ for (Locator iid: topic.getItemIdentifiers()) {
+ String addr = iid.toExternalForm();
+ int idx = addr.indexOf('#');
+ if (idx > 0) {
+ String id = addr.substring(idx+1);
+ if (_isValidId(id)) {
+ if (_keepAbsoluteIIDs && !addr.startsWith(_baseIRI)) {
+ refs.add(Reference.createItemIdentifier(iid));
}
else {
- String id = addr.substring(idx+1);
- iri = _isValidId(id) ? id : null;
+ refs.add(Reference.createId(id));
}
}
- if (iri == null) {
- iri = "id-" + topic.getId();
+ else {
+ refs.add(Reference.createItemIdentifier(iid));
}
- ref = TopicReference.createId(iri);
}
- _topic2Reference.put(topic, ref);
}
+ if (refs.isEmpty()) {
+ for (Locator sid: topic.getSubjectIdentifiers()) {
+ refs.add(Reference.createSubjectIdentifier(sid));
+ }
+ }
+ if (refs.isEmpty()) {
+ for (Locator sid: topic.getSubjectLocators()) {
+ refs.add(Reference.createSubjectLocator(sid));
+ }
+ }
+ if (!refs.isEmpty()) {
+ Reference[] refArray = refs.toArray(new Reference[refs.size()]);
+ Arrays.sort(refArray);
+ ref = refArray[0];
+ }
+ else {
+ ref = Reference.createItemIdentifier("#" + topic.getId());
+ }
return ref;
}
+ /**
+ * Returns if the provided <tt>id</tt> is a valid CTM topic identifier.
+ *
+ * @param id The id to check.
+ * @return <tt>true</tt> if the id is valid, otherwise <tt>false</tt>.
+ */
private boolean _isValidId(String id) {
return _ID_PATTERN.matcher(id).matches();
}
+ /**
+ * Returns if the provided <tt>id</tt> is a valid local part of a QName.
+ *
+ * @param id The id to check.
+ * @return <tt>true</tt> if the id is valid, otherwise <tt>false</tt>.
+ */
+ private boolean _isValidLocalPart(String id) {
+ return _LOCAL_PATTERN.matcher(id).matches();
+ }
+
+ /**
+ * Returns if the provided <tt>literal</tt> is supported by CTM natively.
+ *
+ * @param literal The literal to check.
+ * @return <tt>true</tt> if the literal is supported, else <tt>false</tt>.
+ */
private boolean _isNativelySupported(ILiteral literal) {
Locator datatype = literal.getDatatype();
return XSD.STRING.equals(datatype)
@@ -804,10 +1184,21 @@
|| "-INF".equals(literal.getValue()));
}
+ /**
+ * Writes a EOL character.
+ *
+ * @throws IOException In case of an error.
+ */
private void _newline() throws IOException {
_out.write('\n');
}
+ /**
+ * Writes a section name.
+ *
+ * @param name The section name to write.
+ * @throws IOException In case of an error.
+ */
private void _writeSection(String name) throws IOException {
_newline();
_newline();
@@ -827,9 +1218,10 @@
LOG.warning("Invalid CTM: '" + msg + "'");
}
-
-
- private static final class TopicReference {
+ /**
+ * Represents a reference to a topic.
+ */
+ static final class Reference implements Comparable<Reference> {
static final int
ID = 0,
SID = 1,
@@ -838,28 +1230,60 @@
final int type;
final String reference;
- private TopicReference(int type, String reference) {
+ private Reference(int type, String reference) {
this.type = type;
this.reference = reference;
}
- public static TopicReference createId(String reference) {
- return new TopicReference(ID, reference);
+ public Reference(int type, Locator loc) {
+ this(type, loc.toExternalForm());
}
+
+ public static Reference createId(String reference) {
+ return new Reference(ID, reference);
+ }
- public static TopicReference createSubjectIdentifier(String reference) {
- return new TopicReference(SID, reference);
+ public static Reference createSubjectIdentifier(Locator reference) {
+ return new Reference(SID, reference);
}
- public static TopicReference createSubjectLocator(String reference) {
- return new TopicReference(SLO, reference);
+ public static Reference createSubjectLocator(Locator reference) {
+ return new Reference(SLO, reference);
}
- public static TopicReference createItemIdentifier(String reference) {
- return new TopicReference(IID, reference);
+ public static Reference createItemIdentifier(String string) {
+ return new Reference(IID, string);
}
+ public static Reference createItemIdentifier(Locator reference) {
+ return createItemIdentifier(reference.toExternalForm());
+ }
+
/* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder buff = new StringBuilder();
+ switch (type) {
+ case Reference.ID:
+ return reference;
+ case Reference.IID:
+ buff.append('^');
+ break;
+ case Reference.SID:
+ break;
+ case Reference.SLO:
+ buff.append("= ");
+ break;
+ }
+ buff.append('<');
+ buff.append(reference);
+ buff.append('>');
+ return buff.toString();
+ }
+
+ /* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
@@ -867,11 +1291,11 @@
if (this == obj) {
return true;
}
- if (!(obj instanceof TopicReference)) {
+ if (!(obj instanceof Reference)) {
return false;
}
- TopicReference other = (TopicReference) obj;
- return (type == other.type && reference.equals(other.reference));
+ Reference other = (Reference) obj;
+ return type == other.type && reference.equals(other.reference);
}
/* (non-Javadoc)
@@ -882,6 +1306,18 @@
return type + reference.hashCode();
}
+ /* (non-Javadoc)
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ @Override
+ public int compareTo(Reference o) {
+ int res = type - o.type;
+ if (res == 0) {
+ res = reference.compareTo(o.reference);
+ }
+ return res;
+ }
+
}
@@ -889,33 +1325,33 @@
* Comparators.
*/
- /**
- * Topic comparator.
- * - Topics with less types are considered less than others with types.
- * - Topics with less subject identifiers are considered less than others with sids.
- * - Topics with less subject locators are considered less than other with slos
- * - Topics with less item identifiers are less than others with iids
- *
- */
- private final class TopicComparator implements Comparator<Topic> {
+ private class TopicIdComparator implements Comparator<Topic> {
/* (non-Javadoc)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(Topic o1, Topic o2) {
- if (o1 == o2) {
- return 0;
- }
- int res = o1.getTypes().size() - o2.getTypes().size();
+ return _getTopicReference(o1).compareTo(_getTopicReference(o2));
+ }
+ }
+
+ private class TopicComparator implements Comparator<Topic> {
+
+ private final TopicTypeSetComparator _typesComparator;
+
+ TopicComparator() {
+ _typesComparator = new TopicTypeSetComparator();
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public int compare(Topic o1, Topic o2) {
+ int res = _typesComparator.compare(o1.getTypes(), o2.getTypes());
if (res == 0) {
- res = _locSetComparator.compare(o1.getSubjectIdentifiers(), o2.getSubjectIdentifiers());
- if (res == 0) {
- res = _locSetComparator.compare(o1.getSubjectLocators(), o2.getSubjectLocators());
- if (res == 0) {
- res = _locSetComparator.compare(o1.getItemIdentifiers(), o2.getItemIdentifiers());
- }
- }
+ res = _getTopicReference(o1).compareTo(_getTopicReference(o2));
}
return res;
}
@@ -948,7 +1384,7 @@
* second.
*/
int compareType(Typed o1, Typed o2) {
- return _topicComparator.compare(o1.getType(), o2.getType());
+ return _topicIdComparator.compare(o1.getType(), o2.getType());
}
/**
* Extracts the scope of the scoped Topic Maps constructs and compares
@@ -976,6 +1412,9 @@
int compareReifier(Reifiable o1, Reifiable o2) {
Topic reifier1 = o1.getReifier();
Topic reifier2 = o2.getReifier();
+ if (reifier1 == reifier2) {
+ return 0;
+ }
int res = 0;
if (reifier1 == null) {
res = reifier2 == null ? 0 : -1;
@@ -983,7 +1422,7 @@
else if (reifier2 == null) {
res = 1;
}
- return res != 0 ? res : _topicComparator.compare(reifier1, reifier2);
+ return res != 0 ? res : _topicIdComparator.compare(reifier1, reifier2);
}
}
@@ -1030,7 +1469,7 @@
private Comparator<Set<Role>> _roleSetComparator;
AssociationComparator() {
- //_roleSetComparator = new RoleSetComparator();
+ _roleSetComparator = new RoleSetComparator();
}
/* (non-Javadoc)
@@ -1043,7 +1482,7 @@
}
int res = compareType(o1, o2);
if (res == 0) {
- //res = _roleSetComparator.compare(o1.getRoles(), o2.getRoles());
+ res = _roleSetComparator.compare(o1.getRoles(), o2.getRoles());
if (res == 0) {
res = compareScope(o1, o2);
}
@@ -1069,7 +1508,7 @@
}
int res = compareType(o1, o2);
if (res == 0) {
- res = _topicComparator.compare(o1.getPlayer(), o2.getPlayer());
+ res = _topicIdComparator.compare(o1.getPlayer(), o2.getPlayer());
}
return res;
}
@@ -1220,59 +1659,71 @@
}
/**
- * Compares the scope of two scoped Topic Maps constructs.
+ * Compares role sets. The parent of the roles is ignored!
*/
- private final class ScopeComparator extends AbstractSetComparator<Topic> {
+ private final class RoleSetComparator extends AbstractSetComparator<Role> {
+ private RoleComparator _roleCmp;
+
+ RoleSetComparator() {
+ _roleCmp = new RoleComparator();
+ }
+
+ /* (non-Javadoc)
+ * @see org.tinytim.mio.CXTMTopicMapWriter.AbstractSetComparator#compareContent(java.util.Set, java.util.Set, int)
+ */
@Override
- int compareContent(Set<Topic> o1, Set<Topic> o2, int size) {
- int res = 0 ;
- Topic[] topics1 = o1.toArray(new Topic[size]);
- Topic[] topics2 = o2.toArray(new Topic[size]);
- Arrays.sort(topics1, _topicComparator);
- Arrays.sort(topics2, _topicComparator);
+ int compareContent(Set<Role> o1, Set<Role> o2,
+ int size) {
+ int res = 0;
+ Role[] roles1 = o1.toArray(new Role[size]);
+ Role[] roles2 = o2.toArray(new Role[size]);
+ Arrays.sort(roles1, _roleCmp);
+ Arrays.sort(roles2, _roleCmp);
for (int i=0; i < size && res == 0; i++) {
- res = _topicComparator.compare(topics1[i], topics2[i]);
+ res = _roleCmp.compare(roles1[i], roles2[i]);
}
return res;
}
}
- /**
- * Compares {@link org.tmapi.core.Locator}s.
- */
- private final class LocatorComparator implements Comparator<Locator> {
+ private final class TopicTypeSetComparator extends AbstractSetComparator<Topic> {
/* (non-Javadoc)
- * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+ * @see org.tinytim.mio.CXTMTopicMapWriter.AbstractSetComparator#compareContent(java.util.Set, java.util.Set, int)
*/
@Override
- public int compare(Locator o1, Locator o2) {
- if (o1 == o2) {
- return 0;
+ int compareContent(Set<Topic> o1, Set<Topic> o2,
+ int size) {
+ int res = 0;
+ Topic[] types1 = o1.toArray(new Topic[size]);
+ Topic[] types2 = o2.toArray(new Topic[size]);
+ Arrays.sort(types1, _topicIdComparator);
+ Arrays.sort(types2, _topicIdComparator);
+ for (int i=0; i < size && res == 0; i++) {
+ res = _topicIdComparator.compare(types1[i], types2[i]);
}
- return o1.getReference().compareTo(o2.getReference());
+ return res;
}
-
}
/**
- * Comparator for sets of {@link org.tmapi.core.Locator}s.
+ * Compares the scope of two scoped Topic Maps constructs.
*/
- private final class LocatorSetComparator extends AbstractSetComparator<Locator> {
+ private final class ScopeComparator extends AbstractSetComparator<Topic> {
/* (non-Javadoc)
* @see org.tinytim.mio.CTMTopicMapWriter.AbstractSetComparator#compareContent(java.util.Set, java.util.Set, int)
*/
@Override
- int compareContent(Set<Locator> o1, Set<Locator> o2, int size) {
- int res = 0;
- Locator[] locs1 = o1.toArray(new Locator[size]);
- Locator[] locs2 = o2.toArray(new Locator[size]);
- Arrays.sort(locs1, _locComparator);
- Arrays.sort(locs2, _locComparator);
+ int compareContent(Set<Topic> o1, Set<Topic> o2, int size) {
+ int res = 0 ;
+ Topic[] topics1 = o1.toArray(new Topic[size]);
+ Topic[] topics2 = o2.toArray(new Topic[size]);
+ Arrays.sort(topics1, _topicIdComparator);
+ Arrays.sort(topics2, _topicIdComparator);
for (int i=0; i < size && res == 0; i++) {
- res = _locComparator.compare(locs1[i], locs2[i]);
+ res = _topicIdComparator.compare(topics1[i], topics2[i]);
}
return res;
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|