001 /* 002 * JBoss, Home of Professional Open Source. 003 * Copyright 2008, Red Hat Middleware LLC, and individual contributors 004 * as indicated by the @author tags. See the copyright.txt file in the 005 * distribution for a full listing of individual contributors. 006 * 007 * This is free software; you can redistribute it and/or modify it 008 * under the terms of the GNU Lesser General Public License as 009 * published by the Free Software Foundation; either version 2.1 of 010 * the License, or (at your option) any later version. 011 * 012 * This software is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this software; if not, write to the Free 019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 021 */ 022 package org.jboss.dna.sequencer.xml; 023 024 import java.io.InputStream; 025 import org.jboss.dna.common.text.TextDecoder; 026 import org.jboss.dna.graph.JcrNtLexicon; 027 import org.jboss.dna.graph.properties.Name; 028 import org.jboss.dna.graph.sequencers.SequencerContext; 029 import org.jboss.dna.graph.sequencers.SequencerOutput; 030 import org.jboss.dna.graph.sequencers.StreamSequencer; 031 import org.xml.sax.InputSource; 032 import org.xml.sax.SAXNotRecognizedException; 033 import org.xml.sax.SAXNotSupportedException; 034 import org.xml.sax.XMLReader; 035 import org.xml.sax.helpers.XMLReaderFactory; 036 037 /** 038 * A sequencer for XML files, which maintains DTD, entity, comments, and other content. Note that by default the sequencer uses 039 * the {@link XmlSequencer.AttributeScoping#USE_DEFAULT_NAMESPACE default namespace} for unqualified attribute rather than 040 * {@link XmlSequencer.AttributeScoping#INHERIT_ELEMENT_NAMESPACE inheriting the namespace from the element}. (See also 041 * {@link InheritingXmlSequencer}. 042 * 043 * @author John Verhaeg 044 */ 045 public class XmlSequencer implements StreamSequencer { 046 047 /** 048 * The choices for how attributes that have no namespace prefix should be assigned a namespace. 049 * 050 * @author Randall Hauch 051 */ 052 public enum AttributeScoping { 053 /** The attribute's namespace is the default namespace */ 054 USE_DEFAULT_NAMESPACE, 055 /** The attribute's namespace is the same namespace as the containing element */ 056 INHERIT_ELEMENT_NAMESPACE; 057 } 058 059 /*package*/static final String DEFAULT_PRIMARY_TYPE = "nt:unstructured"; 060 /*package*/static final String DECL_HANDLER_FEATURE = "http://xml.org/sax/properties/declaration-handler"; 061 /*package*/static final String ENTITY_RESOLVER_2_FEATURE = "http://xml.org/sax/features/use-entity-resolver2"; 062 /*package*/static final String LEXICAL_HANDLER_FEATURE = "http://xml.org/sax/properties/lexical-handler"; 063 /*package*/static final String RESOLVE_DTD_URIS_FEATURE = "http://xml.org/sax/features/resolve-dtd-uris"; 064 /*package*/static final String LOAD_EXTERNAL_DTDS_FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; 065 066 private AttributeScoping scoping = AttributeScoping.USE_DEFAULT_NAMESPACE; 067 068 /** 069 * @param scoping Sets scoping to the specified value. 070 */ 071 public void setAttributeScoping( AttributeScoping scoping ) { 072 this.scoping = scoping; 073 } 074 075 /** 076 * @return scoping 077 */ 078 public AttributeScoping getAttributeScoping() { 079 return scoping; 080 } 081 082 /** 083 * {@inheritDoc} 084 * 085 * @see org.jboss.dna.graph.sequencers.StreamSequencer#sequence(InputStream, SequencerOutput, SequencerContext) 086 */ 087 public void sequence( InputStream stream, 088 SequencerOutput output, 089 SequencerContext context ) { 090 XMLReader reader; 091 try { 092 // Set up the XML handler ... 093 Name primaryType = JcrNtLexicon.UNSTRUCTURED; 094 Name nameAttribute = null; 095 TextDecoder decoder = null; 096 XmlSequencerHandler handler = new XmlSequencerHandler(output, context, nameAttribute, primaryType, decoder, scoping); 097 // Create the reader ... 098 reader = XMLReaderFactory.createXMLReader(); 099 reader.setContentHandler(handler); 100 reader.setErrorHandler(handler); 101 // Ensure handler acting as entity resolver 2 102 reader.setProperty(DECL_HANDLER_FEATURE, handler); 103 // Ensure handler acting as lexical handler 104 reader.setProperty(LEXICAL_HANDLER_FEATURE, handler); 105 // Ensure handler acting as entity resolver 2 106 setFeature(reader, ENTITY_RESOLVER_2_FEATURE, true); 107 // Prevent loading of external DTDs 108 setFeature(reader, LOAD_EXTERNAL_DTDS_FEATURE, false); 109 // Prevent the resolving of DTD entities into fully-qualified URIS 110 setFeature(reader, RESOLVE_DTD_URIS_FEATURE, false); 111 // Parse XML document 112 reader.parse(new InputSource(stream)); 113 } catch (Exception error) { 114 context.getLogger(getClass()).error(error, XmlSequencerI18n.fatalErrorSequencingXmlDocument, error); 115 context.getProblems().addError(error, XmlSequencerI18n.fatalErrorSequencingXmlDocument, error); 116 } 117 } 118 119 /** 120 * Sets the reader's named feature to the supplied value, only if the feature is not already set to that value. This method 121 * does nothing if the feature is not known to the reader. 122 * 123 * @param reader the reader; may not be null 124 * @param featureName the name of the feature; may not be null 125 * @param value the value for the feature 126 */ 127 /*package*/static void setFeature( XMLReader reader, 128 String featureName, 129 boolean value ) { 130 try { 131 if (reader.getFeature(featureName) != value) { 132 reader.setFeature(featureName, value); 133 } 134 } catch (SAXNotRecognizedException meansFeatureNotRecognized) { 135 } catch (SAXNotSupportedException meansFeatureNotSupported) { 136 } 137 } 138 139 }