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

Last change on this file since 26848 was 26848, checked in by davidb, 11 years ago

Extra check added to avoid a null-pointer exception (situation came to light when emacs has stored an auto-save file in the XSL area that was not well formed XML). Change is to skip any file that returns NULL when asked for its XML form

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