View Javadoc

1   package org.astrogrid.registry.common;
2   
3   import org.apache.commons.logging.Log;
4   import org.apache.commons.logging.LogFactory;
5   
6   import java.net.URL;
7   import java.io.InputStream;
8   
9   import org.w3c.dom.*;
10  import org.xml.sax.SAXException;
11  import java.io.*;
12  
13  import javax.xml.transform.*;
14  import javax.xml.transform.dom.*;
15  import javax.xml.transform.stream.*;
16  
17  import javax.xml.parsers.DocumentBuilder; 
18  import javax.xml.parsers.DocumentBuilderFactory; 
19  import javax.xml.parsers.ParserConfigurationException;
20  import org.astrogrid.util.DomHelper;
21  /*** 
22   * Class: XSLHelper
23   * Description: A small XSL helper class that simply loads up xsl stylesheets and tranforms the XML. 
24   * @todo fix exception handling
25   * @todo factor commonality of methods into a private helper method*/
26  public class XSLHelper {
27      /***
28       * Commons Logger for this class
29       */
30      private static final Log logger = LogFactory.getLog(XSLHelper.class);
31     
32     /***
33      * The default name of our database.
34      *
35      */
36     public static final String DEFAULT_DATABASE_XSL = "XSLDBProcess.xsl" ;
37  
38     /***
39      * The default resource name for our JDO config file.
40      *
41      */
42     public static final String DEFAULT_CASTOR_XML = "CastorXSLProcess.xsl" ;
43     
44     
45     /***
46      * Empty constructor -- should delete later, this is automatic.
47      *
48      */
49     public XSLHelper() {
50        
51     }
52     
53     /***
54      * Load a Stylesheet from the CLASSPath jars given a particular name.
55      * @param name A string name of the xsl stylesheet file name. Normally in the classpath or in a jar file.
56      * @return A InputStream to a xsl stylesheet.
57      */
58     private InputStream loadStyleSheet(String name) {
59         ClassLoader loader = this.getClass().getClassLoader();
60         return loader.getResourceAsStream(name);
61     }
62        
63     /***
64      * Load a particular known xsl stylesheet for processing xml from the db. Used mainly to go from Castor
65      * to better more undertandable XML.
66      * @deprecated - no longer in use, because Castor is no longer in use on the registry side. A client must do
67      * Castor itself.
68      * @return A InputStream to a xsl stylesheet. 
69      */
70     private InputStream loadDBXSL() {
71        ClassLoader loader = this.getClass().getClassLoader();
72        return loader.getResourceAsStream(DEFAULT_DATABASE_XSL);
73     }
74  
75     /***
76      * Load a particular known xsl stylesheet for processing xml for Castor.
77      * @deprecated - no longer in use, because Castor is no longer in use on the registry side. A client must do
78      * Castor itself.
79      * @return A InputStream to a xsl stylesheet. 
80      */   
81     private InputStream loadCastorXSL() {
82        ClassLoader loader = this.getClass().getClassLoader();
83        return loader.getResourceAsStream(DEFAULT_CASTOR_XML);
84     }
85     
86  
87     /***
88      * Method: transformADQLToXQL
89      * Description: Transforms a particular adql version into XQL (XQuery) for use with the eXist db. Other
90      * parameters are passed in to help build the XQuery such as the root node to be queried on.  And the namespaces
91      * that need to be declared for queries. Supports multiple versions of ADQL if a stylesheet is provided.
92      * @param doc ADQL Select/Where XML Node to be processed through a XSL stylesheet
93      * @param versionNumber Version number of ADQL used as part of the adql stylesheet name.
94      * @param rootNode Actually a String of the root Resource (with prefix) to be queries on.
95      * @param namespaceDeclare A long string of all the 'declare namespace' for the XQL.
96      * @return A XQL (XQuery) String to be used for querying on the XML database.
97      */
98     public String transformADQLToXQL(Node doc, String versionNumber,String rootNode, String namespaceDeclare) {
99        
100       Source xmlSource = new DOMSource(doc);
101       Document resultDoc = null;
102       
103       ClassLoader loader = this.getClass().getClassLoader();
104       InputStream is = null;
105       is = loader.getResourceAsStream("ADQLToXQL-" + versionNumber + ".xsl");
106     logger
107             .info("transformADQLToXQL(Node, String) - the file resource = ADQLToXQL-"
108                     + versionNumber + ".xsl");
109       Source xslSource = new StreamSource(is);
110       DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
111       
112       try {
113          builderFactory.setNamespaceAware(true);
114          DocumentBuilder builder = builderFactory.newDocumentBuilder();
115          resultDoc = builder.newDocument();
116          
117          TransformerFactory transformerFactory = TransformerFactory.newInstance();
118          //DOMResult result = new DOMResult(resultDoc);
119          StringWriter sw = new StringWriter();
120          StreamResult result = new StreamResult(sw);
121          
122          Transformer transformer = transformerFactory.newTransformer(xslSource);
123          transformer.setParameter("resource_elem", rootNode);
124          transformer.setParameter("declare_elems", namespaceDeclare);         
125          transformer.transform(xmlSource,result);
126          String xqlResult = sw.toString();
127          if (xqlResult.startsWith("<?")) {
128             xqlResult = xqlResult.substring(xqlResult.indexOf("?>")+2);
129          }
130          
131          
132          xqlResult = xqlResult.replaceAll("&gt;", ">").replaceAll("&lt;", "<");
133          return xqlResult;
134          //System.out.println("the resultwriter transform = " + sw.toString());
135          //System.out.println("The result of adql to xql = " + DomHelper.DocumentToString(resultDoc));
136       }catch(ParserConfigurationException pce) {
137         logger.error("transformADQLToXQL(Node, String)", pce);
138       }catch(TransformerConfigurationException tce) {
139         logger.error("transformADQLToXQL(Node, String)", tce);
140       }catch(TransformerException te) {
141         logger.error("transformADQLToXQL(Node, String)", te);
142       }
143       //@todo never return null on error
144       return null;
145    }
146    
147    /***
148     * Method: transformResourceToResource
149     * Description: A nice helper method normally used for clients, to allow of tranforming one XML conforming to a 
150     * Registry schema back to another XML conforming to a different Registry schema version. ex: 0.10 to 0.9 is the
151     * most commonly used for the portal.  Handy use if a client cannot be easily upgradable to a new version of the
152     * registry. The source and target versions are passed in for forming the xsl stylesheet name.  Meaning multiple
153     * version tranformations can be supported (version numbers come from the main vr namespace).
154     * @param doc XML Document root node of the sourceVersion.
155     * @param sourceVersion version number of the source of the XML. ex: 0.10
156     * @param targetVersion version number of the result/target for the XML ex: 0.9
157     * @return XML of the converted schema.
158     */
159    public Document transformResourceToResource(Node doc, String sourceVersion, String targetVersion) {
160        sourceVersion = sourceVersion.replace('.','_');
161        targetVersion = targetVersion.replace('.','_');
162        
163        String fileName = "VOResource-v" + sourceVersion + "-v" + targetVersion + ".xsl";
164 
165        
166        Source xmlSource = new DOMSource(doc);
167        Document resultDoc = null;
168        
169        ClassLoader loader = this.getClass().getClassLoader();
170        InputStream is = null;
171        is = loader.getResourceAsStream(fileName);
172        Source xslSource = new StreamSource(is);
173              
174        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
175        try {
176           builderFactory.setNamespaceAware(true);
177           DocumentBuilder builder = builderFactory.newDocumentBuilder();
178           resultDoc = builder.newDocument();
179           //DocumentFragment df = resultDoc.createDocumentFragment();
180           TransformerFactory transformerFactory = TransformerFactory.newInstance();
181           
182           DOMResult result = new DOMResult(resultDoc);
183           Transformer transformer = transformerFactory.newTransformer(xslSource);
184           
185           transformer.transform(xmlSource,result);
186           //System.out.println("the resultwriter transform = " + sw.toString());
187        }catch(ParserConfigurationException pce) {
188          logger.error("transformToOAI(Node, String)", pce);
189        }catch(TransformerConfigurationException tce) {
190          logger.error("transformToOAI(Node, String)", tce);
191        }catch(TransformerException te) {
192          logger.error("transformToOAI(Node, String)", te);
193        }
194        //@todo never return null on error.
195        return resultDoc;
196     }
197    
198    /***
199     * Method: transformToOAI
200     * Description: Transforms XML from a given query in the XML database to a OAI GetRecord XML. Used mainly by 
201     * thrid party tool which shows various OAI xml verbs in the correct XML format. Handles multiple versions of
202     * XML from the Registry depending on the version number from the main vr namespace.
203     * @param doc XML Root Node of a document from a  query of the given database.
204     * @param versionNumber version number from the vr namespace, which is the main Resource namespace defined.
205     * @return XML Document object of a OAI GetRecord XML.
206     */
207    public Document transformToOAI(Node doc, String versionNumber) {
208       String fileName = "Resourcev" + versionNumber + "-OAI.xsl";
209 
210       Source xmlSource = new DOMSource(doc);
211       Document resultDoc = null;
212       
213       ClassLoader loader = this.getClass().getClassLoader();
214       InputStream is = null;
215       is = loader.getResourceAsStream(fileName);
216       Source xslSource = new StreamSource(is);
217             
218       DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
219       try {
220          builderFactory.setNamespaceAware(true);
221          DocumentBuilder builder = builderFactory.newDocumentBuilder();
222          resultDoc = builder.newDocument();
223          //DocumentFragment df = resultDoc.createDocumentFragment();
224          TransformerFactory transformerFactory = TransformerFactory.newInstance();
225          
226          DOMResult result = new DOMResult(resultDoc);
227          Transformer transformer = transformerFactory.newTransformer(xslSource);
228          
229          transformer.transform(xmlSource,result);
230          //System.out.println("the resultwriter transform = " + sw.toString());
231       }catch(ParserConfigurationException pce) {
232         logger.error("transformToOAI(Node, String)", pce);
233       }catch(TransformerConfigurationException tce) {
234         logger.error("transformToOAI(Node, String)", tce);
235       }catch(TransformerException te) {
236         logger.error("transformToOAI(Node, String)", te);
237       }
238       //@todo never return null on error.
239       return resultDoc;
240    }
241    
242    /***
243     * Method: transformExistResult
244     * Description: When querying eXist XML database (at the moment on Rest style) it will put a known <exist:Result> root
245     * element around the results of the query (so the xml will be valid xml).  This transforms that XML to simply get rid of
246     * that root element and put a different root element around.  Also for convenience a responseElement parameter is used
247     * for Web Service methods to wrap the Results with another Element for use in the Soap:body on the response.
248     * @param doc XML Root node results from a query of the XML database.
249     * @param versionNumber version number from main vr namespace.
250     * @param responseElement An optional string to wrap around another element for web service methods.
251     * @return XML document to be returned.
252     */
253    public Document transformExistResult(Node doc, String versionNumber, String responseElement) {
254       
255       Source xmlSource = new DOMSource(doc);
256       Document resultDoc = null;
257       
258       ClassLoader loader = this.getClass().getClassLoader();
259       InputStream is = null;
260 
261       is = loader.getResourceAsStream("ExistRegistryResult" + versionNumber + ".xsl");
262       
263       Source xslSource = new StreamSource(is);
264       
265       DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
266       
267       try {
268          builderFactory.setNamespaceAware(true);
269          DocumentBuilder builder = builderFactory.newDocumentBuilder();
270          resultDoc = builder.newDocument();
271          //DocumentFragment df = resultDoc.createDocumentFragment();
272          TransformerFactory transformerFactory = TransformerFactory.newInstance();
273          
274          DOMResult result = new DOMResult(resultDoc);
275          Transformer transformer = transformerFactory.newTransformer(xslSource);
276          
277          transformer.transform(xmlSource,result);
278          if(responseElement != null && responseElement.trim().length() > 0) {
279              Element currentRoot = resultDoc.getDocumentElement();
280              Element root = resultDoc.createElement(responseElement);
281              root.appendChild(currentRoot);
282              resultDoc.appendChild(root);
283          }
284       }catch(ParserConfigurationException pce) {
285         logger.error("transformExistResult(Node, String, String)", pce);
286       }catch(TransformerConfigurationException tce) {
287         logger.error("transformExistResult(Node, String, String)", tce);
288       }catch(TransformerException te) {
289         logger.error("transformExistResult(Node, String, String)", te);
290       }
291       return resultDoc;
292    }
293    
294    /***
295     * Method: transformUpdate
296     * Description: Allows the registry to transform the XML before going into the database. It is multiple versioned
297     * based on the vr namespace and can be applied to regular updates (from Astrogrid) or on harvests from other
298     * registries. 
299     * This usefullness is best described in some examples:
300     * ex 1: 0.9 - used substitution groups hence same xml could be expressed in 2 or 3 ways meaning xql queries very
301     * difficult, so a xsl stylesshet was applied to make the xml the same.
302     * ex 2: 0.10 - no more subtituion groups, but the way the main Resource element was described any number of XML name 
303     * with any number of namespaces could be of type Resouce.  So XSL is used to again put it in the way that is 
304     * consistent in the db.
305     * @param doc XML to be transformed.
306     * @param versionNumber version number of main vr namespace.
307     * @param harvestUpdate is this used on a harvest, if so use a different xsl stylesheet name.
308     * @return XML to be updated into the database.
309     */
310    public Document transformUpdate(Node doc,String versionNumber,boolean harvestUpdate) {
311        
312        Source xmlSource = new DOMSource(doc);
313        Document resultDoc = null;
314        String harvestName = "";
315        if(harvestUpdate)
316            harvestName = "Harvest_";
317        String styleSheetName = "UpdateProcess_" + harvestName + versionNumber + ".xsl";
318        logger.info("transformUpdate(Node, String) - the stylesheet name = "
319             + styleSheetName);
320        Source xslSource = new StreamSource(loadStyleSheet(styleSheetName));
321        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
322        try {
323           builderFactory.setNamespaceAware(true);
324           DocumentBuilder builder = builderFactory.newDocumentBuilder();
325           resultDoc = builder.newDocument();
326           //DocumentFragment df = resultDoc.createDocumentFragment();
327           TransformerFactory transformerFactory = TransformerFactory.newInstance();
328           
329           DOMResult result = new DOMResult(resultDoc);
330           Transformer transformer = transformerFactory.newTransformer(xslSource);
331           
332           transformer.transform(xmlSource,result);
333           
334        }catch(ParserConfigurationException pce) {
335         logger.error("transformUpdate(Node, String)", pce);
336        }catch(TransformerConfigurationException tce) {
337         logger.error("transformUpdate(Node, String)", tce);
338        }catch(TransformerException te) {
339         logger.error("transformUpdate(Node, String)", te);
340        }
341     logger
342             .info("transformUpdate(Node, String) - THIS IS AFTER THE TRANSFORMUPDATE");
343        DomHelper.DocumentToStream(resultDoc,System.out);
344        return resultDoc;
345     }   
346    
347    
348    /***
349     * Used for Castor, deprecating and hope to delete soon. Clients should use Castor themselves now, not let the
350     * registry do it.
351     * @deprecated No longer in use
352     * @param doc
353     * @return
354     */
355    public Document transformDatabaseProcess(Node doc) {
356       
357       Source xmlSource = new DOMSource(doc);
358       Document resultDoc = null;
359       
360       Source xslSource = new StreamSource(loadDBXSL());
361       
362       DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
363       
364       try {
365          builderFactory.setNamespaceAware(true);
366          DocumentBuilder builder = builderFactory.newDocumentBuilder();
367          resultDoc = builder.newDocument();
368          //DocumentFragment df = resultDoc.createDocumentFragment();
369          TransformerFactory transformerFactory = TransformerFactory.newInstance();
370          
371          DOMResult result = new DOMResult(resultDoc);
372          Transformer transformer = transformerFactory.newTransformer(xslSource);
373          
374          transformer.transform(xmlSource,result);
375          
376       }catch(ParserConfigurationException pce) {
377         logger.error("transformDatabaseProcess(Node)", pce);
378       }catch(TransformerConfigurationException tce) {
379         logger.error("transformDatabaseProcess(Node)", tce);
380       }catch(TransformerException te) {
381         logger.error("transformDatabaseProcess(Node)", te);
382       }
383       return resultDoc;
384       
385    }
386 
387    /***
388     * Used for Castor, deprecating and hope to delete soon. Clients should use Castor themselves now, not let the
389     * registry do it.
390     * @deprecated No longer in use
391     * @param doc
392     * @return
393     */
394    public Document transformCastorProcess(Node doc) {
395       
396       Source xmlSource = new DOMSource(doc);
397       Document resultDoc = null;
398       
399       Source xslSource = new StreamSource(loadCastorXSL());
400       
401       DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
402       
403       try {
404          builderFactory.setNamespaceAware(true);
405          DocumentBuilder builder = builderFactory.newDocumentBuilder();
406          resultDoc = builder.newDocument();
407          //DocumentFragment df = resultDoc.createDocumentFragment();
408          TransformerFactory transformerFactory = TransformerFactory.newInstance();
409          
410          DOMResult result = new DOMResult(resultDoc);
411          Transformer transformer = transformerFactory.newTransformer(xslSource);
412          
413          transformer.transform(xmlSource,result);
414          
415       }catch(ParserConfigurationException pce) {
416         logger.error("transformCastorProcess(Node)", pce);
417       }catch(TransformerConfigurationException tce) {
418         logger.error("transformCastorProcess(Node)", tce);
419       }catch(TransformerException te) {
420         logger.error("transformCastorProcess(Node)", te);
421       }
422       return resultDoc;
423    }
424    
425    
426    
427 }