001: package org.restlet.resource;
002:
003: import java.io.IOException;
004: import java.io.OutputStream;
005: import java.util.logging.Level;
006:
007: import javax.xml.transform.Source;
008: import javax.xml.transform.Transformer;
009: import javax.xml.transform.TransformerConfigurationException;
010: import javax.xml.transform.TransformerException;
011: import javax.xml.transform.TransformerFactory;
012: import javax.xml.transform.TransformerFactoryConfigurationError;
013: import javax.xml.transform.URIResolver;
014: import javax.xml.transform.stream.StreamResult;
015: import javax.xml.transform.stream.StreamSource;
016:
017: import org.restlet.Context;
018: import org.restlet.data.Reference;
019: import org.restlet.data.Response;
020:
021: /**
022: * Representation able to apply an XSLT transformation. The internal JAXP
023: * transformer is created when the getTransformer() method is first called. So,
024: * if you need to specify a custom URI resolver, you need to do it before
025: * actually using the representation for a transformation.<br>
026: * <br>
027: * This representation should be viewed as a wrapper representation that applies
028: * a transform sheet on a source representation when it is read or written out.
029: * Therefore, it isn't intended to be reused on different sources. For this use
030: * case, you should instead use the {@link org.restlet.Transformer} filter.
031: *
032: * @author Jerome Louvel (contact@noelios.com) <a
033: * href="http://www.noelios.com/">Noelios Consulting</a>
034: */
035: public class TransformRepresentation extends OutputRepresentation {
036: /** The source representation to transform. */
037: private Representation source;
038:
039: /** The transformer to be used and reused. */
040: private Transformer transformer;
041:
042: /** The XSLT transform sheet to apply to message entities. */
043: private Representation transformSheet;
044:
045: /** The URI resolver. */
046: private URIResolver uriResolver;
047:
048: /**
049: * Constructor.
050: *
051: * @param context
052: * The parent context.
053: * @param source
054: * The source representation to transform.
055: * @param transformSheet
056: * The XSLT transform sheet.
057: */
058: public TransformRepresentation(Context context,
059: Representation source, Representation transformSheet) {
060: super (null);
061: this .source = source;
062: this .transformSheet = transformSheet;
063: this .uriResolver = (context == null) ? null
064: : new ContextResolver(context);
065: }
066:
067: /**
068: * Returns the source representation to transform.
069: *
070: * @return The source representation to transform.
071: */
072: public Representation getSourceRepresentation() {
073: return this .source;
074: }
075:
076: /**
077: * Returns the transformer to be used and reused.
078: *
079: * @return The transformer to be used and reused.
080: */
081: public Transformer getTransformer() throws IOException {
082: if (this .transformer == null) {
083: try {
084: // Prepare the XSLT transformer documents
085: StreamSource transformSource = new StreamSource(
086: getTransformSheet().getStream());
087:
088: if (getTransformSheet().getIdentifier() != null) {
089: transformSource.setSystemId(getTransformSheet()
090: .getIdentifier().getTargetRef().toString());
091: }
092:
093: // Create the transformer factory
094: TransformerFactory transformerFactory = TransformerFactory
095: .newInstance();
096:
097: // Set the URI resolver
098: if (getURIResolver() != null) {
099: transformerFactory.setURIResolver(getURIResolver());
100: }
101:
102: // Create a new transformer
103: this .transformer = transformerFactory
104: .newTransformer(transformSource);
105: } catch (TransformerConfigurationException tce) {
106: throw new IOException(
107: "Transformer configuration exception. "
108: + tce.getMessage());
109: } catch (TransformerFactoryConfigurationError tfce) {
110: throw new IOException(
111: "Transformer factory configuration exception. "
112: + tfce.getMessage());
113: }
114: }
115:
116: return this .transformer;
117: }
118:
119: /**
120: * Returns the XSLT transform sheet to apply to the source representation.
121: *
122: * @return The XSLT transform sheet to apply.
123: */
124: public Representation getTransformSheet() {
125: return this .transformSheet;
126: }
127:
128: /**
129: * Returns the URI resolver.
130: *
131: * @return The URI resolver.
132: */
133: public URIResolver getURIResolver() {
134: return this .uriResolver;
135: }
136:
137: /**
138: * Sets the XSLT transform sheet to apply to message entities.
139: *
140: * @param transformSheet
141: * The XSLT transform sheet to apply to message entities.
142: */
143: public void setTransformSheet(Representation transformSheet) {
144: this .transformSheet = transformSheet;
145: }
146:
147: @Override
148: public void write(OutputStream outputStream) throws IOException {
149: try {
150: // Prepare the source and result documents
151: StreamSource sourceDocument = new StreamSource(
152: getSourceRepresentation().getStream());
153: StreamResult resultDocument = new StreamResult(outputStream);
154:
155: if (getSourceRepresentation().getIdentifier() != null) {
156: sourceDocument.setSystemId(getSourceRepresentation()
157: .getIdentifier().getTargetRef().toString());
158: }
159:
160: // Generates the result of the transformation
161: getTransformer().transform(sourceDocument, resultDocument);
162: } catch (TransformerException te) {
163: throw new IOException("Transformer exception. "
164: + te.getMessage());
165: }
166: }
167:
168: /**
169: * URI resolver based on a Restlet Context instance.
170: *
171: * @author Jerome Louvel (contact@noelios.com)
172: */
173: private final static class ContextResolver implements URIResolver {
174: /** The Restlet context. */
175: private Context context;
176:
177: /**
178: * Constructor.
179: *
180: * @param context
181: * The Restlet context.
182: */
183: public ContextResolver(Context context) {
184: this .context = context;
185: }
186:
187: /**
188: * Resolves a target reference into a Source document.
189: *
190: * @see javax.xml.transform.URIResolver#resolve(java.lang.String,
191: * java.lang.String)
192: */
193: public Source resolve(String href, String base)
194: throws TransformerException {
195: Source result = null;
196:
197: if (this .context != null) {
198: Reference targetRef = null;
199:
200: if ((base != null) && !base.equals("")) {
201: // Potentially a relative reference
202: Reference baseRef = new Reference(base);
203: targetRef = new Reference(baseRef, href);
204: } else {
205: // No base, assume "href" is an absolute URI
206: targetRef = new Reference(href);
207: }
208:
209: final String targetUri = targetRef.getTargetRef()
210: .toString();
211: Response response = this .context.getDispatcher().get(
212: targetUri);
213: if (response.getStatus().isSuccess()
214: && response.isEntityAvailable()) {
215: try {
216: result = new StreamSource(response.getEntity()
217: .getStream());
218: result.setSystemId(targetUri);
219:
220: } catch (IOException e) {
221: this .context
222: .getLogger()
223: .log(
224: Level.WARNING,
225: "I/O error while getting the response stream",
226: e);
227: }
228: }
229: }
230:
231: return result;
232: }
233: }
234: }
|