001/* An actor that read an XSLT file and apply it to its input. 002 003@Copyright (c) 2003-2014 The Regents of the University of California. 004All rights reserved. 005 006Permission is hereby granted, without written agreement and without 007license or royalty fees, to use, copy, modify, and distribute this 008software and its documentation for any purpose, provided that the 009above copyright notice and the following two paragraphs appear in all 010copies of this software. 011 012IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 013FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 016SUCH DAMAGE. 017 018THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023ENHANCEMENTS, OR MODIFICATIONS. 024 025PT_COPYRIGHT_VERSION 2 026COPYRIGHTENDKEY 027 */ 028package ptolemy.actor.lib.xslt; 029 030import java.io.BufferedReader; 031import java.io.ByteArrayOutputStream; 032import java.io.IOException; 033import java.util.Iterator; 034 035import javax.xml.transform.TransformerException; 036 037import org.w3c.dom.Document; 038 039import ptolemy.actor.lib.Transformer; 040import ptolemy.actor.parameters.PortParameter; 041import ptolemy.data.RecordToken; 042import ptolemy.data.StringToken; 043import ptolemy.data.Token; 044import ptolemy.data.XMLToken; 045import ptolemy.data.expr.FileParameter; 046import ptolemy.data.type.BaseType; 047import ptolemy.data.type.RecordType; 048import ptolemy.data.type.Type; 049import ptolemy.kernel.CompositeEntity; 050import ptolemy.kernel.util.IllegalActionException; 051import ptolemy.kernel.util.NameDuplicationException; 052import ptolemy.kernel.util.Workspace; 053 054/////////////////////////////////////////////////////////////////// 055//// XSLTTransformer 056 057/** 058 This actor reads an XSLT file and apply it to a dom tree. The file or 059 URL is specified using any form acceptable to the FileParameter class. 060 061 <p>Currently, this actor requires the 062 <a href="http://saxon.sourceforge.net/">Saxon</a> XSLT processor 063 so as to ensure reproducible results. This restriction may 064 be relaxed in later versions of this actor. 065 066 <p>FIXME: what should the type of the input/output ports be???. 067 068 @see ptolemy.actor.lib.javasound.AudioReader 069 @author Yang Zhao, Christopher Hylands Brooks 070 @version $Id$ 071 @since Ptolemy II 4.0 072 @Pt.ProposedRating Red (liuj) 073 @Pt.AcceptedRating Red (liuj) 074 */ 075public class XSLTransformer extends Transformer { 076 /** Construct an actor with the given container and name. 077 * @param container The container. 078 * @param name The name of this actor. 079 * @exception IllegalActionException If the actor cannot be contained 080 * by the proposed container. 081 * @exception NameDuplicationException If the container already has an 082 * actor with this name. 083 */ 084 public XSLTransformer(CompositeEntity container, String name) 085 throws IllegalActionException, NameDuplicationException { 086 super(container, name); 087 088 // Set the type of the input port. 089 //input.setMultiport(true); 090 input.setTypeEquals(BaseType.XMLTOKEN); 091 092 // Set the type of the output port. 093 //output.setMultiport(true); 094 output.setTypeEquals(BaseType.STRING); 095 096 styleSheetParameters = new PortParameter(this, "styleSheetParameters"); 097 styleSheetParameters 098 .setTypeAtMost(new RecordType(new String[0], new Type[0])); 099 styleSheetParameters.setExpression("emptyRecord()"); 100 styleSheetFile = new FileParameter(this, "styleSheetFile"); 101 102 } 103 104 /////////////////////////////////////////////////////////////////// 105 //// ports and parameters //// 106 107 /** The file name or URL from which to read. This is a string with 108 * any form accepted by FileParameter. 109 * @see FileParameter 110 */ 111 public FileParameter styleSheetFile; 112 113 /** The parameters to be used in the stylesheet. This is a record 114 * that defaults to "emptyRecord()", an expression language command 115 * that returns an empty record. 116 * For example, if the parameter used in the style sheet is named 117 * <i>a</i> with type <i>int</i>, then the styleSheetParameters has 118 * type <i>{a = int}</i>. If the style sheet has multiple parameters, 119 * then each of them is represented as a field of the record. 120 */ 121 public PortParameter styleSheetParameters; 122 123 /////////////////////////////////////////////////////////////////// 124 //// public methods //// 125 126 /** Clone the actor into the specified workspace. This calls the 127 * base class and then set the filename public member. 128 * @param workspace The workspace for the new object. 129 * @return A new actor. 130 * @exception CloneNotSupportedException If a derived class contains 131 * an attribute that cannot be cloned. 132 */ 133 @Override 134 public Object clone(Workspace workspace) throws CloneNotSupportedException { 135 XSLTransformer newObject = (XSLTransformer) super.clone(workspace); 136 newObject.input.setTypeEquals(BaseType.XMLTOKEN); 137 newObject.output.setTypeEquals(BaseType.STRING); 138 return newObject; 139 } 140 141 /** Consume an XMLToken from the input and apply the XSL transform 142 * specified by the styleSheetFile parameter. If the styleSheetFile parameter 143 * does not name a file, then the input is copied to the output 144 * without modification. 145 * @exception IllegalActionException If the parent class throws it 146 */ 147 @Override 148 public void fire() throws IllegalActionException { 149 super.fire(); 150 styleSheetParameters.update(); 151 ByteArrayOutputStream out = new ByteArrayOutputStream(); 152 javax.xml.transform.Result result = new javax.xml.transform.stream.StreamResult( 153 out); 154 155 if (_debugging) { 156 _debug("--- open an output stream for the result. \n"); 157 } 158 159 if (_transformer != null) { 160 RecordToken parameters = (RecordToken) styleSheetParameters 161 .getToken(); 162 if (parameters != null) { 163 Iterator labels = parameters.labelSet().iterator(); 164 165 while (labels.hasNext()) { 166 String name = (String) labels.next(); 167 Token token = parameters.get(name); 168 if (token instanceof StringToken) { 169 StringToken s = (StringToken) token; 170 _transformer.setParameter(name, s.stringValue()); 171 } else { 172 _transformer.setParameter(name, token.toString()); 173 } 174 } 175 } 176 for (int i = 0; i < input.getWidth(); i++) { 177 if (input.hasToken(i)) { 178 XMLToken in = (XMLToken) input.get(i); 179 Document doc = in.getDomTree(); 180 181 try { 182 javax.xml.transform.Source xmlSource = new javax.xml.transform.dom.DOMSource( 183 doc); 184 _transformer.transform(xmlSource, result); 185 186 if (_debugging) { 187 _debug("--- transform the xmlSource: " 188 + in.toString() + "\n"); 189 } 190 191 if (out != null) { 192 if (_debugging) { 193 _debug("--- moml change request string: " 194 + out.toString() + "\n"); 195 } 196 197 StringToken outputToken = new StringToken( 198 out.toString()); 199 output.broadcast(outputToken); 200 201 if (_debugging) { 202 _debug("--- change request string token " 203 + "send out. \n"); 204 } 205 } 206 } catch (TransformerException ex) { 207 throw new IllegalActionException(this, ex, 208 "Failed to process '" + in + "'"); 209 } 210 211 try { 212 out.flush(); 213 out.close(); 214 } catch (IOException ex) { 215 throw new IllegalActionException(this, ex, 216 "Failed to close or flush '" + out + "'"); 217 } 218 } 219 } 220 } else { 221 // If there is no transformer, then output the xml string. 222 for (int i = 0; i < input.getWidth(); i++) { 223 if (input.hasToken(i)) { 224 XMLToken in = (XMLToken) input.get(i); 225 output.broadcast(new StringToken(in.toString())); 226 } 227 } 228 } 229 } 230 231 /** Open the XSL file named by the styleSheetFile parameter and 232 * set up the transformer. 233 * @exception IllegalActionException If the TransformFactory 234 * class name does not start with net.sf.saxon. 235 */ 236 @Override 237 public void initialize() throws IllegalActionException { 238 super.initialize(); 239 _xsltSource = null; 240 _transformer = null; 241 242 try { 243 BufferedReader reader; 244 245 // Ignore if the styleSheetFile is blank. 246 if (styleSheetFile.getExpression().trim().equals("")) { 247 reader = null; 248 } else { 249 reader = styleSheetFile.openForReading(); 250 } 251 252 if (reader != null) { 253 _xsltSource = new javax.xml.transform.stream.StreamSource( 254 reader); 255 } else { 256 _xsltSource = null; 257 } 258 259 if (_debugging) { 260 _debug("processing xsltSource change in " + getFullName()); 261 } 262 263 if (_xsltSource != null) { 264 _transformerFactory = javax.xml.transform.TransformerFactory 265 .newInstance(); 266 267 /* if (!_transformerFactory.getClass().getName().startsWith( 268 "net.sf.saxon")) { 269 throw new IllegalActionException( 270 this, 271 "The XSLTransformer actor works best\nwith " 272 + "saxon7.jar.\n" 273 + "The transformerFactory was '" 274 + _transformerFactory.getClass().getName() 275 + "'.\nIf saxon7.jar was in the classpath, then " 276 + "it should have\nstarted with " 277 + "\"net.sf.saxon\".\n" 278 + "If this actor does not use " 279 + "saxon, then the results will be " 280 + "different between\nruns that " 281 + "use saxon and runs that " 282 + "do not.\nDetails:\n" 283 + "This actor uses " 284 + "javax.xml.transform.TransformerFactory.\nThe " 285 + "concrete TransformerFactory class can be " 286 + "adjusted by\nsetting the " 287 + "javax.xml.transform.TransformerFactory " 288 + "property or by\nreading in a jar file that " 289 + "has the appropriate\nService Provider set.\n" 290 + "(For details about Jar Service Providers,\nsee " 291 + "http://download.oracle.com/javase/6/docs/technotes/guides/jar/jar.html\n" 292 + "The saxon7.jar file includes a\n" 293 + "META-INF/services/javax.xml.transform.TransformerFactory " 294 + "\nfile that sets the TransformerFactory " 295 + "class name start with 'net.sf.saxon'."); 296 }*/ 297 298 _transformer = _transformerFactory.newTransformer(_xsltSource); 299 300 if (_debugging) { 301 _debug("1 processing xsltSource change in " 302 + getFullName()); 303 } 304 } else { 305 _transformer = null; 306 } 307 } catch (Throwable throwable) { 308 throw new IllegalActionException(this, throwable, 309 "Failed to initialize."); 310 } 311 } 312 313 /////////////////////////////////////////////////////////////////// 314 //// private members //// 315 private javax.xml.transform.Source _xsltSource; 316 317 private javax.xml.transform.TransformerFactory _transformerFactory; 318 319 private javax.xml.transform.Transformer _transformer; 320}