source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/core/TransformingReceptionist.java@ 25423

Last change on this file since 25423 was 25423, checked in by ak19, 12 years ago

Better error reporting during transformations. Extended the TransformErrorListener to store the XSLT upon construction and print it out (besides the exception message) upon exception.

  • Property svn:keywords set to Author Date Id Revision
File size: 35.4 KB
Line 
1package org.greenstone.gsdl3.core;
2
3import org.greenstone.util.GlobalProperties;
4import org.greenstone.gsdl3.util.*;
5import org.greenstone.gsdl3.action.*;
6// XML classes
7import org.w3c.dom.Node;
8import org.w3c.dom.NodeList;
9import org.w3c.dom.Comment;
10import org.w3c.dom.Text;
11import org.w3c.dom.Document;
12import org.w3c.dom.Element;
13import org.xml.sax.InputSource;
14import org.w3c.dom.NamedNodeMap;
15
16// other java classes
17import java.io.ByteArrayInputStream;
18import java.io.File;
19import java.io.StringReader;
20import java.io.StringWriter;
21import java.io.FileReader;
22import java.io.FileNotFoundException;
23import java.util.ArrayList;
24import java.util.HashMap;
25import java.util.Enumeration;
26
27import javax.xml.parsers.*;
28import javax.xml.transform.*;
29import javax.xml.transform.dom.*;
30import javax.xml.transform.stream.*;
31import org.apache.log4j.*;
32import org.apache.tools.zip.ExtraFieldUtils;
33import org.apache.xerces.dom.*;
34import org.apache.xerces.parsers.DOMParser;
35
36import org.apache.commons.lang3.StringUtils;
37
38/**
39 * A receptionist that uses xslt to transform the page_data before returning it.
40 * . Receives requests consisting of an xml representation of cgi args, and
41 * returns the page of data - in html by default. The requests are processed by
42 * the appropriate action class
43 *
44 * @see Action
45 */
46public class TransformingReceptionist extends Receptionist
47{
48
49 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.core.TransformingReceptionist.class.getName());
50
51 /** The preprocess.xsl file is in a fixed location */
52 static final String preprocess_xsl_filename = GlobalProperties.getGSDL3Home() + File.separatorChar + "ui" + File.separatorChar + "xslt" + File.separatorChar + "preProcess.xsl";
53
54 /** the list of xslt to use for actions */
55 protected HashMap xslt_map = null;
56
57 /** a transformer class to transform xml using xslt */
58 protected XMLTransformer transformer = null;
59
60 protected TransformerFactory transformerFactory = null;
61 protected DOMParser parser = null;
62
63 public TransformingReceptionist()
64 {
65 super();
66 this.xslt_map = new HashMap();
67 this.transformer = new XMLTransformer();
68 try
69 {
70 transformerFactory = org.apache.xalan.processor.TransformerFactoryImpl.newInstance();
71 this.converter = new XMLConverter();
72 //transformerFactory.setURIResolver(new MyUriResolver()) ;
73
74 parser = new DOMParser();
75 parser.setFeature("http://xml.org/sax/features/validation", false);
76 // don't try and load external DTD - no need if we are not validating, and may cause connection errors if a proxy is not set up.
77 parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
78 // a performance test showed that having this on lead to increased
79 // memory use for small-medium docs, and not much gain for large
80 // docs.
81 // http://www.sosnoski.com/opensrc/xmlbench/conclusions.html
82 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false);
83 parser.setFeature("http://apache.org/xml/features/continue-after-fatal-error", true);
84 // setting a handler for when fatal errors, errors or warnings happen during xml parsing
85 // call XMLConverter's getParseErrorMessage() to get the errorstring that can be rendered as web page
86 this.parser.setErrorHandler(new XMLConverter.ParseErrorHandler());
87 }
88 catch (Exception e)
89 {
90 e.printStackTrace();
91 }
92
93 }
94
95 /** configures the receptionist - overwrite this to set up the xslt map */
96 public boolean configure()
97 {
98
99 if (this.config_params == null)
100 {
101 logger.error(" config variables must be set before calling configure");
102 return false;
103 }
104 if (this.mr == null)
105 {
106 logger.error(" message router must be set before calling configure");
107 return false;
108 }
109
110 // find the config file containing a list of actions
111 File interface_config_file = new File(GSFile.interfaceConfigFile(GSFile.interfaceHome(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.INTERFACE_NAME))));
112 if (!interface_config_file.exists())
113 {
114 logger.error(" interface config file: " + interface_config_file.getPath() + " not found!");
115 return false;
116 }
117 Document config_doc = this.converter.getDOM(interface_config_file, "utf-8");
118 if (config_doc == null)
119 {
120 logger.error(" could not parse interface config file: " + interface_config_file.getPath());
121 return false;
122 }
123 Element config_elem = config_doc.getDocumentElement();
124 String base_interface = config_elem.getAttribute("baseInterface");
125 setUpBaseInterface(base_interface);
126 setUpInterfaceOptions(config_elem);
127
128 Element action_list = (Element) GSXML.getChildByTagName(config_elem, GSXML.ACTION_ELEM + GSXML.LIST_MODIFIER);
129 NodeList actions = action_list.getElementsByTagName(GSXML.ACTION_ELEM);
130
131 for (int i = 0; i < actions.getLength(); i++)
132 {
133 Element action = (Element) actions.item(i);
134 String class_name = action.getAttribute("class");
135 String action_name = action.getAttribute("name");
136 Action ac = null;
137 try
138 {
139 ac = (Action) Class.forName("org.greenstone.gsdl3.action." + class_name).newInstance();
140 }
141 catch (Exception e)
142 {
143 logger.error(" couldn't load in action " + class_name);
144 e.printStackTrace();
145 continue;
146 }
147 ac.setConfigParams(this.config_params);
148 ac.setMessageRouter(this.mr);
149 ac.configure();
150 ac.addActionParameters(this.params);
151 this.action_map.put(action_name, ac);
152
153 // now do the xslt map
154 String xslt = action.getAttribute("xslt");
155 if (!xslt.equals(""))
156 {
157 this.xslt_map.put(action_name, xslt);
158 }
159 NodeList subactions = action.getElementsByTagName(GSXML.SUBACTION_ELEM);
160 for (int j = 0; j < subactions.getLength(); j++)
161 {
162 Element subaction = (Element) subactions.item(j);
163 String subname = subaction.getAttribute(GSXML.NAME_ATT);
164 String subxslt = subaction.getAttribute("xslt");
165
166 String map_key = action_name + ":" + subname;
167 logger.debug("adding in to xslt map, " + map_key + "->" + subxslt);
168 this.xslt_map.put(map_key, subxslt);
169 }
170 }
171 Element lang_list = (Element) GSXML.getChildByTagName(config_elem, "languageList");
172 if (lang_list == null)
173 {
174 logger.error(" didn't find a language list in the config file!!");
175 }
176 else
177 {
178 this.language_list = (Element) this.doc.importNode(lang_list, true);
179 }
180
181 return true;
182 }
183
184 protected Node postProcessPage(Element page)
185 {
186 // might need to add some data to the page
187 addExtraInfo(page);
188 // transform the page using xslt
189 Node transformed_page = transformPage(page);
190
191 // if the user has specified they want only a part of the full page then subdivide it
192 boolean subdivide = false;
193 String excerptID = null;
194 String excerptTag = null;
195 Element request = (Element) GSXML.getChildByTagName(page, GSXML.PAGE_REQUEST_ELEM);
196 Element cgi_param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
197 if (cgi_param_list != null)
198 {
199 HashMap params = GSXML.extractParams(cgi_param_list, false);
200 if ((excerptID = (String) params.get(GSParams.EXCERPT_ID)) != null)
201 {
202 subdivide = true;
203 }
204 if ((excerptTag = (String) params.get(GSParams.EXCERPT_TAG)) != null)
205 {
206 subdivide = true;
207 }
208 }
209
210 if (subdivide)
211 {
212 Node subdivided_page = subdivide(transformed_page, excerptID, excerptTag);
213 if (subdivided_page != null)
214 {
215 return subdivided_page;
216 }
217 }
218
219 return transformed_page;
220 }
221
222 protected Node subdivide(Node transformed_page, String excerptID, String excerptTag)
223 {
224 if (excerptID != null)
225 {
226 Node selectedElement = getNodeByIdRecursive(transformed_page, excerptID);
227 modifyNodesByTagRecursive(selectedElement, "a");
228 return selectedElement;
229 }
230 else if (excerptTag != null)
231 {
232 /*
233 * // define a list
234 *
235 * Node selectedElement =
236 * modifyNodesByTagRecursive(transformed_page, excerptTag);
237 */
238
239 Node selectedElement = getNodeByTagRecursive(transformed_page, excerptTag);
240 return selectedElement;
241
242 }
243 return transformed_page;
244 }
245
246 protected Node getNodeByIdRecursive(Node parent, String id)
247 {
248 if (parent.hasAttributes() && ((Element) parent).getAttribute("id").equals(id))
249 {
250 return parent;
251 }
252
253 NodeList children = parent.getChildNodes();
254 for (int i = 0; i < children.getLength(); i++)
255 {
256 Node result = null;
257 if ((result = getNodeByIdRecursive(children.item(i), id)) != null)
258 {
259 return result;
260 }
261 }
262 return null;
263 }
264
265 protected Node getNodeByTagRecursive(Node parent, String tag)
266 {
267 if (parent.getNodeType() == Node.ELEMENT_NODE && ((Element) parent).getTagName().equals(tag))
268 {
269 return parent;
270 }
271
272 NodeList children = parent.getChildNodes();
273 for (int i = 0; i < children.getLength(); i++)
274 {
275 Node result = null;
276 if ((result = getNodeByTagRecursive(children.item(i), tag)) != null)
277 {
278 return result;
279 }
280 }
281 return null;
282 }
283
284 protected Node modifyNodesByTagRecursive(Node parent, String tag)
285 {
286 if (parent == null || (parent.getNodeType() == Node.ELEMENT_NODE && ((Element) parent).getTagName().equals(tag)))
287 {
288 return parent;
289 }
290
291 NodeList children = parent.getChildNodes();
292 for (int i = 0; i < children.getLength(); i++)
293 {
294 Node result = null;
295 if ((result = modifyNodesByTagRecursive(children.item(i), tag)) != null)
296 {
297 //return result;
298 //logger.error("Modify node value = "+result.getNodeValue()); //NamedItem("href"););
299 //logger.error("BEFORE Modify node attribute = " + result.getAttributes().getNamedItem("href").getNodeValue());
300 //String url = result.getAttributes().getNamedItem("href").getNodeValue();
301 //url = url + "&excerptid=results";
302 //result.getAttributes().getNamedItem("href").setNodeValue(url);
303 //logger.error("AFTER Modify node attribute = " + result.getAttributes().getNamedItem("href").getNodeValue());
304 }
305 }
306 return null;
307 }
308
309 /**
310 * overwrite this to add any extra info that might be needed in the page
311 * before transformation
312 */
313 protected void addExtraInfo(Element page)
314 {
315 }
316
317 /**
318 * transform the page using xslt we need to get any format element out of
319 * the page and add it to the xslt before transforming
320 */
321 protected Node transformPage(Element page)
322 {
323 boolean allowsClientXSLT = (Boolean) config_params.get(GSConstants.ALLOW_CLIENT_SIDE_XSLT);
324 //System.out.println("Client side transforms allowed? " + allowsClientXSLT);
325
326 String currentInterface = (String) config_params.get(GSConstants.INTERFACE_NAME);
327
328 Element request = (Element) GSXML.getChildByTagName(page, GSXML.PAGE_REQUEST_ELEM);
329 String output = request.getAttribute(GSXML.OUTPUT_ATT);
330
331 //System.out.println("Current output mode is: " + output + ", current interface name is: " + currentInterface);
332
333 if (allowsClientXSLT)
334 {
335 if (!currentInterface.endsWith(GSConstants.CLIENT_SIDE_XSLT_INTERFACE_SUFFIX) && output.equals("html"))
336 {
337 System.out.println("output is html and we are not currently using a client side version, switching");
338 // Switch the interface
339 config_params.put(GSConstants.INTERFACE_NAME, currentInterface.concat(GSConstants.CLIENT_SIDE_XSLT_INTERFACE_SUFFIX));
340 }
341 else if ((currentInterface.endsWith(GSConstants.CLIENT_SIDE_XSLT_INTERFACE_SUFFIX) && !output.equals("html")) || output.equals("server"))
342 {
343 // The reverse needs to happen too
344 config_params.put(GSConstants.INTERFACE_NAME, currentInterface.substring(0, currentInterface.length() - GSConstants.CLIENT_SIDE_XSLT_INTERFACE_SUFFIX.length()));
345 }
346 }
347 else if (currentInterface.endsWith(GSConstants.CLIENT_SIDE_XSLT_INTERFACE_SUFFIX))
348 {
349 config_params.put(GSConstants.INTERFACE_NAME, currentInterface.substring(0, currentInterface.length() - GSConstants.CLIENT_SIDE_XSLT_INTERFACE_SUFFIX.length()));
350 }
351
352 // DocType defaults in case the skin doesn't have an "xsl:output" element
353 String qualifiedName = "html";
354 String publicID = "-//W3C//DTD HTML 4.01 Transitional//EN";
355 String systemID = "http://www.w3.org/TR/html4/loose.dtd";
356
357 // We need to create an empty document with a predefined DocType,
358 // that will then be used for the transformation by the DOMResult
359 Document docWithDoctype = converter.newDOM(qualifiedName, publicID, systemID);
360
361 if (output.equals("xsltclient"))
362 {
363 // If you're just getting the client-side transform page, why bother with the rest of this?
364 Element html = docWithDoctype.createElement("html");
365 Element img = docWithDoctype.createElement("img");
366 img.setAttribute("src", "interfaces/default/images/loading.gif"); // Make it dynamic
367 img.setAttribute("alt", "Please wait...");
368 Text title_text = docWithDoctype.createTextNode("Please wait..."); // Make this language dependent
369 Element head = docWithDoctype.createElement("head");
370 Element title = docWithDoctype.createElement("title");
371 title.appendChild(title_text);
372 Element body = docWithDoctype.createElement("body");
373 Element script = docWithDoctype.createElement("script");
374 Element jquery = docWithDoctype.createElement("script");
375 jquery.setAttribute("src", "jquery.js");
376 jquery.setAttribute("type", "text/javascript");
377 Comment jquery_comment = docWithDoctype.createComment("jQuery");
378 Comment script_comment = docWithDoctype.createComment("Filler for browser");
379 script.setAttribute("src", "test.js");
380 script.setAttribute("type", "text/javascript");
381 Element pagevar = docWithDoctype.createElement("script");
382 Element style = docWithDoctype.createElement("style");
383 style.setAttribute("type", "text/css");
384 Text style_text = docWithDoctype.createTextNode("body { text-align: center; padding: 50px; font: 14pt Arial, sans-serif; font-weight: bold; }");
385 pagevar.setAttribute("type", "text/javascript");
386 Text page_var_text = docWithDoctype.createTextNode("var placeholder = true;");
387
388 html.appendChild(head);
389 head.appendChild(title);
390 head.appendChild(style);
391 style.appendChild(style_text);
392 html.appendChild(body);
393 head.appendChild(pagevar);
394 head.appendChild(jquery);
395 head.appendChild(script);
396 pagevar.appendChild(page_var_text);
397 jquery.appendChild(jquery_comment);
398 script.appendChild(script_comment);
399 body.appendChild(img);
400 docWithDoctype.appendChild(html);
401
402 return (Node) docWithDoctype;
403 }
404
405 // Passing in the pretty string here means it needs to be generated even when not debugging; so use custom function to return blank when debug is off
406 logger.debug("page before transforming:");
407 logger.debug(this.converter.getPrettyStringLogger(page, logger));
408
409 String action = request.getAttribute(GSXML.ACTION_ATT);
410 String subaction = request.getAttribute(GSXML.SUBACTION_ATT);
411
412 // we should choose how to transform the data based on output, eg diff
413 // choice for html, and wml??
414 // for now, if output=xml, we don't transform the page, we just return
415 // the page xml
416 Document theXML = null;
417
418 if (output.equals("xml") || output.equals("clientside"))
419 {
420 // Append some bits and pieces first...
421 theXML = converter.newDOM();
422 // Import into new document first!
423 Node newPage = theXML.importNode(page, true);
424 theXML.appendChild(newPage);
425 Element root = theXML.createElement("xsltparams");
426 newPage.appendChild(root);
427
428 Element libname = theXML.createElement("param");
429 libname.setAttribute("name", "library_name");
430 Text libnametext = theXML.createTextNode((String) config_params.get(GSConstants.LIBRARY_NAME));
431 libname.appendChild(libnametext);
432
433 Element intname = theXML.createElement("param");
434 intname.setAttribute("name", "interface_name");
435 Text intnametext = theXML.createTextNode((String) config_params.get(GSConstants.INTERFACE_NAME));
436 intname.appendChild(intnametext);
437
438 Element siteName = theXML.createElement("param");
439 siteName.setAttribute("name", "site_name");
440 Text siteNameText = theXML.createTextNode((String) config_params.get(GSConstants.SITE_NAME));
441 siteName.appendChild(siteNameText);
442
443 Element filepath = theXML.createElement("param");
444 filepath.setAttribute("name", "filepath");
445 Text filepathtext = theXML.createTextNode(GlobalProperties.getGSDL3Home());
446 filepath.appendChild(filepathtext);
447
448 root.appendChild(libname);
449 root.appendChild(intname);
450 root.appendChild(siteName);
451 root.appendChild(filepath);
452
453 if (output.equals("xml"))
454 return theXML.getDocumentElement();
455 }
456
457 Element cgi_param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
458 String collection = "";
459 String inlineTemplate = "";
460 if (cgi_param_list != null)
461 {
462 // Don't waste time getting all the parameters
463 HashMap params = GSXML.extractParams(cgi_param_list, false);
464 collection = (String) params.get(GSParams.COLLECTION);
465 if (collection == null)
466 {
467 collection = "";
468 }
469
470 inlineTemplate = (String) params.get(GSParams.INLINE_TEMPLATE);
471 }
472
473 Document style_doc = getXSLTDocument(action, subaction, collection);
474 if (style_doc == null)
475 {
476 String errorPage = this.converter.getParseErrorMessage();
477 if (errorPage != null)
478 {
479 return XMLTransformer.constructErrorXHTMLPage("Cannot parse the xslt file\n" + errorPage);
480 }
481 return page;
482 }
483
484 // put the page into a document - this is necessary for xslt to get
485 // the paths right if you have paths relative to the document root
486 // eg /page.
487 Document doc = this.converter.newDOM();
488 doc.appendChild(doc.importNode(page, true));
489 Element page_response = (Element) GSXML.getChildByTagName(page, GSXML.PAGE_RESPONSE_ELEM);
490 Element format_elem = (Element) GSXML.getChildByTagName(page_response, GSXML.FORMAT_ELEM);
491 if (output.equals("formatelem"))
492 {
493 return format_elem;
494 }
495 if (format_elem != null)
496 {
497 //page_response.removeChild(format_elem);
498 logger.debug("format elem=" + this.converter.getPrettyStringLogger(format_elem, logger));
499 // need to transform the format info
500 String configStylesheet_file = GSFile.stylesheetFile(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces, "config_format.xsl");
501 Document configStylesheet_doc = this.converter.getDOM(new File(configStylesheet_file));
502
503 if (configStylesheet_doc != null)
504 {
505 Document format_doc = this.converter.newDOM();
506 format_doc.appendChild(format_doc.importNode(format_elem, true));
507 Node result = this.transformer.transform(configStylesheet_doc, format_doc, config_params); // Needs addressing <-
508
509 // Since we started creating documents with DocTypes, we can end up with
510 // Document objects here. But we will be working with an Element instead,
511 // so we grab the DocumentElement() of the Document object in such a case.
512 Element new_format;
513 if (result.getNodeType() == Node.DOCUMENT_NODE)
514 {
515 new_format = ((Document) result).getDocumentElement();
516 }
517 else
518 {
519 new_format = (Element) result;
520 }
521 logger.debug("new format elem=" + this.converter.getPrettyStringLogger(new_format, logger));
522 if (output.equals("newformat"))
523 {
524 return new_format;
525 }
526
527 // add extracted GSF statements in to the main stylesheet
528 GSXSLT.mergeStylesheets(style_doc, new_format);
529 //System.out.println("added extracted GSF statements into the main stylesheet") ;
530
531 // add extracted GSF statements in to the debug test stylesheet
532 //GSXSLT.mergeStylesheets(oldStyle_doc, new_format);
533 }
534 else
535 {
536 logger.error(" couldn't parse the config_format stylesheet, adding the format info as is");
537 GSXSLT.mergeStylesheets(style_doc, format_elem);
538 //GSXSLT.mergeStylesheets(oldStyle_doc, format_elem);
539 }
540 logger.debug("the converted stylesheet is:");
541 logger.debug(this.converter.getPrettyStringLogger(style_doc.getDocumentElement(), logger));
542 }
543
544 //for debug purposes only
545 Document oldStyle_doc = style_doc;
546
547 Document preprocessingXsl;
548 try
549 {
550 preprocessingXsl = getDoc(preprocess_xsl_filename);
551 String errMsg = ((XMLConverter.ParseErrorHandler) parser.getErrorHandler()).getErrorMessage();
552 if (errMsg != null)
553 {
554 return XMLTransformer.constructErrorXHTMLPage("error loading preprocess xslt file: " + preprocess_xsl_filename + "\n" + errMsg);
555 }
556 }
557 catch (java.io.FileNotFoundException e)
558 {
559 return fileNotFoundErrorPage(e.getMessage());
560 }
561 catch (Exception e)
562 {
563 e.printStackTrace();
564 System.out.println("error loading preprocess xslt");
565 return XMLTransformer.constructErrorXHTMLPage("error loading preprocess xslt\n" + e.getMessage());
566 }
567
568 Document libraryXsl = null;
569 try
570 {
571 libraryXsl = getDoc(this.getLibraryXSLFilename());
572 String errMsg = ((XMLConverter.ParseErrorHandler) parser.getErrorHandler()).getErrorMessage();
573 if (errMsg != null)
574 {
575 return XMLTransformer.constructErrorXHTMLPage("Error loading xslt file: " + this.getLibraryXSLFilename() + "\n" + errMsg);
576 }
577 }
578 catch (java.io.FileNotFoundException e)
579 {
580 return fileNotFoundErrorPage(e.getMessage());
581 }
582 catch (Exception e)
583 {
584 e.printStackTrace();
585 System.out.println("error loading library xslt");
586 return XMLTransformer.constructErrorXHTMLPage("error loading library xslt\n" + e.getMessage());
587 }
588
589 // Combine the skin file and library variables/templates into one document.
590 // Please note: We dont just use xsl:import because the preprocessing stage
591 // needs to know what's available in the library.
592
593 Document skinAndLibraryXsl = null;
594 Document skinAndLibraryDoc = converter.newDOM();
595 try
596 {
597
598 skinAndLibraryXsl = converter.newDOM();
599 Element root = skinAndLibraryXsl.createElement("skinAndLibraryXsl");
600 skinAndLibraryXsl.appendChild(root);
601
602 Element s = skinAndLibraryXsl.createElement("skinXsl");
603 s.appendChild(skinAndLibraryXsl.importNode(style_doc.getDocumentElement(), true));
604 root.appendChild(s);
605
606 Element l = skinAndLibraryXsl.createElement("libraryXsl");
607 Element libraryXsl_el = libraryXsl.getDocumentElement();
608 l.appendChild(skinAndLibraryXsl.importNode(libraryXsl_el, true));
609 root.appendChild(l);
610 //System.out.println("Skin and Library XSL are now together") ;
611
612 //System.out.println("Pre-processing the skin file...") ;
613
614 //pre-process the skin style sheet
615 //In other words, apply the preProcess.xsl to 'skinAndLibraryXsl' in order to
616 //expand all GS-Lib statements into complete XSL statements and also to create
617 //a valid xsl style sheet document.
618
619 Transformer preProcessor = transformerFactory.newTransformer(new DOMSource(preprocessingXsl));
620 preProcessor.setErrorListener(new XMLTransformer.TransformErrorListener(preprocessingXsl));
621 DOMResult result = new DOMResult();
622 result.setNode(skinAndLibraryDoc);
623 preProcessor.transform(new DOMSource(skinAndLibraryXsl), result);
624 //System.out.println("GS-Lib statements are now expanded") ;
625
626 }
627 catch (TransformerException e)
628 {
629 e.printStackTrace();
630 System.out.println("TransformerException while preprocessing the skin xslt");
631 return XMLTransformer.constructErrorXHTMLPage(e.getMessage());
632 }
633 catch (Exception e)
634 {
635 e.printStackTrace();
636 System.out.println("Error while preprocessing the skin xslt");
637 return XMLTransformer.constructErrorXHTMLPage(e.getMessage());
638 }
639
640 //The following code is to be uncommented if we need to append the extracted GSF statements
641 //after having extracted the GSLib elements. In case of a problem during postprocessing.
642 /*
643 * // put the page into a document - this is necessary for xslt to get
644 * // the paths right if you have paths relative to the document root //
645 * eg /page. Document doc = this.converter.newDOM();
646 * doc.appendChild(doc.importNode(page, true)); Element page_response =
647 * (Element)GSXML.getChildByTagName(page, GSXML.PAGE_RESPONSE_ELEM);
648 * Element format_elem = (Element)GSXML.getChildByTagName(page_response,
649 * GSXML.FORMAT_ELEM); if (output.equals("formatelem")) { return
650 * format_elem; } if (format_elem != null) {
651 * //page_response.removeChild(format_elem);
652 * logger.debug("format elem="+
653 * this.converter.getPrettyString(format_elem)); // need to transform
654 * the format info String configStylesheet_file =
655 * GSFile.stylesheetFile(GlobalProperties.getGSDL3Home(),
656 * (String)this.config_params.get(GSConstants.SITE_NAME), collection,
657 * (String)this.config_params.get(GSConstants.INTERFACE_NAME),
658 * base_interfaces, "config_format.xsl"); Document configStylesheet_doc
659 * = this.converter.getDOM(new File(configStylesheet_file)); if
660 * (configStylesheet_doc != null) { Document format_doc =
661 * this.converter.newDOM();
662 * format_doc.appendChild(format_doc.importNode(format_elem, true));
663 * Node result = this.transformer.transform(configStylesheet_doc,
664 * format_doc);
665 *
666 * // Since we started creating documents with DocTypes, we can end up
667 * with // Document objects here. But we will be working with an Element
668 * instead, // so we grab the DocumentElement() of the Document object
669 * in such a case. Element new_format; if(result.getNodeType() ==
670 * Node.DOCUMENT_NODE) { new_format =
671 * ((Document)result).getDocumentElement(); } else { new_format =
672 * (Element)result; }
673 * logger.debug("new format elem="+this.converter.getPrettyString
674 * (new_format)); if (output.equals("newformat")) { return new_format; }
675 *
676 * // add extracted GSF statements in to the main stylesheet
677 * GSXSLT.mergeStylesheets(skinAndLibraryDoc, new_format);
678 * //System.out.println
679 * ("added extracted GSF statements into the main stylesheet") ;
680 *
681 * // add extracted GSF statements in to the debug test stylesheet
682 * //GSXSLT.mergeStylesheets(oldStyle_doc, new_format); } else {
683 * logger.error(
684 * " couldn't parse the config_format stylesheet, adding the format info as is"
685 * ); GSXSLT.mergeStylesheets(skinAndLibraryDoc, format_elem); //
686 * GSXSLT.mergeStylesheets(oldStyle_doc, format_elem); }
687 * logger.debug("the converted stylesheet is:");
688 * logger.debug(this.converter
689 * .getPrettyString(skinAndLibraryDoc.getDocumentElement())); }
690 */
691
692 // there is a thing called a URIResolver which you can set for a
693 // transformer or transformer factory. may be able to use this
694 // instead of this absoluteIncludepaths hack
695
696 GSXSLT.absoluteIncludePaths(skinAndLibraryDoc, GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces);
697
698 //Same but for the debug version when we want the do the transformation like we use to do
699 //without any gslib elements.
700 GSXSLT.absoluteIncludePaths(oldStyle_doc, GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces);
701
702 //Send different stages of the skin xslt to the browser for debug purposes only
703 //using &o=skindoc or &o=skinandlib etc...
704 if (output.equals("skindoc"))
705 {
706 return converter.getDOM(getStringFromDocument(style_doc));
707 }
708 if (output.equals("skinandlib"))
709 {
710 return converter.getDOM(getStringFromDocument(skinAndLibraryXsl));
711 }
712 if (output.equals("skinandlibdoc") || output.equals("clientside"))
713 {
714
715 Node skinAndLib = converter.getDOM(getStringFromDocument(skinAndLibraryDoc));
716
717 if (output.equals("skinandlibdoc"))
718 {
719 return skinAndLib;
720 }
721 else
722 {
723 // Send XML and skinandlibdoc down the line together
724 Document finalDoc = converter.newDOM();
725 Node finalDocSkin = finalDoc.importNode(skinAndLibraryDoc.getDocumentElement(), true);
726 Node finalDocXML = finalDoc.importNode(theXML.getDocumentElement(), true);
727 Element root = finalDoc.createElement("skinlibPlusXML");
728 root.appendChild(finalDocSkin);
729 root.appendChild(finalDocXML);
730 finalDoc.appendChild(root);
731 return (Node) finalDoc.getDocumentElement();
732 }
733 }
734 if (output.equals("oldskindoc"))
735 {
736 return converter.getDOM(getStringFromDocument(oldStyle_doc));
737 }
738
739 // Try to get the system and public ID from the current skin xsl document
740 // otherwise keep the default values.
741 Element root = skinAndLibraryDoc.getDocumentElement();
742 NodeList nodes = root.getElementsByTagName("xsl:output");
743 // If there is at least one "xsl:output" command in the final xsl then...
744 if (nodes.getLength() != 0)
745 {
746 // There should be only one element called xsl:output,
747 // but if this is not the case get the last one
748 Element xsl_output = (Element) nodes.item(nodes.getLength() - 1);
749 if (xsl_output != null)
750 {
751 // Qualified name will always be html even for xhtml pages
752 //String attrValue = xsl_output.getAttribute("method");
753 //qualifiedName = attrValue.equals("") ? qualifiedName : attrValue;
754
755 String attrValue = xsl_output.getAttribute("doctype-system");
756 systemID = attrValue.equals("") ? systemID : attrValue;
757
758 attrValue = xsl_output.getAttribute("doctype-public");
759 publicID = attrValue.equals("") ? publicID : attrValue;
760 }
761 }
762
763 //System.out.println(converter.getPrettyString(docWithDoctype));
764 //System.out.println("Doctype vals: " + qualifiedName + " " + publicID + " " + systemID) ;
765
766 docWithDoctype = converter.newDOM(qualifiedName, publicID, systemID);
767
768 //System.out.println("Generate final HTML from current skin") ;
769 //Transformation of the XML message from the receptionist to HTML with doctype
770
771 if (inlineTemplate != null)
772 {
773 try
774 {
775 Document inlineTemplateDoc = this.converter.getDOM("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:java=\"http://xml.apache.org/xslt/java\" xmlns:util=\"xalan://org.greenstone.gsdl3.util.XSLTUtil\" xmlns:gsf=\"http://www.greenstone.org/greenstone3/schema/ConfigFormat\">" + inlineTemplate + "</xsl:stylesheet>");
776 GSXSLT.mergeStylesheets(skinAndLibraryDoc, inlineTemplateDoc.getDocumentElement());
777 }
778 catch (Exception ex)
779 {
780 ex.printStackTrace();
781 }
782 }
783
784 if (skinAndLibraryDoc.getElementsByTagName("gsf:metadata").getLength() > 0)
785 {
786 secondConfigFormatPass(collection, skinAndLibraryDoc, doc, new UserContext(request));
787 }
788
789 if (output.equals("xmlfinal"))
790 {
791 return doc;
792 }
793
794 return this.transformer.transform(skinAndLibraryDoc, doc, config_params, docWithDoctype); // The default
795
796 // The line below will do the transformation like we use to do before having Skin++ implemented,
797 // it will not contain any GS-Lib statements expanded, and the result will not contain any doctype.
798
799 //return (Element)this.transformer.transform(style_doc, doc, config_params);
800 //return null; // For now - change later
801 }
802
803 protected void secondConfigFormatPass(String collection, Document skinAndLibraryDoc, Document doc, UserContext userContext)
804 {
805 String to = GSPath.appendLink(collection, "DocumentMetadataRetrieve"); // Hard-wired?
806 Element metaMessage = this.doc.createElement(GSXML.MESSAGE_ELEM);
807 Element metaRequest = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_PROCESS, to, userContext);
808 Element paramList = this.doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
809 Element docNodeList = this.doc.createElement(GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
810
811 NodeList metaNodes = skinAndLibraryDoc.getElementsByTagName("gsf:metadata");
812
813 for (int i = 0; i < metaNodes.getLength(); i++)
814 {
815 Element param = this.doc.createElement(GSXML.PARAM_ELEM);
816 param.setAttribute(GSXML.NAME_ATT, "metadata");
817 param.setAttribute(GSXML.VALUE_ATT, ((Element) metaNodes.item(i)).getAttribute(GSXML.NAME_ATT));
818 paramList.appendChild(param);
819 }
820 metaRequest.appendChild(paramList);
821
822 NodeList docNodes = doc.getElementsByTagName("documentNode");
823 for (int i = 0; i < docNodes.getLength(); i++)
824 {
825 Element docNode = this.doc.createElement(GSXML.DOC_NODE_ELEM);
826 docNode.setAttribute(GSXML.NODE_ID_ATT, ((Element) docNodes.item(i)).getAttribute(GSXML.NODE_ID_ATT));
827 docNode.setAttribute(GSXML.NODE_TYPE_ATT, ((Element) docNodes.item(i)).getAttribute(GSXML.NODE_TYPE_ATT));
828 docNodeList.appendChild(docNode);
829 }
830 metaRequest.appendChild(docNodeList);
831
832 metaMessage.appendChild(metaRequest);
833 Element response = (Element) mr.process(metaMessage);
834
835 NodeList metaDocNodes = response.getElementsByTagName(GSXML.DOC_NODE_ELEM);
836 for (int i = 0; i < docNodes.getLength(); i++)
837 {
838 GSXML.mergeMetadataLists(docNodes.item(i), metaDocNodes.item(i));
839 }
840
841 String configStylesheet_file = GSFile.stylesheetFile(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces, "config_format.xsl");
842 Document configStylesheet_doc = this.converter.getDOM(new File(configStylesheet_file));
843
844 if (configStylesheet_doc != null)
845 {
846 Document format_doc = this.converter.newDOM();
847 format_doc.appendChild(format_doc.importNode(skinAndLibraryDoc.getDocumentElement().cloneNode(true), true));
848 Node result = this.transformer.transform(configStylesheet_doc, format_doc, config_params);
849 GSXSLT.mergeStylesheets(skinAndLibraryDoc, ((Document) result).getDocumentElement());
850 }
851 }
852
853 // method to convert Document to a proper XML string for debug purposes only
854 protected String getStringFromDocument(Document doc)
855 {
856 String content = "";
857 try
858 {
859 DOMSource domSource = new DOMSource(doc);
860 StringWriter writer = new StringWriter();
861 StreamResult result = new StreamResult(writer);
862 TransformerFactory tf = TransformerFactory.newInstance();
863 Transformer transformer = tf.newTransformer();
864 transformer.transform(domSource, result);
865 content = writer.toString();
866 System.out.println("Change the & to &Amp; for proper debug display");
867 content = StringUtils.replace(content, "&", "&amp;");
868 writer.flush();
869 }
870 catch (TransformerException ex)
871 {
872 ex.printStackTrace();
873 return null;
874 }
875 return content;
876 }
877
878 protected synchronized Document getDoc(String docName) throws Exception
879 {
880 File xslt_file = new File(docName);
881
882 FileReader reader = new FileReader(xslt_file);
883 InputSource xml_source = new InputSource(reader);
884 this.parser.parse(xml_source);
885 Document doc = this.parser.getDocument();
886
887 return doc;
888 }
889
890 protected Document getXSLTDocument(String action, String subaction, String collection)
891 {
892 String name = null;
893 if (!subaction.equals(""))
894 {
895 String key = action + ":" + subaction;
896 name = (String) this.xslt_map.get(key);
897 }
898 // try the action by itself
899 if (name == null)
900 {
901 name = (String) this.xslt_map.get(action);
902 }
903 // now find the absolute path
904 ArrayList<File> stylesheets = GSFile.getStylesheetFiles(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces, name);
905
906 if (stylesheets.size() == 0)
907 {
908 logger.error(" Can't find stylesheet for " + name);
909 return null;
910 }
911 logger.debug("Stylesheet: " + name);
912
913 Document finalDoc = this.converter.getDOM(stylesheets.get(stylesheets.size() - 1), "UTF-8");
914 if (finalDoc == null)
915 {
916 return null;
917 }
918
919 for (int i = stylesheets.size() - 2; i >= 0; i--)
920 {
921 Document currentDoc = this.converter.getDOM(stylesheets.get(i), "UTF-8");
922 if (currentDoc == null)
923 {
924 return null;
925 }
926
927 GSXSLT.mergeStylesheets(finalDoc, currentDoc.getDocumentElement());
928 }
929
930 return finalDoc;
931 }
932
933 // returns the library.xsl path of the library file that is applicable for the current interface
934 protected String getLibraryXSLFilename()
935 {
936 return GSFile.xmlTransformDir(GSFile.interfaceHome(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.INTERFACE_NAME))) + File.separatorChar + "library.xsl";
937 }
938
939 // Call this when a FileNotFoundException could be thrown when loading an xsl (xml) file.
940 // Returns an error xhtml page indicating which xsl (or other xml) file is missing.
941 protected Document fileNotFoundErrorPage(String filenameMessage)
942 {
943 String errorMessage = "ERROR missing file: " + filenameMessage;
944 Element errPage = XMLTransformer.constructErrorXHTMLPage(errorMessage);
945 logger.error(errorMessage);
946 return errPage.getOwnerDocument();
947 }
948}
Note: See TracBrowser for help on using the repository browser.