001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: /**
029: * Transforms an XML file according to given XSL file.
030: */package com.sun.xml.transform;
031:
032: import java.io.*;
033: import java.util.*;
034:
035: import javax.xml.parsers.*;
036: import javax.xml.transform.*;
037: import javax.xml.transform.dom.*;
038: import javax.xml.transform.stream.StreamResult;
039: import javax.xml.transform.stream.StreamSource;
040: import org.w3c.dom.*;
041: import org.xml.sax.ErrorHandler;
042: import org.xml.sax.SAXParseException;
043: import org.xml.sax.SAXException;
044:
045: /**
046: * Represents transformation
047: */
048: class Transformation {
049: /** XML file name to apply transformation to */
050: public String xmlFileName = "";
051:
052: /** XSL files to be applied */
053: public String xslFileName = null;
054:
055: /** Transformation parameters: names */
056: public Vector xslParamNames = new Vector();
057:
058: /** Transformation parameters: values */
059: public Vector xslparamValues = new Vector();
060:
061: /** Output file name */
062: public String outFileName = "";
063:
064: /** Do input file validation */
065: public boolean validate = false;
066: }
067:
068: /**
069: * Driver class with main(). Parses command line arguments and invokes
070: * CodeTransformerImpl instance that does all the transformation work.
071: */
072: public class CodeTransformer {
073: /** Transformer that does actual transformation */
074: private static CodeTransformerImpl transformer = null;
075:
076: /** Transformations to perform */
077: private static Vector transformations = new Vector();
078:
079: /** Print debug output while running */
080: private static boolean debug = false;
081:
082: /** Print usage info and exit */
083: private static boolean printHelp = false;
084:
085: /**
086: * Main method
087: *
088: * @param args Command line arguments
089: */
090: public static void main(String[] args) {
091: try {
092: parseArgs(args);
093: if (printHelp) {
094: printHelp();
095: return;
096: }
097:
098: transformer = new CodeTransformerImpl(debug);
099: for (int i = 0; i < transformations.size(); ++i) {
100: Transformation tr = (Transformation) transformations
101: .elementAt(i);
102: transformer.transform(tr);
103: }
104: } catch (SAXException e) {
105: // error was already reported
106: e.printStackTrace();
107: System.exit(1);
108: } catch (Exception e) {
109: e.printStackTrace();
110: System.exit(1);
111: }
112: }
113:
114: /**
115: * Parse command line arguments, adding Transformation objects to
116: * <tt>transformations</tt> vector for each transformation specified.
117: *
118: * @param args command line arguments
119: */
120: private static void parseArgs(String[] args) {
121: Transformation tr = new Transformation();
122: for (int i = 0; i < args.length; ++i) {
123: String arg = args[i];
124: if (debug) {
125: System.err.println("arg: " + arg);
126: }
127: if (arg.equals("-xml")) {
128: tr.xmlFileName = args[++i];
129: } else if (arg.equals("-xsl")) {
130: tr.xslFileName = args[++i];
131: } else if (arg.equals("-params")) {
132: int j = i + 1;
133: for (; j < args.length; j += 2) {
134: if ('-' == args[j].charAt(0)) {
135: break;
136: }
137: if (debug) {
138: System.err.println("pname : "
139: + (String) args[j]);
140: System.err.println("pvalue: "
141: + (String) args[j + 1]);
142: }
143: tr.xslParamNames.add(args[j]);
144: tr.xslparamValues.add(args[j + 1]);
145: }
146: i = j - 1;
147: } else if (arg.equals("-validate")) {
148: tr.validate = true;
149: } else if (arg.equals("-out")) {
150: tr.outFileName = args[++i];
151: // -out ends current transformation arguments,
152: // i.e all agruments coming after it belong
153: // to next transformation(s)
154: transformations.add(tr);
155: tr = new Transformation();
156: } else if (arg.equals("-debug")) {
157: debug = true;
158: } else {
159: printHelp = true;
160: }
161: }
162: }
163:
164: /**
165: * Print usage information
166: */
167: private static void printHelp() {
168: /**
169: * Following options are recognized:
170: * -validate: Do validation of input XML file.
171: * -xml: XML file to transform.
172: * -xsl: XSL file to apply to given XML file.
173: * -out: Output file. If empty, output will be to stdout.
174: * -params: Transformations parameters in form of "Name"
175: * "Value" pairs.
176: * -help: Print usage information
177: * -debug: Be verbose: print some debug info while running.
178: *
179: * In order to improve performance, CodeTransformer is capable of
180: * doing multiple transformations per invocation. -out option marks
181: * the end of arguments for single transformation. All arguments
182: * after it belong to next transformation(s).
183: */
184: System.err
185: .println("Usage: java -jar <code_transformer_jar_file>"
186: + "[-validate] " + "-xml <localXMLFile> "
187: + "-xsl <localXSLFile> "
188: + "-params <paramName> <paramValue>... "
189: + "-out <localOutputFile> "
190: + "-xml <localXMLFile> " + "... " + "[-debug] "
191: + "[-help]");
192: }
193: }
194:
195: /**
196: * Perform the transformation
197: */
198: class CodeTransformerImpl {
199: /** Factory constructing Transformer objects */
200: private TransformerFactory transformerFactory = TransformerFactory
201: .newInstance();
202:
203: /**
204: * Since most of transformations are applied to the same XML file,
205: * we don't want to load it on each transformation, so we cache last
206: * used XML file as DOMSource
207: */
208: /** Last source used */
209: private DOMSource lastSource = null;
210: /** Last document used */
211: private Document lastDoc = null;
212: /** File name of the last used source */
213: private String lastSourceFileName = null;
214:
215: /** Be verbose: print some debug info while running */
216: private boolean debug = false;
217:
218: /**
219: * Constructor
220: *
221: * @param dbg print some debug output while running
222: */
223: public CodeTransformerImpl(boolean dbg) {
224: debug = dbg;
225: }
226:
227: /**
228: * Converts errors.
229: */
230: class TransformerErrorHandler implements ErrorHandler {
231:
232: /**
233: * Handles errors.
234: * @param e the parsing exception
235: */
236: public void error(SAXParseException e) throws SAXParseException {
237: reportError(e);
238: // rethrow exception to stop processing on first error
239: throw e;
240: }
241:
242: /**
243: * Handles fatal errors.
244: * @param e the parsing exception
245: */
246: public void fatalError(SAXParseException e) {
247: reportError(e);
248: }
249:
250: /**
251: * Handles warnings.
252: * @param e the parsing exception
253: */
254: public void warning(SAXParseException e) {
255: reportError(e);
256: }
257:
258: /**
259: * Outputs diagnostic messages.
260: * @param e the parsing exception
261: */
262: private void reportError(SAXParseException e) {
263: String msg = e.getMessage();
264: String location = e.getSystemId();
265: int line = e.getLineNumber();
266:
267: System.err.print("Error: URI=" + location);
268: System.err.println(" Line=" + line + ": " + msg);
269: }
270: }
271:
272: /**
273: * Do the actual transformation
274: *
275: * @param tr transformation to perform
276: */
277: public void transform(Transformation tr) throws Exception {
278:
279: if (debug) {
280: System.err.println("xml file: " + tr.xmlFileName);
281: System.err.println("out file: " + tr.outFileName);
282: System.err.println("prev xml: " + lastSourceFileName);
283: }
284:
285: // source XML file
286: DOMSource source = null;
287: Document doc;
288: if (lastSource != null
289: && lastSourceFileName.equals(tr.xmlFileName)) {
290: source = lastSource;
291: doc = lastDoc;
292: } else {
293: // load XML file as DOM tree
294: DocumentBuilderFactory domFactory = DocumentBuilderFactory
295: .newInstance();
296:
297: if (tr.validate) {
298: domFactory.setValidating(true);
299: }
300: DocumentBuilder domBuilder = domFactory
301: .newDocumentBuilder();
302:
303: domBuilder.setErrorHandler(new TransformerErrorHandler());
304: doc = domBuilder.parse(new File(tr.xmlFileName));
305:
306: // make source from it
307: source = new DOMSource(doc);
308: }
309:
310: // if output and input files are the same,
311: // we can't reuse cached input file since
312: // it's going to change
313: if ((tr.xmlFileName).equals(tr.outFileName)) {
314: lastSource = null;
315: lastDoc = null;
316: lastSourceFileName = null;
317: } else {
318: lastSource = source;
319: lastDoc = doc;
320: lastSourceFileName = tr.xmlFileName;
321: }
322:
323: // apply XSL stylesheet
324: if (debug) {
325: System.err.println("xsl file: " + tr.xslFileName);
326: }
327:
328: // output file
329: StreamResult outStream = null;
330: if (tr.outFileName.length() == 0) {
331: // send transformed output to the stdout
332: outStream = new StreamResult(System.out);
333: } else {
334: // send transformed output to the file
335: makeDirectoryTree(tr.outFileName);
336: outStream = new StreamResult(new File(tr.outFileName));
337: }
338:
339: // create Transformer that will apply stylesheet
340: StreamSource xslFile = new StreamSource(
341: new File(tr.xslFileName));
342: Transformer transformer = transformerFactory
343: .newTransformer(xslFile);
344:
345: // pass parameters to Transformer
346: for (int j = 0; j < tr.xslParamNames.size(); ++j) {
347: transformer.setParameter((String) tr.xslParamNames
348: .elementAt(j), tr.xslparamValues.elementAt(j));
349: }
350:
351: // finally, apply the stylesheet
352: transformer.transform(source, outStream);
353: }
354:
355: /**
356: * Creates a directory structure.
357: *
358: * @param fullFileName Full path to the file to be created. If directory
359: * in which file is to be created doesn't exists, it will be created
360: * @exception IOException is thrown if directory couldn't be created
361: */
362: private void makeDirectoryTree(String fullFileName)
363: throws IOException {
364: if (debug == true) {
365: System.out.println("mkdir: " + fullFileName);
366: }
367: int index = fullFileName.lastIndexOf(File.separatorChar);
368: if (index == -1) {
369: // To be compatible with MKS-hosted build on win32, which
370: // does not translate / to \.
371: index = fullFileName.lastIndexOf('/');
372: }
373: File outputDirectory = new File(fullFileName
374: .substring(0, index));
375:
376: if (!(outputDirectory).exists()) {
377: if (!(outputDirectory).mkdirs()) {
378: throw new IOException(
379: "failed to create output directory");
380: }
381: }
382: }
383: }
|