/*
* #!
* 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.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Collection;
import java.util.Collections;
import org.xml.sax.Locator;
import org.xml.sax.Attributes;
import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;
import org.xml.sax.ContentHandler;
import org.xml.sax.ext.DeclHandler;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.AttributesImpl;
import net.ontopia.utils.OntopiaRuntimeException;
import net.ontopia.infoset.core.LocatorIF;
import net.ontopia.topicmaps.core.TopicMapStoreFactoryIF;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* INTERNAL: This content handler is used to detect whether the XTM
* event stream being read is an XTM 1.0 or 2.x document. Once this is
* clear, the handler configures the parser accordingly with the
* correct handlers.
*/
public class XTMSnifferContentHandler extends DefaultHandler
implements DeclHandler, LexicalHandler {
// Define a logging category.
static Logger log = LoggerFactory.getLogger(XTMSnifferContentHandler.class.getName());
private XTMTopicMapReader reader;
private XMLReader parser;
private LocatorIF base_address;
private XTMContentHandler handler1;
private XTM2ContentHandler handler2;
private TopicMapStoreFactoryIF store_factory;
private Map entities;
private int stack_depth; // used to avoid unbalanced stacks in XTM 1.0
private Locator locator; // stored to be passed on to real ContentHandlers
private static final Attributes EMPTY_ATTS = new AttributesImpl();
protected static final String EMPTY_NAMESPACE = "";
protected static final String EMPTY_LOCALNAME = "";
public XTMSnifferContentHandler(XTMTopicMapReader reader,
TopicMapStoreFactoryIF store_factory,
XMLReader parser,
LocatorIF base_address) {
this.reader = reader;
this.store_factory = store_factory;
this.parser = parser;
this.base_address = base_address;
this.entities = new HashMap();
}
public void startElement(String uri, String name, String qname,
Attributes atts) throws SAXException {
try {
startElement_(uri, name, qname, atts);
} catch (Exception e) {
if (logError()) log.error("Exception was thrown from within startElement", e);
throw new OntopiaRuntimeException(e);
}
}
public void startElement_(String uri, String name, String qname,
Attributes atts) throws SAXException {
// The XTM 1.0 reader can handle XML files where the XTM 1.0
// content is wrapped in other XML content. We therefore need to
// be able to pass by multiple elements before reaching the
// topicMap element.
ContentHandler outer_handler = null;
if (XTMContentHandler.NS_XTM.equals(uri) ||
("".equals(uri) && "topicMap".equals(qname))) {
// We are reading XTM 1.0. Update accordingly.
handler1 = new XTMContentHandler(store_factory,
base_address);
handler1.setExternalReferenceHandler(reader.getExternalReferenceHandler());
handler1.register(parser);
outer_handler = handler1;
if (reader.getValidation()) {
outer_handler = new XTMValidatingContentHandler(handler1);
parser.setContentHandler(outer_handler);
}
// pass on events
if (locator != null)
outer_handler.setDocumentLocator(locator);
Iterator it = entities.keySet().iterator();
while (it.hasNext()) {
String ename = (String) it.next();
handler1.externalEntityDecl(ename, null, (String) entities.get(ename));
}
outer_handler.startDocument();
for (int ix = 0; ix < stack_depth; ix++) // avoid EmptyStackException
outer_handler.startElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "fake-element", EMPTY_ATTS);
outer_handler.startElement(uri, name, qname, atts);
} else if (XTM2ContentHandler.NS_XTM2.equals(uri)) {
// We are reading XTM 2.x. Update accordingly.
handler2 = new XTM2ContentHandler(store_factory,
base_address);
parser.setContentHandler(handler2);
outer_handler = handler2;
if (reader.getValidation()) {
outer_handler = new XTMValidatingContentHandler(handler2,
XTMVersion.XTM_2_0);
parser.setContentHandler(outer_handler);
}
if (locator != null)
outer_handler.setDocumentLocator(locator);
outer_handler.startDocument();
outer_handler.startElement(uri, name, qname, atts);
}
stack_depth++;
}
public void endElement(String uri, String name, String qname) {
stack_depth--;
}
public void endDocument() {
// if we get here it means we never found any 1.0 or 2.0 TMs
if (reader.getValidation())
throw new InvalidTopicMapException("XTM input is neither 1.0 nor 2.0");
}
public void setDocumentLocator(Locator locator) {
this.locator = locator; // store it so we can pass it on
}
// --- DeclHandler
// This is here so we can pass on entity information to the XTM 1.0
// handler which makes use of this information.
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) {
}
// --- LexicalHandler
public void startEntity(String name) {
if (handler1 != null)
handler1.startEntity(name);
}
public void endEntity(String name) {
if (handler1 != null)
handler1.endEntity(name);
}
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() {
}
// --- Internal methods
private boolean logError() {
try {
return Boolean.valueOf(System.getProperty("net.ontopia.topicmaps.xml.XTMContentHandler.logError")).booleanValue();
} catch (SecurityException e) {
return false;
}
}
// --- External interface
public Collection getTopicMaps() {
if (handler1 != null)
return handler1.getTopicMaps();
else if (handler2 != null)
return handler2.getTopicMaps();
else
return Collections.EMPTY_SET;
}
public XTMVersion getXTMVersion() {
if (handler1 != null)
return XTMVersion.XTM_1_0;
else if (handler2 != null)
return XTMVersion.XTM_2_0;
else
return null;
}
}