Source for gnu.xml.libxmlj.transform.GnomeTransformer

   1: /* GnomeTransformer.java -
   2:    Copyright (C) 2004 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package gnu.xml.libxmlj.transform;
  39: 
  40: import java.io.InputStream;
  41: import java.io.IOException;
  42: import java.io.OutputStream;
  43: 
  44: import java.net.URL;
  45: 
  46: import java.util.HashMap;
  47: import java.util.Iterator;
  48: import java.util.Map;
  49: import java.util.Properties;
  50: 
  51: import javax.xml.transform.ErrorListener;
  52: import javax.xml.transform.Source;
  53: import javax.xml.transform.SourceLocator;
  54: import javax.xml.transform.Result;
  55: import javax.xml.transform.Templates;
  56: import javax.xml.transform.Transformer;
  57: import javax.xml.transform.TransformerConfigurationException;
  58: import javax.xml.transform.TransformerException;
  59: import javax.xml.transform.URIResolver;
  60: 
  61: import javax.xml.transform.dom.DOMSource;
  62: import javax.xml.transform.dom.DOMResult;
  63: import javax.xml.transform.sax.SAXResult;
  64: import javax.xml.transform.stream.StreamSource;
  65: import javax.xml.transform.stream.StreamResult;
  66: 
  67: import org.w3c.dom.Node;
  68: 
  69: import org.xml.sax.EntityResolver;
  70: import org.xml.sax.ErrorHandler;
  71: 
  72: import gnu.xml.libxmlj.dom.GnomeDocument;
  73: import gnu.xml.libxmlj.sax.GnomeXMLReader;
  74: import gnu.xml.libxmlj.util.NamedInputStream;
  75: import gnu.xml.libxmlj.util.StandaloneLocator;
  76: import gnu.xml.libxmlj.util.XMLJ;
  77: 
  78: /**
  79:  * An implementation of {@link javax.xml.transform.Transformer} which
  80:  * performs XSLT transformation using <code>libxslt</code>.
  81:  *
  82:  * @author Julian Scheid
  83:  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
  84:  */
  85: public class GnomeTransformer
  86:   extends Transformer
  87:   implements Templates
  88: {
  89: 
  90:   /**
  91:    * The parameters added by the user via {@link setParameter()}.
  92:    */
  93:   private Map parameters;
  94: 
  95:   /**
  96:    * The output properties set by the user.
  97:    */
  98:   private Properties outputProperties;
  99: 
 100:   /**
 101:    * The URI resolver to use during transformation.
 102:    */
 103:   private URIResolver resolver;
 104: 
 105:   /**
 106:    * The error listener for transformation errors.
 107:    */
 108:   private ErrorListener errorListener;
 109: 
 110:   /**
 111:    * Handle to the source stylesheet.
 112:    * This is a native pointer of type xsltStylesheetPtr.
 113:    */
 114:   private Object stylesheet;
 115: 
 116:   /**
 117:    * Constructor.
 118:    * @param source the XSLT stylesheet document source
 119:    * @param resolver the resolver to use during transformation
 120:    * @param errorListener the error listener for transformation errors
 121:    */
 122:   GnomeTransformer (Source source,
 123:                     URIResolver resolver,
 124:                     ErrorListener errorListener)
 125:     throws TransformerConfigurationException
 126:   {
 127:     this.resolver = resolver;
 128:     this.errorListener = errorListener;
 129:     parameters = new HashMap ();
 130:     outputProperties = new Properties ();
 131: 
 132:     if (source == null)
 133:       {
 134:         stylesheet = newStylesheet ();
 135:       }
 136:     else if (source instanceof StreamSource)
 137:       {
 138:         try
 139:           {
 140:             StreamSource ss = (StreamSource) source;
 141:             NamedInputStream in = XMLJ.getInputStream (ss);
 142:             String systemId = ss.getSystemId ();
 143:             String publicId = ss.getPublicId ();
 144:             String base = XMLJ.getBaseURI (systemId);
 145:             byte[] detectBuffer = in.getDetectBuffer ();
 146:             if (detectBuffer == null)
 147:               {
 148:                 String msg = "No document element";
 149:                 throw new TransformerConfigurationException (msg);
 150:               }
 151:             stylesheet = newStylesheetFromStream (in, detectBuffer, publicId,
 152:                                                   systemId, base,
 153:                                                   (resolver != null),
 154:                                                   (errorListener != null));
 155:           }
 156:         catch (IOException e)
 157:           {
 158:             throw new TransformerConfigurationException (e);
 159:           }
 160:       }
 161:     else if (source instanceof DOMSource)
 162:       {
 163:         DOMSource ds = (DOMSource) source;
 164:         Node node = ds.getNode ();
 165:         if (!(node instanceof GnomeDocument))
 166:           {
 167:             String msg = "Node is not a GnomeDocument";
 168:             throw new TransformerConfigurationException (msg);
 169:           }
 170:         GnomeDocument doc = (GnomeDocument) node;
 171:         stylesheet = newStylesheetFromDoc (doc);
 172:       }
 173:     else
 174:       {
 175:         String msg = "Source type not supported (" + source + ")";
 176:         throw new TransformerConfigurationException (msg);
 177:       }
 178:   }
 179: 
 180:   /**
 181:    * Copy constructor.
 182:    */
 183:   private GnomeTransformer (Object stylesheet,
 184:                             URIResolver resolver,
 185:                             ErrorListener errorListener,
 186:                             Map parameters,
 187:                             Properties outputProperties)
 188:   {
 189:     this.stylesheet = stylesheet;
 190:     this.resolver = resolver;
 191:     this.errorListener = errorListener;
 192:     this.parameters = parameters;
 193:     this.outputProperties = outputProperties;
 194:   }
 195: 
 196:   private native Object newStylesheet ()
 197:     throws TransformerConfigurationException;
 198: 
 199:   private native Object newStylesheetFromStream (InputStream in,
 200:                                                  byte[] detectBuffer,
 201:                                                  String publicId,
 202:                                                  String systemId,
 203:                                                  String base,
 204:                                                  boolean entityResolver,
 205:                                                  boolean errorHandler)
 206:     throws TransformerConfigurationException;
 207: 
 208:   private native Object newStylesheetFromDoc (GnomeDocument doc)
 209:     throws TransformerConfigurationException;
 210: 
 211:   //--- Implementation of javax.xml.transform.Transformer follows.
 212: 
 213:   // Set, get and clear the parameters to use on transformation
 214: 
 215:   public synchronized void setParameter (String parameter, Object value)
 216:   {
 217:     parameters.put (parameter, value);
 218:   }
 219: 
 220:   public synchronized Object getParameter (String name)
 221:   {
 222:     return parameters.get (name);
 223:   }
 224: 
 225:   public synchronized void clearParameters ()
 226:   {
 227:     parameters.clear ();
 228:   }
 229: 
 230:   // Set and get the ErrorListener to use on transformation
 231: 
 232:   public void setErrorListener (ErrorListener listener)
 233:   {
 234:     this.errorListener = listener;
 235:   }
 236: 
 237:   public ErrorListener getErrorListener ()
 238:   {
 239:     return errorListener;
 240:   }
 241: 
 242:   // Set and get the URIResolver to use on transformation
 243: 
 244:   public void setURIResolver (URIResolver resolver)
 245:   {
 246:     this.resolver = resolver;
 247:   }
 248: 
 249:   public URIResolver getURIResolver ()
 250:   {
 251:     return resolver;
 252:   }
 253: 
 254:   // Set the output properties to use on transformation; get default
 255:   // output properties and output properties specified in the
 256:   // stylesheet or by the user.
 257: 
 258:   public void setOutputProperties (Properties outputProperties)
 259:   {
 260:     // Note: defensive copying
 261:     this.outputProperties = new Properties (outputProperties);
 262:   }
 263: 
 264:   public void setOutputProperty (String name, String value)
 265:   {
 266:     outputProperties.setProperty (name, value);
 267:   }
 268: 
 269:   public Properties getOutputProperties ()
 270:   {
 271:     // Note: defensive copying
 272:     return new Properties (this.outputProperties);
 273:   }
 274: 
 275:   public String getOutputProperty (String name)
 276:   {
 277:     return outputProperties.getProperty (name);
 278:   }
 279: 
 280:   // -- Templates --
 281: 
 282:   public Transformer newTransformer ()
 283:   {
 284:     return new GnomeTransformer (stylesheet, resolver, errorListener,
 285:                                  new HashMap (parameters),
 286:                                  new Properties (outputProperties));
 287:   }
 288: 
 289:   // -- transform --
 290: 
 291:   /**
 292:    * Transforms the given source and writes the result to the
 293:    * given target.
 294:    */
 295:   public void transform (Source source, Result result)
 296:     throws TransformerException
 297:   {
 298:     if (source instanceof StreamSource)
 299:       {
 300:         try
 301:           {
 302:             StreamSource ss = (StreamSource) source;
 303:             NamedInputStream in = XMLJ.getInputStream (ss);
 304:             String publicId = ss.getPublicId ();
 305:             String systemId = ss.getSystemId ();
 306:             String base = XMLJ.getBaseURI (systemId);
 307:             byte[] detectBuffer = in.getDetectBuffer ();
 308:             if (detectBuffer == null)
 309:               {
 310:                 throw new TransformerException ("No document element");
 311:               }
 312:             if (result instanceof StreamResult)
 313:               {
 314:                 OutputStream out = XMLJ.getOutputStream ((StreamResult) result);
 315:                 transformStreamToStream (in, detectBuffer, publicId, systemId,
 316:                                          base, (resolver != null),
 317:                                          (errorListener != null), out);
 318:               }
 319:             else if (result instanceof DOMResult)
 320:               {
 321:                 DOMResult dr = (DOMResult) result;
 322:                 GnomeDocument ret =
 323:                   transformStreamToDoc (in, detectBuffer, publicId, systemId,
 324:                                         base, (resolver != null),
 325:                                         (errorListener != null));
 326:                 dr.setNode (ret);
 327:                 dr.setSystemId (null);
 328:               }
 329:             else if (result instanceof SAXResult)
 330:               {
 331:                 SAXResult sr = (SAXResult) result;
 332:                 transformStreamToSAX (in, detectBuffer, publicId, systemId,
 333:                                       base, (resolver != null),
 334:                                       (errorListener != null),
 335:                                       getSAXContext (sr));
 336:               }
 337:             else
 338:               {
 339:                 String msg = "Result type not supported (" + result + ")";
 340:                 throw new TransformerConfigurationException (msg);
 341:               }
 342:           }
 343:         catch (IOException e)
 344:           {
 345:             throw new TransformerException (e);
 346:           }
 347:       }
 348:     else if (source instanceof DOMSource)
 349:       {
 350:         DOMSource ds = (DOMSource) source;
 351:         Node node = ds.getNode ();
 352:         if (!(node instanceof GnomeDocument))
 353:           {
 354:             String msg = "Node is not a GnomeDocument (" + node + ")";
 355:             throw new TransformerException (msg);
 356:           }
 357:         GnomeDocument doc = (GnomeDocument) node;
 358:         if (result instanceof StreamResult)
 359:           {
 360:             try
 361:               {
 362:                 OutputStream out = XMLJ.getOutputStream ((StreamResult) result);
 363:                 transformDocToStream (doc, out);
 364:               }
 365:             catch (IOException e)
 366:               {
 367:                 throw new TransformerException (e);
 368:               }
 369:           }
 370:         else if (result instanceof DOMResult)
 371:           {
 372:             DOMResult dr = (DOMResult) result;
 373:             GnomeDocument ret = transformDocToDoc (doc);
 374:             dr.setNode (ret);
 375:             dr.setSystemId (null);
 376:           }
 377:         else if (result instanceof SAXResult)
 378:           {
 379:             SAXResult sr = (SAXResult) result;
 380:             transformDocToSAX (doc, getSAXContext (sr));
 381:           }
 382:         else
 383:           {
 384:             String msg = "Result type not supported";
 385:             throw new TransformerConfigurationException (msg);
 386:           }
 387:       }
 388:     else
 389:       {
 390:         String msg = "Source type not supported";
 391:         throw new TransformerConfigurationException (msg);
 392:       }
 393:   }
 394: 
 395:   private GnomeXMLReader getSAXContext (SAXResult result)
 396:   {
 397:     GnomeXMLReader ctx = new GnomeXMLReader ();
 398:     ctx.setContentHandler (result.getHandler ());
 399:     ctx.setLexicalHandler (result.getLexicalHandler ());
 400:     if (errorListener != null)
 401:       {
 402:         ErrorHandler errorHandler =
 403:           new ErrorListenerErrorHandler (errorListener);
 404:         ctx.setErrorHandler (errorHandler);
 405:       }
 406:     if (resolver != null)
 407:       {
 408:         EntityResolver entityResolver =
 409:           new URIResolverEntityResolver (resolver);
 410:         ctx.setEntityResolver (entityResolver);
 411:       }
 412:     return ctx;
 413:   }
 414: 
 415:   private native void transformStreamToStream (InputStream in,
 416:                                                byte[] detectBuffer,
 417:                                                String publicId,
 418:                                                String systemId,
 419:                                                String base,
 420:                                                boolean entityResolver,
 421:                                                boolean errorHandler,
 422:                                                OutputStream out)
 423:     throws TransformerException;
 424: 
 425:   private native GnomeDocument transformStreamToDoc (InputStream in,
 426:                                                      byte[] detectBuffer,
 427:                                                      String publicId,
 428:                                                      String systemId,
 429:                                                      String base,
 430:                                                      boolean entityResolver,
 431:                                                      boolean errorHandler)
 432:     throws TransformerException;
 433: 
 434:   private native void transformStreamToSAX (InputStream in,
 435:                                             byte[] detectBuffer,
 436:                                             String publicId,
 437:                                             String systemId,
 438:                                             String base,
 439:                                             boolean entityResolver,
 440:                                             boolean errorHandler,
 441:                                             GnomeXMLReader out)
 442:     throws TransformerException;
 443: 
 444:   private native void transformDocToStream (GnomeDocument in,
 445:                                             OutputStream out)
 446:     throws TransformerException;
 447: 
 448:   private native GnomeDocument transformDocToDoc (GnomeDocument in)
 449:     throws TransformerException;
 450: 
 451:   private native void transformDocToSAX (GnomeDocument in,
 452:                                          GnomeXMLReader out)
 453:     throws TransformerException;
 454: 
 455:   /*
 456:    * Retrieve parameters as a string array.
 457:    * This is a convenience method called from native code.
 458:    */
 459:   private String[] getParameterArray ()
 460:   {
 461:     String[] parameterArray = new String[parameters.size () * 2];
 462:     int index = 0;
 463:     for (Iterator it = parameters.keySet ().iterator ();
 464:          it.hasNext ();
 465:          ++index)
 466:       {
 467:         String parameterKey = (String) it.next ();
 468:         String parameterValue = (String) parameters.get (parameterKey);
 469:         parameterArray[index * 2 + 0] = parameterKey;
 470:         parameterArray[index * 2 + 1] =
 471:           "'" + ((parameterValue != null) ? parameterValue : "") + "'";
 472:         // FIXME encode parameter value correctly for XPath
 473:       }
 474:     return parameterArray;
 475:   }
 476: 
 477:   // -- Free xsltStylesheet handle --
 478: 
 479:   public void finalize ()
 480:   {
 481:     if (stylesheet != null)
 482:       {
 483:         free ();
 484:         stylesheet = null;
 485:       }
 486:   }
 487: 
 488:   private native void free ();
 489: 
 490:   // -- Callbacks --
 491: 
 492:   private InputStream resolveEntity (String publicId, String systemId)
 493:     throws TransformerException
 494:   {
 495:     if (resolver != null)
 496:       {
 497:         systemId = resolver.resolve (null, systemId).getSystemId ();
 498:       }
 499:     if (systemId == null)
 500:       {
 501:         return null;
 502:       }
 503:     try
 504:       {
 505:         URL url = new URL (systemId);
 506:         return XMLJ.getInputStream (url);
 507:       }
 508:     catch (IOException e)
 509:       {
 510:         throw new TransformerException (e);
 511:       }
 512:   }
 513: 
 514:   private void setDocumentLocator (Object ctx, Object loc)
 515:   {
 516:   }
 517: 
 518:   private void warning (String message,
 519:                         int lineNumber,
 520:                         int columnNumber,
 521:                         String publicId,
 522:                         String systemId)
 523:     throws TransformerException
 524:   {
 525:     if (errorListener == null)
 526:       {
 527:         return;
 528:       }
 529:     SourceLocator l = new StandaloneLocator (lineNumber,
 530:                                              columnNumber,
 531:                                              publicId,
 532:                                              systemId);
 533:     errorListener.warning (new TransformerException (message, l));
 534:   }
 535: 
 536:   private void error (String message,
 537:                       int lineNumber,
 538:                       int columnNumber,
 539:                       String publicId,
 540:                       String systemId)
 541:     throws TransformerException
 542:   {
 543:     if (errorListener == null)
 544:       {
 545:         return;
 546:       }
 547:     SourceLocator l = new StandaloneLocator (lineNumber,
 548:                                              columnNumber,
 549:                                              publicId,
 550:                                              systemId);
 551:     errorListener.error (new TransformerException (message, l));
 552:   }
 553: 
 554:   private void fatalError (String message,
 555:                            int lineNumber,
 556:                            int columnNumber,
 557:                            String publicId,
 558:                            String systemId)
 559:     throws TransformerException
 560:   {
 561:     if (errorListener == null)
 562:       {
 563:         return;
 564:       }
 565:     SourceLocator l = new StandaloneLocator (lineNumber,
 566:                                              columnNumber,
 567:                                              publicId,
 568:                                              systemId);
 569:     errorListener.fatalError (new TransformerException (message, l));
 570:   }
 571: 
 572: }