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
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(">", ">").replaceAll("<", "<");
133 return xqlResult;
134
135
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
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
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
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
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
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
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
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
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
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
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
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 }