001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.xsl.utils;
042:
043: import java.io.File;
044: import java.io.InputStream;
045: import java.io.IOException;
046: import java.net.URL;
047: import java.net.MalformedURLException;
048: import java.security.*;
049:
050: import org.xml.sax.*;
051: import javax.xml.parsers.*;
052: import javax.xml.transform.*;
053: import javax.xml.transform.sax.*;
054: import javax.xml.transform.stream.*;
055:
056: import org.openide.filesystems.*;
057: import org.openide.loaders.DataObject;
058: import org.openide.execution.*;
059:
060: import org.netbeans.api.xml.cookies.*;
061: import org.netbeans.api.xml.services.UserCatalog;
062: import org.netbeans.spi.xml.cookies.*;
063:
064: import org.netbeans.modules.xsl.XSLDataObject;
065: import org.openide.util.NbBundle;
066:
067: /**
068: * Transformation utilities.
069: *
070: * @author Libor Kramolis
071: * @version 0.1
072: */
073: public class TransformUtil {
074:
075: private static final String SAX_FEATURES_NAMESPACES = "http://xml.org/sax/features/namespaces"; // NOI18N
076:
077: public static final String DEFAULT_OUTPUT_EXT = "html";
078: private static TransformerFactory transformerFactory;
079: private static SAXParserFactory saxParserFactory;
080:
081: public static boolean isXSLTransformation(DataObject dataObject) {
082: // return ( dataObject instanceof XSLDataObject );
083: return dataObject.getPrimaryFile().getMIMEType().equals(
084: XSLDataObject.MIME_TYPE);
085: }
086:
087: public static String getURLName(FileObject fileObject)
088: throws MalformedURLException, FileStateInvalidException {
089: URL fileURL = null;
090: File file = FileUtil.toFile(fileObject);
091:
092: if (file != null) {
093: // if ( Util.THIS.isLoggable() ) /* then */ {
094: // try {
095: // Util.THIS.debug ("[TransformUtil.getURLName]");
096: // Util.THIS.debug (" file = " + file);
097: // Util.THIS.debug (" file.getCanonicalPath = " + file.getCanonicalPath());
098: // Util.THIS.debug (" file.getAbsolutePath = " + file.getAbsolutePath());
099: // Util.THIS.debug (" file.toString = " + file.toString());
100: // Util.THIS.debug (" file.toURL = " + file.toURL());
101: // } catch (Exception exc) {
102: // Util.THIS.debug ("DEBUG Exception", exc);
103: // }
104: // }
105:
106: fileURL = file.toURL();
107: } else {
108: fileURL = fileObject.getURL();
109: }
110:
111: return fileURL.toExternalForm();
112: }
113:
114: public static URL createURL(URL baseURL, String fileName)
115: throws MalformedURLException, FileStateInvalidException {
116: // if ( Util.THIS.isLoggable() ) /* then */ {
117: // Util.THIS.debug ("TransformUtil.createURL:");
118: // Util.THIS.debug (" baseURL = " + baseURL);
119: // Util.THIS.debug (" fileName = " + fileName);
120: // }
121:
122: URL url = new URL(baseURL, fileName);
123:
124: // if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug (" return URL = " + url);
125:
126: return url;
127: }
128:
129: public static Source createSource(URL baseURL, String fileName)
130: throws IOException, MalformedURLException,
131: FileStateInvalidException, ParserConfigurationException,
132: SAXException {
133: URL url = createURL(baseURL, fileName);
134: // test right url
135: InputStream is = url.openStream();
136: is.close();
137:
138: XMLReader reader = TransformUtil.newXMLReader();
139:
140: // if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("TransformUtil.createSource: XMLReader (http://xml.org/sax/features/namespaces) : "
141: // + reader.getFeature (SAX_FEATURES_NAMESPACES));
142:
143: reader.setEntityResolver(TransformUtil.getEntityResolver());
144: Source source = new SAXSource(reader, new InputSource(url
145: .toExternalForm()));
146:
147: return source;
148: }
149:
150: public static URIResolver getURIResolver() {
151: UserCatalog catalog = UserCatalog.getDefault();
152: URIResolver res = (catalog == null ? null : catalog
153: .getURIResolver());
154: return res;
155: }
156:
157: public static EntityResolver getEntityResolver() {
158: UserCatalog catalog = UserCatalog.getDefault();
159: EntityResolver res = (catalog == null ? null : catalog
160: .getEntityResolver());
161: return res;
162: }
163:
164: private static TransformerFactory getTransformerFactory() {
165: if (transformerFactory == null) {
166: transformerFactory = TransformerFactory.newInstance();
167: transformerFactory.setURIResolver(getURIResolver()); //!!! maybe that it should be set every call if UsersCatalog instances are dynamic
168: }
169: return transformerFactory;
170: }
171:
172: private static SAXParserFactory getSAXParserFactory()
173: throws ParserConfigurationException,
174: SAXNotRecognizedException, SAXNotSupportedException {
175: if (saxParserFactory == null) {
176: saxParserFactory = SAXParserFactory.newInstance();
177: saxParserFactory.setFeature(SAX_FEATURES_NAMESPACES, true);
178: }
179: return saxParserFactory;
180: }
181:
182: public static Transformer newTransformer(Source xsl)
183: throws TransformerConfigurationException {
184: Transformer transformer = getTransformerFactory()
185: .newTransformer(xsl);
186:
187: // if ( Util.THIS.isLoggable() ) /* then */ transformer.setParameter ("transformer", xsl); // debug
188:
189: return transformer;
190: }
191:
192: public static XMLReader newXMLReader()
193: throws ParserConfigurationException, SAXException {
194: SAXParser parser = getSAXParserFactory().newSAXParser();
195: return parser.getXMLReader();
196: }
197:
198: /* @return associated stylesheet or <code>null</code>.
199: */
200: public static Source getAssociatedStylesheet(URL baseURL) {
201: // if ( Util.THIS.isLoggable() ) /* then */ {
202: // Util.THIS.debug ("TransformUtil.getAssociatedStylesheet:");
203: // Util.THIS.debug (" baseURL = " + baseURL);
204: // }
205:
206: Source xml_stylesheet = null;
207:
208: try {
209: XMLReader reader = newXMLReader();
210: reader.setEntityResolver(getEntityResolver());
211: SAXSource source = new SAXSource(reader, new InputSource(
212: baseURL.toExternalForm()));
213:
214: xml_stylesheet = getTransformerFactory()
215: .getAssociatedStylesheet(source, null, null, null);
216:
217: // if ( Util.THIS.isLoggable() ) /* then */ {
218: // Util.THIS.debug (" source = " + source.getSystemId());
219: // Util.THIS.debug (" xml_stylesheet = " + xml_stylesheet);
220: // }
221: } catch (Exception exc) { // ParserConfigurationException, SAXException, TransformerConfigurationException
222: // ignore it
223: // if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("TransformUtil.getAssociatedStylesheet: !!!", exc); // debug
224: }
225:
226: return xml_stylesheet;
227: }
228:
229: public static String guessOutputExt(Source source) {
230: String ext = DEFAULT_OUTPUT_EXT;
231:
232: try {
233: Transformer transformer = newTransformer(source);
234: String method = transformer
235: .getOutputProperty(OutputKeys.METHOD);
236:
237: // if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("[TransformUtil] guessOutputExt: method = " + method);
238:
239: if ("text".equals(method)) { // NOI18N
240: ext = "txt"; // NOI18N
241: } else if (method != null) {
242: ext = method;
243: }
244: } catch (Exception exc) {
245: // ignore it
246:
247: // if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug (exc);
248: }
249:
250: // if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("[TransformUtil] guessOutputExt: extension = " + ext);
251:
252: return ext;
253: }
254:
255: /**
256: * @throws TransformerException it indicates
257: */
258: public static void transform(Source xml,
259: TransformableCookie transformable, Source xsl,
260: Result output, CookieObserver notifier)
261: throws TransformerException {
262: // if ( Util.THIS.isLoggable() ) /* then */ {
263: // Util.THIS.debug ("TransformUtil.transform");
264: // Util.THIS.debug (" XML source = " + xml.getSystemId());
265: // Util.THIS.debug (" TransformableCookie = " + transformable);
266: // Util.THIS.debug (" XSL source = " + xsl.getSystemId());
267: // Util.THIS.debug (" Output Result = " + output.getSystemId());
268: // Util.THIS.debug (" CookieObserver = " + notifier);
269: // }
270:
271: if (transformable != null) {
272:
273: transformable.transform(xsl, output, notifier);
274:
275: } else {
276:
277: try {
278: Transformer transformer = TransformUtil
279: .newTransformer(xsl);
280:
281: if (notifier != null) {
282:
283: // inform user about used implementation
284:
285: ProtectionDomain domain = transformer.getClass()
286: .getProtectionDomain();
287: CodeSource codeSource = domain.getCodeSource();
288: if (codeSource == null) {
289: notifier.receive(new CookieMessage(NbBundle
290: .getMessage(TransformUtil.class,
291: "BK000", transformer.getClass()
292: .getName())));
293: } else {
294: URL location = codeSource.getLocation();
295: notifier
296: .receive(new CookieMessage(NbBundle
297: .getMessage(
298: TransformUtil.class,
299: "BK001", location,
300: transformer.getClass()
301: .getName())));
302: }
303:
304: Proxy proxy = new Proxy(notifier);
305: transformer.setErrorListener(proxy);
306: }
307:
308: //if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("\n==> transform: param [transformer] = " + transformer.getParameter ("transformer")); // debug
309:
310: transformer.transform(xml, output);
311:
312: } catch (Exception exc) { // TransformerException, ParserConfigurationException, SAXException, FileStateInvalidException
313: // if ( Util.THIS.isLoggable() ) /* then */ {
314: // Util.THIS.debug (" EXCEPTION during transformation: " + exc.getClass().getName(), exc);
315: // Util.THIS.debug (" exception's message = " + exc.getLocalizedMessage());
316: //
317: // Throwable tempExc = unwrapException (exc);
318: // Util.THIS.debug (" wrapped exception = " + tempExc.getLocalizedMessage());
319: // }
320:
321: TransformerException transExcept = null;
322: Object detail = null;
323:
324: if (exc instanceof TransformerException) {
325: transExcept = (TransformerException) exc;
326: if ((notifier != null)
327: && (exc instanceof TransformerConfigurationException)) {
328: detail = new DefaultXMLProcessorDetail(
329: transExcept);
330: }
331: } else if (exc instanceof SAXParseException) {
332: transExcept = new TransformerException(exc);
333: if (notifier != null) {
334: detail = new DefaultXMLProcessorDetail(
335: (SAXParseException) exc);
336: }
337: } else {
338: transExcept = new TransformerException(exc);
339: if (notifier != null) {
340: detail = new DefaultXMLProcessorDetail(
341: transExcept);
342: }
343: }
344:
345: if ((notifier != null) && (detail != null)) {
346: CookieMessage message = new CookieMessage(
347: unwrapException(exc).getLocalizedMessage(),
348: CookieMessage.FATAL_ERROR_LEVEL, detail);
349: notifier.receive(message);
350: }
351:
352: // if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("--> throw transExcept: " + transExcept);
353:
354: throw transExcept;
355: }
356: }
357: }
358:
359: /** Unwrap wrapped cause exception.
360: */
361: public static Throwable unwrapException(Throwable exc) {
362: Throwable wrapped = null;
363: if (exc instanceof TransformerException) {
364: wrapped = ((TransformerException) exc).getException();
365: } else if (exc instanceof SAXException) {
366: wrapped = ((SAXException) exc).getException();
367: } else {
368: return exc;
369: }
370:
371: if (wrapped == null) {
372: return exc;
373: }
374:
375: return unwrapException(wrapped);
376: }
377:
378: //
379: // class Proxy
380: //
381:
382: private static class Proxy implements ErrorListener {
383:
384: private final CookieObserver peer;
385:
386: public Proxy(CookieObserver peer) {
387: if (peer == null) {
388: throw new NullPointerException();
389: }
390: this .peer = peer;
391: }
392:
393: public void error(TransformerException tex)
394: throws TransformerException {
395: report(CookieMessage.ERROR_LEVEL, tex);
396: }
397:
398: public void fatalError(TransformerException tex)
399: throws TransformerException {
400: report(CookieMessage.FATAL_ERROR_LEVEL, tex);
401:
402: throw tex;
403: }
404:
405: public void warning(TransformerException tex)
406: throws TransformerException {
407: report(CookieMessage.WARNING_LEVEL, tex);
408: }
409:
410: private void report(int level, TransformerException tex)
411: throws TransformerException {
412: // if ( Util.THIS.isLoggable() ) /* then */ {
413: // Util.THIS.debug ("[TransformableSupport::Proxy]: report [" + level + "]: ", tex);
414: // Util.THIS.debug (" exception's message = " + tex.getLocalizedMessage());
415: //
416: // Throwable tempExc = unwrapException (tex);
417: // Util.THIS.debug (" wrapped exception = " + tempExc.getLocalizedMessage());
418: // }
419:
420: Throwable unwrappedExc = unwrapException(tex);
421: CookieMessage message = new CookieMessage(unwrappedExc
422: .getLocalizedMessage(), level,
423: new DefaultXMLProcessorDetail(tex));
424: peer.receive(message);
425: }
426:
427: } // class Proxy
428:
429: }
|