001: /*
002: * Copyright 1999-2005 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package com.opensymphony.xwork.util;
017:
018: import java.util.Map;
019:
020: import com.opensymphony.util.ClassLoaderUtil;
021:
022: import com.opensymphony.xwork.util.location.Location;
023: import com.opensymphony.xwork.util.location.LocationAttributes;
024: import com.opensymphony.xwork.XworkException;
025: import com.opensymphony.xwork.ObjectFactory;
026: import org.w3c.dom.Document;
027: import org.w3c.dom.Element;
028: import org.w3c.dom.Node;
029: import org.xml.sax.Attributes;
030: import org.xml.sax.ContentHandler;
031: import org.xml.sax.Locator;
032: import org.xml.sax.InputSource;
033: import org.xml.sax.SAXException;
034: import org.xml.sax.SAXParseException;
035: import org.xml.sax.helpers.DefaultHandler;
036:
037: import org.apache.commons.logging.Log;
038: import org.apache.commons.logging.LogFactory;
039:
040: import javax.xml.parsers.SAXParserFactory;
041: import javax.xml.parsers.SAXParser;
042:
043: import javax.xml.transform.TransformerFactory;
044: import javax.xml.transform.dom.DOMResult;
045: import javax.xml.transform.sax.SAXTransformerFactory;
046: import javax.xml.transform.sax.TransformerHandler;
047:
048: /**
049: * Helper class to create and retrieve information from location-enabled
050: * DOM-trees.
051: *
052: * @since 1.2
053: */
054: public class DomHelper {
055:
056: private static final Log LOG = LogFactory.getLog(DomHelper.class);
057:
058: public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
059:
060: public static Location getLocationObject(Element element) {
061: return LocationAttributes.getLocation(element);
062: }
063:
064: /**
065: * Creates a W3C Document that remembers the location of each element in
066: * the source file. The location of element nodes can then be retrieved
067: * using the {@link #getLocationObject(Element)} method.
068: *
069: * @param inputSource the inputSource to read the document from
070: */
071: public static Document parse(InputSource inputSource) {
072: return parse(inputSource, null);
073: }
074:
075: /**
076: * Creates a W3C Document that remembers the location of each element in
077: * the source file. The location of element nodes can then be retrieved
078: * using the {@link #getLocationObject(Element)} method.
079: *
080: * @param inputSource the inputSource to read the document from
081: * @param dtdMappings a map of DTD names and public ids
082: */
083: public static Document parse(InputSource inputSource,
084: Map dtdMappings) {
085: SAXParserFactory factory = null;
086: String parserProp = System
087: .getProperty("xwork.saxParserFactory");
088: if (parserProp != null) {
089: try {
090: Class clazz = ObjectFactory.getObjectFactory()
091: .getClassInstance(parserProp);
092: factory = (SAXParserFactory) clazz.newInstance();
093: } catch (ClassNotFoundException e) {
094: LOG
095: .error(
096: "Unable to load saxParserFactory set by system property 'xwork.saxParserFactory': "
097: + parserProp, e);
098: } catch (Exception e) {
099: LOG
100: .error(
101: "Unable to load saxParserFactory set by system property 'xwork.saxParserFactory': "
102: + parserProp, e);
103: }
104: }
105:
106: if (factory == null) {
107: factory = SAXParserFactory.newInstance();
108: }
109:
110: factory.setValidating((dtdMappings != null));
111: factory.setNamespaceAware(true);
112:
113: SAXParser parser = null;
114: try {
115: parser = factory.newSAXParser();
116: } catch (Exception ex) {
117: throw new XworkException("Unable to create SAX parser", ex);
118: }
119:
120: DOMBuilder builder = new DOMBuilder();
121:
122: // Enhance the sax stream with location information
123: ContentHandler locationHandler = new LocationAttributes.Pipe(
124: builder);
125:
126: try {
127: parser.parse(inputSource, new StartHandler(locationHandler,
128: dtdMappings));
129: } catch (Exception ex) {
130: throw new XworkException(ex);
131: }
132:
133: return builder.getDocument();
134: }
135:
136: /**
137: * The <code>DOMBuilder</code> is a utility class that will generate a W3C
138: * DOM Document from SAX events.
139: *
140: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
141: */
142: static public class DOMBuilder implements ContentHandler {
143:
144: /** The default transformer factory shared by all instances */
145: protected static SAXTransformerFactory FACTORY;
146:
147: /** The transformer factory */
148: protected SAXTransformerFactory factory;
149:
150: /** The result */
151: protected DOMResult result;
152:
153: /** The parentNode */
154: protected Node parentNode;
155:
156: protected ContentHandler nextHandler;
157:
158: static {
159: String parserProp = System
160: .getProperty("xwork.saxTransformerFactory");
161: if (parserProp != null) {
162: try {
163: Class clazz = ObjectFactory.getObjectFactory()
164: .getClassInstance(parserProp);
165: FACTORY = (SAXTransformerFactory) clazz
166: .newInstance();
167: } catch (ClassNotFoundException e) {
168: LOG
169: .error(
170: "Unable to load SAXTransformerFactory set by system property 'xwork.saxTransformerFactory': "
171: + parserProp, e);
172: } catch (Exception e) {
173: LOG
174: .error(
175: "Unable to load SAXTransformerFactory set by system property 'xwork.saxTransformerFactory': "
176: + parserProp, e);
177: }
178: }
179:
180: if (FACTORY == null) {
181: FACTORY = (SAXTransformerFactory) TransformerFactory
182: .newInstance();
183: }
184: }
185:
186: /**
187: * Construct a new instance of this DOMBuilder.
188: */
189: public DOMBuilder() {
190: this ((Node) null);
191: }
192:
193: /**
194: * Construct a new instance of this DOMBuilder.
195: */
196: public DOMBuilder(SAXTransformerFactory factory) {
197: this (factory, null);
198: }
199:
200: /**
201: * Constructs a new instance that appends nodes to the given parent node.
202: */
203: public DOMBuilder(Node parentNode) {
204: this (null, parentNode);
205: }
206:
207: /**
208: * Construct a new instance of this DOMBuilder.
209: */
210: public DOMBuilder(SAXTransformerFactory factory, Node parentNode) {
211: this .factory = factory == null ? FACTORY : factory;
212: this .parentNode = parentNode;
213: setup();
214: }
215:
216: /**
217: * Setup this instance transformer and result objects.
218: */
219: private void setup() {
220: try {
221: TransformerHandler handler = this .factory
222: .newTransformerHandler();
223: nextHandler = handler;
224: if (this .parentNode != null) {
225: this .result = new DOMResult(this .parentNode);
226: } else {
227: this .result = new DOMResult();
228: }
229: handler.setResult(this .result);
230: } catch (javax.xml.transform.TransformerException local) {
231: throw new XworkException(
232: "Fatal-Error: Unable to get transformer handler",
233: local);
234: }
235: }
236:
237: /**
238: * Return the newly built Document.
239: */
240: public Document getDocument() {
241: if (this .result == null || this .result.getNode() == null) {
242: return null;
243: } else if (this .result.getNode().getNodeType() == Node.DOCUMENT_NODE) {
244: return (Document) this .result.getNode();
245: } else {
246: return this .result.getNode().getOwnerDocument();
247: }
248: }
249:
250: public void setDocumentLocator(Locator locator) {
251: nextHandler.setDocumentLocator(locator);
252: }
253:
254: public void startDocument() throws SAXException {
255: nextHandler.startDocument();
256: }
257:
258: public void endDocument() throws SAXException {
259: nextHandler.endDocument();
260: }
261:
262: public void startElement(String uri, String loc, String raw,
263: Attributes attrs) throws SAXException {
264: nextHandler.startElement(uri, loc, raw, attrs);
265: }
266:
267: public void endElement(String arg0, String arg1, String arg2)
268: throws SAXException {
269: nextHandler.endElement(arg0, arg1, arg2);
270: }
271:
272: public void startPrefixMapping(String arg0, String arg1)
273: throws SAXException {
274: nextHandler.startPrefixMapping(arg0, arg1);
275: }
276:
277: public void endPrefixMapping(String arg0) throws SAXException {
278: nextHandler.endPrefixMapping(arg0);
279: }
280:
281: public void characters(char[] arg0, int arg1, int arg2)
282: throws SAXException {
283: nextHandler.characters(arg0, arg1, arg2);
284: }
285:
286: public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
287: throws SAXException {
288: nextHandler.ignorableWhitespace(arg0, arg1, arg2);
289: }
290:
291: public void processingInstruction(String arg0, String arg1)
292: throws SAXException {
293: nextHandler.processingInstruction(arg0, arg1);
294: }
295:
296: public void skippedEntity(String arg0) throws SAXException {
297: nextHandler.skippedEntity(arg0);
298: }
299: }
300:
301: public static class StartHandler extends DefaultHandler {
302:
303: private ContentHandler nextHandler;
304: private Map dtdMappings;
305:
306: /**
307: * Create a filter that is chained to another handler.
308: * @param next the next handler in the chain.
309: */
310: public StartHandler(ContentHandler next, Map dtdMappings) {
311: nextHandler = next;
312: this .dtdMappings = dtdMappings;
313: }
314:
315: public void setDocumentLocator(Locator locator) {
316: nextHandler.setDocumentLocator(locator);
317: }
318:
319: public void startDocument() throws SAXException {
320: nextHandler.startDocument();
321: }
322:
323: public void endDocument() throws SAXException {
324: nextHandler.endDocument();
325: }
326:
327: public void startElement(String uri, String loc, String raw,
328: Attributes attrs) throws SAXException {
329: nextHandler.startElement(uri, loc, raw, attrs);
330: }
331:
332: public void endElement(String arg0, String arg1, String arg2)
333: throws SAXException {
334: nextHandler.endElement(arg0, arg1, arg2);
335: }
336:
337: public void startPrefixMapping(String arg0, String arg1)
338: throws SAXException {
339: nextHandler.startPrefixMapping(arg0, arg1);
340: }
341:
342: public void endPrefixMapping(String arg0) throws SAXException {
343: nextHandler.endPrefixMapping(arg0);
344: }
345:
346: public void characters(char[] arg0, int arg1, int arg2)
347: throws SAXException {
348: nextHandler.characters(arg0, arg1, arg2);
349: }
350:
351: public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
352: throws SAXException {
353: nextHandler.ignorableWhitespace(arg0, arg1, arg2);
354: }
355:
356: public void processingInstruction(String arg0, String arg1)
357: throws SAXException {
358: nextHandler.processingInstruction(arg0, arg1);
359: }
360:
361: public void skippedEntity(String arg0) throws SAXException {
362: nextHandler.skippedEntity(arg0);
363: }
364:
365: public InputSource resolveEntity(String publicId,
366: String systemId) {
367: if (dtdMappings != null
368: && dtdMappings.containsKey(publicId)) {
369: String val = dtdMappings.get(publicId).toString();
370: return new InputSource(ClassLoaderUtil
371: .getResourceAsStream(val, DomHelper.class));
372: }
373: return null;
374: }
375:
376: public void warning(SAXParseException exception) {
377: }
378:
379: public void error(SAXParseException exception)
380: throws SAXException {
381: LOG.error(exception.getMessage() + " at ("
382: + exception.getPublicId() + ":"
383: + exception.getLineNumber() + ":"
384: + exception.getColumnNumber() + ")");
385: throw exception;
386: }
387:
388: public void fatalError(SAXParseException exception)
389: throws SAXException {
390: LOG.fatal(exception.getMessage() + " at ("
391: + exception.getPublicId() + ":"
392: + exception.getLineNumber() + ":"
393: + exception.getColumnNumber() + ")");
394: throw exception;
395: }
396: }
397:
398: }
|