/*************************************************************************
*
* $RCSfile: OOoTransform.java,v $
*
* $Revision: 1.1.4.1 $
*
* last change: $Author: vg $ $Date: 2005/06/10 13:31:33 $
*
* The Contents of this file are made available subject to the terms of
* either of the following licenses
*
* - GNU Lesser General Public License Version 2.1
* - Sun Industry Standards Source License Version 1.1
*
* Sun Microsystems Inc., October, 2000
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2002 by Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, CA 94303, USA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*
* Sun Industry Standards Source License Version 1.1
* =================================================
* The contents of this file are subject to the Sun Industry Standards
* Source License Version 1.1 (the "License"); You may not use this file
* except in compliance with the License. You may obtain a copy of the
* License at http://www.openoffice.org/license.html.
*
* Software provided under this License is provided on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
* WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
* See the License for the specific provisions governing your rights and
* obligations concerning the Software.
*
* The Initial Developer of the Original Code is: Sun Microsystems, Inc.
*
* Copyright: 2002 by Sun Microsystems, Inc.
*
* All Rights Reserved.
*
* Contributor(s): _______________________________________
*
*
************************************************************************/
/**
* Description: This class is used for the transformation of Global documents.
* For each child document referenced by a URL of the text:section a transformation will
* be started.
* This is necessary as global document children don't 'know' of each other nor of the global
* document.
* Due to that no consequence numbering of the chapter would be possible nor a HTML linking between
* child documents an to the content-table (global document).
* The class collects all necessary parameters and starts the transformation of each child document.
*
* This class works only with the XT processor of James Clark. This was the easiest way to go and should be
* expanded/splitt later.
* <p>
* For further documentation and updates visit http://xml.openoffice.org/sx2ml
*/
package org.openoffice.xslt;
import java.io.*;
import java.util.*;
import java.net.*;
import java.lang.reflect.Array;
import org.xml.sax.*;
import javax.xml.parsers.SAXParser;
import com.jclark.xsl.sax.*;
import com.jclark.xsl.om.NodeIterator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.EntityResolver;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.helpers.XMLReaderAdapter;
import com.jclark.xsl.expr.*;
import com.jclark.xsl.om.*;
import java.lang.NullPointerException;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.SAXParserFactory;
public class OOoTransform {
static boolean mDebug = false;
static boolean isInitialized = false;
static boolean isPrimarySource = false;
static String mSourceDesc;
static String mSourceRef;
static String mTargetRef;
/** The XSLT processor instance with parsed style-sheet, to be cloned for performance gain*/
static XSLProcessor clonableXSLProcessor;
static XMLReaderAdapter xmlReaderAdapter;
static InputSource inputSource;
public static void main(String args[]){
try{
int nArgs = 0;
Properties cmdlineProps = new Properties();
for (int i = 0; i < args.length; i++) {
int k = args[i].indexOf('=');
if (k > 0) {
cmdlineProps.setProperty(args[i].substring(0, k),
args[i].substring(k + 1));
} else {
args[nArgs] = args[i];
nArgs++;
}
}
if (nArgs < 3) {
System.err.println("usage: java org.openoffice.xslt.OOoMasterDocument source stylesheet result [param=value]...\n\n");
System.err.println("\tIf the source is a SXG (global document) containing links to child documents.");
System.err.println("\tSuch a child document will be implizit transformed via a java call to OOoMasterDocument.");
System.err.println("\tFor Usability reasons the child output gonna get HTML links to the ContentTable.");
System.exit(1);
}
if(System.getProperty("com.jclark.xsl.sax.parser") == null)
System.setProperty("com.jclark.xsl.sax.parser", "org.xml.sax.helpers.XMLReaderAdapter");
OOoTransformProps.xslStylesheet = args[1];
String inputFile = new File(args[0]).getAbsolutePath().replace('\\','/');
String outputFile = new File(args[2]).getAbsolutePath().replace('\\','/');
initialize(inputFile, outputFile, cmdlineProps);
// potential DOS paths (and other protocols) are being cut, as XT processor won't work with them
if(inputFile.indexOf(':') != -1)
inputFile = inputFile.substring(inputFile.lastIndexOf(':') + 1);
if(outputFile.indexOf(':') != -1)
outputFile = outputFile.substring(outputFile.lastIndexOf(':') + 1);
transform(inputFile, outputFile);
}catch(Exception e){
System.out.println(e.getMessage());
e.printStackTrace();
}
}
/**
* Initializes a converter before usage.
*/
static void initialize(String inputFile, String outputFile, Properties cmdlineProps){
try {
String debugParameter = cmdlineProps.getProperty("debug");
if(debugParameter != null){
if(debugParameter.equalsIgnoreCase("true")){
OOoMasterDocument.mDebug= true;
OOoTransformProps.mDebug = true;
mDebug = true;
System.out.println("debug activated!");
}
}
String masterDocument = cmdlineProps.getProperty("masterDocument");
if(masterDocument != null){
OOoTransformProps.mMasterDocumentEnabled = true;
if(masterDocument.equalsIgnoreCase("true")){
OOoTransformProps.mMasterDocument = true;
}else
System.out.println("Global document feature disabled!");
}
OOoTransformProps.jarRootURL = "jar:file:///" + inputFile +"!/";
OOoTransformProps.setMasterDocumentDir(inputFile);
OOoTransformProps.dpi = cmdlineProps.getProperty("dpi");
OOoTransformProps.mStyleDebugOutputOnly = cmdlineProps.getProperty("onlyStyleOutput");
OOoTransformProps.setOutputType(cmdlineProps.getProperty("outputType"));
//2DO: Master Document feature should only be addressed when required
OOoTransformProps.setContentTableURL(outputFile);
isPrimarySource = true;
initializeXSLT();
}catch(Exception e) {
System.out.println(e.getMessage());
java.io.StringWriter sw = new java.io.StringWriter();
e.printStackTrace(new java.io.PrintWriter(sw));
System.out.println(sw.toString());
}
}
static void initializeXSLT(){
try {
// INITIALIZE XSLT CONCEREND PARAMETERS
if(OOoTransformProps.xslStylesheet == null){
if(OOoTransformProps.mOutputType != null && OOoTransformProps.mOutputType.equals("WML"))
OOoTransformProps.xslStylesheet = "office2wml.xsl";
else
OOoTransformProps.xslStylesheet = "ooo2xhtml.xsl";
}
// create the XSLT processor (to be cloned later)
if(clonableXSLProcessor == null){
initialXSLTEngine();
}
isInitialized = true;
OOoMasterDocument.initialize();
}catch(Exception e) {
System.out.println(e.getMessage());
java.io.StringWriter sw = new java.io.StringWriter();
e.printStackTrace(new java.io.PrintWriter(sw));
System.out.println(sw.toString());
}
}
/**
* Initilizes a clonable XSL Processor instance of the XT processor from James Clark.
* Depends on the initialized variables xslStylesheet.
*
*/
private static void initialXSLTEngine(){
try{
System.setProperty("javax.xml.parsers.SAXParserFactory", "org.apache.xerces.jaxp.SAXParserFactoryImpl");
SAXParserFactory factory = null;
try{
factory = SAXParserFactory.newInstance();
}catch(FactoryConfigurationError e){
System.out.println(e.getMessage());
System.out.println("The default parser implementation (Xerces) was not found,\nthe JRE parser (Crimson) is being used instead.");
System.setProperty("javax.xml.parsers.SAXParserFactory", "org.apache.crimson.jaxp.SAXParserFactoryImpl");
factory = SAXParserFactory.newInstance();
}
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
xmlReaderAdapter = new XMLReaderAdapter(xmlReader);
xmlReaderAdapter.setEntityResolver(new OOoDTDResolver());
// preparing the original for the clones of the XSL Processor
clonableXSLProcessor = new XSLProcessorImpl();
clonableXSLProcessor.setParser(xmlReaderAdapter);
if(mDebug) System.out.println("The xslStylesheet is: " + OOoTransformProps.xslStylesheet);
// starting '/' before the name does not add packagename to filename
InputSource inputSource = new InputSource(new BufferedInputStream((Object.class).getResourceAsStream('/' + OOoTransformProps.xslStylesheet)));
String styleSheetURL = null;
try{
styleSheetURL = (Object.class).getResource('/' + OOoTransformProps.xslStylesheet).toString();
}catch(NullPointerException e){
System.out.println("No stylesheet named as '" + OOoTransformProps.xslStylesheet + "' found");
}
if(mDebug) System.out.println("The styleSheetURL is: " + styleSheetURL);
String systemID = styleSheetURL.substring(0, styleSheetURL.lastIndexOf('/') + 1);
inputSource.setSystemId(systemID);
if(mDebug) System.out.println("The stylesheet can be found at " + systemID);
clonableXSLProcessor.loadStylesheet(inputSource);
}
catch(Exception e){
System.out.println(e.getMessage());
if(mDebug){
java.io.StringWriter sw = new java.io.StringWriter();
e.printStackTrace(new java.io.PrintWriter(sw));
System.out.println(sw.toString());
}
}
}
static boolean transform(String newSourceRef){
try{
// getting the new destination document for the ongoing transformation
String targetRef = OOoMasterDocument.getOutputURLForHTML(newSourceRef);
if(targetRef.startsWith("file://")){
targetRef = targetRef.substring(targetRef.indexOf("file:/") + 7).replaceAll("//","/").replaceAll("//","/");
}else if(targetRef.startsWith("file:/")){
targetRef = targetRef.substring(targetRef.indexOf("file:/") + 6).replaceAll("//","/").replaceAll("//","/");
}
transform(newSourceRef, targetRef);
}catch(Exception e){
System.out.println(e.getMessage());
e.printStackTrace();
}
return false;
}
private static boolean transform(String newSourceRef, String targetRef){
try{
if(isPrimarySource){
System.out.println("\n\nNew transformation:");
System.out.println("===================");
isPrimarySource = false;
}else{
System.out.println("\n\nNew child transformation:");
System.out.println("=========================");
}
mTargetRef = targetRef;
if(mDebug) System.out.println("Output: " + targetRef);
// getting the inputSource and setting the destinationDirURL
InputSource inputSource = createInputSource(newSourceRef);
// setting the relative path (from the output directory) to the original source directory
String outputFile = new File(targetRef).getAbsolutePath().replace('\\','/');
String inputFile = new File(newSourceRef).getAbsolutePath().replace('\\','/');
OOoTransformProps.setDestinationDirURL(outputFile.substring(0, outputFile.lastIndexOf('/')));
OOoTransformProps.setAbsoluteSourceDirURL(inputFile.substring(0, inputFile.lastIndexOf('/')));
// the links from the output need to prefix the relativ URL from the destination directory back to the source directory
OOoTransformProps.setRelativeSourceDirURL(outputFile, inputFile);
transform(inputSource, new FileDestination(targetRef));
return true;
}catch(Exception e){
System.out.println(e.getMessage());
e.printStackTrace();
}
return false;
}
private static void transform(InputSource inputSource, GenericDestination outDest){
try{
// setting up the XSL processor
XSLProcessor aXSLProcessor = (XSLProcessor) clonableXSLProcessor.clone();
OutputMethodHandlerImpl outputMethodHandler = new OutputMethodHandlerImpl(aXSLProcessor);
outputMethodHandler.setDestination(outDest);
aXSLProcessor.setOutputMethodHandler(outputMethodHandler);
setStyleSheetParameters(aXSLProcessor);
aXSLProcessor.setParser(xmlReaderAdapter);
aXSLProcessor.setErrorHandler(new ErrorHandlerImpl());
if(inputSource != null){
System.out.println(mSourceDesc);
System.out.println("Source: " + mSourceRef);
System.out.println("Output: " + mTargetRef);
System.out.println("Start processing...");
aXSLProcessor.parse(inputSource);
}else
throw new Exception("The XSLT process was aborted the Input '"+ inputSource +"' is not available!");
}catch(Exception e){
System.out.println(e.getMessage());
e.printStackTrace();
if(mDebug){
java.io.StringWriter sw = new java.io.StringWriter();
e.printStackTrace(new java.io.PrintWriter(sw));
if(mDebug) System.out.println(sw.toString());
}
}
}
/** Getting the inputSource for the transformation and setting the path for the dependent partial source
* files styles.xml and meta.xml (setting metaFileURL and stylesFileURL variables), which can be
* the same as the input file in case of a flat XML file */
private static InputSource createInputSource(String newSourceRef){
try{
if(OOoTransformProps.getDestinationDirURL() == null){
// The output should be in the same dir as the source document.
// The path of a linked document from the global document (newSourceRef)
// might be relative URL to the global document, we need the absolute to be able to transform
int pathEnd = newSourceRef.lastIndexOf('/');
OOoTransformProps.setDestinationDirURL(newSourceRef.substring(0, pathEnd));
}
// check if it's a relative URL or absolute URL
String sourceRef = null;
if(newSourceRef.charAt(1) != ':'
&& !newSourceRef.startsWith("/")
&& !newSourceRef.startsWith("file:")
&& !newSourceRef.startsWith("jar:")
&& !newSourceRef.startsWith("http:")) {
// it is a relative URL, the absolute URL have to be concatenated
sourceRef = OOoTransformProps.getAbsoluteSourceDirURL() + newSourceRef;
}else{
// it is an absolute URL no concatenation needed
sourceRef = newSourceRef;
}
mSourceRef = sourceRef;
if(mDebug) System.out.println("Source: "+ sourceRef);
int sourceURLLength = sourceRef.length();
// Is the input source from a Jar URL or a SX? OpenOffice jared/compressed document
boolean isOpenOfficeDocument = sourceRef.regionMatches(true,(sourceURLLength - 4),".sx",0,3);
URL sourceURL = null;
try{
if(sourceRef.startsWith("jar:") || isOpenOfficeDocument) {
OOoTransformProps.mbZippedSource = true;
if(sourceRef.startsWith("jar:")) {
mSourceDesc = "The source is a content provided by JAR URL.";
sourceURL = new URL(sourceRef); // already JAR URL
String jarURLCompressedFile = sourceRef.substring(0, sourceRef.lastIndexOf('!'));
OOoTransformProps.metaFileURL = jarURLCompressedFile + "!/meta.xml";
OOoTransformProps.stylesFileURL = jarURLCompressedFile + "!/styles.xml";
}else {
mSourceDesc = "The source is a compressed OpenOffice document.";
//if file URL or absolute Path
if(newSourceRef.startsWith("file:") || newSourceRef.charAt(1) != ':' || newSourceRef.startsWith("/")){
sourceRef = (file2URL(new File(sourceRef))).toString();
}
//this works sourceURL = new URL("file://h:/xsltTest/testfiles/testfile/wd-so-xml-intro.xml");
// sourceURL = new URL("jar", "", "file:/h:/xsltTest/testfiles/testfile/wd-so-xml.jar!/content.xml");
sourceURL = new URL("jar", "", sourceRef + "!/content.xml");
OOoTransformProps.metaFileURL = "jar:" + sourceRef + "!/meta.xml";
OOoTransformProps.stylesFileURL = "jar:" + sourceRef + "!/styles.xml";
}
if (mDebug) System.out.println("sourceRef: "+ sourceRef);
if (mDebug) System.out.println("user.dir: " + System.getProperty("user.dir"));
JarURLConnection jarConn = (JarURLConnection) sourceURL.openConnection();
//test stream
//copyStream(new BufferedInputStream(jarConn.getInputStream()),System.out);
try{
inputSource = new InputSource(new BufferedInputStream(jarConn.getInputStream()));
}catch(java.util.zip.ZipException e){
if(mDebug){
System.out.println(e.getMessage());
e.printStackTrace();
}
System.err.println("The input source " + sourceRef + " could not be found!");
System.exit(-1);
}
}// flatfilter XML inputfile (old '.xml' or new '.fsx*' suffix)
else if((sourceRef.regionMatches(true,(sourceURLLength - 4),".xml",0,4)) ||
(sourceRef.regionMatches(true,(sourceURLLength - 5),".fsx",0,4))) {
mSourceDesc = "The source is a XML flatfile.";
try{
// Fix of XT issue ':' of URL containing DOS path results in error
System.out.println("Old sourceRef: " + sourceRef);
int index1 = sourceRef.indexOf(':');
int index2 = sourceRef.substring(index1 + 1).indexOf(':');
if(index2 != -1){
index2 = sourceRef.lastIndexOf(':');
sourceRef = sourceRef.substring(index2 - 1);
System.out.println("New sourceRef: " + sourceRef);
}
File sourceFile = new File(sourceRef);
inputSource = new InputSource(new BufferedInputStream(new FileInputStream(sourceFile)));
sourceURL = file2URL(sourceFile);
}catch(Exception e){
if(mDebug){
System.out.println(e.getMessage());
e.printStackTrace();
}
// otherwise if sourceRef wasn't a relative URL nor a file URL, we use it as different URL
sourceURL = new URL(sourceRef);
inputSource = new InputSource(new BufferedInputStream(sourceURL.openStream()));
}
// in the flat xml file both files are integrated in the content.xml
OOoTransformProps.metaFileURL = sourceRef;
OOoTransformProps.stylesFileURL = sourceRef;
}else{
System.err.println("No InputSource for "+ sourceRef +" was able to be set!");
}
if(mDebug)System.out.println("The OpenOffice styles can be found under: " + OOoTransformProps.stylesFileURL);
if(inputSource == null)
throw new Exception("The inputsource is null");
else
inputSource.setSystemId(sourceURL.toString());
// setting the adequate headerlevel for this source
if(OOoMasterDocument.precedingHeaderCounterMap != null)
OOoMasterDocument.initialHeaderLevel = (int[]) OOoMasterDocument.precedingHeaderCounterMap.get(newSourceRef);
return inputSource;
}catch(java.net.MalformedURLException e){
System.out.println("\n\nERROR: The inputsource could not be found, check the source path!\n");
if(mDebug){
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}catch(Exception e){
System.out.println(e.getMessage());
e.printStackTrace();
}
return null;
}
static private void setStyleSheetParameters(XSLProcessor aXSLProcessor){
try{
// for tunneling through session based WebApplications with the optionalURLSuffix as URL suffix
// (neccessary in case of URLREWRITING and single sign on)
if(OOoTransformProps.optionalURLSuffix != null)
aXSLProcessor.setParameter("optionalURLSuffix", OOoTransformProps.optionalURLSuffix);
// tunneling through Presentation Server using PackageURLs to access content of the jar
if(OOoTransformProps.jarRootURL != null && OOoTransformProps.mbZippedSource)
aXSLProcessor.setParameter("sourceBaseURL", OOoTransformProps.jarRootURL);
if(OOoTransformProps.stylesFileURL != null)
aXSLProcessor.setParameter("stylesFileURL", OOoTransformProps.stylesFileURL);
if(OOoTransformProps.metaFileURL != null)
aXSLProcessor.setParameter("metaFileURL", OOoTransformProps.metaFileURL);
/*
if(OOoTransformProps.getRelativeSourceDirURL() != null)
aXSLProcessor.setParameter("sourceDirURL", OOoTransformProps.getRelativeSourceDirURL());
*/
if(OOoTransformProps.dpi != null)
aXSLProcessor.setParameter("dpi", OOoTransformProps.dpi);
if(OOoTransformProps.mMasterDocumentEnabled)
if(OOoTransformProps.mMasterDocument)
aXSLProcessor.setParameter("childDocumentExist", "true");
else
aXSLProcessor.setParameter("childDocumentExist", "false");
if(OOoTransformProps.mContentTableURL != null)
aXSLProcessor.setParameter("contentTableURL", OOoTransformProps.mContentTableURL);
if(mDebug)
aXSLProcessor.setParameter("debug", "true");
if(OOoTransformProps.mStyleDebugOutputOnly != null && !OOoTransformProps.mStyleDebugOutputOnly.equalsIgnoreCase("false"))
aXSLProcessor.setParameter("onlyStyleOutput", "true");
if(OOoMasterDocument.currentChildContentRef != null)
aXSLProcessor.setParameter("currentChildContentRef", OOoMasterDocument.currentChildContentRef);
if(OOoTransformProps.mOutputType != null){
aXSLProcessor.setParameter("outputType", OOoTransformProps.mOutputType);
}
if(OOoMasterDocument.getContentTableHeadings() != null){
aXSLProcessor.setParameter("contentTableHeadings", OOoMasterDocument.getContentTableHeadings());
}
if(OOoMasterDocument.initialHeaderLevel != null){
if (mDebug)System.out.print("Parameter precedingChapterLevel: ");
for(int i=1; i<=OOoMasterDocument.LEVELMAX; i++){
aXSLProcessor.setParameter("precedingChapterLevel" + Integer.toString(i), Integer.toString(OOoMasterDocument.initialHeaderLevel[i]));
if (mDebug)System.out.print("L:"+i+"V:"+Integer.toString(OOoMasterDocument.initialHeaderLevel[i])+" ");
} if (mDebug)System.out.println("");
}
}catch(Exception e){
if(mDebug) System.out.println(e.getMessage());
e.printStackTrace();
}
}
static URL file2URL(File file) {
String path = file.getAbsolutePath();
String fSep = System.getProperty("file.separator");
if (fSep != null && fSep.length() == 1) {
path = path.replace(fSep.charAt(0), '/');
}
if (path.length() > 0 && path.charAt(0) != '/') {
path = '/' + path;
}
try {
return new URL("file", "", path);
}
catch (java.net.MalformedURLException e) {
e.printStackTrace();
/* According to the spec this could only happen if the file
protocol were not recognized. */
throw new Error("unexpected MalformedURLException");
}
}
/**
* Debugging method:
* Copies the the whole inputstream to the outputstream. Flushing output, but not closing the streams after usage.
*
* @param in The inputstream to be copied to the outputstream
* @param out The outputstream as desired destination of the inputstream
*
* @return the absolute path of a file with choosen pre- and suffix in the tempory directory of the webservices/WebDAV server.
*/
static private void copyStream(InputStream in, OutputStream out){
try{
final int BYTEARRAYLENGTH = 16384;
byte[] b = new byte[BYTEARRAYLENGTH];
int len = in.read(b);
while (len != -1) {
out.write(b, 0, len);
len = in.read(b);
}
out.flush();
}catch (Exception e){
System.out.println(e.getMessage());
if(mDebug){
java.io.StringWriter sw = new java.io.StringWriter();
e.printStackTrace(new java.io.PrintWriter(sw));
System.out.println(sw.toString());
}
}
}
static class ErrorHandlerImpl implements ErrorHandler {
public void warning(SAXParseException e) {
printSAXParseException(e);
}
public void error(SAXParseException e) {
printSAXParseException(e);
}
public void fatalError(SAXParseException e) throws SAXException {
throw e;
}
public void printSAXParseException(SAXParseException e) {
String systemId = e.getSystemId();
int lineNumber = e.getLineNumber();
if (systemId != null) {
System.err.print(systemId + ":");
}
if (lineNumber >= 0) {
System.err.print(lineNumber + ":");
}
if (systemId != null || lineNumber >= 0) {
System.err.print(" ");
}
System.err.println(e.getMessage());
}
}
}