Ignore:
Timestamp:
2012-11-21T16:13:59+13:00 (11 years ago)
Author:
ak19
Message:
  1. Adding new Library class TransformingLibrary.java that takes a GS3 o=xml input file and an XSLT file (like o=skinandlibdoc) and applies the XSLT to the XML to produce the final GS3 html page. 2. Needed a custom transform() method in XMLTransformer to get this to work as the XML and XSLT files can be located anywhere, but the XSLT import statements that the input XSLT file makes should be resolved to absolute paths despite specifying only relative paths. 3. Updated Library2.java to ensure GSDL3SRCHOME is set before it is run, in order to set GSDL3HOME correctly.
Location:
main/trunk/greenstone3/src/java/org/greenstone/gsdl3
Files:
1 added
2 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/Library2.java

    r26454 r26494  
    9595    {
    9696
     97        if (System.getenv("GSDL3SRCHOME") == null) {
     98        System.out.println("Before calling this script, run: source gs3-setup.sh");
     99        System.exit(1);
     100        }
     101
    97102        if (args.length != 2)
    98103        {
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/XMLTransformer.java

    r26457 r26494  
    2525import java.io.FileWriter;
    2626import java.io.InputStreamReader;
     27import java.io.IOException;
    2728import java.io.StringReader;
    2829import java.io.StringWriter;
    2930import java.io.UnsupportedEncodingException;
     31import java.net.MalformedURLException;
     32import java.net.URISyntaxException;
     33import java.net.URI;
     34import java.net.URL;
    3035import java.util.HashMap;
    3136import java.util.Iterator;
     
    4045import javax.xml.transform.TransformerException;
    4146import javax.xml.transform.TransformerFactory;
     47import javax.xml.transform.URIResolver;
    4248import javax.xml.transform.dom.DOMResult;
    4349import javax.xml.transform.dom.DOMSource;
     
    264270        }
    265271    }
     272
     273    /**
     274      When transforming an XML with an XSLT from the command-line, there's two problems if calling
     275      XMLTransformer.transform(File xslt, File xml):
     276
     277      1. Need to run the transformation process from the location of the stylesheet, else it can't find
     278      the stylesheet in order to load it. This is resolved by setting the SystemID for the stylesheet.
     279
     280      2. The XSLT stylesheet has <xsl:import> statements importing further XSLTs which are furthermore
     281      specified by relative paths, requiring that the XSLT (and perhaps also XML) input file is located
     282      in the folder from which the relative paths of the import statements are specified. This is resolved
     283      by working out the absolute path of each imported XSLT file and setting their SystemIDs too.
     284
     285      Without solving these problems, things will only work if we 1. run the transformation from the
     286      web/interfaces/default/transform folder and 2. need to have the input XSLT file (and XML?)
     287      placed in the web/interfacces/default/transform folder too
     288
     289      Both parts of the solution are described in:
     290      http://stackoverflow.com/questions/3699860/resolving-relative-paths-when-loading-xslt-filse
     291
     292      After the systemID on the XSLT input file is set, we can run the command from the toplevel GS3
     293      folder.
     294      After resolving the URIs of the XSLT files imported by the input XSLT, by setting their systemIDs,
     295      the input XSLT (and XML) file need not be located in web/interfaces/default/transform anymore.
     296     */
     297    public Node transform(File stylesheet, File source, String interfaceName, Document docDocType)
     298    {
     299        try
     300        {
     301            TransformErrorListener transformerErrorListener = (TransformErrorListener) this.t_factory.getErrorListener();
     302            transformerErrorListener.setStylesheet(stylesheet);
     303
     304            // http://stackoverflow.com/questions/3699860/resolving-relative-paths-when-loading-xslt-files
     305            Source xsltSource = new StreamSource(new InputStreamReader(new FileInputStream(stylesheet), "UTF-8"));
     306            URI systemID = stylesheet.toURI();
     307            try {               
     308                URL url = systemID.toURL();         
     309                xsltSource.setSystemId(url.toExternalForm());
     310            } catch (MalformedURLException mue) {
     311                logger.error("Warning: Unable to set systemID for stylesheet to " + systemID);
     312                logger.error("Got exception: " + mue.getMessage(), mue);
     313            }
     314
     315            this.t_factory.setURIResolver(new ClasspathResourceURIResolver(interfaceName));         
     316            Transformer transformer = this.t_factory.newTransformer(xsltSource);
     317            // Alternative way of instantiating a newTransformer():
     318            //Templates cachedXSLT = this.t_factory.newTemplates(xsltSource);
     319            //Transformer transformer = cachedXSLT.newTransformer();
     320
     321            DOMResult result = (docDocType == null) ? new DOMResult() : new DOMResult(docDocType);
     322            StreamSource streamSource = new StreamSource(new InputStreamReader(new FileInputStream(source), "UTF-8"));
     323
     324            transformer.setErrorListener(new TransformErrorListener(stylesheet, streamSource));
     325
     326            transformer.transform(streamSource, result);
     327            return result.getNode().getFirstChild();
     328        }
     329        catch (TransformerConfigurationException e)
     330        {
     331            return transformError("XMLTransformer.transform(File, File)" + "\ncouldn't create transformer object for files\n" + stylesheet + "\n" + source, e);
     332        }
     333        catch (TransformerException e)
     334        {
     335            return transformError("XMLTransformer.transform(File, File)" + "\ncouldn't transform the source for files\n" + stylesheet + "\n" + source, e);
     336        }
     337        catch (UnsupportedEncodingException e)
     338        {
     339            return transformError("XMLTransformer.transform(File, File)" + "\ncouldn't read file due to an unsupported encoding\n" + stylesheet + "\n" + source, e);
     340        }
     341        catch (FileNotFoundException e)
     342        {
     343            return transformError("XMLTransformer.transform(File, File)" + "\ncouldn't find the file specified\n" + stylesheet + "\n" + source, e);
     344        }
     345    }
     346
     347    /**
     348     * Class for resolving the relative paths used for <xsl:import>s
     349     * when transforming XML with an XSLT
     350    */
     351    class ClasspathResourceURIResolver implements URIResolver {
     352    String interface_name;
     353
     354    public ClasspathResourceURIResolver(String interfaceName) {
     355        interface_name = interfaceName;     
     356    }
     357
     358    /**
     359     * Override the URIResolver.resolve() method to turn relative paths of imported xslt files into
     360     * absolute paths and set these absolute paths as their SystemIDs when loading them into memory.
     361     * @see http://stackoverflow.com/questions/3699860/resolving-relative-paths-when-loading-xslt-files
     362     */
     363   
     364    @Override
     365        public Source resolve(String href, String base) throws TransformerException {
     366       
     367        //System.err.println("href: " + href); // e.g. href: layouts/main.xsl
     368        //System.err.println("base: " + base); // e.g for *toplevel* base: file:/path/to/gs3-svn/CL1.xslt
     369
     370
     371        // 1. for any xsl imported by the original stylesheet, try to work out its absolute path location
     372        // by concatenating the parent folder of the base stylesheet file with the href of the file it
     373        // imports (which is a path relative to the base file). This need not hold true for the very 1st
     374        // base stylesheet: it may be located somewhere entirely different from the greenstone XSLT
     375        // files it refers to. This is the case when running transforms on the commandline for testing
     376
     377        File importXSLfile = new File(href); // assume the xsl imported has an absolute path
     378
     379        if(!importXSLfile.isAbsolute()) { // imported xsl specifies a path relative to parent of base
     380        try {
     381            URI baseURI = new URI(base);
     382            importXSLfile = new File(new File(baseURI).getParent(), href);
     383
     384            if(!importXSLfile.exists()) { // if the imported file does not exist, it's not
     385            // relative to the base (the stylesheet file importing it). So look in the
     386            // interface's transform subfolder for the xsl file to be imported
     387            importXSLfile = new File(GSFile.interfaceStylesheetFile(GlobalProperties.getGSDL3Home(), interface_name, href));
     388            }
     389        } catch(URISyntaxException use) {
     390            importXSLfile = new File(href); // try
     391        }
     392        }
     393       
     394        String importXSLfilepath = ""; // for printing the expected file path on error
     395        try {
     396        // path of import XSL file after resolving any .. and . dir paths
     397        importXSLfilepath = importXSLfile.getCanonicalPath();
     398        } catch(IOException ioe) { // resort to using the absolute path
     399        importXSLfilepath = importXSLfile.getAbsolutePath();
     400        }
     401
     402        // 2. now we know where the XSL file being imported lives, so set the systemID to its
     403        // absolute path when loading it into a Source object
     404        URI systemID = importXSLfile.toURI();
     405        Source importXSLsrc = null;
     406        try {
     407        importXSLsrc = new StreamSource(new InputStreamReader(new FileInputStream(importXSLfile), "UTF-8"));       
     408        URL url = systemID.toURL();         
     409        importXSLsrc.setSystemId(url.toExternalForm());
     410        } catch (MalformedURLException mue) {
     411        logger.error("Warning: Unable to set systemID for imported stylesheet to " + systemID);
     412        logger.error("Got exception: " + mue.getMessage(), mue);
     413        } catch(FileNotFoundException fne) {
     414        logger.error("ERROR: importXSLsrc file does not exist " + importXSLfilepath);
     415        logger.error("\tError msg: " + fne.getMessage(), fne);
     416        } catch(UnsupportedEncodingException uee) {
     417        logger.error("ERROR: could not resolve relative path of import XSL file " + importXSLfilepath
     418                 + " because of encoding issues: " + uee.getMessage(), uee);
     419        }       
     420        return importXSLsrc;
     421        //return new StreamSource(this.getClass().getClassLoader().getResourceAsStream(href)); // won't work
     422    }   
     423    }
    266424
    267425    public Node transform(File stylesheet, File source)
Note: See TracChangeset for help on using the changeset viewer.