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

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

oops, I must have 'tidied' up the file and then not compiled it to check as it didn't compile. erroneous and missing brackets

  • Property svn:keywords set to Author Date Id Revision
File size: 42.9 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("format"))
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("newformat"))
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 if (inline_template != null)
823 {
824 try
825 {
826 Document 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");
827
828 if (_debug)
829 {
830 GSXSLT.mergeStylesheetsDebug(page_xsl, inline_template_doc.getDocumentElement(), true, true, "OTHER2", "INLINE");
831 }
832 else
833 {
834 //GSXSLT.mergeStylesheets(skinAndLibraryDoc, inlineTemplateDoc.getDocumentElement(), true);
835 GSXSLT.mergeStylesheets(page_xsl, inline_template_doc.getDocumentElement(), true);
836 }
837 }
838 catch (Exception ex)
839 {
840 ex.printStackTrace();
841 }
842 }
843
844 if (output.equals("xsl3")) {
845 return converter.getDOM(getStringFromDocument(page_xsl));
846 }
847
848 // 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.
849
850 // 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
851
852 if (_debug)
853 {
854 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);
855 }
856 else
857 {
858 GSXSLT.inlineImportAndIncludeFiles(page_xsl, null, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces);
859
860 }
861
862 if (output.equals("xsl4")) {
863 return converter.getDOM(getStringFromDocument(page_xsl));
864 }
865
866 // The next steg is to process the page_xsl + gslib.xsl by preProcess.xsl to expand all the
867 // gslib elements
868
869 Document preprocessingXsl;
870 try
871 {
872 // interfaces/core/transform/preProcess.xsl
873 // this takes skinandLibraryXsl, copies skinXSL, merges elements of libraryXsl into it, and replaces gslib elements
874 preprocessingXsl = getDoc(preprocess_xsl_filename);
875 String errMsg = ((XMLConverter.ParseErrorHandler) parser.getErrorHandler()).getErrorMessage();
876 if (errMsg != null)
877 {
878 return XMLTransformer.constructErrorXHTMLPage("error loading core/transform/preProcess xslt file: " + preprocess_xsl_filename + "\n" + errMsg);
879 }
880 }
881 catch (java.io.FileNotFoundException e)
882 {
883 return fileNotFoundErrorPage(e.getMessage());
884 }
885 catch (Exception e)
886 {
887 e.printStackTrace();
888 System.out.println("error loading preprocess xslt");
889 return XMLTransformer.constructErrorXHTMLPage("Error loading interfaces/core/transform/preProcess xslt\n" + e.getMessage());
890 }
891
892 // gslib.xsl
893 Document libraryXsl = null;
894 try
895 {
896 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);
897 }
898 catch (Exception e)
899 {
900 e.printStackTrace();
901 System.out.println("error loading gslib xslt");
902 return XMLTransformer.constructErrorXHTMLPage("error loading gslib xslt\n" + e.getMessage());
903 }
904
905 if (output.equals("preprocess")) {
906 return converter.getDOM(getStringFromDocument(preprocessingXsl));
907 }
908 if (output.equals("gslib")) {
909 return converter.getDOM(getStringFromDocument(libraryXsl));
910 }
911 // Combine the skin file and library variables/templates into one document.
912 // Please note: We dont just use xsl:import because the preprocessing stage
913 // needs to know what's available in the library.
914
915 Document skinAndLibraryXsl = null;
916 Document skinAndLibraryDoc = converter.newDOM();
917
918 // add in all gslib.xsl's include and import files
919 // debug?? use debug method?? or does it not make sense here??
920 GSXSLT.inlineImportAndIncludeFiles(libraryXsl, null, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces);
921
922 // now, we transform all the gslib elements
923 {
924
925
926 skinAndLibraryXsl = converter.newDOM();
927 Element root = skinAndLibraryXsl.createElement("skinAndLibraryXsl");
928 skinAndLibraryXsl.appendChild(root);
929
930 Element s = skinAndLibraryXsl.createElement("skinXsl");
931 s.appendChild(skinAndLibraryXsl.importNode(page_xsl.getDocumentElement(), true));
932 root.appendChild(s);
933
934 Element l = skinAndLibraryXsl.createElement("libraryXsl");
935 if (libraryXsl != null)
936 {
937 Element libraryXsl_el = libraryXsl.getDocumentElement();
938 l.appendChild(skinAndLibraryXsl.importNode(libraryXsl_el, true));
939 }
940 root.appendChild(l);
941
942 // actually merge the gslib file with the page
943 XMLTransformer preProcessor = new XMLTransformer();
944 preProcessor.transform_withResultNode(preprocessingXsl, skinAndLibraryXsl, skinAndLibraryDoc);
945
946 }
947 if (output.equals("xsl5")) {
948 return converter.getDOM(getStringFromDocument(skinAndLibraryDoc));
949 }
950
951 // now we need to transform all the gsf elements
952 skinAndLibraryDoc = (Document) transformGSFElements(collection, skinAndLibraryDoc, TEXT_FORMAT_FILE);
953
954 skinAndLibraryDoc = (Document) transformGSFElements(collection, skinAndLibraryDoc, CONFIG_FORMAT_FILE);
955
956
957
958 if (output.equals("xsl6") || output.equals("skinandlibdocfinal"))
959 {
960 return converter.getDOM(getStringFromDocument(skinAndLibraryDoc));
961 }
962
963 if (output.equals("clientside"))
964 {
965
966 // Go through and 'fix up' any 'util:...' or 'java:...' attributes the skinAndLibraryDoc has
967 String lang = (String)config_params.get("lang");
968 resolveExtendedNamespaceAttributesXSLT(skinAndLibraryDoc,currentInterface,lang); // test= and select= attributes
969 resolveExtendedNamespaceAttributesXML(skinAndLibraryDoc,currentInterface,lang); // href= and src= attributes
970 Node skinAndLibFinal = converter.getDOM(getStringFromDocument(skinAndLibraryDoc));
971
972 // Send XML and skinandlibdoc down the line together
973 Document finalDoc = converter.newDOM();
974 Node finalDocSkin = finalDoc.importNode(skinAndLibraryDoc.getDocumentElement(), true);
975 Node finalDocXML = finalDoc.importNode(page_with_xslt_params_doc.getDocumentElement(), true);
976 Element root = finalDoc.createElement("skinlibfinalPlusXML");
977 root.appendChild(finalDocSkin);
978 root.appendChild(finalDocXML);
979 finalDoc.appendChild(root);
980 return (Node) finalDoc.getDocumentElement();
981 }
982
983 logger.debug("final xml is ");
984 logger.debug(XMLConverter.getPrettyString(page_xml_doc));
985
986 logger.debug("final xsl is");
987 logger.debug(XMLConverter.getPrettyString(skinAndLibraryDoc));
988
989 // The transformer will now work out the resulting doctype from any set in the (merged) stylesheets and
990 // will set this in the output document it creates. So don't pass in any docWithDocType to the transformer
991
992 // Here, we finally transform the page xml source with the complete xsl file
993 Node finalResult = this.transformer.transform(skinAndLibraryDoc, page_xml_doc, config_params);
994
995 if (_debug)
996 {
997 GSXSLT.fixTables((Document) finalResult);
998 }
999
1000 return finalResult;
1001
1002 }
1003
1004 protected Node generateXSLTClientOutput(Element request) {
1005
1006 // DocType defaults in case the skin doesn't have an "xsl:output" element
1007 String qualifiedName = "html";
1008 String publicID = "-//W3C//DTD HTML 4.01 Transitional//EN";
1009 String systemID = "http://www.w3.org/TR/html4/loose.dtd";
1010
1011 // We need to create an empty document with a predefined DocType,
1012 // that will then be used for the transformation by the DOMResult
1013 Document docWithDoctype = converter.newDOM(qualifiedName, publicID, systemID);
1014 String baseURL = request.getAttribute(GSXML.BASE_URL);
1015
1016 // If you're just getting the client-side transform page, why bother with the rest of this?
1017 Element html = docWithDoctype.createElement("html");
1018 Element img = docWithDoctype.createElement("img");
1019 img.setAttribute("src", "loading.gif"); // Make it dynamic
1020 img.setAttribute("alt", "Please wait...");
1021 Text title_text = docWithDoctype.createTextNode("Please wait..."); // Make this language dependent
1022 Element head = docWithDoctype.createElement("head");
1023
1024 // e.g., <base href="http://localhost:8383/greenstone3/" /><!-- [if lte IE 6]></base><![endif] -->
1025 Element base = docWithDoctype.createElement("base");
1026 base.setAttribute("href",baseURL);
1027 Comment opt_end_base = docWithDoctype.createComment("[if lte IE 6]></base><![endif]");
1028
1029 Element title = docWithDoctype.createElement("title");
1030 title.appendChild(title_text);
1031
1032 Element body = docWithDoctype.createElement("body");
1033
1034 Element jquery_script = docWithDoctype.createElement("script");
1035 jquery_script.setAttribute("src", "jquery-1.10-min.js");
1036 jquery_script.setAttribute("type", "text/javascript");
1037 Comment jquery_comment = docWithDoctype.createComment("jQuery");
1038 jquery_script.appendChild(jquery_comment);
1039
1040 Element saxonce_script = docWithDoctype.createElement("script");
1041 saxonce_script.setAttribute("src", "Saxonce/Saxonce.nocache.js");
1042 saxonce_script.setAttribute("type", "text/javascript");
1043 Comment saxonce_comment = docWithDoctype.createComment("SaxonCE");
1044 saxonce_script.appendChild(saxonce_comment);
1045
1046 Element xsltutil_script = docWithDoctype.createElement("script");
1047 xsltutil_script.setAttribute("src", "xslt-util.js");
1048 xsltutil_script.setAttribute("type", "text/javascript");
1049 Comment xsltutil_comment = docWithDoctype.createComment("JavaScript version of XSLTUtil.java");
1050 xsltutil_script.appendChild(xsltutil_comment);
1051
1052 Element script = docWithDoctype.createElement("script");
1053 Comment script_comment = docWithDoctype.createComment("Filler for browser");
1054 script.setAttribute("src", "client-side-xslt.js");
1055 script.setAttribute("type", "text/javascript");
1056 script.appendChild(script_comment);
1057
1058 Element pagevar = docWithDoctype.createElement("script");
1059 Element style = docWithDoctype.createElement("style");
1060 style.setAttribute("type", "text/css");
1061 Text style_text = docWithDoctype.createTextNode("body { text-align: center; padding: 50px; font: 14pt Arial, sans-serif; font-weight: bold; }");
1062 pagevar.setAttribute("type", "text/javascript");
1063 Text page_var_text = docWithDoctype.createTextNode("var placeholder = true;");
1064
1065 html.appendChild(head);
1066 head.appendChild(base); head.appendChild(opt_end_base);
1067 head.appendChild(title);
1068 head.appendChild(style);
1069 style.appendChild(style_text);
1070 html.appendChild(body);
1071 head.appendChild(pagevar);
1072 head.appendChild(jquery_script);
1073 head.appendChild(saxonce_script);
1074 head.appendChild(xsltutil_script);
1075 head.appendChild(script);
1076 pagevar.appendChild(page_var_text);
1077
1078 body.appendChild(img);
1079 docWithDoctype.appendChild(html);
1080
1081 return (Node) docWithDoctype;
1082
1083 }
1084
1085 // transform the xsl with xsl to replace all gsf elements. We do this in 2 stages -
1086 // first do just a text pass, that way we can have gsf elements in the text content
1087 protected Node transformGSFElements(String collection, Document skinAndLibraryDoc, String formatFile) {
1088
1089 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);
1090 Document configStylesheet_doc = this.converter.getDOM(new File(configStylesheet_file));
1091
1092 if (configStylesheet_doc != null)
1093 {
1094 return this.transformer.transform(configStylesheet_doc, skinAndLibraryDoc, config_params);
1095 }
1096 return skinAndLibraryDoc;
1097
1098 }
1099
1100 // method to convert Document to a proper XML string for debug purposes only
1101 protected String getStringFromDocument(Document doc)
1102 {
1103 String content = "";
1104 try
1105 {
1106 DOMSource domSource = new DOMSource(doc);
1107 StringWriter writer = new StringWriter();
1108 StreamResult result = new StreamResult(writer);
1109 TransformerFactory tf = TransformerFactory.newInstance();
1110 Transformer transformer = tf.newTransformer();
1111 transformer.transform(domSource, result);
1112 content = writer.toString();
1113 System.out.println("Change the & to &Amp; for proper debug display");
1114 content = StringUtils.replace(content, "&", "&amp;");
1115 writer.flush();
1116 }
1117 catch (TransformerException ex)
1118 {
1119 ex.printStackTrace();
1120 return null;
1121 }
1122 return content;
1123 }
1124
1125 protected synchronized Document getDoc(String docName) throws Exception
1126 {
1127 File xslt_file = new File(docName);
1128
1129 FileReader reader = new FileReader(xslt_file);
1130 InputSource xml_source = new InputSource(reader);
1131 this.parser.parse(xml_source);
1132 Document doc = this.parser.getDocument();
1133
1134 return doc;
1135 }
1136
1137 protected String getXSLTFilename(String action, String subaction) {
1138 String name = null;
1139 if (!subaction.equals(""))
1140 {
1141 String key = action + ":" + subaction;
1142 name = this.xslt_map.get(key);
1143 }
1144 // try the action by itself
1145 if (name == null)
1146 {
1147 name = this.xslt_map.get(action);
1148 }
1149 if (name == null)
1150 {
1151 // so we can reandomly create any named page
1152 if (action.equals("p") && !subaction.equals(""))
1153 {
1154 // TODO: pages/ won't work for interface other than default!!
1155 name = "pages/" + subaction + ".xsl";
1156 }
1157
1158 }
1159 return name;
1160 }
1161
1162
1163 protected Document getXSLTDocument(String action, String subaction, String collection)
1164 {
1165 String name = getXSLTFilename(action, subaction);
1166 Document finalDoc = null;
1167 if(name != null)
1168 {
1169 // this finds all the stylesheets named "name" and merges them together, in the order of
1170 // base interface, current interface, site, collection - the latter overriding the former.
1171 // templates with the same name will replace earlier versions
1172 finalDoc = GSXSLT.mergedXSLTDocumentCascade(name, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces, _debug);
1173 }
1174 return finalDoc;
1175 }
1176
1177 // returns the path to the gslib.xsl file that is applicable for the current interface
1178 protected String getGSLibXSLFilename()
1179 {
1180 return GSFile.xmlTransformDir(GSFile.interfaceHome(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.INTERFACE_NAME))) + File.separatorChar + "gslib.xsl";
1181 }
1182
1183 // Call this when a FileNotFoundException could be thrown when loading an xsl (xml) file.
1184 // Returns an error xhtml page indicating which xsl (or other xml) file is missing.
1185 protected Document fileNotFoundErrorPage(String filenameMessage)
1186 {
1187 String errorMessage = "ERROR missing file: " + filenameMessage;
1188 Element errPage = XMLTransformer.constructErrorXHTMLPage(errorMessage);
1189 logger.error(errorMessage);
1190 return errPage.getOwnerDocument();
1191 }
1192}
Note: See TracBrowser for help on using the repository browser.