Changeset 26494 for main

Show
Ignore:
Timestamp:
21.11.2012 16:13:59 (7 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 modified

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)