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

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