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-2007 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.spi.xml.cookies;
042:
043: import java.io.PrintWriter;
044: import java.io.StringWriter;
045: import java.net.URL;
046: import java.security.ProtectionDomain;
047: import java.security.CodeSource;
048:
049: import org.xml.sax.SAXException;
050: import org.xml.sax.SAXParseException;
051: import javax.xml.transform.TransformerFactory;
052: import javax.xml.transform.Transformer;
053: import javax.xml.transform.Source;
054: import javax.xml.transform.Result;
055: import javax.xml.transform.ErrorListener;
056: import javax.xml.transform.TransformerException;
057: import javax.xml.transform.TransformerConfigurationException;
058: import javax.xml.transform.URIResolver;
059:
060: import org.netbeans.api.xml.cookies.*;
061: import org.netbeans.api.xml.services.UserCatalog;
062:
063: /**
064: * Perform Transform action on XML document.
065: * Default implementation of {@link TransformableCookie} cookie.
066: *
067: * @author Libor Kramolis
068: */
069: public final class TransformableSupport implements TransformableCookie {
070:
071: // associated source
072: private final Source source;
073: /** cached TransformerFactory instance. */
074: private static TransformerFactory transformerFactory;
075:
076: /**
077: * Create new TransformableSupport for given data object.
078: * @param source Supported <code>Source</code>.
079: */
080: public TransformableSupport(Source source) {
081: if (source == null)
082: throw new NullPointerException();
083: this .source = source;
084: }
085:
086: /**
087: * Transform this object by XSL Transformation.
088: *
089: * @param transformSource source of transformation.
090: * @param outputResult result of transformation.
091: * @param notifier optional listener (<code>null</code> allowed)
092: * giving judgement details.
093: * @throws TransformerException if an unrecoverable error occurs during the course of the transformation
094: */
095: public void transform(Source transformSource, Result outputResult,
096: CookieObserver notifier) throws TransformerException {
097: try {
098: if (Util.THIS.isLoggable()) /* then */{
099: Util.THIS.debug("TransformableSupport.transform");
100: Util.THIS.debug(" transformSource = "
101: + transformSource.getSystemId());
102: Util.THIS.debug(" outputResult = "
103: + outputResult.getSystemId());
104: }
105:
106: Source xmlSource = source;
107:
108: if (Util.THIS.isLoggable()) /* then */
109: Util.THIS.debug(" xmlSource = "
110: + xmlSource.getSystemId());
111:
112: // prepare transformer == parse stylesheet, errors may occur
113: Transformer transformer = newTransformer(transformSource);
114:
115: // transform
116: if (notifier != null) {
117:
118: // inform user about used implementation
119:
120: ProtectionDomain domain = transformer.getClass()
121: .getProtectionDomain();
122: CodeSource codeSource = domain.getCodeSource();
123: if (codeSource == null) {
124: notifier.receive(new CookieMessage(Util.THIS
125: .getString("BK000", transformer.getClass()
126: .getName())));
127: } else {
128: URL location = codeSource.getLocation();
129: notifier.receive(new CookieMessage(Util.THIS
130: .getString("BK001", location, transformer
131: .getClass().getName())));
132: }
133:
134: Proxy proxy = new Proxy(notifier);
135: transformer.setErrorListener(proxy);
136: }
137: transformer.transform(xmlSource, outputResult);
138:
139: } catch (Exception exc) { // TransformerException, ParserConfigurationException, SAXException, FileStateInvalidException
140: if (Util.THIS.isLoggable()) /* then */{
141: Util.THIS.debug(" EXCEPTION during transformation: "
142: + exc.getClass().getName(), exc);
143: Util.THIS.debug(" exception's message = "
144: + exc.getLocalizedMessage());
145:
146: Throwable tempExc = unwrapException(exc);
147: Util.THIS.debug(" wrapped exception = "
148: + tempExc.getLocalizedMessage());
149: }
150:
151: TransformerException transExcept = null;
152: Object detail = null;
153:
154: if (exc instanceof TransformerException) {
155: transExcept = (TransformerException) exc;
156: if ((notifier != null)
157: && (exc instanceof TransformerConfigurationException)) {
158: detail = new DefaultXMLProcessorDetail(transExcept);
159: }
160: } else if (exc instanceof SAXParseException) {
161: transExcept = new TransformerException(exc);
162: if (notifier != null) {
163: detail = new DefaultXMLProcessorDetail(
164: (SAXParseException) exc);
165: }
166: } else {
167: transExcept = new TransformerException(exc);
168: if (notifier != null) {
169: detail = new DefaultXMLProcessorDetail(transExcept);
170: }
171: }
172:
173: if ((notifier != null) && (detail != null)) {
174: CookieMessage message = new CookieMessage(message(exc),
175: CookieMessage.FATAL_ERROR_LEVEL, detail);
176: notifier.receive(message);
177: }
178:
179: if (Util.THIS.isLoggable()) /* then */
180: Util.THIS
181: .debug("--> throw transExcept: " + transExcept);
182:
183: throw transExcept;
184: } // catch (Exception exc)
185: }
186:
187: //
188: // utils
189: //
190:
191: private static Throwable unwrapException(Throwable exc) {
192: Throwable wrapped = null;
193: if (exc instanceof TransformerException) {
194: wrapped = ((TransformerException) exc).getException();
195: } else if (exc instanceof SAXException) {
196: wrapped = ((SAXException) exc).getException();
197: } else {
198: return exc;
199: }
200:
201: if (wrapped == null) {
202: return exc;
203: }
204:
205: return unwrapException(wrapped);
206: }
207:
208: private static URIResolver getURIResolver() {
209: UserCatalog catalog = UserCatalog.getDefault();
210: URIResolver res = (catalog == null ? null : catalog
211: .getURIResolver());
212: return res;
213: }
214:
215: private static TransformerFactory getTransformerFactory() {
216: if (transformerFactory == null) {
217: transformerFactory = TransformerFactory.newInstance();
218: transformerFactory.setURIResolver(getURIResolver()); //!!! maybe that it should be set every call if UsersCatalog instances are dynamic
219: }
220: return transformerFactory;
221: }
222:
223: private static Transformer newTransformer(Source xsl)
224: throws TransformerConfigurationException {
225: return getTransformerFactory().newTransformer(xsl);
226: }
227:
228: /**
229: * Extract message from exception or use exception name.
230: */
231: private static String message(Throwable t) {
232: String msg = t.getLocalizedMessage();
233: return (msg != null ? msg : new ExceptionWriter(t).toString());
234: }
235:
236: /**
237: * Print first four exception lines.
238: */
239: private static class ExceptionWriter extends PrintWriter {
240: private int counter = 4;
241: private Throwable t;
242:
243: public ExceptionWriter(Throwable t) {
244: super (new StringWriter());
245: this .t = t;
246: }
247:
248: public void println(String s) {
249: if (counter-- > 0)
250: super .println(s);
251: }
252:
253: public void println(Object o) {
254: if (counter-- > 0)
255: super .println(o);
256: }
257:
258: public String toString() {
259: t.printStackTrace(this );
260: flush();
261: return ((StringWriter) out).getBuffer().toString();
262: }
263: }
264:
265: //
266: // class Proxy
267: //
268:
269: private static class Proxy implements ErrorListener {
270:
271: private final CookieObserver peer;
272:
273: public Proxy(CookieObserver peer) {
274: if (peer == null) {
275: throw new NullPointerException();
276: }
277: this .peer = peer;
278: }
279:
280: public void error(TransformerException tex)
281: throws TransformerException {
282: report(CookieMessage.ERROR_LEVEL, tex);
283: }
284:
285: public void fatalError(TransformerException tex)
286: throws TransformerException {
287: report(CookieMessage.FATAL_ERROR_LEVEL, tex);
288:
289: throw tex;
290: }
291:
292: public void warning(TransformerException tex)
293: throws TransformerException {
294: report(CookieMessage.WARNING_LEVEL, tex);
295: }
296:
297: private void report(int level, TransformerException tex)
298: throws TransformerException {
299: if (Util.THIS.isLoggable()) /* then */{
300: Util.THIS.debug(
301: "[TransformableSupport::Proxy]: report ["
302: + level + "]: ", tex);
303: Util.THIS.debug(" exception's message = "
304: + tex.getLocalizedMessage());
305:
306: Throwable tempExc = unwrapException(tex);
307: Util.THIS.debug(" wrapped exception = "
308: + tempExc.getLocalizedMessage());
309: }
310:
311: Throwable unwrappedExc = unwrapException(tex);
312: CookieMessage message = new CookieMessage(
313: message(unwrappedExc), level,
314: new DefaultXMLProcessorDetail(tex));
315:
316: peer.receive(message);
317: }
318:
319: } // class Proxy
320:
321: }
|