/* * Copyright (c) 2007-2008 Mozilla Foundation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ package nu.validator.xml; import java.io.IOException; import java.util.Set; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.DTDHandler; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; 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.SAXParseException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.AttributesImpl; /** * This does not extend XMLFilterImpl, because XMLFilterImpl constructor overwrites * handlers on the wrapped XMLReader. * * @version $Id$ * @author hsivonen */ public final class NamespaceDroppingXMLReaderWrapper implements XMLReader, ContentHandler { private final static String[] ARRAY_TYPE = new String[0]; private static String[] toInternedArray(Set<String> set) { String[] rv = set.toArray(ARRAY_TYPE); for (int i = 0; i < rv.length; i++) { rv[i] = rv[i].intern(); } return rv; } private final XMLReader wrappedReader; private final String[] namespacesToRemove; private ContentHandler contentHandler; private int depth; private boolean alreadyWarnedAboutForeign; private boolean alreadyWarnedAboutFiltering; private boolean rootSeen; private Locator locator = null; public NamespaceDroppingXMLReaderWrapper(XMLReader wrappedReader, Set<String> namespacesToRemove) { this.wrappedReader = wrappedReader; this.namespacesToRemove = toInternedArray(namespacesToRemove); this.contentHandler = wrappedReader.getContentHandler(); wrappedReader.setContentHandler(this); } /** * @see org.xml.sax.helpers.XMLFilterImpl#characters(char[], int, int) */ @Override public void characters(char[] ch, int start, int length) throws SAXException { if (depth == 0) { contentHandler.characters(ch, start, length); } } /** * @see org.xml.sax.helpers.XMLFilterImpl#endElement(java.lang.String, * java.lang.String, java.lang.String) */ @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (depth == 0) { contentHandler.endElement(uri, localName, qName); } else { depth--; } } /** * @see org.xml.sax.helpers.XMLFilterImpl#startDocument() */ @Override public void startDocument() throws SAXException { depth = 0; alreadyWarnedAboutForeign = false; alreadyWarnedAboutFiltering = false; rootSeen = false; contentHandler.startDocument(); } /** * @see org.xml.sax.helpers.XMLFilterImpl#startElement(java.lang.String, * java.lang.String, java.lang.String, org.xml.sax.Attributes) */ @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { if (depth == 0) { if (isInNamespacesToRemove(uri)) { if (rootSeen) { depth = 1; if (!alreadyWarnedAboutFiltering) { warning(new SAXParseException( "Content is being hidden from the validator based on namespace filtering.", locator)); alreadyWarnedAboutFiltering = true; } } else { warning(new SAXParseException( "Cannot filter out the root element.", locator)); contentHandler.startElement(uri, localName, qName, filterAttributes(atts)); } } else { contentHandler.startElement(uri, localName, qName, filterAttributes(atts)); } } else { if (!alreadyWarnedAboutForeign && !isInNamespacesToRemove(uri)) { warning(new SAXParseException( "Filtering out selected namespaces causes descendants in other namespaces to be dropped as well.", locator)); alreadyWarnedAboutForeign = true; } depth++; } rootSeen = true; } private void warning(SAXParseException exception) throws SAXException { wrappedReader.getErrorHandler().warning(exception); } private Attributes filterAttributes(Attributes atts) throws SAXException { int length = atts.getLength(); int i = 0; while (i < length) { if (isInNamespacesToRemove(atts.getURI(i))) { if (!alreadyWarnedAboutFiltering) { warning(new SAXParseException( "Content is being hidden from the validator based on namespace filtering.", locator)); alreadyWarnedAboutFiltering = true; } AttributesImpl rv = new AttributesImpl(); for (int j = 0; j < i; j++) { rv.addAttribute(atts.getURI(j), atts.getLocalName(j), atts.getQName(j), atts.getType(j), atts.getValue(j)); } i++; while (i < length) { String uri = atts.getURI(i); if (!isInNamespacesToRemove(uri)) { rv.addAttribute(uri, atts.getLocalName(i), atts.getQName(i), atts.getType(i), atts.getValue(i)); } i++; } return rv; } i++; } return atts; } private boolean isInNamespacesToRemove(String uri) { for (String namespace : namespacesToRemove) { if (uri == namespace) { return true; } } return false; } /** * @see org.xml.sax.helpers.XMLFilterImpl#setDocumentLocator(org.xml.sax.Locator) */ @Override public void setDocumentLocator(Locator locator) { this.locator = locator; contentHandler.setDocumentLocator(locator); } @Override public ContentHandler getContentHandler() { return contentHandler; } /** * @throws SAXException * @see org.xml.sax.ContentHandler#endDocument() */ @Override public void endDocument() throws SAXException { contentHandler.endDocument(); } /** * @param prefix * @throws SAXException * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String) */ @Override public void endPrefixMapping(String prefix) throws SAXException { contentHandler.endPrefixMapping(prefix); } /** * @param ch * @param start * @param length * @throws SAXException * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) */ @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { contentHandler.ignorableWhitespace(ch, start, length); } /** * @param target * @param data * @throws SAXException * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String) */ @Override public void processingInstruction(String target, String data) throws SAXException { contentHandler.processingInstruction(target, data); } /** * @param name * @throws SAXException * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String) */ @Override public void skippedEntity(String name) throws SAXException { contentHandler.skippedEntity(name); } /** * @param prefix * @param uri * @throws SAXException * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String) */ @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { contentHandler.startPrefixMapping(prefix, uri); } /** * @return * @see org.xml.sax.XMLReader#getDTDHandler() */ @Override public DTDHandler getDTDHandler() { return wrappedReader.getDTDHandler(); } /** * @return * @see org.xml.sax.XMLReader#getEntityResolver() */ @Override public EntityResolver getEntityResolver() { return wrappedReader.getEntityResolver(); } /** * @return * @see org.xml.sax.XMLReader#getErrorHandler() */ @Override public ErrorHandler getErrorHandler() { return wrappedReader.getErrorHandler(); } /** * @param name * @return * @throws SAXNotRecognizedException * @throws SAXNotSupportedException * @see org.xml.sax.XMLReader#getFeature(java.lang.String) */ @Override public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException { return wrappedReader.getFeature(name); } /** * @param name * @return * @throws SAXNotRecognizedException * @throws SAXNotSupportedException * @see org.xml.sax.XMLReader#getProperty(java.lang.String) */ @Override public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException { return wrappedReader.getProperty(name); } /** * @param input * @throws IOException * @throws SAXException * @see org.xml.sax.XMLReader#parse(org.xml.sax.InputSource) */ @Override public void parse(InputSource input) throws IOException, SAXException { wrappedReader.parse(input); } /** * @param systemId * @throws IOException * @throws SAXException * @see org.xml.sax.XMLReader#parse(java.lang.String) */ @Override public void parse(String systemId) throws IOException, SAXException { wrappedReader.parse(systemId); } /** * @param handler * @see org.xml.sax.XMLReader#setContentHandler(org.xml.sax.ContentHandler) */ @Override public void setContentHandler(ContentHandler handler) { contentHandler = handler; } /** * @param handler * @see org.xml.sax.XMLReader#setDTDHandler(org.xml.sax.DTDHandler) */ @Override public void setDTDHandler(DTDHandler handler) { wrappedReader.setDTDHandler(handler); } /** * @param resolver * @see org.xml.sax.XMLReader#setEntityResolver(org.xml.sax.EntityResolver) */ @Override public void setEntityResolver(EntityResolver resolver) { wrappedReader.setEntityResolver(resolver); } /** * @param handler * @see org.xml.sax.XMLReader#setErrorHandler(org.xml.sax.ErrorHandler) */ @Override public void setErrorHandler(ErrorHandler handler) { wrappedReader.setErrorHandler(handler); } /** * @param name * @param value * @throws SAXNotRecognizedException * @throws SAXNotSupportedException * @see org.xml.sax.XMLReader#setFeature(java.lang.String, boolean) */ @Override public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException { wrappedReader.setFeature(name, value); } /** * @param name * @param value * @throws SAXNotRecognizedException * @throws SAXNotSupportedException * @see org.xml.sax.XMLReader#setProperty(java.lang.String, java.lang.Object) */ @Override public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException { wrappedReader.setProperty(name, value); } }