/** * */ package org.cdlib.xtf.xslt; /* * Copyright (c) 2007, Regents of the University of California * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the University of California nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import java.io.IOException; import java.util.Stack; import org.cdlib.xtf.servletBase.DTDSuppressingXMLReader; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.DTDHandler; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.ext.LexicalHandler; /** * Like a normal XMLReader, except that it stops processing when the first * end-element tag is encountered. This reads in the first part of an XML file, * which is like a "stub" version of the file. Also, we jump through very * special hoops to make the DTD declaration available (it's not normally * part of Saxon's data model.) * * This file created November 1, 2007 by Martin Haye */ class XMLStubReader extends DTDSuppressingXMLReader { /** Thrown after the first element end marker is found */ private class GetOut extends RuntimeException { } /** * Establish the content handler that will receive events. We wrap it * to perform special processing. */ public void setContentHandler(ContentHandler handler) { StubContentHandler stubHandler = new StubContentHandler(handler); super.setContentHandler(stubHandler); } /** * Catch requests to set the "lexical handler". We insert a stub handler * in the chain so we can catch the DTD declaration. */ public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException { if (name.equals("http://xml.org/sax/properties/lexical-handler")) { StubLexicalHandler stubHandler = new StubLexicalHandler((LexicalHandler)value); super.setProperty(name, stubHandler); } else super.setProperty(name, value); } /** Parse the input document, but stop at the first end-element marker */ public void parse (InputSource input) throws IOException, SAXException { try { super.parse(input); } catch (GetOut g) { // This is normal: do nothing. } } /** * Handles 'lexical' events. We mainly need to catch the start of the DTD * and turn that even into an unparsed entity declaration so that Saxon will * keep a record of it. */ private class StubLexicalHandler implements LexicalHandler { private LexicalHandler out; public StubLexicalHandler(LexicalHandler out) { this.out = out; } ////////////////// Special delegate methods ///////////////// public void startDTD(String name, String publicId, String systemId) throws SAXException { out.startDTD(name, publicId, systemId); if (out instanceof DTDHandler && systemId != null) ((DTDHandler)out).unparsedEntityDecl(name, publicId, systemId, null); } /////////////// Pass-through delegate methods ///////////////// public void endDTD() throws SAXException { out.endDTD(); } public void startEntity(String name) throws SAXException { out.startEntity(name); } public void endEntity(String name) throws SAXException { out.endEntity(name); } public void comment(char[] ch, int start, int length) throws SAXException { out.comment(ch, start, length); } public void startCDATA() throws SAXException { out.startCDATA(); } public void endCDATA() throws SAXException { out.endCDATA(); } } /** * Handles content events from the XML parser. */ private class StubContentHandler implements ContentHandler { private ContentHandler out; private Stack<String[]> eventStack = new Stack<String[]>(); /** Construct the content handler, passing events to 'out' */ public StubContentHandler(ContentHandler out) { this.out = out; } ///////////// Special delegate methods ///////////// public void startElement(String uri, String localName, String name, Attributes atts) throws SAXException { // Keep track of which elements have been started, so we can auto-close // them later. // eventStack.push(new String[] { "element", uri, localName, name }); out.startElement(uri, localName, name, atts); } public void endElement(String uri, String localName, String name) throws SAXException { // We've reached the first end-element. Let's simulate the situation that // the document just ends here. So we unwind the event stack, closing all // elements and prefix mappings. // while (!eventStack.isEmpty()) { String[] event = eventStack.pop(); if (event[0].equals("element")) out.endElement(event[1], event[2], event[3]); else if (event[0].equals("prefix")) out.endPrefixMapping(event[1]); else assert false; } // All done. End the document, and abort the rest of the parse(). out.endDocument(); throw new GetOut(); } public void startPrefixMapping(String prefix, String uri) throws SAXException { // Keep track of open prefix mappings so we can auto-close them later. eventStack.push(new String[] { "prefix", prefix }); out.startPrefixMapping(prefix, uri); } public void endPrefixMapping(String prefix) throws SAXException { out.endPrefixMapping(prefix); String[] event = eventStack.pop(); assert event[0].equals("prefix"); assert event[1].equals(prefix); } /////////// Pass-through delegate methods /////////// public void startDocument() throws SAXException { out.startDocument(); } public void endDocument() throws SAXException { out.endDocument(); } public void characters(char[] ch, int start, int length) throws SAXException { out.characters(ch, start, length); } public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { out.ignorableWhitespace(ch, start, length); } public void processingInstruction(String target, String data) throws SAXException { out.processingInstruction(target, data); } public void setDocumentLocator(Locator locator) { out.setDocumentLocator(locator); } public void skippedEntity(String name) throws SAXException { out.skippedEntity(name); } } }