/*
* #!
* Ontopia Engine
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* !#
*/
package net.ontopia.topicmaps.xml;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import net.ontopia.infoset.core.LocatorIF;
import net.ontopia.infoset.impl.basic.URILocator;
import net.ontopia.topicmaps.core.AssociationIF;
import net.ontopia.topicmaps.core.AssociationRoleIF;
import net.ontopia.topicmaps.core.ConstraintViolationException;
import net.ontopia.topicmaps.core.OccurrenceIF;
import net.ontopia.topicmaps.core.ReifiableIF;
import net.ontopia.topicmaps.core.ScopedIF;
import net.ontopia.topicmaps.core.TMObjectIF;
import net.ontopia.topicmaps.core.TopicIF;
import net.ontopia.topicmaps.core.TopicMapBuilderIF;
import net.ontopia.topicmaps.core.TopicMapIF;
import net.ontopia.topicmaps.core.TopicMapStoreFactoryIF;
import net.ontopia.topicmaps.core.TopicMapStoreIF;
import net.ontopia.topicmaps.core.TopicNameIF;
import net.ontopia.topicmaps.core.VariantNameIF;
import net.ontopia.topicmaps.core.index.ClassInstanceIndexIF;
import net.ontopia.topicmaps.core.index.ScopeIndexIF;
import net.ontopia.topicmaps.utils.PSI;
import net.ontopia.topicmaps.utils.SameStoreFactory;
import net.ontopia.utils.ObjectUtils;
import net.ontopia.utils.OntopiaRuntimeException;
import net.ontopia.xml.DefaultXMLReaderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.DeclHandler;
import org.xml.sax.ext.LexicalHandler;
/**
* INTERNAL: SAX2 content handler used for importing XTM 1.0 topic map
* documents. The content handler builds a topic map object based on a
* SAX event stream conforming to the XTM topic map interchange
* syntax. See http://www.topicmaps.org/xtm/1.0/
*/
// Keep a map of:
//
// - subject indicators -> topic
// - element ids / source locator -> object
//
// Todos:
//
// - should make the source locator registration optional.
// - could configure the content handler via a Map/Properties object.
// - the registerSourceLocator call on <topic> can be optimized
//
// FIXMEs:
//
// - maintain list of mergable topics?
// - note that resourceData.id is required.
// - keep track of nested merge maps, themes should also be added to all scoped objects in nested mergeMaps.
// - what do we do with the xml:base attribute? should it become the TopicMap.baseAddress?
//
// Source locators: topicMap, topic, association
//
// +id on association
// +id on baseName
// id on baseNameString
// id on instanceOf
// +id on member ! [no 1-1 mapping]
// id on mergeMap
// +id on occurrence
// id on parameters
// id on resourceData
// id on resourceRef
// id on roleSpec
// id on scope
// id on subjectIdentity
// id on subjectIndicatorRef
// +id on topic
// +id on topicMap
// id on topicRef
// +id on variant
// id on variantName
//
// o EL_ASSOCIATION
// o EL_BASENAME
// o EL_BASENAMESTRING ~
// o EL_INSTANCEOF
// o EL_MEMBER
// EL_MERGEMAP
// o EL_OCCURRENCE
// o EL_PARAMETERS
// o EL_RESOURCEDATA ~
// o EL_RESOURCEREF *
// o EL_ROLESPEC
// o EL_SCOPE
// o EL_SUBJECTIDENTITY
// o EL_SUBJECTINDICATORREF *
// o EL_TOPIC
// o EL_TOPICMAP
// o EL_TOPICREF *
// o EL_VARIANT
// o EL_VARIANTNAME
//
// * = EMPTY
// ~ = #PCDATA
//
public class XTMContentHandler extends AbstractTopicMapContentHandler
implements LexicalHandler, DeclHandler {
static final String NO_URI = "URI";
static final String EL_ASSOCIATION = "association";
static final String EL_BASENAME = "baseName";
static final String EL_BASENAMESTRING = "baseNameString";
static final String EL_INSTANCEOF = "instanceOf";
static final String EL_MEMBER = "member";
static final String EL_MERGEMAP = "mergeMap";
static final String EL_OCCURRENCE = "occurrence";
static final String EL_PARAMETERS = "parameters";
static final String EL_RESOURCEDATA = "resourceData";
static final String EL_RESOURCEREF = "resourceRef";
static final String EL_ROLESPEC = "roleSpec";
static final String EL_SCOPE = "scope";
static final String EL_SUBJECTIDENTITY = "subjectIdentity";
static final String EL_SUBJECTINDICATORREF = "subjectIndicatorRef";
static final String EL_TOPIC = "topic";
static final String EL_TOPICMAP = "topicMap";
static final String EL_TOPICREF = "topicRef";
static final String EL_VARIANT = "variant";
static final String EL_VARIANTNAME = "variantName";
static final String NS_XTM = "http://www.topicmaps.org/xtm/1.0/";
static final String NS_XLINK = "http://www.w3.org/1999/xlink";
static final String NS_NS = "http://www.w3.org/XML/1998/namespace";
static final String SAX_LEXICAL_HANDLER = "http://xml.org/sax/properties/lexical-handler";
static final String SAX_DECL_HANDLER = "http://xml.org/sax/properties/declaration-handler";
// Define a logging category.
static Logger log = LoggerFactory.getLogger(XTMContentHandler.class.getName());
protected TopicMapStoreFactoryIF stores;
private TopicMapIF topicmap;
private TopicMapBuilderIF builder;
private Collection topicmaps;
private Stack bases;
private StringBuilder content;
private boolean keep_content;
/**
* Keeps track of the declared entities, in order that the base URI
* can be set correctly in external entities.
*/
protected Map entities;
/**
* Used to tell if we are reading the top-level XTM document (false)
* or if we are reading a merged-in XTM document (true).
*/
protected boolean isSubDocument;
protected ExternalReferenceHandlerIF ref_handler;
public XTMContentHandler(TopicMapStoreFactoryIF stores, LocatorIF base_address) {
super(base_address);
this.stores = stores;
this.entities = new HashMap();
this.bases = new Stack();
}
public XTMContentHandler(TopicMapStoreFactoryIF stores, LocatorIF base_address, Collection processed_documents) {
super(base_address, processed_documents);
this.stores = stores;
this.entities = new HashMap();
this.bases = new Stack();
}
/**
* INTERNAL: Gets the topic maps found after having parsed the input source.
*/
public Collection getTopicMaps() {
return topicmaps;
}
/**
* INTERNAL: Gets the external reference handler. The reference
* handler will receive notifications on references to external
* topics and topic maps.
*/
public ExternalReferenceHandlerIF getExternalReferenceHandler() {
return ref_handler;
}
/**
* INTERNAL: Sets the external reference handler.
*/
public void setExternalReferenceHandler(ExternalReferenceHandlerIF ref_handler) {
this.ref_handler = ref_handler;
}
/**
* INTERNAL: Tell the handler whether this is a top-level document
* or not.
*/
public void setSubDocument(boolean isSubDocument) {
this.isSubDocument = isSubDocument;
}
/**
* INTERNAL: Registers the handler with the parser and configures the
* parser.
*/
public void register(XMLReader parser) {
super.register(parser);
try {
parser.setProperty(SAX_LEXICAL_HANDLER, this);
} catch (SAXException e) {
log.warn("Parser does not support SAX LexicalHandler: " +e.getMessage());
}
try {
parser.setProperty(SAX_DECL_HANDLER, this);
} catch (SAXException e) {
log.warn("Parser does not support SAX DeclHandler: " + e.getMessage());
throw new OntopiaRuntimeException(e);
}
}
// --------------------------------------------------------------------------
// Document events
// --------------------------------------------------------------------------
public void startDocument () {
// Initialize variables
parents.clear();
info.clear();
bases.clear();
keep_content = false;
content = new StringBuilder();
topicmaps = new ArrayList();
// Initialize base address stack. Top level base added to
// stack. The user specified base address overrides the system id
// of the source.
if (doc_address != null)
bases.push(doc_address);
else if (locator != null && locator.getSystemId() != null) {
try {
bases.push(new URILocator(locator.getSystemId()));
} catch (MalformedURLException e) {
// Ignore; throw exception later instead [see getBaseAddress()].
}
}
log.debug("Processing document '" + doc_address + "'.");
// Initialize list of accumulated processed documents
this.processed_documents_accumulated = new HashSet();
// Add this document to the list of processed documents. Note: we
// are adding it here, since all the topic maps in the document
// have the same base address.
this.processed_documents_from_parent.add(getBaseAddress());
}
public void endDocument () {
// Copy list of accumulated processed documents to parent list
this.processed_documents_from_parent.addAll(processed_documents_accumulated);
// log.debug("Stack size: " + parents.size());
// log.debug("Info map: " + info);
}
public void startElement (String uri, String name, String qName, Attributes atts) throws SAXException {
try {
//System.out.println("S: '" + uri + "' " + qName + " (" + getLocationInfo() + ")");
// Handle xml:base attribute.
if (atts.getValue(NS_NS, "base") != null) {
// Push new base address onto bases stack
// Note: xml:base can contain relative URIs
LocatorIF base_address = createLocator(atts.getValue(NS_NS, "base"));
bases.push(base_address);
} else {
// Push parent base address onto bases stack
bases.push(bases.peek());
}
if (uri.equals(NS_XTM) || uri.equals("")) {
if (uri.equals(NS_XTM))
qName = name; // use the local name, since qName may have prefix
// -----------------------------------------------------------------------------
// S: topicRef
// -----------------------------------------------------------------------------
if (EL_TOPICREF.equals(qName)) {
// Resolve reference
String href = atts.getValue(NS_XLINK, "href");
if (href == null)
href = atts.getValue("xlink:href");
// Process in context of parent
String parent_type = (String)parents.peek();
// FIXME: subjectIdentity.topicRef vs. instanceOf.topicRef
if (EL_SUBJECTIDENTITY.equals(parent_type) &&
info.get(EL_TOPIC) == this.lazyTopic && this.lazyTopic != null) {
LocatorIF loc = createLocator(href);
addItemIdentifier(this.lazyTopic, loc);
if (!loc.getAddress().startsWith(getBaseAddress().getAddress() + '#'))
// load external document
getReferencedExternalTopic(loc);
} else {
TopicIF referenced_topic = resolveTopicRef(href);
// Process the topic reference in the correct context
processTopicReference(referenced_topic);
}
}
// -----------------------------------------------------------------------------
// S: instanceof
// -----------------------------------------------------------------------------
else if (EL_INSTANCEOF.equals(qName)) {
// Push element on parent stack
parents.push(EL_INSTANCEOF);
}
// -----------------------------------------------------------------------------
// S: member
// -----------------------------------------------------------------------------
else if (EL_MEMBER.equals(qName)) {
// Push element on parent stack
parents.push(EL_MEMBER);
}
// -----------------------------------------------------------------------------
// S: roleSpec
// -----------------------------------------------------------------------------
else if (EL_ROLESPEC.equals(qName)) {
// Push element on parent stack
parents.push(EL_ROLESPEC);
}
// -----------------------------------------------------------------------------
// S: association
// -----------------------------------------------------------------------------
else if (EL_ASSOCIATION.equals(qName)) {
if (builder == null)
throw new InvalidTopicMapException("Association outside topic map. Did you forget or misspell the 'topicMap' element?");
// Create association
TopicIF atype = getNullTopic(builder.getTopicMap());
AssociationIF assoc = builder.makeAssociation(atype);
// Add propagated themes
propagateThemes(assoc);
// Element id
registerSourceLocator(assoc, atts.getValue("", "id"));
// Put association on info map
info.put(EL_ASSOCIATION, assoc);
// Push element on parent stack
parents.push(EL_ASSOCIATION);
}
// -----------------------------------------------------------------------------
// S: baseName
// -----------------------------------------------------------------------------
else if (EL_BASENAME.equals(qName)) {
// Create basename
TopicIF topic = getParentTopic();
TopicNameIF basename = builder.makeTopicName(topic, "");
// FIXME: register with parent topic, but this might be
// troublesome since topics can be merged.
// Add propagated themes
propagateThemes(basename);
// Register element id
registerSourceLocator(basename, atts.getValue("", "id"));
// Put basename on info map
info.put(EL_BASENAME, basename);
// Push element on parent stack
parents.push(EL_BASENAME);
}
// -----------------------------------------------------------------------------
// S: baseNameString
// -----------------------------------------------------------------------------
else if (EL_BASENAMESTRING.equals(qName)) {
keep_content = true;
content.setLength(0);
}
// -----------------------------------------------------------------------------
// S: topic
// -----------------------------------------------------------------------------
else if (EL_TOPIC.equals(qName)) {
// Check to see if topic already exist
String id = atts.getValue("", "id");
// Look up existing topic
TopicIF topic = null;
LocatorIF locator = null;
if (id != null) {
locator = createLocator('#' + id);
topic = resolveSourceLocatorOrSubjectIndicator(locator);
}
// Put lazy topic on info map if not already found
if (topic != null)
info.put(EL_TOPIC, topic);
else {
if (this.lazyTopic == null) {
topic = builder.makeTopic();
if (locator != null)
addItemIdentifier(topic, locator);
info.put(EL_TOPIC, topic);
} else {
if (locator != null)
addItemIdentifier(this.lazyTopic, locator);
info.put(EL_TOPIC, this.lazyTopic);
}
}
// Push element on parent stack
parents.push(EL_TOPIC);
}
// -----------------------------------------------------------------------------
// S: occurrence
// -----------------------------------------------------------------------------
else if (EL_OCCURRENCE.equals(qName)) {
// Create occurrence
TopicIF topic = getParentTopic();
TopicIF otype = getDefaultOccurrenceTopic(builder.getTopicMap());
OccurrenceIF occurs = builder.makeOccurrence(topic, otype, "");
// FIXME: register with parent topic, but this might be
// troublesome since topics can be merged.
// Add propagated themes
propagateThemes(occurs);
// Put occurrence on info map
info.put(EL_OCCURRENCE, occurs);
// Register element id
registerSourceLocator(occurs, atts.getValue("", "id"));
// Push element on parent stack
parents.push(EL_OCCURRENCE);
}
// -----------------------------------------------------------------------------
// S: resourceRef
// -----------------------------------------------------------------------------
else if (EL_RESOURCEREF.equals(qName)) {
// Create locator
String href = atts.getValue(NS_XLINK, "href");
if (href == null)
href = atts.getValue("xlink:href");
// Process in context of parent
String parent_type = (String)parents.peek();
// References a topic:
if (EL_MEMBER.equals(parent_type)) {
// Resolve referenced topic
TopicIF referenced_topic = resolveResourceRef(createLocator(href));
// Create new association role
processMember(referenced_topic);
}
else if (EL_MERGEMAP.equals(parent_type)) {
// Resolve referenced topic
TopicIF referenced_topic = resolveResourceRef(createLocator(href));
ExternalDocument merge_map = (ExternalDocument)info.get(EL_MERGEMAP);
merge_map.addTheme(referenced_topic);
}
else if (EL_SCOPE.equals(parent_type)) {
// Resolve referenced topic
TopicIF referenced_topic = resolveResourceRef(createLocator(href));
processTheme(referenced_topic);
}
// References an information resource:
else if (EL_OCCURRENCE.equals(parent_type)) {
// Create locator
LocatorIF locator = createLocator(href);
// Set occurrence locator
OccurrenceIF occurs = (OccurrenceIF)info.get(EL_OCCURRENCE);
occurs.setLocator(locator);
}
else if (EL_VARIANTNAME.equals(parent_type)) {
// Create locator
LocatorIF locator = createLocator(href);
// Set variant name locator
Stack variants = (Stack)info.get(EL_VARIANT);
VariantNameIF vname = (VariantNameIF)variants.peek();
vname.setLocator(locator);
}
// Can reference both:
else if (EL_SUBJECTIDENTITY.equals(parent_type)) {
// FIXME: Need to check if the topic is a local topic.
// Local topics are easily recognizable as long as the
// locator syntax is ok.
// FIXME: Don't know what to do if reference resolves to an
// external topic. It is actually very hard, if not
// impossible, to realize whether the reference references
// an external information resource or an external topic.
// FIXME: Should the topics merge if the addressable subject
// is another topic?
LocatorIF subject = createLocator(href);
if (info.get(EL_TOPIC) == this.lazyTopic && this.lazyTopic != null) {
this.lazyTopic.addSubjectLocator(subject);
} else {
// Check to see if another topic has this addressable topic.
TopicIF other_topic = topicmap.getTopicBySubjectLocator(subject);
TopicIF current_topic = (TopicIF)info.get(EL_TOPIC);
// Create new topic
if (other_topic == null) {
// Set subject resource
current_topic.addSubjectLocator(subject);
} else if (other_topic != current_topic) {
if (log.isInfoEnabled())
log.debug("Topic " + current_topic + " merged with " + other_topic +
" because they both have the same addressable subject: " + subject);
// Merge existing topic with current topic.
other_topic.merge(current_topic);
// update info map
info.put(EL_TOPIC, other_topic);
}
}
}
else
throw new OntopiaRuntimeException("Unknown parent: " + parent_type);
}
// -----------------------------------------------------------------------------
// S: resourceData
// -----------------------------------------------------------------------------
else if (EL_RESOURCEDATA.equals(qName)) {
keep_content = true;
content.setLength(0);
}
// -----------------------------------------------------------------------------
// S: variantName
// -----------------------------------------------------------------------------
else if (EL_VARIANTNAME.equals(qName)) {
// Push element on parent stack
parents.push(EL_VARIANTNAME);
}
// -----------------------------------------------------------------------------
// S: variant
// -----------------------------------------------------------------------------
else if (EL_VARIANT.equals(qName)) {
// Create variant name
TopicNameIF bname = (TopicNameIF)info.get(EL_BASENAME);
VariantNameIF vname = builder.makeVariantName(bname, "", Collections.EMPTY_SET);
// Add variant to parent name
if (info.containsKey(EL_VARIANT)) {
Stack variants = (Stack)info.get(EL_VARIANT);
// Loop over parent variant names and inherit their themes
Iterator iter = variants.iterator();
while (iter.hasNext()) {
Iterator themes = ((VariantNameIF)iter.next()).getScope().iterator();
while (themes.hasNext())
vname.addTheme((TopicIF)themes.next());
}
// This is a nested variant so put it on the stack.
variants.push(vname);
} else {
// This is a top level variant, so create a new stack.
Stack variants = new Stack();
variants.push(vname);
info.put(EL_VARIANT, variants);
}
// Register element id
registerSourceLocator(vname, atts.getValue("", "id"));
// Push element on parent stack
parents.push(EL_VARIANT);
}
// -----------------------------------------------------------------------------
// S: parameters
// -----------------------------------------------------------------------------
else if (EL_PARAMETERS.equals(qName)) {
// Push element on parent stack
parents.push(EL_PARAMETERS);
}
// -----------------------------------------------------------------------------
// S: scope
// -----------------------------------------------------------------------------
else if (EL_SCOPE.equals(qName)) {
// Push element on parent stack
parents.push(EL_SCOPE);
}
// -----------------------------------------------------------------------------
// S: subjectIndicatorRef
// -----------------------------------------------------------------------------
else if (EL_SUBJECTINDICATORREF.equals(qName)) {
// Get reference
String href = atts.getValue(NS_XLINK, "href");
if (href == null)
href = atts.getValue("xlink:href");
LocatorIF indicator = createLocator(href);
// Process in context of parent
String parent_type = (String)parents.peek();
// If the parent is subjectIdentity this reference should
// become a subject indicator of the parent topic.
if (EL_SUBJECTIDENTITY.equals(parent_type)) {
// FIXME: Should merge with parent topic if it references
// another topic.
if (info.get(EL_TOPIC) == this.lazyTopic && this.lazyTopic != null) {
TopicIF t = addSubjectIdentifier(this.lazyTopic, indicator);
if (t != this.lazyTopic) // topic was merged away
info.put(EL_TOPIC, t);
} else {
// Check to see if another topic has this addressable topic.
TopicIF current_topic = (TopicIF)info.get(EL_TOPIC);
TopicIF rtopic = registerSubjectIndicator(current_topic, indicator);
// update info map
if (rtopic != current_topic) info.put(EL_TOPIC, rtopic);
}
} else {
// Resolve reference
TopicIF rtopic = registerSubjectIndicator(null, indicator);
// Other parent elements
processTopicReference(rtopic);
}
}
// -----------------------------------------------------------------------------
// S: subjectIdentity
// -----------------------------------------------------------------------------
else if (EL_SUBJECTIDENTITY.equals(qName)) {
// Push element on parent stack
parents.push(EL_SUBJECTIDENTITY);
}
// -----------------------------------------------------------------------------
// S: mergeMap
// -----------------------------------------------------------------------------
else if (EL_MERGEMAP.equals(qName)) {
// Get merge map address
String href = atts.getValue(NS_XLINK, "href");
if (href == null)
href = atts.getValue("xlink:href");
// Put merge map on info map
ExternalDocument merge_map = new ExternalDocument(createLocator(href));
info.put(EL_MERGEMAP, merge_map);
// Push element on parent stack
parents.push(EL_MERGEMAP);
}
// -----------------------------------------------------------------------------
// S: topicMap
// -----------------------------------------------------------------------------
else if (EL_TOPICMAP.equals(qName)) {
// Initialize the list of processed documents for the current topic map
processed_documents_current = new HashSet(processed_documents_from_parent);
// Get topic map object
topicmap = stores.createStore().getTopicMap();
topicmaps.add(topicmap);
if (topicmap instanceof net.ontopia.topicmaps.impl.basic.TopicMap)
this.lazyTopic = null;
else
this.lazyTopic = new LazyTopic();
// Get hold of topic map builder
builder = topicmap.getBuilder();
// Set base address on in-memory store
TopicMapStoreIF store = topicmap.getStore();
if ((store instanceof net.ontopia.topicmaps.impl.utils.AbstractTopicMapStore) &&
store.getBaseAddress() == null)
((net.ontopia.topicmaps.impl.utils.AbstractTopicMapStore)store).setBaseAddress(doc_address);
// Register element id
if (!isSubDocument)
// if we are a subdocument we must not create a source locator since
// that would imply that the sub-topic map is the same as the parent
// http://www.y12.doe.gov/sgml/sc34/document/0299.htm#merge-prop-srclocs
// also see bug #457
registerSourceLocator(topicmap, atts.getValue("", "id"));
// Push element on parent stack
parents.push(EL_TOPICMAP);
} else
log.warn("Unknown element '" + qName + "'");
} else { // if there is a namespace URI that is not the XTM ns URI
// log.error("Unknown element: {" + uri + "}" + name + "[" + qName + "]");
if (EL_TOPICMAP.equals(qName)) {
log.error("Unrecognized <topicMap> element " + getLocationInfo());
}
}
} catch (Throwable e) {
if (logError()) log.error("Exception was thrown from within startElement", e);
ObjectUtils.throwRuntimeException(e);
}
}
public void characters (char ch[], int start, int length) {
if (keep_content)
content.append(ch, start, length);
}
public void endElement (String uri, String name, String qName) throws SAXException {
//System.out.println("E: " + qName + " (" + getLocationInfo() + ")");
try {
if (NS_XTM.equals(uri) || uri.equals("")) {
if (NS_XTM.equals(uri))
qName = name;
// -----------------------------------------------------------------------------
// E: instanceOf
// -----------------------------------------------------------------------------
if (EL_INSTANCEOF.equals(qName)) {
// Pop element off parent stack
parents.pop();
}
// -----------------------------------------------------------------------------
// E: member
// -----------------------------------------------------------------------------
else if (EL_MEMBER.equals(qName)) {
// Pop element off parent stack
parents.pop();
// If EL_MEMBER key doesn't exist no players occurred.
if (info.containsKey(EL_MEMBER)) {
info.remove(EL_MEMBER);
} else {
// Note that we're not setting the player, since there isn't one.
// Add association role to association
AssociationIF assoc = (AssociationIF)info.get(EL_ASSOCIATION);
// Set role type if it was specified
TopicIF nullTopic = getNullTopic(builder.getTopicMap());
TopicIF roletype = (TopicIF)info.get(EL_ROLESPEC);
if (roletype == null) roletype = nullTopic;
TopicIF player = nullTopic;
AssociationRoleIF role = builder.makeAssociationRole(assoc, roletype, player);
}
// Remove role type from info map
info.remove(EL_ROLESPEC);
}
// -----------------------------------------------------------------------------
// E: roleSpec
// -----------------------------------------------------------------------------
else if (EL_ROLESPEC.equals(qName)) {
// Pop element of parent stack
parents.pop();
}
// -----------------------------------------------------------------------------
// E: association
// -----------------------------------------------------------------------------
else if (EL_ASSOCIATION.equals(qName)) {
// Pop element of parent stack
parents.pop();
// Remove association from info map
info.remove(EL_ASSOCIATION);
}
// -----------------------------------------------------------------------------
// E: baseName
// -----------------------------------------------------------------------------
else if (EL_BASENAME.equals(qName)) {
// Pop element of parent stack
parents.pop();
// Remove occurrence from info map
info.remove(EL_BASENAME);
}
// -----------------------------------------------------------------------------
// E: baseNameString
// -----------------------------------------------------------------------------
else if (EL_BASENAMESTRING.equals(qName)) {
// Set the name value of the base name
TopicNameIF basename = (TopicNameIF)info.get(EL_BASENAME);
basename.setValue(content.toString());
keep_content = false;
}
// -----------------------------------------------------------------------------
// E: topic
// -----------------------------------------------------------------------------
else if (EL_TOPIC.equals(qName)) {
// resolve lazy topic if it still exists
if (info.get(EL_TOPIC) == this.lazyTopic && this.lazyTopic != null) {
createTopicFromLazyTopic();
}
// Pop element of parent stack
parents.pop();
// Remove topic from info map
info.remove(EL_TOPIC);
}
// -----------------------------------------------------------------------------
// E: occurrence
// -----------------------------------------------------------------------------
else if (EL_OCCURRENCE.equals(qName)) {
// Pop element of parent stack
parents.pop();
// Remove occurrence from info map
info.remove(EL_OCCURRENCE);
}
// -----------------------------------------------------------------------------
// E: resourceData
// -----------------------------------------------------------------------------
else if (EL_RESOURCEDATA.equals(qName)) {
if (info.containsKey(EL_VARIANT)) {
Stack variants = (Stack)info.get(EL_VARIANT);
VariantNameIF vname = (VariantNameIF)variants.peek();
vname.setValue(content.toString());
keep_content = false;
}
else if (info.containsKey(EL_OCCURRENCE)) {
OccurrenceIF occurs = (OccurrenceIF)info.get(EL_OCCURRENCE);
occurs.setValue(content.toString());
keep_content = false;
}
else
throw new OntopiaRuntimeException("Unknown resourceData context.");
}
// -----------------------------------------------------------------------------
// E: variant
// -----------------------------------------------------------------------------
else if (EL_VARIANT.equals(qName)) {
// Pop element of parent stack
parents.pop();
// Process in context of parent
String parent_type = (String)parents.peek();
if (EL_BASENAME.equals(parent_type)) {
// Remove variant stack from info map
info.remove(EL_VARIANT);
}
else if (EL_VARIANT.equals(parent_type)) {
// Pop variant name of info map variant name stack.
Stack variants = (Stack)info.get(EL_VARIANT);
variants.pop();
}
}
// -----------------------------------------------------------------------------
// E: variantName
// -----------------------------------------------------------------------------
else if (EL_VARIANTNAME.equals(qName)) {
// Pop element of parent stack
parents.pop();
}
// -----------------------------------------------------------------------------
// E: parameters
// -----------------------------------------------------------------------------
else if (EL_PARAMETERS.equals(qName)) {
// Pop element of parent stack
parents.pop();
}
// -----------------------------------------------------------------------------
// E: scope
// -----------------------------------------------------------------------------
else if (EL_SCOPE.equals(qName)) {
// Pop element of parent stack
parents.pop();
}
// -----------------------------------------------------------------------------
// E: subjectIdentity
// -----------------------------------------------------------------------------
else if (EL_SUBJECTIDENTITY.equals(qName)) {
// Pop element off parent stack
parents.pop();
}
// -----------------------------------------------------------------------------
// E: mergeMap
// -----------------------------------------------------------------------------
else if (EL_MERGEMAP.equals(qName)) {
// Get merge map from info map
ExternalDocument merge_map = (ExternalDocument)info.get(EL_MERGEMAP);
LocatorIF locator = merge_map.getLocator();
// Ask external topic map reference handler whether merge map
// reference should be traversed
if (getExternalReferenceHandler() != null)
locator = getExternalReferenceHandler().externalTopicMap(locator);
// Import topic map if merge map locator is not null. Note
// that the reference handler can set it to null if it decides
// that the reference is not to be resolved.
if (locator != null)
// Process merge map
merge_map.importInto(topicmap);
// Pop element off parent stack
parents.pop();
// Remove merge map from info map
info.remove(EL_MERGEMAP);
}
// -----------------------------------------------------------------------------
// E: topicMap
// -----------------------------------------------------------------------------
else if (EL_TOPICMAP.equals(qName)) {
// Add processed documents to accumulated list.
processed_documents_accumulated.addAll(processed_documents_current);
// Remove null-topic if not used
removeNullTopic(topicmap);
// Remove occurrence default topic if not used
removeDefaultOccurrenceTopic(topicmap);
// Clear topic map related info
this.topicmap = null;
this.lazyTopic = null;
this.builder = null;
// Pop element of parent stack
parents.pop();
}
}
// Pop current base address off bases stack
bases.pop();
} catch (Throwable e) {
if (logError()) log.error("Exception was thrown from within endElement", e);
ObjectUtils.throwRuntimeException(e);
}
}
public void startPrefixMapping(String prefix, String uri) {
// log.debug("Start prefix: {" + uri + "} '" + prefix + "'");
}
public void endPrefixMapping(String prefix) {
// log.debug("End prefix: '" + prefix + "'");
}
// ---------------------------------------------------------------------------
// Misc. methods
// ---------------------------------------------------------------------------
// returns topic because reify() can merge topics
private TopicIF reify(ReifiableIF reifiable, TopicIF reifier) {
reifiable.setReifier(reifier);
return reifiable.getReifier();
}
private boolean logError() {
try {
return Boolean.valueOf(System.getProperty("net.ontopia.topicmaps.xml.XTMContentHandler.logError")).booleanValue();
} catch (SecurityException e) {
return false;
}
}
protected LocatorIF getBaseAddress() {
if (bases.size() > 0) {
LocatorIF base = (LocatorIF)bases.peek();
if (base != null) return base;
}
throw new OntopiaRuntimeException("Base address is not specified.");
}
protected TopicIF resolveTopicRef(String address) throws SAXException {
LocatorIF locator = createLocator(address);
TMObjectIF object = topicmap.getObjectByItemIdentifier(locator);
if (object != null && !(object instanceof TopicIF))
throw new OntopiaRuntimeException("topicRef element with URI '" + address +
"' referred to non-topic: " + object);
TopicIF topic = (TopicIF) object;
if (topic == null)
topic = topicmap.getTopicBySubjectIdentifier(locator);
if (topic == null) {
if ((address.length() > 0 && address.charAt(0) == '#') ||
locator.getAddress().startsWith(getBaseAddress().getAddress() + '#')) {
// this is a local reference; create a topic and return it
topic = registerSourceLocator(topic, locator);
} else
// this is an external reference; resolve it
topic = getReferencedExternalTopic(locator);
}
return topic;
}
protected TopicIF resolveResourceRef(LocatorIF locator) {
// Look up in the topic map to see if a topic with the identity already exist
TopicIF topic = topicmap.getTopicBySubjectLocator(locator);
if (topic != null) return topic;
// If there is no such topic create a new one with the given subject indicator.
topic = builder.makeTopic();
// log.debug("New topic (resourceRef): " + locator);
// Set addressable subject
topic.addSubjectLocator(locator);
return topic;
}
protected TopicIF registerSubjectLocator(TopicIF topic, LocatorIF locator) {
// merge with existing, if any
TopicIF existing = topicmap.getTopicBySubjectLocator(locator);
if (existing != null && ObjectUtils.different(existing, topic)) {
existing.merge(topic);
topic = existing;
}
// add subject locator
topic.addSubjectLocator(locator);
return topic;
}
protected void registerSourceLocator(TMObjectIF tmobject, String id) {
// No need to register source locator if id is null
if (id == null) return;
addItemIdentifier(tmobject, createLocator('#' + id));
}
protected TopicIF registerSourceLocator(TopicIF topic, LocatorIF locator) {
TopicIF tsrcloc = resolveSourceLocatorOrSubjectIndicator(locator);
if (tsrcloc != null) {
if (topic != null && tsrcloc != topic) {
if (log.isInfoEnabled())
log.debug("Topic " + topic + " merged with " + tsrcloc +
" because the subject indicator is the same as the source locator of the other: " + locator);
tsrcloc.merge(topic);
}
topic = tsrcloc;
}
if (topic == null) {
// create new topic if none exists
topic = builder.makeTopic();
}
// add source locator
addItemIdentifier(topic, locator);
return topic;
}
protected TopicIF registerSubjectIndicator(TopicIF topic, LocatorIF locator) {
TopicIF tsrcloc = resolveSourceLocatorOrSubjectIndicator(locator);
if (tsrcloc != null) {
if (topic != null && tsrcloc != topic) {
if (log.isInfoEnabled())
log.debug("Topic " + topic + " merged with " + tsrcloc +
" because the subject indicator is the same as the source locator of the other: " + locator);
tsrcloc.merge(topic);
}
topic = tsrcloc;
}
if (topic == null) {
// create new topic if none exists
topic = builder.makeTopic();
}
// add subject indicator
return addSubjectIdentifier(topic, locator);
}
protected TopicIF resolveSourceLocatorOrSubjectIndicator(LocatorIF locator) {
// look up objects
TopicIF topic = topicmap.getTopicBySubjectIdentifier(locator);
TMObjectIF osrcloc = topicmap.getObjectByItemIdentifier(locator);
if (osrcloc != null && osrcloc instanceof TopicIF) {
TopicIF tsrcloc = (TopicIF)osrcloc;
if (topic != null && tsrcloc != topic) {
if (log.isInfoEnabled())
log.debug("Topic " + topic + " merged with " + tsrcloc +
" because the subject indicator is the same as the source locator of the other: " + locator);
// ISSUE: should we keep the oldest topic in this case? or
// perhaps the one with the indicator
tsrcloc.merge(topic);
}
return tsrcloc;
}
return topic;
}
protected LocatorIF createLocator(String address) {
if (address.length() == 0)
return getBaseAddress();
else if (address.charAt(0) == '#')
// this is necessary because URI refs of the form "#foo" are
// same-document references (RFC 2396 - 4.2), and resolve
// relative to the document address, regardless of what base
// address may be in effect inside the document
return doc_address.resolveAbsolute(address);
else
return getBaseAddress().resolveAbsolute(address);
}
protected LocatorIF createURILocator(String address) {
try {
return new URILocator(address);
} catch (MalformedURLException e) {
throw new OntopiaRuntimeException(e);
}
}
protected TopicIF getReferencedExternalTopic(LocatorIF orig_locator) throws SAXException {
/// 0) Check to see if we have this topic already
TMObjectIF tsrcloc = topicmap.getObjectByItemIdentifier(orig_locator);
if (tsrcloc != null && tsrcloc instanceof TopicIF)
return (TopicIF)tsrcloc;
/// 1) Load external topic map, if necessary
// Ask external topic reference handler whether reference should be traversed
LocatorIF locator = orig_locator;
if (getExternalReferenceHandler() != null)
locator = getExternalReferenceHandler().externalTopic(orig_locator);
if (locator != null) {
String locstr = locator.getAddress();
int frag_offset = locstr.indexOf('#');
LocatorIF ref;
if (frag_offset == -1)
ref = locator;
else
ref = createURILocator(locstr.substring(0, frag_offset));
// Traverse external reference
ExternalDocument doc = new ExternalDocument(ref);
doc.importInto(topicmap);
// Make a new attempt at resolving source locator, since the imported
// file usually will have created this topic. This fixes bug #750.
tsrcloc = topicmap.getObjectByItemIdentifier(orig_locator);
if (tsrcloc != null && tsrcloc instanceof TopicIF)
return (TopicIF)tsrcloc;
}
/// 2) If topic still doesn't exist, create it
return registerSourceLocator(null, orig_locator);
}
protected void processTheme(TopicIF theme) {
// Locate scoped object
ScopedIF scoped;
if (info.containsKey(EL_VARIANT)) {
Stack variants = (Stack)info.get(EL_VARIANT);
scoped = (ScopedIF)variants.peek();
}
else if (info.containsKey(EL_BASENAME))
scoped = (ScopedIF)info.get(EL_BASENAME);
else if (info.containsKey(EL_OCCURRENCE))
scoped = (ScopedIF)info.get(EL_OCCURRENCE);
else if (info.containsKey(EL_ASSOCIATION))
scoped = (ScopedIF)info.get(EL_ASSOCIATION);
else if (info.containsKey(EL_MERGEMAP)) {
ExternalDocument merge_map = (ExternalDocument)info.get(EL_MERGEMAP);
merge_map.addTheme(theme);
return;
} else
throw new OntopiaRuntimeException("Unknown resourceData context.");
// Add referenced topic to scope of scoped object
scoped.addTheme(theme);
}
protected void processTopicReference(TopicIF referenced_topic) {
// This method is used by topicRef and subjectIndicatorRef.
// Process in context of parent
String parent_type = (String)parents.peek();
if (EL_INSTANCEOF.equals(parent_type)) {
if (info.containsKey(EL_ASSOCIATION)) {
// Set association type
AssociationIF assoc = (AssociationIF)info.get(EL_ASSOCIATION);
assoc.setType(referenced_topic);
}
else if (info.containsKey(EL_OCCURRENCE)) {
// Set occurrence type
OccurrenceIF occurs = (OccurrenceIF)info.get(EL_OCCURRENCE);
occurs.setType(referenced_topic);
}
else if (info.containsKey(EL_BASENAME)) {
// Set basename type
TopicNameIF bname = (TopicNameIF)info.get(EL_BASENAME);
bname.setType(referenced_topic);
}
else if (info.containsKey(EL_TOPIC)) {
TopicIF topic = (TopicIF)info.get(EL_TOPIC);
topic.addType(referenced_topic);
}
}
else if (EL_SCOPE.equals(parent_type)) {
processTheme(referenced_topic);
}
else if (EL_MEMBER.equals(parent_type)) {
// Create new association role
processMember(referenced_topic);
}
else if (EL_ROLESPEC.equals(parent_type)) {
// Put role type on info map
info.put(EL_ROLESPEC, referenced_topic);
}
else if (EL_PARAMETERS.equals(parent_type)) {
processTheme(referenced_topic);
}
else if (EL_SUBJECTIDENTITY.equals(parent_type)) {
TopicIF current_topic = (TopicIF)info.get(EL_TOPIC);
if (current_topic != referenced_topic) {
if (log.isInfoEnabled())
log.debug("Topic " + current_topic + " merged with " + referenced_topic +
" because it is an addressable subject (subjectIndentity>:<topicRef>)");
// merge referenced topic with current topic.
referenced_topic.merge(current_topic);
// update info map
info.put(EL_TOPIC, referenced_topic);
}
}
else if (EL_MERGEMAP.equals(parent_type)) {
// FIXME: do something else?
processTheme(referenced_topic);
}
else
throw new OntopiaRuntimeException("Unknown parent: " + parent_type);
}
protected void processMember(TopicIF player) {
// Add association role to association
AssociationIF assoc = (AssociationIF)info.get(EL_ASSOCIATION);
TopicIF roletype = (TopicIF)info.get(EL_ROLESPEC);
if (roletype == null)
roletype = getNullTopic(builder.getTopicMap());
if (player == null)
player = getNullTopic(builder.getTopicMap());
AssociationRoleIF role = builder.makeAssociationRole(assoc, roletype, player);
// Put previous role onto info map, so that it is possible to
// recognize whether the member element was empty or not.
info.put(EL_MEMBER, role);
}
protected void addItemIdentifier(TMObjectIF tmobject, LocatorIF sourceLocator) {
tmobject.addItemIdentifier(sourceLocator);
// handle implicit reification
if (tmobject instanceof ReifiableIF) {
TopicIF reifier = topicmap.getTopicBySubjectIdentifier(sourceLocator);
if (reifier != null)
reify((ReifiableIF)tmobject, reifier);
}
}
// can cause merging, therefore returns topic
protected TopicIF addSubjectIdentifier(TopicIF topic, LocatorIF subjectIndicator) {
topic.addSubjectIdentifier(subjectIndicator);
// handle implicit reification
if (!(topic instanceof LazyTopic)) {
TMObjectIF reified = topicmap.getObjectByItemIdentifier(subjectIndicator);
if (reified != null && reified instanceof ReifiableIF)
return reify((ReifiableIF)reified, topic);
}
return topic;
}
// --------------------------------------------------------------------------
// Lazy initialization of topics
// --------------------------------------------------------------------------
// creating topics can be postponed until first characteristic found
class LazyTopic implements TopicIF {
protected List types = new ArrayList();
protected List subjectLocators = new ArrayList();
protected List subjectIndicators = new ArrayList();
protected List sourceLocators = new ArrayList();
// implementations of methods collecting lazy data
public void addSubjectLocator(LocatorIF subject) throws ConstraintViolationException {
this.subjectLocators.add(subject);
}
public void addSubjectIdentifier(LocatorIF subject_identifier) throws ConstraintViolationException {
this.subjectIndicators.add(subject_identifier);
}
public void addItemIdentifier(LocatorIF source_locator) throws ConstraintViolationException {
this.sourceLocators.add(source_locator);
}
// methods below should never be invoked
public Collection getSubjectLocators() {
throw new UnsupportedOperationException();
}
public Collection getSubjectIdentifiers() {
throw new UnsupportedOperationException();
}
public void removeSubjectLocator(LocatorIF subject_identifier) {
throw new UnsupportedOperationException();
}
public void removeSubjectIdentifier(LocatorIF subject_identifier) {
throw new UnsupportedOperationException();
}
public void removeItemIdentifier(LocatorIF source_locator) {
throw new UnsupportedOperationException();
}
public Collection getTopicNames() {
throw new UnsupportedOperationException();
}
public Collection<TopicNameIF> getTopicNamesByType(TopicIF type) {
throw new UnsupportedOperationException();
}
public Collection getOccurrences() {
throw new UnsupportedOperationException();
}
public Collection<OccurrenceIF> getOccurrencesByType(TopicIF type) {
throw new UnsupportedOperationException();
}
public Collection getRoles() {
throw new UnsupportedOperationException();
}
public Collection getRolesByType(TopicIF roletype) {
throw new UnsupportedOperationException();
}
public Collection getRolesByType(TopicIF roletype, TopicIF assoc_type) {
throw new UnsupportedOperationException();
}
public Collection<AssociationIF> getAssociations() {
throw new UnsupportedOperationException();
}
public Collection<AssociationIF> getAssociationsByType(TopicIF type) {
throw new UnsupportedOperationException();
}
public void merge(TopicIF topic) {
throw new UnsupportedOperationException();
}
public Collection getScope() {
throw new UnsupportedOperationException();
}
public void addTheme(TopicIF theme) {
throw new UnsupportedOperationException();
}
public void removeTheme(TopicIF theme) {
throw new UnsupportedOperationException();
}
public String getObjectId() {
throw new UnsupportedOperationException();
}
public boolean isReadOnly() {
throw new UnsupportedOperationException();
}
public TopicMapIF getTopicMap() {
throw new UnsupportedOperationException();
}
public Collection getItemIdentifiers() {
throw new UnsupportedOperationException();
}
public Collection getTypes() {
throw new UnsupportedOperationException();
}
public void addType(TopicIF type) {
this.types.add(type);
}
public void removeType(TopicIF type) {
throw new UnsupportedOperationException();
}
public void remove() {
throw new UnsupportedOperationException();
}
public ReifiableIF getReified() {
throw new UnsupportedOperationException();
}
}
protected LazyTopic lazyTopic;
protected TopicIF getParentTopic() {
TopicIF topic = (TopicIF)info.get(EL_TOPIC);
if (topic == null) {
// create new topic
topic = builder.makeTopic();
info.put(EL_TOPIC, topic);
return topic;
} else if (topic == this.lazyTopic && this.lazyTopic != null) {
return createTopicFromLazyTopic();
} else
return topic;
}
protected TopicIF createTopicFromLazyTopic() {
// loop over identities until first existing topic is found
TopicIF topic = null;
for (int i=0; i < lazyTopic.subjectLocators.size(); i++) {
topic = topicmap.getTopicBySubjectLocator((LocatorIF)lazyTopic.subjectLocators.get(i));
if (topic != null) break;
}
if (topic == null) {
for (int i=0; i < lazyTopic.subjectIndicators.size(); i++) {
topic = topicmap.getTopicBySubjectIdentifier((LocatorIF)lazyTopic.subjectIndicators.get(i));
if (topic != null) break;
}
}
if (topic == null) {
for (int i=0; i < lazyTopic.sourceLocators.size(); i++) {
TMObjectIF object = topicmap.getObjectByItemIdentifier((LocatorIF)lazyTopic.sourceLocators.get(i));
if (object instanceof TopicIF)
topic = (TopicIF)object;
if (topic != null) break;
}
}
// create new topic if not already found
if (topic == null) {
topic = builder.makeTopic();
}
// then add remaining identities
for (int i=0; i < lazyTopic.subjectLocators.size(); i++) {
topic = registerSubjectLocator(topic, (LocatorIF)lazyTopic.subjectLocators.get(i));
}
for (int i=0; i < lazyTopic.subjectIndicators.size(); i++) {
topic = registerSubjectIndicator(topic, (LocatorIF)lazyTopic.subjectIndicators.get(i));
}
for (int i=0; i < lazyTopic.sourceLocators.size(); i++) {
topic = registerSourceLocator(topic, (LocatorIF)lazyTopic.sourceLocators.get(i));
}
// copy types
for (int i=0; i < lazyTopic.types.size(); i++) {
topic.addType((TopicIF)lazyTopic.types.get(i));
}
// reset lazy topic
lazyTopic.subjectLocators.clear();
lazyTopic.subjectIndicators.clear();
lazyTopic.sourceLocators.clear();
lazyTopic.types.clear();
// update info map
info.put(EL_TOPIC, topic);
return topic;
}
// --------------------------------------------------------------------------
// External documents
// --------------------------------------------------------------------------
class ExternalDocument {
protected LocatorIF href;
protected Set scope = new HashSet();
ExternalDocument(LocatorIF href) {
this.href = href;
}
public LocatorIF getLocator() {
return href;
}
public void setLocator(LocatorIF href) {
this.href = href;
}
public Collection getScope() {
return scope;
}
public void addTheme(TopicIF theme) {
scope.add(theme);
}
public void removeTheme(TopicIF theme) {
scope.remove(theme);
}
public boolean importInto(TopicMapIF topicmap) throws SAXException {
// Process merge map if the document hasn't already been read.
if (!processed_documents_current.contains(getLocator())) {
// Create new parser object
XMLReader parser = DefaultXMLReaderFactory.createXMLReader();
// Initialize nested content handler
TopicMapStoreFactoryIF sfactory = new SameStoreFactory(topicmap.getStore());
XTMContentHandler handler = new XTMContentHandler(sfactory, getLocator(),
processed_documents_current);
// Copy handler configuration
handler.setExternalReferenceHandler(getExternalReferenceHandler());
// Tell handler it is reading a sub-document
handler.setSubDocument(true);
// Set propagated themes
Collection themes = new HashSet();
if (propagated_themes != null) themes.addAll(propagated_themes);
themes.addAll(getScope());
handler.setPropagatedThemes(themes);
// Register parser with content handler
handler.register(parser);
// Parse input source
try {
String url = getLocator().getExternalForm();
InputSource source = new InputSource(url);
parser.parse(source);
} catch (IOException e) {
throw new OntopiaRuntimeException("Cannot include topic map '" + getLocator().getAddress() + "': " + e.getMessage(), e);
}
// Resource was processed
return true;
} else {
log.debug("Resource has already been processed '" + href + "' (ignoring). " + getLocationInfo());
// Resource wasn't processed
return false;
}
}
}
// --------------------------------------------------------------------------
// Lexical events
// --------------------------------------------------------------------------
// the purpose of this machinery is to ensure that URIs are resolved
// correctly inside external entities.
public void startEntity(String name) {
String sysid = (String) entities.get(name);
if (sysid != null)
bases.push(createLocator(sysid));
}
public void endEntity(String name) {
if (entities.get(name) != null)
bases.pop();
}
public void comment(char[] ch, int start, int length) {
}
public void startCDATA() {
}
public void endCDATA() {
}
public void startDTD(String name, String publicId, String systemId) {
}
public void endDTD() {
}
// --------------------------------------------------------------------------
// DTD events
// --------------------------------------------------------------------------
// we must intercept these events in order to know the URIs of entities
public void externalEntityDecl(String name, String publicId,
String systemId) {
if (systemId != null)
entities.put(name, systemId);
}
public void attributeDecl(String eName, String aName, String type,
String mode, String value) {
}
public void elementDecl(String name, String model) {
}
public void internalEntityDecl(String name, String value) {
}
// --------------------------------------------------------------------------
// Null topic
// --------------------------------------------------------------------------
public static LocatorIF nullPSI = URILocator.create("http://psi.ontopia.net/xtm/1.0/null-topic");
public static TopicIF getNullTopic(TopicMapIF topicmap) {
TopicIF topic = topicmap.getTopicBySubjectIdentifier(nullPSI);
if (topic == null) {
topic = topicmap.getBuilder().makeTopic();
topic.addSubjectIdentifier(nullPSI);
}
return topic;
}
public static TopicIF getDefaultOccurrenceTopic(TopicMapIF topicmap) {
TopicIF topic = topicmap
.getTopicBySubjectIdentifier(PSI.getXTMOccurrence());
if (topic == null) {
topic = topicmap.getBuilder().makeTopic();
topic.addSubjectIdentifier(PSI.getXTMOccurrence());
}
return topic;
}
public static void removeNullTopic(TopicMapIF topicmap) {
TopicIF topic = topicmap.getTopicBySubjectIdentifier(nullPSI);
if (topic != null) {
if (topic.getReified() != null) return;
ClassInstanceIndexIF cindex = (ClassInstanceIndexIF)topicmap.getIndex(ClassInstanceIndexIF.class.getName());
if (cindex.usedAsType(topic)) return;
ScopeIndexIF sindex = (ScopeIndexIF)topicmap.getIndex(ScopeIndexIF.class.getName());
if (sindex.usedAsTheme(topic)) return;
topic.remove();
}
}
public static void removeDefaultOccurrenceTopic(TopicMapIF topicmap) {
TopicIF topic = topicmap
.getTopicBySubjectIdentifier(PSI.getXTMOccurrence());
if (topic != null) {
if (topic.getReified() != null) return;
ClassInstanceIndexIF cindex = (ClassInstanceIndexIF)topicmap.getIndex(ClassInstanceIndexIF.class.getName());
if (cindex.usedAsType(topic)) return;
ScopeIndexIF sindex = (ScopeIndexIF)topicmap.getIndex(ScopeIndexIF.class.getName());
if (sindex.usedAsTheme(topic)) return;
topic.remove();
}
}
}