/* * SAXFilter.java * * Version: $Revision: 3705 $ * * Date: $Date: 2009-04-11 17:02:24 +0000 (Sat, 11 Apr 2009) $ * * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts * Institute of Technology. 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 Hewlett-Packard Company nor the name of the * Massachusetts Institute of Technology nor the names of their * 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 * HOLDERS 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. */ package org.dspace.app.xmlui.objectmanager; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.NamespaceSupport; /** * This is a swiss army like SAX Filter, it's purpose is to filter out * undesierable SAX events from the stream. The primary application of * this is for inserting SAX fragment into an existing SAX pipeline, * under this senario you would not want new startDocument or * endDocument events interfering with the existing pipeline thus * this class can filter those out. * * The swiss army part comes in because it's configurable. Instead of * defining a static set of events that are filled out by default all * events are filled out and must be turned on to allow each type * individualy. * * Primarily you can filter events based upon their type, i.e. start/end * elements or start/end documents. However there is one special control, * and that is to only allow elements below a minimum level. * . * * @author Scott Phillips */ public class SAXFilter implements ContentHandler, LexicalHandler { /** Control for which type of SAX events to allow */ private boolean allowDocuments = false; private boolean allowDocumentLocators = false; private boolean allowProcessingInstructions = false; private boolean allowPrefixMappings = false; private boolean allowElements = false; private boolean allowIgnorableWhitespace = false; private boolean allowSkippedEntities = false; private boolean allowCharacters = false; private boolean allowDTDs = false; private boolean allowEntities = false; private boolean allowCDATA = false; private boolean allowComments = false; /** The minimum level an element must be before it will be allowed */ private int minimumElementLevel = -1; /** * The current XML level, each time start element is encountered this * is increased, and each time an end element is encountered it is * decressed. */ private int currentElementLevel = 0; /** * If no uri is provided then substitute this default prefix and URI: */ private String defaultURI; /** The sax handlers and namespace support */ private ContentHandler contentHandler; private LexicalHandler lexicalHandler; private NamespaceSupport namespaces; /** * Construct a new SAXFilter such that the allowed events will be routed * to the corresponding content and lexcial handlers. * * @param contentHandler The SAX content handler. * @param lexicalHandler The SAX lexical handler. * @param namespaces Namespace support which records what prefixes have been defined. */ public SAXFilter(ContentHandler contentHandler, LexicalHandler lexicalHandler, NamespaceSupport namespaces) { this.contentHandler = contentHandler; this.lexicalHandler = lexicalHandler; this.namespaces = namespaces; } /** Allow start/end document events */ public SAXFilter allowDocuments() { this.allowDocuments = true; return this; } /** Allow document locator events */ public SAXFilter allowDocumentLocators() { this.allowDocumentLocators = true; return this; } /** Allow processing instruction events */ public SAXFilter allowProcessingInstructions() { this.allowProcessingInstructions = true; return this; } /** allow start/end prefix mapping events */ public SAXFilter allowPrefixMappings() { this.allowPrefixMappings = true; return this; } /** allow start/end element events */ public SAXFilter allowElements() { this.allowElements = true; return this; } /** * Allow start/end element events. * * However only allow those start / end events if * they are below the given XML level. I.e. each nested * element is a new level. * * @param belowElementLevel * the minumum level required. * @return */ public SAXFilter allowElements(int minimumElementLevel) { this.allowElements = true; this.minimumElementLevel = minimumElementLevel; return this; } /** Allow ignorable whitespace events */ public SAXFilter allowIgnorableWhitespace() { this.allowIgnorableWhitespace = true; return this; } /** Allow start / end events for skipped entities */ public SAXFilter allowSkippedEntities() { this.allowSkippedEntities = true; return this; } /** Allow character events */ public SAXFilter allowCharacters() { this.allowCharacters = true; return this; } /** Allow DTD events */ public SAXFilter allowDTDs() { this.allowDTDs = true; return this; } /** Allow XML entities events */ public SAXFilter allowEntities() { this.allowEntities = true; return this; } /** Allow CDATA events */ public SAXFilter allowCDATA() { this.allowCDATA = true; return this; } /** Allow comment events */ public SAXFilter allowComments() { this.allowComments = true; return this; } /** * Add a default namespace is none is provided. The namespace * should have allready been declared (add added to the * namespace support object * * @param uri the default namespace uri. */ public SAXFilter setDefaultNamespace(String uri) { this.defaultURI = uri; return this; } /** * SAX Content events */ public void startDocument() throws SAXException { if (allowDocuments) contentHandler.startDocument(); } public void endDocument() throws SAXException { if (allowDocuments) contentHandler.endDocument(); } public void setDocumentLocator(Locator locator) { if (allowDocumentLocators) contentHandler.setDocumentLocator(locator); } public void processingInstruction(String target, String data) throws SAXException { if (allowProcessingInstructions) contentHandler.processingInstruction(target, data); } public void startPrefixMapping(String prefix, String uri) throws SAXException { if (allowPrefixMappings) contentHandler.startPrefixMapping(prefix, uri); } public void endPrefixMapping(String prefix) throws SAXException { if (allowPrefixMappings) contentHandler.endPrefixMapping(prefix); } public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { if (allowElements) { currentElementLevel++; // check if we are past the minimum level requirement if (minimumElementLevel < currentElementLevel) { if (defaultURI != null && (uri == null || "".equals(uri))) { // No namespace provided, use the default namespace. String prefix = namespaces.getPrefix(defaultURI); if (!(prefix == null || "".equals(prefix))) { qName = prefix+":"+localName; } contentHandler.startElement(defaultURI, localName, qName, atts); } else { // let the event pass through unmodified. contentHandler.startElement(uri, localName, localName, atts); } } } } public void endElement(String uri, String localName, String qName) throws SAXException { if (allowElements) { // check if we are past the minimum level requirements if (minimumElementLevel < currentElementLevel) { if (defaultURI != null && (uri == null || "".equals(uri))) { // No namespace provided, use the default namespace. String prefix = namespaces.getPrefix(defaultURI); if (!(prefix == null || "".equals(prefix))) { qName = prefix+":"+localName; } contentHandler.endElement(defaultURI, localName, qName); } else { // Let the event pass through unmodified. contentHandler.endElement(uri, localName, localName); } } currentElementLevel--; } } public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { if (allowIgnorableWhitespace) contentHandler.ignorableWhitespace(ch, start, length); } public void skippedEntity(String name) throws SAXException { if (allowSkippedEntities) contentHandler.skippedEntity(name); } public void characters(char[] ch, int start, int length) throws SAXException { if (allowCharacters) contentHandler.characters(ch, start, length); } /** * SAX Lexical events */ public void startDTD(String name, String publicId, String systemId) throws SAXException { if (allowDTDs) lexicalHandler.startDTD(name, publicId, systemId); } public void endDTD() throws SAXException { if (allowDTDs) lexicalHandler.endDTD(); } public void startEntity(String name) throws SAXException { if (allowEntities) lexicalHandler.startEntity(name); } public void endEntity(String name) throws SAXException { if (allowEntities) lexicalHandler.endEntity(name); } public void startCDATA() throws SAXException { if (allowCDATA) lexicalHandler.startCDATA(); } public void endCDATA() throws SAXException { if (allowCDATA) lexicalHandler.endCDATA(); } public void comment(char[] ch, int start, int length) throws SAXException { if (allowComments) lexicalHandler.comment(ch, start, length); } }