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

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

Error reporting is now improved again, much better than in previous commit: no longer just writes out the XSLT stylesheet to the log file, but follows Dr Bainbridge and Sam's suggestion of performing the transformation that failed again with physical files instead of in-memory as before. This points out the exact line location of errors.

  • Property svn:keywords set to Author Date Id Revision
File size: 35.1 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
596 // Applying the preprocessing XSLT - in its own block {} to allow use of non-unique variable names
597 {
598
599 skinAndLibraryXsl = converter.newDOM();
600 Element root = skinAndLibraryXsl.createElement("skinAndLibraryXsl");
601 skinAndLibraryXsl.appendChild(root);
602
603 Element s = skinAndLibraryXsl.createElement("skinXsl");
604 s.appendChild(skinAndLibraryXsl.importNode(style_doc.getDocumentElement(), true));
605 root.appendChild(s);
606
607 Element l = skinAndLibraryXsl.createElement("libraryXsl");
608 Element libraryXsl_el = libraryXsl.getDocumentElement();
609 l.appendChild(skinAndLibraryXsl.importNode(libraryXsl_el, true));
610 root.appendChild(l);
611 //System.out.println("Skin and Library XSL are now together") ;
612
613 //System.out.println("Pre-processing the skin file...") ;
614
615 //pre-process the skin style sheet
616 //In other words, apply the preProcess.xsl to 'skinAndLibraryXsl' in order to
617 //expand all GS-Lib statements into complete XSL statements and also to create
618 //a valid xsl style sheet document.
619
620 XMLTransformer preProcessor = new XMLTransformer();
621 // Perform the transformation, by passing in:
622 // preprocess-stylesheet, source-xsl (skinAndLibraryXsl), and the node that should
623 // be in the result (skinAndLibraryDoc)
624 preProcessor.transform_withResultNode(preprocessingXsl, skinAndLibraryXsl, skinAndLibraryDoc);
625 //System.out.println("GS-Lib statements are now expanded") ;
626 }
627
628
629 //The following code is to be uncommented if we need to append the extracted GSF statements
630 //after having extracted the GSLib elements. In case of a problem during postprocessing.
631 /*
632 * // put the page into a document - this is necessary for xslt to get
633 * // the paths right if you have paths relative to the document root //
634 * eg /page. Document doc = this.converter.newDOM();
635 * doc.appendChild(doc.importNode(page, true)); Element page_response =
636 * (Element)GSXML.getChildByTagName(page, GSXML.PAGE_RESPONSE_ELEM);
637 * Element format_elem = (Element)GSXML.getChildByTagName(page_response,
638 * GSXML.FORMAT_ELEM); if (output.equals("formatelem")) { return
639 * format_elem; } if (format_elem != null) {
640 * //page_response.removeChild(format_elem);
641 * logger.debug("format elem="+
642 * this.converter.getPrettyString(format_elem)); // need to transform
643 * the format info String configStylesheet_file =
644 * GSFile.stylesheetFile(GlobalProperties.getGSDL3Home(),
645 * (String)this.config_params.get(GSConstants.SITE_NAME), collection,
646 * (String)this.config_params.get(GSConstants.INTERFACE_NAME),
647 * base_interfaces, "config_format.xsl"); Document configStylesheet_doc
648 * = this.converter.getDOM(new File(configStylesheet_file)); if
649 * (configStylesheet_doc != null) { Document format_doc =
650 * this.converter.newDOM();
651 * format_doc.appendChild(format_doc.importNode(format_elem, true));
652 * Node result = this.transformer.transform(configStylesheet_doc,
653 * format_doc);
654 *
655 * // Since we started creating documents with DocTypes, we can end up
656 * with // Document objects here. But we will be working with an Element
657 * instead, // so we grab the DocumentElement() of the Document object
658 * in such a case. Element new_format; if(result.getNodeType() ==
659 * Node.DOCUMENT_NODE) { new_format =
660 * ((Document)result).getDocumentElement(); } else { new_format =
661 * (Element)result; }
662 * logger.debug("new format elem="+this.converter.getPrettyString
663 * (new_format)); if (output.equals("newformat")) { return new_format; }
664 *
665 * // add extracted GSF statements in to the main stylesheet
666 * GSXSLT.mergeStylesheets(skinAndLibraryDoc, new_format);
667 * //System.out.println
668 * ("added extracted GSF statements into the main stylesheet") ;
669 *
670 * // add extracted GSF statements in to the debug test stylesheet
671 * //GSXSLT.mergeStylesheets(oldStyle_doc, new_format); } else {
672 * logger.error(
673 * " couldn't parse the config_format stylesheet, adding the format info as is"
674 * ); GSXSLT.mergeStylesheets(skinAndLibraryDoc, format_elem); //
675 * GSXSLT.mergeStylesheets(oldStyle_doc, format_elem); }
676 * logger.debug("the converted stylesheet is:");
677 * logger.debug(this.converter
678 * .getPrettyString(skinAndLibraryDoc.getDocumentElement())); }
679 */
680
681 // there is a thing called a URIResolver which you can set for a
682 // transformer or transformer factory. may be able to use this
683 // instead of this absoluteIncludepaths hack
684
685 GSXSLT.absoluteIncludePaths(skinAndLibraryDoc, GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces);
686
687 //Same but for the debug version when we want the do the transformation like we use to do
688 //without any gslib elements.
689 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);
690
691 //Send different stages of the skin xslt to the browser for debug purposes only
692 //using &o=skindoc or &o=skinandlib etc...
693 if (output.equals("skindoc"))
694 {
695 return converter.getDOM(getStringFromDocument(style_doc));
696 }
697 if (output.equals("skinandlib"))
698 {
699 return converter.getDOM(getStringFromDocument(skinAndLibraryXsl));
700 }
701 if (output.equals("skinandlibdoc") || output.equals("clientside"))
702 {
703
704 Node skinAndLib = converter.getDOM(getStringFromDocument(skinAndLibraryDoc));
705
706 if (output.equals("skinandlibdoc"))
707 {
708 return skinAndLib;
709 }
710 else
711 {
712 // Send XML and skinandlibdoc down the line together
713 Document finalDoc = converter.newDOM();
714 Node finalDocSkin = finalDoc.importNode(skinAndLibraryDoc.getDocumentElement(), true);
715 Node finalDocXML = finalDoc.importNode(theXML.getDocumentElement(), true);
716 Element root = finalDoc.createElement("skinlibPlusXML");
717 root.appendChild(finalDocSkin);
718 root.appendChild(finalDocXML);
719 finalDoc.appendChild(root);
720 return (Node) finalDoc.getDocumentElement();
721 }
722 }
723 if (output.equals("oldskindoc"))
724 {
725 return converter.getDOM(getStringFromDocument(oldStyle_doc));
726 }
727
728 // Try to get the system and public ID from the current skin xsl document
729 // otherwise keep the default values.
730 Element root = skinAndLibraryDoc.getDocumentElement();
731 NodeList nodes = root.getElementsByTagName("xsl:output");
732 // If there is at least one "xsl:output" command in the final xsl then...
733 if (nodes.getLength() != 0)
734 {
735 // There should be only one element called xsl:output,
736 // but if this is not the case get the last one
737 Element xsl_output = (Element) nodes.item(nodes.getLength() - 1);
738 if (xsl_output != null)
739 {
740 // Qualified name will always be html even for xhtml pages
741 //String attrValue = xsl_output.getAttribute("method");
742 //qualifiedName = attrValue.equals("") ? qualifiedName : attrValue;
743
744 String attrValue = xsl_output.getAttribute("doctype-system");
745 systemID = attrValue.equals("") ? systemID : attrValue;
746
747 attrValue = xsl_output.getAttribute("doctype-public");
748 publicID = attrValue.equals("") ? publicID : attrValue;
749 }
750 }
751
752 //System.out.println(converter.getPrettyString(docWithDoctype));
753 //System.out.println("Doctype vals: " + qualifiedName + " " + publicID + " " + systemID) ;
754
755 docWithDoctype = converter.newDOM(qualifiedName, publicID, systemID);
756
757 //System.out.println("Generate final HTML from current skin") ;
758 //Transformation of the XML message from the receptionist to HTML with doctype
759
760 if (inlineTemplate != null)
761 {
762 try
763 {
764 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>");
765 GSXSLT.mergeStylesheets(skinAndLibraryDoc, inlineTemplateDoc.getDocumentElement());
766 }
767 catch (Exception ex)
768 {
769 ex.printStackTrace();
770 }
771 }
772
773 if (skinAndLibraryDoc.getElementsByTagName("gsf:metadata").getLength() > 0)
774 {
775 secondConfigFormatPass(collection, skinAndLibraryDoc, doc, new UserContext(request));
776 }
777
778 if (output.equals("xmlfinal"))
779 {
780 return doc;
781 }
782
783 return this.transformer.transform(skinAndLibraryDoc, doc, config_params, docWithDoctype); // The default
784
785 // The line below will do the transformation like we use to do before having Skin++ implemented,
786 // it will not contain any GS-Lib statements expanded, and the result will not contain any doctype.
787
788 //return (Element)this.transformer.transform(style_doc, doc, config_params);
789 //return null; // For now - change later
790 }
791
792 protected void secondConfigFormatPass(String collection, Document skinAndLibraryDoc, Document doc, UserContext userContext)
793 {
794 String to = GSPath.appendLink(collection, "DocumentMetadataRetrieve"); // Hard-wired?
795 Element metaMessage = this.doc.createElement(GSXML.MESSAGE_ELEM);
796 Element metaRequest = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_PROCESS, to, userContext);
797 Element paramList = this.doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
798 Element docNodeList = this.doc.createElement(GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
799
800 NodeList metaNodes = skinAndLibraryDoc.getElementsByTagName("gsf:metadata");
801
802 for (int i = 0; i < metaNodes.getLength(); i++)
803 {
804 Element param = this.doc.createElement(GSXML.PARAM_ELEM);
805 param.setAttribute(GSXML.NAME_ATT, "metadata");
806 param.setAttribute(GSXML.VALUE_ATT, ((Element) metaNodes.item(i)).getAttribute(GSXML.NAME_ATT));
807 paramList.appendChild(param);
808 }
809 metaRequest.appendChild(paramList);
810
811 NodeList docNodes = doc.getElementsByTagName("documentNode");
812 for (int i = 0; i < docNodes.getLength(); i++)
813 {
814 Element docNode = this.doc.createElement(GSXML.DOC_NODE_ELEM);
815 docNode.setAttribute(GSXML.NODE_ID_ATT, ((Element) docNodes.item(i)).getAttribute(GSXML.NODE_ID_ATT));
816 docNode.setAttribute(GSXML.NODE_TYPE_ATT, ((Element) docNodes.item(i)).getAttribute(GSXML.NODE_TYPE_ATT));
817 docNodeList.appendChild(docNode);
818 }
819 metaRequest.appendChild(docNodeList);
820
821 metaMessage.appendChild(metaRequest);
822 Element response = (Element) mr.process(metaMessage);
823
824 NodeList metaDocNodes = response.getElementsByTagName(GSXML.DOC_NODE_ELEM);
825 for (int i = 0; i < docNodes.getLength(); i++)
826 {
827 GSXML.mergeMetadataLists(docNodes.item(i), metaDocNodes.item(i));
828 }
829
830 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");
831 Document configStylesheet_doc = this.converter.getDOM(new File(configStylesheet_file));
832
833 if (configStylesheet_doc != null)
834 {
835 Document format_doc = this.converter.newDOM();
836 format_doc.appendChild(format_doc.importNode(skinAndLibraryDoc.getDocumentElement().cloneNode(true), true));
837 Node result = this.transformer.transform(configStylesheet_doc, format_doc, config_params);
838 GSXSLT.mergeStylesheets(skinAndLibraryDoc, ((Document) result).getDocumentElement());
839 }
840 }
841
842 // method to convert Document to a proper XML string for debug purposes only
843 protected String getStringFromDocument(Document doc)
844 {
845 String content = "";
846 try
847 {
848 DOMSource domSource = new DOMSource(doc);
849 StringWriter writer = new StringWriter();
850 StreamResult result = new StreamResult(writer);
851 TransformerFactory tf = TransformerFactory.newInstance();
852 Transformer transformer = tf.newTransformer();
853 transformer.transform(domSource, result);
854 content = writer.toString();
855 System.out.println("Change the & to &Amp; for proper debug display");
856 content = StringUtils.replace(content, "&", "&amp;");
857 writer.flush();
858 }
859 catch (TransformerException ex)
860 {
861 ex.printStackTrace();
862 return null;
863 }
864 return content;
865 }
866
867 protected synchronized Document getDoc(String docName) throws Exception
868 {
869 File xslt_file = new File(docName);
870
871 FileReader reader = new FileReader(xslt_file);
872 InputSource xml_source = new InputSource(reader);
873 this.parser.parse(xml_source);
874 Document doc = this.parser.getDocument();
875
876 return doc;
877 }
878
879 protected Document getXSLTDocument(String action, String subaction, String collection)
880 {
881 String name = null;
882 if (!subaction.equals(""))
883 {
884 String key = action + ":" + subaction;
885 name = (String) this.xslt_map.get(key);
886 }
887 // try the action by itself
888 if (name == null)
889 {
890 name = (String) this.xslt_map.get(action);
891 }
892 // now find the absolute path
893 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);
894
895 if (stylesheets.size() == 0)
896 {
897 logger.error(" Can't find stylesheet for " + name);
898 return null;
899 }
900 logger.debug("Stylesheet: " + name);
901
902 Document finalDoc = this.converter.getDOM(stylesheets.get(stylesheets.size() - 1), "UTF-8");
903 if (finalDoc == null)
904 {
905 return null;
906 }
907
908 for (int i = stylesheets.size() - 2; i >= 0; i--)
909 {
910 Document currentDoc = this.converter.getDOM(stylesheets.get(i), "UTF-8");
911 if (currentDoc == null)
912 {
913 return null;
914 }
915
916 GSXSLT.mergeStylesheets(finalDoc, currentDoc.getDocumentElement());
917 }
918
919 return finalDoc;
920 }
921
922 // returns the library.xsl path of the library file that is applicable for the current interface
923 protected String getLibraryXSLFilename()
924 {
925 return GSFile.xmlTransformDir(GSFile.interfaceHome(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.INTERFACE_NAME))) + File.separatorChar + "library.xsl";
926 }
927
928 // Call this when a FileNotFoundException could be thrown when loading an xsl (xml) file.
929 // Returns an error xhtml page indicating which xsl (or other xml) file is missing.
930 protected Document fileNotFoundErrorPage(String filenameMessage)
931 {
932 String errorMessage = "ERROR missing file: " + filenameMessage;
933 Element errPage = XMLTransformer.constructErrorXHTMLPage(errorMessage);
934 logger.error(errorMessage);
935 return errPage.getOwnerDocument();
936 }
937}
Note: See TracBrowser for help on using the repository browser.