001: /*
002: * Copyright 2005-2007 Noelios Consulting.
003: *
004: * The contents of this file are subject to the terms of the Common Development
005: * and Distribution License (the "License"). You may not use this file except in
006: * compliance with the License.
007: *
008: * You can obtain a copy of the license at
009: * http://www.opensource.org/licenses/cddl1.txt See the License for the specific
010: * language governing permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL HEADER in each file and
013: * include the License file at http://www.opensource.org/licenses/cddl1.txt If
014: * applicable, add the following below this CDDL HEADER, with the fields
015: * enclosed by brackets "[]" replaced with your own identifying information:
016: * Portions Copyright [yyyy] [name of copyright owner]
017: */
018:
019: package org.restlet.resource;
020:
021: import java.io.IOException;
022: import java.io.OutputStream;
023:
024: import javax.xml.namespace.QName;
025: import javax.xml.parsers.DocumentBuilder;
026: import javax.xml.parsers.DocumentBuilderFactory;
027: import javax.xml.parsers.ParserConfigurationException;
028: import javax.xml.transform.Result;
029: import javax.xml.transform.Source;
030: import javax.xml.transform.TransformerConfigurationException;
031: import javax.xml.transform.TransformerException;
032: import javax.xml.transform.TransformerFactory;
033: import javax.xml.transform.TransformerFactoryConfigurationError;
034: import javax.xml.transform.dom.DOMSource;
035: import javax.xml.transform.sax.SAXResult;
036: import javax.xml.transform.stream.StreamSource;
037: import javax.xml.xpath.XPath;
038: import javax.xml.xpath.XPathFactory;
039:
040: import org.restlet.data.MediaType;
041: import org.restlet.util.XmlWriter;
042: import org.w3c.dom.Document;
043: import org.xml.sax.ContentHandler;
044:
045: /**
046: * XML representation for SAX events processing. The purpose is to create a
047: * streamable content based on a custom Java object model instead of a neutral
048: * DOM tree. This domain object can then be directly modified and efficiently
049: * serialized at a later time.<br/> Subclasses only need to override the
050: * ContentHandler methods required for the reading and also the write(XmlWriter
051: * writer) method when serialization is requested.<br/>
052: *
053: * @author Jerome Louvel (contact@noelios.com)
054: */
055: public class SaxRepresentation extends XmlRepresentation {
056: /** The wrapped DOM document to parse. */
057: private Document xmlDocument;
058:
059: /** The wrapped XML representation. */
060: private Representation xmlRepresentation;
061:
062: /**
063: * Constructor.
064: *
065: * @param mediaType
066: * The representation media type.
067: */
068: public SaxRepresentation(MediaType mediaType) {
069: super (mediaType);
070: }
071:
072: /**
073: * Constructor.
074: *
075: * @param mediaType
076: * The representation's media type.
077: * @param xmlDocument
078: * A source DOM representation to parse.
079: */
080: public SaxRepresentation(MediaType mediaType, Document xmlDocument) {
081: super (mediaType);
082: this .xmlDocument = xmlDocument;
083: }
084:
085: /**
086: * Constructor.
087: *
088: * @param xmlRepresentation
089: * A source XML representation to parse.
090: */
091: public SaxRepresentation(Representation xmlRepresentation)
092: throws IOException {
093: super (xmlRepresentation.getMediaType());
094: this .xmlRepresentation = xmlRepresentation;
095: }
096:
097: /**
098: * Parses the source and sends SAX events to a content handler.
099: *
100: * @param contentHandler
101: * The SAX content handler to use for parsing.
102: */
103: public void parse(ContentHandler contentHandler) throws IOException {
104: if (contentHandler != null) {
105: try {
106: Source source = null;
107:
108: if (this .xmlDocument != null) {
109: source = new DOMSource(xmlDocument);
110: } else {
111: source = new StreamSource(xmlRepresentation
112: .getStream());
113: }
114:
115: if (xmlRepresentation.getIdentifier() != null) {
116: source.setSystemId(xmlRepresentation
117: .getIdentifier().getTargetRef().toString());
118: }
119:
120: Result result = new SAXResult(contentHandler);
121: TransformerFactory.newInstance().newTransformer()
122: .transform(source, result);
123: } catch (TransformerConfigurationException tce) {
124: throw new IOException(
125: "Couldn't parse the source representation: "
126: + tce.getMessage());
127: } catch (TransformerException te) {
128: te.printStackTrace();
129: throw new IOException(
130: "Couldn't parse the source representation: "
131: + te.getMessage());
132: } catch (TransformerFactoryConfigurationError tfce) {
133: throw new IOException(
134: "Couldn't parse the source representation: "
135: + tfce.getMessage());
136: }
137: } else {
138: throw new IOException(
139: "Couldn't parse the source representation: no content restlet defined.");
140: }
141: }
142:
143: /**
144: * Writes the representation to a byte stream.
145: *
146: * @param outputStream
147: * The output stream.
148: */
149: public void write(OutputStream outputStream) throws IOException {
150: write(new XmlWriter(outputStream, "UTF-8"));
151: }
152:
153: /**
154: * Writes the representation to a XML writer. The default implementation
155: * does nothing and is intended to be overriden.
156: *
157: * @param writer
158: * The XML writer to write to.
159: * @throws IOException
160: */
161: public void write(XmlWriter writer) throws IOException {
162: // Do nothing by default.
163: }
164:
165: /**
166: * Returns a document builder properly configured.
167: *
168: * @return A document builder properly configured.
169: */
170: private DocumentBuilder getDocumentBuilder() throws IOException {
171: try {
172: DocumentBuilderFactory dbf = DocumentBuilderFactory
173: .newInstance();
174: dbf.setNamespaceAware(isNamespaceAware());
175: dbf.setValidating(false);
176: return dbf.newDocumentBuilder();
177: } catch (ParserConfigurationException pce) {
178: throw new IOException(
179: "Couldn't create the empty document: "
180: + pce.getMessage());
181: }
182: }
183:
184: @Override
185: public Object evaluate(String expression, QName returnType)
186: throws Exception {
187: Object result = null;
188: XPath xpath = XPathFactory.newInstance().newXPath();
189: xpath.setNamespaceContext(this );
190:
191: if (this .xmlDocument == null) {
192: this .xmlDocument = getDocumentBuilder().parse(
193: this .xmlRepresentation.getStream());
194: }
195:
196: if (this .xmlDocument != null) {
197: result = xpath.evaluate(expression, this .xmlDocument,
198: returnType);
199: } else {
200: throw new Exception(
201: "Unable to obtain a DOM document for the SAX representation. "
202: + "XPath evaluation cancelled.");
203: }
204:
205: return result;
206: }
207:
208: }
|