/** * '$RCSfile$' * Purpose: A Class that transforms an XML text document * into a another type using XSL * Copyright: 2000 Regents of the University of California and the * National Center for Ecological Analysis and Synthesis * Authors: Matt Jones * * '$Author$' * '$Date$' * '$Revision$' * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.ucsb.nceas.metacat; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.StringReader; import java.io.Writer; import java.sql.SQLException; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import javax.xml.transform.TransformerFactory; import javax.xml.transform.Templates; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamResult; import org.apache.log4j.Logger; import org.apache.xerces.parsers.DOMParser; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NodeList; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.apache.xpath.XPathAPI; import edu.ucsb.nceas.metacat.properties.PropertyService; import edu.ucsb.nceas.metacat.properties.SkinPropertyService; import edu.ucsb.nceas.metacat.util.SystemUtil; import edu.ucsb.nceas.utilities.PropertyNotFoundException; import edu.ucsb.nceas.utilities.SortedProperties; /** * A Class that transforms XML documents utitlizing XSL style sheets * Building a transformer from the .xsl is expensive, so Templates are used * as an intermediate construct to allow lightweight construction of Transformers. * (Transformers themselves are not thread safe, so can't be reused) */ public class DBTransform { private static Map TemplatesMap = new HashMap<>(); private static final TransformerFactory transformerFactory = TransformerFactory.newInstance(); private static boolean forceRebuild = false; /** * The method that manages the Templates Map instances that will be used to build * transformers from. * * @param xslSystemId - the URL for the stylesheet * @param forceRebuild - if true, forces reload of the stylesheet from the system, else use the existing one, if there * @return * @throws TransformerConfigurationException */ // NOTE: if changing this method, please note that the DBTransformTest class does not // directly test this class (because it was proving too difficult for me to properly // configure this class in a test environment (related to configurating PropertyService) // If you make changes to this method, you will need to duplicate those changes in the test class. protected static synchronized Transformer getTransformer(String xslSystemId) throws TransformerConfigurationException { //The admin page can reset the value of forceRebuild to true. if(forceRebuild) { TemplatesMap.clear(); forceRebuild = false;//after clearing the cache, we must reset the value to false in order to use the cache again. logMetacat.debug("DBTransform.getTransformer - clear the style sheet cache and will reload the style sheets from disk."); } if (!TemplatesMap.containsKey(xslSystemId) ) { logMetacat.debug("DBTransform.getTransformer - Load the style sheets from disk for the id " + xslSystemId); Templates templates = transformerFactory.newTemplates(new StreamSource(xslSystemId)); TemplatesMap.put(xslSystemId,templates); } else { logMetacat.debug("DBTransform.getTransformer - Load the style sheets from the cache for the id " + xslSystemId); } return TemplatesMap.get(xslSystemId).newTransformer(); } private String configDir = null; private String defaultStyle = null; private static Logger logMetacat = Logger.getLogger(DBTransform.class); private String httpServer = null; private String contextURL = null; private String servletURL = null; private String userManagementURL = null; private String internalContextURL = null; //used to locate the xsl files. /** * construct a DBTransform instance. * * Generally, one calls transformXMLDocument() after constructing the instance * * @param conn the database connection from which to lookup the public ids */ public DBTransform() throws IOException, SQLException, ClassNotFoundException, PropertyNotFoundException { configDir = SystemUtil.getStyleSkinsDir(); defaultStyle = PropertyService.getProperty("application.default-style"); httpServer = SystemUtil.getServerURL(); contextURL = SystemUtil.getContextURL(); servletURL = SystemUtil.getServletURL(); userManagementURL = PropertyService.getProperty("auth.userManagementUrl"); internalContextURL = SystemUtil.getInternalContextURL(); } /** * Transform an XML document using the stylesheet reference from the db * * @param doc the document to be transformed * @param sourcetype the document type of the source * @param targettype the target document type * @param qformat the name of the style set to use * @param pw the PrintWriter to which output is printed * @param params some parameters for eml2 transformation */ public void transformXMLDocument(String doc, String sourceType, String targetType, String qformat, Writer w, Hashtable param, String sessionid) { String xslSystemId = getStyleSystemId(qformat, sourceType, targetType); try { // Look up the stylesheet for this type combination if (xslSystemId != null) { // Create a stylesheet from the system id that was found doc = removeDOCTYPE(doc); StreamSource xml = new StreamSource(new StringReader(doc)); StreamResult result = new StreamResult(w); doTransform(xml, result, xslSystemId, param, qformat, sessionid); } else { // No stylesheet registered form this document type, so just return the // XML stream we were passed w.write(doc); } } catch (Exception e) { try { String msg = xslSystemId + ": Error transforming document in " + "DBTransform.transformXMLDocument: " + e.getMessage(); w.write(msg); w.flush(); logMetacat.error(msg, e); } catch (IOException e1) { logMetacat.error(e1.getMessage(), e1); } } } /** * Reads skin's config file if it exists, and populates Transformer paramaters * with its contents. * It then adds the parameters passed to it via Hashtable param to the Transformer. * It then calls the Transformer.transform method. */ protected void doTransform(StreamSource xml, StreamResult resultOutput, String xslSystemId, Hashtable param, String qformat, String sessionid) throws PropertyNotFoundException, TransformerException { SortedProperties skinOptions; Transformer transformer; String key, value; Enumeration en; Iterator> iterIt; Map.Entry entry; if (xslSystemId != null) { transformer = DBTransform.getTransformer(xslSystemId); // false means use the existing factory template transformer.setParameter("qformat", qformat); logMetacat.info("DBTransform.doTransform - qformat: " + qformat); skinOptions = SkinPropertyService.getProperties(qformat); if (skinOptions != null) { iterIt = skinOptions.getProperties().entrySet().iterator(); while (iterIt.hasNext()) { entry = iterIt.next(); key = entry.getKey(); value = entry.getValue(); //only include the plain properties if (key.indexOf('.') == -1) { transformer.setParameter(key, value); } } } if (sessionid != null && !sessionid.equals("null")) { transformer.setParameter("sessid", sessionid); } //set up the default params (can be overridden by the passed in params) String cgiPrefix = SystemUtil.getCGI_URL(); logMetacat.debug("DBTransform.doTransform - cgi-prefix: " + cgiPrefix); logMetacat.debug("DBTransform.doTransform - httpServer: " + httpServer); logMetacat.debug("DBTransform.doTransform - contextURL: " + contextURL); logMetacat.debug("DBTransform.doTransform - serletURL: " + servletURL); logMetacat.debug("DBTransform.doTransform - userManagementURL: " + userManagementURL); transformer.setParameter("cgi-prefix", cgiPrefix); transformer.setParameter("httpServer", httpServer); transformer.setParameter("contextURL", contextURL); transformer.setParameter("servletURL", servletURL); transformer.setParameter("userManagementURL", userManagementURL); // Set up parameter for transformation if ( param != null) { en = param.keys(); while (en.hasMoreElements()) { key = en.nextElement(); value = (param.get(key))[0]; logMetacat.info("DBTransform.doTransform - param: " + key + " -- " + value); transformer.setParameter(key, value); } } transformer.transform(xml, resultOutput); } }//doTransform /** * gets the content of a tag in a given xml file with the given path * @param f the file to parse * @param path the path to get the content from */ public static NodeList getPathContent(File f, String path) { if(f == null) { return null; } DOMParser parser = new DOMParser(); InputSource in; FileInputStream fs; try { fs = new FileInputStream(f); in = new InputSource(fs); } catch(FileNotFoundException fnf) { fnf.printStackTrace(); return null; } try { parser.parse(in); fs.close(); } catch(Exception e1) { System.err.println("File: " + f.getPath() + " : parse threw: " + e1.toString()); return null; } Document doc = parser.getDocument(); try { NodeList docNodeList = XPathAPI.selectNodeList(doc, path); return docNodeList; } catch(Exception se) { System.err.println("file: " + f.getPath() + " : parse threw: " + se.toString()); return null; } } /** * Lookup a stylesheet reference from the db catalog * * @param qformat the named style-set format * @param sourcetype the document type of the source * @param targettype the document type of the target */ private String getStyleSystemId(String qformat, String sourcetype, String targettype) { String systemId = null; if ((qformat == null) || (qformat.equals("html"))) { qformat = defaultStyle; } // Load the style-set map for this qformat into a DOM try { boolean breakflag = false; String filename = configDir + "/" + qformat + "/" + qformat + ".xml"; logMetacat.info("DBTransform.getStyleSystemId - Trying style-set file: " + filename); File f = new File(filename); NodeList nlDoctype = getPathContent(f, "/style-set/doctype"); NodeList nlDefault = getPathContent(f, "/style-set/default-style"); Node nDefault = nlDefault.item(0); systemId = nDefault.getFirstChild().getNodeValue(); //set the default for(int i=0; i-1) { int stoppos = in.indexOf(">", startpos + 8); ret = in.substring(0,startpos) + in.substring(stoppos+1,in.length()); } else { return in; } return ret; } /** * Set the field of forceRebuild * @param forceRebuild */ public static void setForceRebuild(boolean forceBuild) { forceRebuild = forceBuild; } // private void dbg(int position) { // System.err.println("Debug flag: " + position); // } }