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

Last change on this file since 33647 was 33647, checked in by kjdon, 4 years ago

added/changed a few of the output values for debugging the transform

  • Property svn:keywords set to Author Date Id Revision
File size: 43.1 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;
9import java.util.HashSet;
10import java.util.regex.Pattern;
11import java.util.regex.Matcher;
12
13import javax.xml.transform.Transformer;
14import javax.xml.transform.TransformerException;
15import javax.xml.transform.TransformerFactory;
16import javax.xml.transform.dom.DOMSource;
17import javax.xml.transform.stream.StreamResult;
18
19import org.apache.commons.lang3.StringUtils;
20import org.apache.log4j.Logger;
21import org.apache.xerces.parsers.DOMParser;
22import org.greenstone.gsdl3.action.Action;
23import org.greenstone.gsdl3.util.GSConstants;
24import org.greenstone.gsdl3.util.GSFile;
25import org.greenstone.gsdl3.util.GSParams;
26import org.greenstone.gsdl3.util.GSXML;
27import org.greenstone.gsdl3.util.GSXSLT;
28import org.greenstone.gsdl3.util.UserContext;
29import org.greenstone.gsdl3.util.XMLConverter;
30import org.greenstone.gsdl3.util.XMLTransformer;
31import org.greenstone.gsdl3.util.XSLTUtil;
32import org.greenstone.util.GlobalProperties;
33import org.w3c.dom.Comment;
34import org.w3c.dom.Document;
35import org.w3c.dom.Element;
36import org.w3c.dom.Node;
37import org.w3c.dom.NodeList;
38import org.w3c.dom.Text;
39import org.xml.sax.InputSource;
40
41/**
42 * A receptionist that uses xslt to transform the page_data before returning it.
43 * . Receives requests consisting of an xml representation of cgi args, and
44 * returns the page of data - in html by default. The requests are processed by
45 * the appropriate action class
46 *
47 * @see Action
48 */
49public class TransformingReceptionist extends Receptionist
50{
51 protected static final String CONFIG_FORMAT_FILE = "config_format.xsl"; //int CONFIG_PASS = 1;
52 protected static final String TEXT_FORMAT_FILE = "text_fragment_format.xsl"; //int TEXT_PASS = 2;
53
54 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.core.TransformingReceptionist.class.getName());
55
56 /** The preprocess.xsl file is in a fixed location */
57 static final String preprocess_xsl_filename = GlobalProperties.getGSDL3Home() + File.separatorChar + "interfaces" + File.separatorChar + "core" + File.separatorChar + "transform" + File.separatorChar + "preProcess.xsl";
58
59 static final String gslib_filename = "gslib.xsl";
60
61 /** the list of xslt to use for actions */
62 protected HashMap<String, String> xslt_map = null;
63
64 /** a transformer class to transform xml using xslt */
65 protected XMLTransformer transformer = null;
66
67 protected TransformerFactory transformerFactory = null;
68 protected DOMParser parser = null;
69
70 protected HashMap<String, ArrayList<String>> _metadataRequiredMap = new HashMap<String, ArrayList<String>>();
71
72 boolean _debug = true;
73
74 public TransformingReceptionist()
75 {
76 super();
77 this.xslt_map = new HashMap<String, String>();
78 this.transformer = new XMLTransformer();
79 try
80 {
81 transformerFactory = org.apache.xalan.processor.TransformerFactoryImpl.newInstance();
82 this.converter = new XMLConverter();
83 //transformerFactory.setURIResolver(new MyUriResolver()) ;
84
85 parser = new DOMParser();
86 parser.setFeature("http://xml.org/sax/features/validation", false);
87 // 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.
88 parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
89 // a performance test showed that having this on lead to increased
90 // memory use for small-medium docs, and not much gain for large
91 // docs.
92 // http://www.sosnoski.com/opensrc/xmlbench/conclusions.html
93 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false);
94 parser.setFeature("http://apache.org/xml/features/continue-after-fatal-error", true);
95 // setting a handler for when fatal errors, errors or warnings happen during xml parsing
96 // call XMLConverter's getParseErrorMessage() to get the errorstring that can be rendered as web page
97 this.parser.setErrorHandler(new XMLConverter.ParseErrorHandler());
98 }
99 catch (Exception e)
100 {
101 e.printStackTrace();
102 }
103 }
104
105 /** configures the receptionist - adding in setting up the xslt map */
106 public boolean configure()
107 {
108 if (!super.configure()) {
109
110 return false;
111 }
112
113 logger.info("configuring the TransformingReceptionist");
114
115 // find the config file containing a list of actions
116 File interface_config_file = new File(GSFile.interfaceConfigFile(GSFile.interfaceHome(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.INTERFACE_NAME))));
117 Document config_doc = this.converter.getDOM(interface_config_file, "utf-8");
118 Element config_elem = config_doc.getDocumentElement();
119
120 // Find the actions again so that we can set up the xslt map
121 Element action_list = (Element) GSXML.getChildByTagName(config_elem, GSXML.ACTION_ELEM + GSXML.LIST_MODIFIER);
122 NodeList actions = action_list.getElementsByTagName(GSXML.ACTION_ELEM);
123
124 for (int i = 0; i < actions.getLength(); i++)
125 {
126 Element action = (Element) actions.item(i);
127 String class_name = action.getAttribute("class");
128 String action_name = action.getAttribute("name");
129
130 // now do the xslt map
131 String xslt = action.getAttribute("xslt");
132 if (!xslt.equals(""))
133 {
134 this.xslt_map.put(action_name, xslt);
135 }
136 NodeList subactions = action.getElementsByTagName(GSXML.SUBACTION_ELEM);
137 for (int j = 0; j < subactions.getLength(); j++)
138 {
139 Element subaction = (Element) subactions.item(j);
140 String subname = subaction.getAttribute(GSXML.NAME_ATT);
141 String subxslt = subaction.getAttribute("xslt");
142
143 String map_key = action_name + ":" + subname;
144 logger.debug("adding in to xslt map, " + map_key + "->" + subxslt);
145 this.xslt_map.put(map_key, subxslt);
146 }
147 }
148
149 getRequiredMetadataNamesFromXSLFiles();
150
151 return true;
152 }
153
154 protected void getRequiredMetadataNamesFromXSLFiles()
155 {
156 ArrayList<File> xslFiles = GSFile.getAllXSLFiles((String) this.config_params.get((String) this.config_params.get(GSConstants.SITE_NAME)));
157
158 HashMap<String, ArrayList<String>> includes = new HashMap<String, ArrayList<String>>();
159 HashMap<String, ArrayList<File>> files = new HashMap<String, ArrayList<File>>();
160 HashMap<String, ArrayList<String>> metaNames = new HashMap<String, ArrayList<String>>();
161
162 //First exploratory pass
163 for (File currentFile : xslFiles)
164 {
165
166 String full_filename = currentFile.getPath();
167 int sep_pos = full_filename.lastIndexOf(File.separator)+1;
168 String local_filename = full_filename.substring(sep_pos);
169 if (local_filename.startsWith(".")) {
170 logger.warn("Greenstone does not normally rely on 'dot' files for XSL transformations.\n Is the following file intended to be part of the digital library installation?\n XSL File being read in:\n " + currentFile.getPath());
171 }
172
173 Document currentDoc = this.converter.getDOM(currentFile);
174 if (currentDoc == null)
175 {
176 // Can happen if an editor creates an auto-save temporary file
177 // (such as #header.xsl#) that is not well formed XML
178 continue;
179 }
180
181 HashSet<String> extra_meta_names = new HashSet<String>();
182 GSXSLT.findExtraMetadataNames(currentDoc.getDocumentElement(), extra_meta_names);
183 ArrayList<String> names = new ArrayList<String>(extra_meta_names);
184
185 metaNames.put(currentFile.getAbsolutePath(), names);
186
187 NodeList includeElems = currentDoc.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "include");
188 NodeList importElems = currentDoc.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "import");
189
190
191 ArrayList<String> includeAndImportList = new ArrayList<String>();
192 for (int i = 0; i < includeElems.getLength(); i++)
193 {
194 includeAndImportList.add(((Element) includeElems.item(i)).getAttribute(GSXML.HREF_ATT));
195 }
196 for (int i = 0; i < importElems.getLength(); i++)
197 {
198 includeAndImportList.add(((Element) importElems.item(i)).getAttribute(GSXML.HREF_ATT));
199 }
200 includes.put(currentFile.getAbsolutePath(), includeAndImportList);
201
202 String filename = currentFile.getName();
203 if (files.get(filename) == null)
204 {
205 ArrayList<File> fileList = new ArrayList<File>();
206 fileList.add(currentFile);
207 files.put(currentFile.getName(), fileList);
208 }
209 else
210 {
211 ArrayList<File> fileList = files.get(filename);
212 fileList.add(currentFile);
213 }
214 }
215
216 //Second pass
217 for (File currentFile : xslFiles)
218 {
219 ArrayList<File> filesToGet = new ArrayList<File>();
220 filesToGet.add(currentFile);
221
222 ArrayList<String> fullNameList = new ArrayList<String>();
223
224 while (filesToGet.size() > 0)
225 {
226 File currentFileTemp = filesToGet.remove(0);
227
228 //Add the names from this file
229 ArrayList<String> currentNames = metaNames.get(currentFileTemp.getAbsolutePath());
230 if (currentNames == null)
231 {
232 continue;
233 }
234
235 fullNameList.addAll(currentNames);
236
237 ArrayList<String> includedHrefs = includes.get(currentFileTemp.getAbsolutePath());
238
239 for (String href : includedHrefs)
240 {
241 int lastSepIndex = href.lastIndexOf("/");
242 if (lastSepIndex != -1)
243 {
244 href = href.substring(lastSepIndex + 1);
245 }
246
247 ArrayList<File> filesToAdd = files.get(href);
248 if (filesToAdd != null)
249 {
250 filesToGet.addAll(filesToAdd);
251 }
252 }
253 }
254
255 _metadataRequiredMap.put(currentFile.getAbsolutePath(), fullNameList);
256 }
257 }
258
259 protected void preProcessRequest(Element request)
260 {
261 String action = request.getAttribute(GSXML.ACTION_ATT);
262 String subaction = request.getAttribute(GSXML.SUBACTION_ATT);
263
264 String name = null;
265 if (!subaction.equals(""))
266 {
267 String key = action + ":" + subaction;
268 name = this.xslt_map.get(key);
269 }
270 // try the action by itself
271 if (name == null)
272 {
273 name = this.xslt_map.get(action);
274 }
275
276 Element cgi_param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
277 String collection = "";
278
279 if (cgi_param_list != null)
280 {
281 // Don't waste time getting all the parameters
282 HashMap<String, Serializable> params = GSXML.extractParams(cgi_param_list, false);
283 collection = (String) params.get(GSParams.COLLECTION);
284 if (collection == null)
285 {
286 collection = "";
287 }
288 }
289
290 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);
291
292 Document doc = XMLConverter.newDOM();
293 Element extraMetadataList = doc.createElement(GSXML.EXTRA_METADATA + GSXML.LIST_MODIFIER);
294 HashSet<String> name_set = new HashSet<String>();
295 for (File stylesheet : stylesheets)
296 {
297 ArrayList<String> requiredMetadata = _metadataRequiredMap.get(stylesheet.getAbsolutePath());
298
299 if (requiredMetadata != null)
300 {
301 for (String metadataString : requiredMetadata)
302 {
303 if (!name_set.contains(metadataString)) {
304 name_set.add(metadataString);
305 Element metadataElem = doc.createElement(GSXML.EXTRA_METADATA);
306 metadataElem.setAttribute(GSXML.NAME_ATT, metadataString);
307 extraMetadataList.appendChild(metadataElem);
308 }
309 }
310 }
311 }
312 request.appendChild(request.getOwnerDocument().importNode(extraMetadataList, true));
313 }
314
315 protected Node postProcessPage(Element page)
316 {
317 // might need to add some data to the page
318 addExtraInfo(page);
319
320
321 // transform the page using xslt
322
323 String currentInterface = (String) config_params.get(GSConstants.INTERFACE_NAME);
324
325 Element request = (Element) GSXML.getChildByTagName(page, GSXML.PAGE_REQUEST_ELEM);
326 String output = request.getAttribute(GSXML.OUTPUT_ATT);
327
328 boolean useClientXSLT = (Boolean) config_params.get(GSConstants.USE_CLIENT_SIDE_XSLT);
329 //logger.info("Client side transforms allowed? " + allowsClientXSLT);
330
331 if (useClientXSLT)
332 {
333 // if not specified, output defaults to 'html', but this isn't what we want when useClientXSLT is on
334 if (output.equals("html")) {
335 output = "xsltclient";
336 }
337 }
338 Node transformed_page = transformPage(page,currentInterface,output);
339
340 if (useClientXSLT) {
341 return transformed_page;
342 }
343 // if the user has specified they want only a part of the full page then subdivide it
344 boolean subdivide = false;
345 String excerptID = null;
346 String excerptTag = null;
347 Element cgi_param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
348 if (cgi_param_list != null)
349 {
350 HashMap<String, Serializable> params = GSXML.extractParams(cgi_param_list, false);
351 if ((excerptID = (String) params.get(GSParams.EXCERPT_ID)) != null)
352 {
353 subdivide = true;
354 }
355 if ((excerptTag = (String) params.get(GSParams.EXCERPT_TAG)) != null)
356 {
357 subdivide = true;
358 }
359 }
360
361 if (subdivide)
362 {
363 Node subdivided_page = subdivide(transformed_page, excerptID, excerptTag);
364 if (subdivided_page != null)
365 {
366 return subdivided_page;
367 }
368 else return null;
369 }
370
371 return transformed_page;
372 }
373
374 protected Node subdivide(Node transformed_page, String excerptID, String excerptTag)
375 {
376 if (excerptID != null)
377 {
378 Node selectedElement = getNodeByIdRecursive(transformed_page, excerptID);
379 modifyNodesByTagRecursive(selectedElement, "a");
380 return selectedElement;
381 }
382 else if (excerptTag != null)
383 {
384 Node selectedElement = getNodeByTagRecursive(transformed_page, excerptTag);
385 return selectedElement;
386 }
387 return transformed_page;
388 }
389
390 protected Node getNodeByIdRecursive(Node parent, String id)
391 {
392 if (parent.hasAttributes() && ((Element) parent).getAttribute("id").equals(id))
393 {
394 return parent;
395 }
396
397 NodeList children = parent.getChildNodes();
398 for (int i = 0; i < children.getLength(); i++)
399 {
400 Node result = null;
401 if ((result = getNodeByIdRecursive(children.item(i), id)) != null)
402 {
403 return result;
404 }
405 }
406 return null;
407 }
408
409 protected Node getNodeByTagRecursive(Node parent, String tag)
410 {
411 if (parent.getNodeType() == Node.ELEMENT_NODE && ((Element) parent).getTagName().equals(tag))
412 {
413 return parent;
414 }
415
416 NodeList children = parent.getChildNodes();
417 for (int i = 0; i < children.getLength(); i++)
418 {
419 Node result = null;
420 if ((result = getNodeByTagRecursive(children.item(i), tag)) != null)
421 {
422 return result;
423 }
424 }
425 return null;
426 }
427
428 protected Node modifyNodesByTagRecursive(Node parent, String tag)
429 {
430 if (parent == null || (parent.getNodeType() == Node.ELEMENT_NODE && ((Element) parent).getTagName().equals(tag)))
431 {
432 return parent;
433 }
434
435 NodeList children = parent.getChildNodes();
436 for (int i = 0; i < children.getLength(); i++)
437 {
438 Node result = null;
439 if ((result = modifyNodesByTagRecursive(children.item(i), tag)) != null)
440 {
441 //TODO: DO SOMETHING HERE?
442 }
443 }
444 return null;
445 }
446
447 protected void replaceNodeWithInterfaceText(Document doc, String interface_name, String lang,
448 Element elem, String attr_name, String attr_val)
449 {
450 String pattern_str_3arg = "util:getInterfaceText\\([^,]+,[^,]+,\\s*'(.+?)'\\s*\\)";
451 String pattern_str_4arg = "util:getInterfaceText\\([^,]+,[^,]+,\\s*'(.+?)'\\s*,\\s*(.+?)\\s*\\)$";
452
453 Pattern pattern3 = Pattern.compile(pattern_str_3arg);
454 Matcher matcher3 = pattern3.matcher(attr_val);
455 if (matcher3.find()) {
456 String dict_key = matcher3.group(1);
457 String dict_val = XSLTUtil.getInterfaceText(interface_name,lang,dict_key);
458
459 Node parent_node = elem.getParentNode();
460
461 Text replacement_text_node = doc.createTextNode(dict_val);
462 parent_node.replaceChild(replacement_text_node,elem);
463 }
464 else {
465 Pattern pattern4 = Pattern.compile(pattern_str_4arg);
466 Matcher matcher4 = pattern4.matcher(attr_val);
467 StringBuffer string_buffer4 = new StringBuffer();
468
469 if (matcher4.find()) {
470 String dict_key = matcher4.group(1);
471 String args = matcher4.group(2);
472 args = args.replaceAll("\\$","\\\\\\$");
473
474 String dict_val = XSLTUtil.getInterfaceText(interface_name,lang,dict_key);
475
476 matcher4.appendReplacement(string_buffer4, "js:getInterfaceTextSubstituteArgs('"+dict_val+"',string("+args+"))");
477 matcher4.appendTail(string_buffer4);
478
479 attr_val = string_buffer4.toString();
480 elem.setAttribute(attr_name,attr_val);
481 }
482 else {
483 logger.error("Failed to find match in attribute: " + attr_name + "=\"" + attr_val + "\"");
484 attr_val = attr_val.replaceAll("util:getInterfaceText\\(.+?,.+?,\\s*(.+?)\\s*\\)","$1");
485 elem.setAttribute(attr_name,attr_val);
486 }
487 }
488
489 }
490
491 protected void resolveExtendedNamespaceAttributesXSLT(Document doc, String interface_name, String lang)
492 {
493 String[] attr_list = new String[] {"select","test"};
494
495 // http://stackoverflow.com/questions/13220520/javascript-replace-child-loop-issue
496 // go through nodeList in reverse to avoid the 'skipping' problem, due to
497 // replaceChild() calls removing items from the "live" nodeList
498
499 NodeList nodeList = doc.getElementsByTagName("*");
500 for (int i=nodeList.getLength()-1; i>=0; i--) {
501 Node node = nodeList.item(i);
502 if (node.getNodeType() == Node.ELEMENT_NODE) {
503 Element elem = (Element)node;
504 for (String attr_name : attr_list) {
505 if (elem.hasAttribute(attr_name)) {
506 String attr_val = elem.getAttribute(attr_name);
507
508 if (attr_val.startsWith("util:getInterfaceText(")) {
509 // replace the node with dictionary lookup
510 replaceNodeWithInterfaceText(doc, interface_name,lang, elem,attr_name,attr_val);
511 }
512 else if (attr_val.contains("util:")) {
513
514 attr_val = attr_val.replaceAll("util:getInterfaceStringsAsJavascript\\(.+?,.+?,\\s*(.+?)\\)","$1");
515
516 //attr_val = attr_val.replaceAll("util:escapeNewLinesAndQuotes\\(\\s*(.+?)\\s*\\)","'escapeNLandQ $1'");
517 //attr_val = attr_val.replaceAll("util:escapeNewLinesAndQuotes\\(\\s*(.+?)\\s*\\)","$1");
518
519 // 'contains()' supported in XSLT 1.0, so OK to change any util:contains() into contains()
520 attr_val = attr_val.replaceAll("util:(contains\\(.+?\\))","$1");
521
522 elem.setAttribute(attr_name,attr_val);
523 }
524
525 if (attr_val.contains("java:")) {
526 if (attr_val.indexOf("getInterfaceTextSubstituteArgs")>=4) {
527
528 attr_val = attr_val.replaceAll("java:.+?\\.(\\w+)\\((.*?)\\)$","js:$1($2)");
529 }
530
531 elem.setAttribute(attr_name,attr_val);
532 }
533 }
534 }
535
536 }
537 }
538 }
539
540
541 protected void resolveExtendedNamespaceAttributesXML(Document doc, String interface_name, String lang)
542 {
543 String[] attr_list = new String[] {"src", "href"};
544
545 // http://stackoverflow.com/questions/13220520/javascript-replace-child-loop-issue
546 // go through nodeList in reverse to avoid the 'skipping' problem, due to
547 // replaceChild() calls removing items from the "live" nodeList
548
549 NodeList nodeList = doc.getElementsByTagName("*");
550 for (int i=nodeList.getLength()-1; i>=0; i--) {
551 Node node = nodeList.item(i);
552 if (node.getNodeType() == Node.ELEMENT_NODE) {
553 Element elem = (Element)node;
554 for (String attr_name : attr_list) {
555 if (elem.hasAttribute(attr_name)) {
556 String attr_val = elem.getAttribute(attr_name);
557
558 if (attr_val.contains("util:getInterfaceText(")) {
559 String pattern_str_3arg = "util:getInterfaceText\\([^,]+,[^,]+,\\s*'(.+?)'\\s*\\)";
560 Pattern pattern3 = Pattern.compile(pattern_str_3arg);
561 Matcher matcher3 = pattern3.matcher(attr_val);
562
563 StringBuffer string_buffer3 = new StringBuffer();
564
565 boolean found_match = false;
566
567 while (matcher3.find()) {
568 found_match = true;
569 String dict_key = matcher3.group(1);
570 String dict_val = XSLTUtil.getInterfaceText(interface_name,lang,dict_key);
571
572 matcher3.appendReplacement(string_buffer3, dict_val);
573 }
574 matcher3.appendTail(string_buffer3);
575
576 if (found_match) {
577 attr_val = string_buffer3.toString();
578 elem.setAttribute(attr_name,attr_val);
579 }
580 else {
581 logger.error("Failed to find match in attribute: " + attr_name + "=\"" + attr_val + "\"");
582 attr_val = attr_val.replaceAll("util:getInterfaceText\\(.+?,.+?,\\s*(.+?)\\s*\\)","$1");
583 elem.setAttribute(attr_name,attr_val);
584 }
585 }
586 else if (attr_val.contains("util:")) {
587
588 logger.error("Encountered unexpected 'util:' prefix exension: " + attr_name + "=\"" + attr_val + "\"");
589 }
590
591 if (attr_val.contains("java:")) {
592 // make anything java: safe from the point of an XSLT without extensions
593 logger.error("Encountered unexpected 'java:' prefix exension: " + attr_name + "=\"" + attr_val + "\"");
594
595 }
596 }
597 }
598
599 }
600 }
601 }
602
603
604
605 /**
606 * overwrite this to add any extra info that might be needed in the page
607 * before transformation
608 */
609 protected void addExtraInfo(Element page)
610 {
611 }
612
613 /**
614 * transform the page using xslt.
615 * we need to get any format element out of the page and add it to the xslt before transforming
616 */
617 protected Node transformPage(Element page_xml, String currentInterface, String output)
618 {
619 _debug = false;
620
621 Element request = (Element) GSXML.getChildByTagName(page_xml, GSXML.PAGE_REQUEST_ELEM);
622
623 //logger.info("Current output mode is: " + output + ", current interface name is: " + currentInterface);
624
625 if (output.equals("xsltclient"))
626 {
627 return generateXSLTClientOutput(request);
628 }
629
630 String action = request.getAttribute(GSXML.ACTION_ATT);
631 String subaction = request.getAttribute(GSXML.SUBACTION_ATT);
632
633 // we should choose how to transform the data based on output, eg diff
634 // choice for html, and wml??
635 // for now, if output=xml, we don't transform the page, we just return
636 // the page xml
637 Document page_with_xslt_params_doc = null;
638
639 if (output.equals("xml") || (output.equals("json")) || output.equals("clientside"))
640 {
641 // Append the xsltparams to the page
642 page_with_xslt_params_doc = converter.newDOM();
643 // Import into new document first!
644 Node page_with_xslt_params = page_with_xslt_params_doc.importNode(page_xml, true);
645 page_with_xslt_params_doc.appendChild(page_with_xslt_params);
646 Element xslt_params = page_with_xslt_params_doc.createElement("xsltparams");
647 page_with_xslt_params.appendChild(xslt_params);
648
649 GSXML.addParameter2ToList(xslt_params, "library_name", (String) config_params.get(GSConstants.LIBRARY_NAME));
650 GSXML.addParameter2ToList(xslt_params, "interface_name", (String) config_params.get(GSConstants.INTERFACE_NAME));
651 GSXML.addParameter2ToList(xslt_params, "site_name", (String) config_params.get(GSConstants.SITE_NAME));
652 Boolean useClientXSLT = (Boolean) config_params.get(GSConstants.USE_CLIENT_SIDE_XSLT);
653 GSXML.addParameter2ToList(xslt_params, "use_client_side_xslt", useClientXSLT.toString());
654 GSXML.addParameter2ToList(xslt_params, "filepath", GlobalProperties.getGSDL3Home());
655
656 if ((output.equals("xml")) || output.equals("json"))
657 {
658 // Just return the page XML
659 // in the case of "json", calling method responsible for converting to JSON-string
660 return page_with_xslt_params_doc.getDocumentElement();
661 }
662 // in the case of client side, later on we'll use this doc with xslt params added,
663 // along with the xsl.
664 }
665
666 Element cgi_param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
667 String collection = "";
668 String inline_template = "";
669 if (cgi_param_list != null)
670 {
671 // Don't waste time getting all the parameters
672 HashMap<String, Serializable> params = GSXML.extractParams(cgi_param_list, false);
673 collection = (String) params.get(GSParams.COLLECTION);
674 if (collection == null)
675 {
676 collection = "";
677 }
678
679 inline_template = (String) params.get(GSParams.INLINE_TEMPLATE);
680 String debug_p = (String) params.get(GSParams.DEBUG);
681 if (debug_p != null && (debug_p.equals("on") || debug_p.equals("1") || debug_p.equals("true")))
682 {
683 String[] groups = new UserContext(request).getGroups();
684
685 boolean found = false;
686 for (String g : groups)
687 {
688 if (g.equals("administrator"))
689 {
690 found = true;
691 break;
692 }
693 if (!collection.equals("")) {
694 if (g.equals("all-collections-editor")) {
695 found = true;
696 break;
697 }
698
699 if (g.equals(collection+"-collection-editor")) {
700 found = true;
701 break;
702 }
703 }
704 }
705 if (found)
706 {
707 _debug = true;
708 }
709 }
710 }
711
712 config_params.put("collName", collection);
713
714 // find the appropriate stylesheet (based on action/subaction) - eg a=p&sa=home will be home.xsl
715 // This mapping is defined in interfaceConfig.xsl
716 // All versions of the stylesheet (base interface, interface, site, collection) are
717 // merged together into one document
718 Document page_xsl = getXSLTDocument(action, subaction, collection);
719 String page_xsl_filename = getXSLTFilename(action, subaction); // for debug purposes
720 if (page_xsl == null)
721 {
722 logger.error("Couldn't find and/or load the stylesheet ("+page_xsl_filename+") for a="+action+", sa="+subaction+", in collection "+collection);
723 return XMLTransformer.constructErrorXHTMLPage("Couldn't find and/or load the stylesheet \""+page_xsl_filename+"\" for a="+action+", sa="+subaction+", in collection "+collection);
724 }
725
726 if (output.equals("xsl1")) {
727 // if we just output the page_xsl directly then there may be unescaped & in the javascript,
728 // and the page won't display properly
729 return converter.getDOM(getStringFromDocument(page_xsl));
730 }
731
732
733 // put the page into a document - this is necessary for xslt to get
734 // the paths right if you have paths relative to the document root
735 // eg /page.
736 Document page_xml_doc = XMLConverter.newDOM();
737 page_xml_doc.appendChild(page_xml_doc.importNode(page_xml, true));
738 Element page_response = (Element) GSXML.getChildByTagName(page_xml, GSXML.PAGE_RESPONSE_ELEM);
739 Element format_elem = (Element) GSXML.getChildByTagName(page_response, GSXML.FORMAT_ELEM);
740
741 if (output.equals("format1"))
742 {
743 return format_elem;
744 }
745
746 // do we have language attribute for page?
747 String lang_att = page_xml.getAttribute(GSXML.LANG_ATT);
748 if (lang_att != null && lang_att.length() > 0)
749 {
750 config_params.put("lang", lang_att);
751 }
752
753 if (format_elem != null)
754 {
755 //page_response.removeChild(format_elem);
756
757 // need to transform the format info
758 // run config_format.xsl over the format_elem. We need to do this now to turn
759 // eg gsf:template into xsl:template so that the merging works properly.
760 // xsl:templates will get merged
761 // into the main stylesheet, but gsf:templates won't.
762
763 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_FILE);
764 Document configStylesheet_doc = this.converter.getDOM(new File(configStylesheet_file));
765
766 if (configStylesheet_doc != null)
767 {
768 Document format_doc = XMLConverter.newDOM();
769 format_doc.appendChild(format_doc.importNode(format_elem, true));
770
771 if (_debug)
772 {
773 String siteHome = GSFile.siteHome(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.SITE_NAME));
774 GSXSLT.insertDebugElements(format_doc, GSFile.collectionConfigFile(siteHome, collection));
775 }
776
777 Node result = this.transformer.transform(configStylesheet_doc, format_doc, config_params); // Needs addressing <-
778
779 // Since we started creating documents with DocTypes, we can end up with
780 // Document objects here. But we will be working with an Element instead,
781 // so we grab the DocumentElement() of the Document object in such a case.
782 Element new_format;
783 if (result.getNodeType() == Node.DOCUMENT_NODE)
784 {
785 new_format = ((Document) result).getDocumentElement();
786 }
787 else
788 {
789 new_format = (Element) result;
790 }
791
792 if (output.equals("format"))
793 {
794 return new_format;
795 }
796
797 // add the extracted format statements in to the main stylesheet
798 if (_debug)
799 {
800 String siteHome = GSFile.siteHome(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.SITE_NAME));
801 GSXSLT.mergeStylesheetsDebug(page_xsl, new_format, true, true, "OTHER1", GSFile.collectionConfigFile(siteHome, collection));
802 }
803 else
804 {
805 GSXSLT.mergeStylesheets(page_xsl, new_format, true);
806 }
807
808 }
809 else
810 {
811 // Is this a fatal error, or are there situations where we could carry on here...
812 logger.error(" Couldn't parse the "+CONFIG_FORMAT_FILE+" stylesheet");
813 return XMLTransformer.constructErrorXHTMLPage("Couldn't find and/or load the stylesheet \""+CONFIG_FORMAT_FILE);
814 //GSXSLT.mergeStylesheets(page_xsl, format_elem, true);
815 }
816 }
817
818 if (output.equals("xsl2")) {
819 return converter.getDOM(getStringFromDocument(page_xsl));
820 }
821
822 Document inline_template_doc = null;
823 if (inline_template != null)
824 {
825 try
826 {
827 inline_template_doc = this.converter.getDOM("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsl:stylesheet version=\"1.0\" "+GSXML.ALL_NAMESPACES_ATTS + ">" + inline_template + "</xsl:stylesheet>", "UTF-8");
828
829 if (_debug)
830 {
831 GSXSLT.mergeStylesheetsDebug(page_xsl, inline_template_doc.getDocumentElement(), true, true, "OTHER2", "INLINE");
832 }
833 else
834 {
835 //GSXSLT.mergeStylesheets(skinAndLibraryDoc, inlineTemplateDoc.getDocumentElement(), true);
836 GSXSLT.mergeStylesheets(page_xsl, inline_template_doc.getDocumentElement(), true);
837 }
838 }
839 catch (Exception ex)
840 {
841 ex.printStackTrace();
842 }
843 }
844
845
846 if (output.equals("ilt")) {
847 return converter.getDOM(getStringFromDocument(inline_template_doc));
848 }
849 if (output.equals("xsl3")) {
850 return converter.getDOM(getStringFromDocument(page_xsl));
851 }
852
853 // once we are here, have got the main page xsl loaded up. Have added in any format statements from the source xml, and added in any inline template which came through cgi params.
854
855 // next we load in the import and include files. - these, too, go through the inheritance cascade (base interface, interface, site, collection) before being added into the main document
856
857 if (_debug)
858 {
859 GSXSLT.inlineImportAndIncludeFilesDebug(page_xsl, null, _debug, page_xsl_filename, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces);
860 }
861 else
862 {
863 GSXSLT.inlineImportAndIncludeFiles(page_xsl, null, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces);
864
865 }
866
867 if (output.equals("xsl4")) {
868 return converter.getDOM(getStringFromDocument(page_xsl));
869 }
870
871 // The next steg is to process the page_xsl + gslib.xsl by preProcess.xsl to expand all the
872 // gslib elements
873
874 Document preprocessingXsl;
875 try
876 {
877 // interfaces/core/transform/preProcess.xsl
878 // this takes skinandLibraryXsl, copies skinXSL, merges elements of libraryXsl into it, and replaces gslib elements
879 preprocessingXsl = getDoc(preprocess_xsl_filename);
880 String errMsg = ((XMLConverter.ParseErrorHandler) parser.getErrorHandler()).getErrorMessage();
881 if (errMsg != null)
882 {
883 return XMLTransformer.constructErrorXHTMLPage("error loading core/transform/preProcess xslt file: " + preprocess_xsl_filename + "\n" + errMsg);
884 }
885 }
886 catch (java.io.FileNotFoundException e)
887 {
888 return fileNotFoundErrorPage(e.getMessage());
889 }
890 catch (Exception e)
891 {
892 e.printStackTrace();
893 System.out.println("error loading preprocess xslt");
894 return XMLTransformer.constructErrorXHTMLPage("Error loading interfaces/core/transform/preProcess xslt\n" + e.getMessage());
895 }
896
897 // gslib.xsl
898 Document libraryXsl = null;
899 try
900 {
901 libraryXsl = GSXSLT.mergedXSLTDocumentCascade(gslib_filename, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces, _debug);
902 }
903 catch (Exception e)
904 {
905 e.printStackTrace();
906 System.out.println("error loading gslib xslt");
907 return XMLTransformer.constructErrorXHTMLPage("error loading gslib xslt\n" + e.getMessage());
908 }
909
910 if (output.equals("preprocess")) {
911 return converter.getDOM(getStringFromDocument(preprocessingXsl));
912 }
913 if (output.equals("gslib1")) {
914 return converter.getDOM(getStringFromDocument(libraryXsl));
915 }
916 // Combine the skin file and library variables/templates into one document.
917 // Please note: We dont just use xsl:import because the preprocessing stage
918 // needs to know what's available in the library.
919
920 Document skinAndLibraryXsl = null;
921 Document skinAndLibraryDoc = converter.newDOM();
922
923 // add in all gslib.xsl's include and import files
924 // debug?? use debug method?? or does it not make sense here??
925 GSXSLT.inlineImportAndIncludeFiles(libraryXsl, null, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces);
926 if (output.equals("gslib")) {
927 return converter.getDOM(getStringFromDocument(libraryXsl));
928 }
929
930 // now, we transform all the gslib elements
931 {
932
933
934 skinAndLibraryXsl = converter.newDOM();
935 Element root = skinAndLibraryXsl.createElement("skinAndLibraryXsl");
936 skinAndLibraryXsl.appendChild(root);
937
938 Element s = skinAndLibraryXsl.createElement("skinXsl");
939 s.appendChild(skinAndLibraryXsl.importNode(page_xsl.getDocumentElement(), true));
940 root.appendChild(s);
941
942 Element l = skinAndLibraryXsl.createElement("libraryXsl");
943 if (libraryXsl != null)
944 {
945 Element libraryXsl_el = libraryXsl.getDocumentElement();
946 l.appendChild(skinAndLibraryXsl.importNode(libraryXsl_el, true));
947 }
948 root.appendChild(l);
949
950 // actually merge the gslib file with the page
951 XMLTransformer preProcessor = new XMLTransformer();
952 preProcessor.transform_withResultNode(preprocessingXsl, skinAndLibraryXsl, skinAndLibraryDoc);
953
954 }
955 if (output.equals("xsl5")) {
956 return converter.getDOM(getStringFromDocument(skinAndLibraryDoc));
957 }
958
959 // now we need to transform all the gsf elements
960 skinAndLibraryDoc = (Document) transformGSFElements(collection, skinAndLibraryDoc, TEXT_FORMAT_FILE);
961
962 skinAndLibraryDoc = (Document) transformGSFElements(collection, skinAndLibraryDoc, CONFIG_FORMAT_FILE);
963
964
965
966 if (output.equals("xsl") || output.equals("skinandlibdocfinal"))
967 {
968 return converter.getDOM(getStringFromDocument(skinAndLibraryDoc));
969 }
970
971 if (output.equals("clientside"))
972 {
973
974 // Go through and 'fix up' any 'util:...' or 'java:...' attributes the skinAndLibraryDoc has
975 String lang = (String)config_params.get("lang");
976 resolveExtendedNamespaceAttributesXSLT(skinAndLibraryDoc,currentInterface,lang); // test= and select= attributes
977 resolveExtendedNamespaceAttributesXML(skinAndLibraryDoc,currentInterface,lang); // href= and src= attributes
978 Node skinAndLibFinal = converter.getDOM(getStringFromDocument(skinAndLibraryDoc));
979
980 // Send XML and skinandlibdoc down the line together
981 Document finalDoc = converter.newDOM();
982 Node finalDocSkin = finalDoc.importNode(skinAndLibraryDoc.getDocumentElement(), true);
983 Node finalDocXML = finalDoc.importNode(page_with_xslt_params_doc.getDocumentElement(), true);
984 Element root = finalDoc.createElement("skinlibfinalPlusXML");
985 root.appendChild(finalDocSkin);
986 root.appendChild(finalDocXML);
987 finalDoc.appendChild(root);
988 return (Node) finalDoc.getDocumentElement();
989 }
990
991 ///logger.debug("final xml is ");
992 ///logger.debug(XMLConverter.getPrettyString(page_xml_doc));
993
994 ///logger.debug("final xsl is");
995 ///logger.debug(XMLConverter.getPrettyString(skinAndLibraryDoc));
996
997 // The transformer will now work out the resulting doctype from any set in the (merged) stylesheets and
998 // will set this in the output document it creates. So don't pass in any docWithDocType to the transformer
999
1000 // Here, we finally transform the page xml source with the complete xsl file
1001 Node finalResult = this.transformer.transform(skinAndLibraryDoc, page_xml_doc, config_params);
1002
1003 if (_debug)
1004 {
1005 GSXSLT.fixTables((Document) finalResult);
1006 }
1007
1008 return finalResult;
1009
1010 }
1011
1012 protected Node generateXSLTClientOutput(Element request) {
1013
1014 // DocType defaults in case the skin doesn't have an "xsl:output" element
1015 String qualifiedName = "html";
1016 String publicID = "-//W3C//DTD HTML 4.01 Transitional//EN";
1017 String systemID = "http://www.w3.org/TR/html4/loose.dtd";
1018
1019 // We need to create an empty document with a predefined DocType,
1020 // that will then be used for the transformation by the DOMResult
1021 Document docWithDoctype = converter.newDOM(qualifiedName, publicID, systemID);
1022 String baseURL = request.getAttribute(GSXML.BASE_URL);
1023
1024 // If you're just getting the client-side transform page, why bother with the rest of this?
1025 Element html = docWithDoctype.createElement("html");
1026 Element img = docWithDoctype.createElement("img");
1027 img.setAttribute("src", "loading.gif"); // Make it dynamic
1028 img.setAttribute("alt", "Please wait...");
1029 Text title_text = docWithDoctype.createTextNode("Please wait..."); // Make this language dependent
1030 Element head = docWithDoctype.createElement("head");
1031
1032 // e.g., <base href="http://localhost:8383/greenstone3/" /><!-- [if lte IE 6]></base><![endif] -->
1033 Element base = docWithDoctype.createElement("base");
1034 base.setAttribute("href",baseURL);
1035 Comment opt_end_base = docWithDoctype.createComment("[if lte IE 6]></base><![endif]");
1036
1037 Element title = docWithDoctype.createElement("title");
1038 title.appendChild(title_text);
1039
1040 Element body = docWithDoctype.createElement("body");
1041
1042 Element jquery_script = docWithDoctype.createElement("script");
1043 jquery_script.setAttribute("src", "jquery-1.10-min.js");
1044 jquery_script.setAttribute("type", "text/javascript");
1045 Comment jquery_comment = docWithDoctype.createComment("jQuery");
1046 jquery_script.appendChild(jquery_comment);
1047
1048 Element saxonce_script = docWithDoctype.createElement("script");
1049 saxonce_script.setAttribute("src", "Saxonce/Saxonce.nocache.js");
1050 saxonce_script.setAttribute("type", "text/javascript");
1051 Comment saxonce_comment = docWithDoctype.createComment("SaxonCE");
1052 saxonce_script.appendChild(saxonce_comment);
1053
1054 Element xsltutil_script = docWithDoctype.createElement("script");
1055 xsltutil_script.setAttribute("src", "xslt-util.js");
1056 xsltutil_script.setAttribute("type", "text/javascript");
1057 Comment xsltutil_comment = docWithDoctype.createComment("JavaScript version of XSLTUtil.java");
1058 xsltutil_script.appendChild(xsltutil_comment);
1059
1060 Element script = docWithDoctype.createElement("script");
1061 Comment script_comment = docWithDoctype.createComment("Filler for browser");
1062 script.setAttribute("src", "client-side-xslt.js");
1063 script.setAttribute("type", "text/javascript");
1064 script.appendChild(script_comment);
1065
1066 Element pagevar = docWithDoctype.createElement("script");
1067 Element style = docWithDoctype.createElement("style");
1068 style.setAttribute("type", "text/css");
1069 Text style_text = docWithDoctype.createTextNode("body { text-align: center; padding: 50px; font: 14pt Arial, sans-serif; font-weight: bold; }");
1070 pagevar.setAttribute("type", "text/javascript");
1071 Text page_var_text = docWithDoctype.createTextNode("var placeholder = true;");
1072
1073 html.appendChild(head);
1074 head.appendChild(base); head.appendChild(opt_end_base);
1075 head.appendChild(title);
1076 head.appendChild(style);
1077 style.appendChild(style_text);
1078 html.appendChild(body);
1079 head.appendChild(pagevar);
1080 head.appendChild(jquery_script);
1081 head.appendChild(saxonce_script);
1082 head.appendChild(xsltutil_script);
1083 head.appendChild(script);
1084 pagevar.appendChild(page_var_text);
1085
1086 body.appendChild(img);
1087 docWithDoctype.appendChild(html);
1088
1089 return (Node) docWithDoctype;
1090
1091 }
1092
1093 // transform the xsl with xsl to replace all gsf elements. We do this in 2 stages -
1094 // first do just a text pass, that way we can have gsf elements in the text content
1095 protected Node transformGSFElements(String collection, Document skinAndLibraryDoc, String formatFile) {
1096
1097 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);
1098 Document configStylesheet_doc = this.converter.getDOM(new File(configStylesheet_file));
1099
1100 if (configStylesheet_doc != null)
1101 {
1102 return this.transformer.transform(configStylesheet_doc, skinAndLibraryDoc, config_params);
1103 }
1104 return skinAndLibraryDoc;
1105
1106 }
1107
1108 // method to convert Document to a proper XML string for debug purposes only
1109 protected String getStringFromDocument(Document doc)
1110 {
1111 String content = "";
1112 try
1113 {
1114 DOMSource domSource = new DOMSource(doc);
1115 StringWriter writer = new StringWriter();
1116 StreamResult result = new StreamResult(writer);
1117 TransformerFactory tf = TransformerFactory.newInstance();
1118 Transformer transformer = tf.newTransformer();
1119 transformer.transform(domSource, result);
1120 content = writer.toString();
1121 System.out.println("Change the & to &Amp; for proper debug display");
1122 content = StringUtils.replace(content, "&", "&amp;");
1123 writer.flush();
1124 }
1125 catch (TransformerException ex)
1126 {
1127 ex.printStackTrace();
1128 return null;
1129 }
1130 return content;
1131 }
1132
1133 protected synchronized Document getDoc(String docName) throws Exception
1134 {
1135 File xslt_file = new File(docName);
1136
1137 FileReader reader = new FileReader(xslt_file);
1138 InputSource xml_source = new InputSource(reader);
1139 this.parser.parse(xml_source);
1140 Document doc = this.parser.getDocument();
1141
1142 return doc;
1143 }
1144
1145 protected String getXSLTFilename(String action, String subaction) {
1146 String name = null;
1147 if (!subaction.equals(""))
1148 {
1149 String key = action + ":" + subaction;
1150 name = this.xslt_map.get(key);
1151 }
1152 // try the action by itself
1153 if (name == null)
1154 {
1155 name = this.xslt_map.get(action);
1156 }
1157 if (name == null)
1158 {
1159 // so we can reandomly create any named page
1160 if (action.equals("p") && !subaction.equals(""))
1161 {
1162 // TODO: pages/ won't work for interface other than default!!
1163 name = "pages/" + subaction + ".xsl";
1164 }
1165
1166 }
1167 return name;
1168 }
1169
1170
1171 protected Document getXSLTDocument(String action, String subaction, String collection)
1172 {
1173 String name = getXSLTFilename(action, subaction);
1174 Document finalDoc = null;
1175 if(name != null)
1176 {
1177 // this finds all the stylesheets named "name" and merges them together, in the order of
1178 // base interface, current interface, site, collection - the latter overriding the former.
1179 // templates with the same name will replace earlier versions
1180 finalDoc = GSXSLT.mergedXSLTDocumentCascade(name, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces, _debug);
1181 }
1182 return finalDoc;
1183 }
1184
1185 // returns the path to the gslib.xsl file that is applicable for the current interface
1186 protected String getGSLibXSLFilename()
1187 {
1188 return GSFile.xmlTransformDir(GSFile.interfaceHome(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.INTERFACE_NAME))) + File.separatorChar + "gslib.xsl";
1189 }
1190
1191 // Call this when a FileNotFoundException could be thrown when loading an xsl (xml) file.
1192 // Returns an error xhtml page indicating which xsl (or other xml) file is missing.
1193 protected Document fileNotFoundErrorPage(String filenameMessage)
1194 {
1195 String errorMessage = "ERROR missing file: " + filenameMessage;
1196 Element errPage = XMLTransformer.constructErrorXHTMLPage(errorMessage);
1197 logger.error(errorMessage);
1198 return errPage.getOwnerDocument();
1199 }
1200}
Note: See TracBrowser for help on using the repository browser.