source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/GSXSLT.java@ 26086

Last change on this file since 26086 was 26055, checked in by sjm84, 12 years ago

Adding constants for the various XML namespaces used in the code

  • Property svn:keywords set to Author Date Id Revision
File size: 19.9 KB
RevLine 
[16869]1/*
2 * GSXSLT.java
3 * Copyright (C) 2008 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
[3450]19package org.greenstone.gsdl3.util;
20
[25656]21import java.io.File;
22import java.util.ArrayList;
23import java.util.Vector;
24
25import org.greenstone.util.GlobalProperties;
26import org.w3c.dom.Document;
27import org.w3c.dom.Element;
[3450]28import org.w3c.dom.Node;
29import org.w3c.dom.NodeList;
30
[25987]31import org.apache.log4j.*;
32
[24864]33/** various functions for manipulating Greenstone xslt */
34public class GSXSLT
35{
[25987]36
37 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.GSXSLT.class.getName());
38
[25656]39 public static void mergeStylesheets(Document main_xsl, Element extra_xsl, boolean overwrite)
40 {
41 mergeStylesheetsDebug(main_xsl, extra_xsl, overwrite, false, null, null);
42 }
43
[24864]44 /**
45 * takes a stylesheet Document, and adds in any child nodes from extra_xsl
46 * named templates overwrite any existing one, while match templates are
[25820]47 * just added to the end of the stylesheet
[25913]48 *
49 * elements are added in following order, and added to preserve original
50 * order with imported ones coming after existing ones import, include,
51 * output, variable, template
[24864]52 */
[25656]53 public static void mergeStylesheetsDebug(Document main_xsl, Element extra_xsl, boolean overwrite, boolean debug, String firstDocFileName, String secondDocFileName)
[24864]54 {
[25656]55 if (debug)
56 {
57 System.err.println("ADDING DEBUG ELEMENTS WITH FILE NAME " + firstDocFileName);
58 insertDebugElements(main_xsl, firstDocFileName);
59 }
60
[24864]61 Element main = main_xsl.getDocumentElement();
[25820]62 Node insertion_point = null;
[26055]63 Element last_import = GSXML.getLastElementByTagNameNS(main, GSXML.XSL_NAMESPACE, "import");
[25913]64 if (last_import != null)
65 {
66 insertion_point = last_import.getNextSibling();
67 }
68 else
69 {
70 insertion_point = main.getFirstChild();
71 }
72
[25820]73 // imports
[26055]74 NodeList children = extra_xsl.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "import");
[25656]75 for (int i = 0; i < children.getLength(); i++)
76 {
[25913]77 Element node = (Element) children.item(i);
[25397]78 // If the new xsl:import element is identical (in terms of href attr value)
79 // to any in the merged document, don't copy it over
[26055]80 if (GSXML.getNamedElementNS(main, GSXML.XSL_NAMESPACE, "import", "href", node.getAttribute("href")) == null)
[25656]81 {
82 // Import statements should be the first children of an xsl:stylesheet element
83 // If firstchild is null, then this xsl:import element will be inserted at the "end"
84 // Although Node.insertBefore() will first remove identical nodes before inserting, we check
85 // only the href attribute to see if they're "identical" to any pre-existing <xsl:import>
[25820]86 //main.insertBefore(main_xsl.importNode(node, true), main.getFirstChild());
[25913]87 main.insertBefore(main_xsl.importNode(node, true), insertion_point);
[25656]88 }
89 }
[25913]90
91 // do we have a new insertion point??
[26055]92 Element last_include = GSXML.getLastElementByTagNameNS(main, GSXML.XSL_NAMESPACE, "include");
[25913]93 if (last_include != null)
94 {
95 insertion_point = last_include.getNextSibling();
96 }
97
[25820]98 // includes
[26055]99 children = extra_xsl.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "include");
[25656]100 for (int i = 0; i < children.getLength(); i++)
101 {
[25913]102 Element node = (Element) children.item(i);
[25397]103 // If the new xsl:include element is identical (in terms of href attr value)
104 // to any in the merged document, don't copy it over
105 // Although Node.appendChild() will first remove identical nodes before appending, we check
106 // only the href attribute to see if they're "identical" to any pre-existing <xsl:include>
[26055]107 if (GSXML.getNamedElementNS(main, GSXML.XSL_NAMESPACE, "include", "href", node.getAttribute("href")) == null)
[25656]108 {
[25913]109 //main.appendChild(main_xsl.importNode(node, true));
110 main.insertBefore(main_xsl.importNode(node, true), insertion_point);
[25397]111 }
[25820]112 } // for each include
[25389]113
[26055]114 if (main.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "output").getLength() == 0)
[25913]115 {
[25914]116 // outputs
[26055]117 children = extra_xsl.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "output");
[25914]118 for (int i = 0; i < children.getLength(); i++)
119 {
120 Element node = (Element) children.item(i);
121 // If the new xsl:output element is identical (in terms of the value for the method attr)
122 // to any in the merged document, don't copy it over
123
124 main.insertBefore(main_xsl.importNode(node, true), insertion_point);
125 }
[25913]126 }
127
[25914]128 // variables - only top level ones!!
129 // append to end of document
[26055]130 children = GSXML.getChildrenByTagNameNS(extra_xsl, GSXML.XSL_NAMESPACE, "variable");
[25656]131 for (int i = 0; i < children.getLength(); i++)
132 {
[25913]133 Element node = (Element) children.item(i);
[25914]134 // If the new xsl:import element is identical (in terms of href attr value)
[25397]135 // to any in the merged document, don't copy it over
[26055]136 if (GSXML.getNamedElementNS(main, GSXML.XSL_NAMESPACE, "variable", "name", node.getAttribute("name")) == null)
[25656]137 {
[25914]138 main.appendChild(main_xsl.importNode(node, true));
[25820]139 }
140 }
141
[25914]142 // params - only top level ones!!
[25820]143 // append to end of document
[26055]144 children = GSXML.getChildrenByTagNameNS(extra_xsl, GSXML.XSL_NAMESPACE, "param");
[25820]145 for (int i = 0; i < children.getLength(); i++)
146 {
[25913]147 Element node = (Element) children.item(i);
[25820]148 // If the new xsl:import element is identical (in terms of href attr value)
149 // to any in the merged document, don't copy it over
[26055]150 if (GSXML.getNamedElementNS(main, GSXML.XSL_NAMESPACE, "param", "name", node.getAttribute("name")) == null)
[25820]151 {
[25656]152 main.appendChild(main_xsl.importNode(node, true));
[25397]153 }
[25381]154 }
155
[25820]156 // templates
157 // append to end of document
[26055]158 children = extra_xsl.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "template");
[24864]159 for (int i = 0; i < children.getLength(); i++)
160 {
[25913]161 Element node = (Element) children.item(i);
[25820]162 // remove any previous occurrences of xsl:template with the same value for name or match
[25913]163 String template_match = node.getAttribute("match");
164 String template_name = node.getAttribute("name");
[25924]165 String template_mode = node.getAttribute("mode");
[25656]166
167 if (overwrite)
168 {
[25913]169 // if we have a name attribute, remove any other similarly named template
[26055]170 GSXML.removeElementsWithAttributesNS(main, GSXML.XSL_NAMESPACE, "template", new String[] { "name", "match", "mode" }, new String[] { template_name, template_match, template_mode });
[25926]171
[25913]172 // now add our good template in
173 main.appendChild(main_xsl.importNode(node, true));
[25656]174 }
[25913]175 else
[25656]176 {
[25913]177 // if overwrite is false, then we only add in templates if they don't match something else.
178 // In this case (eg from expanding imported stylesheets)
179 // there can't be any duplicate named templates, so just look for matches
180 // we already have the one with highest import precedence (from the top most level) so don't add any more in
[26055]181 if (GSXML.getElementsWithAttributesNS(main, GSXML.XSL_NAMESPACE, "template", new String[] { "name", "match", "mode" }, new String[] { template_name, template_match, template_mode }).getLength() == 0)
[25913]182 {
183 main.appendChild(main_xsl.importNode(node, true));
184 }
[25656]185 }
[4704]186 }
[25656]187
188 if (debug)
189 {
190 System.err.println("ADDING DEBUG ELEMENTS WITH FILE NAME " + secondDocFileName);
191 insertDebugElements(main_xsl, secondDocFileName);
192 }
[3450]193 }
194
[25656]195 protected static void insertDebugElements(Document doc, String fileName)
196 {
197 NodeList htmlTags = GSXML.getHTMLStructureElements(doc);
198 System.err.println("HTML TAGS SIZE IS " + htmlTags.getLength());
199 for (int i = 0; i < htmlTags.getLength(); i++)
200 {
201 Element current = (Element) htmlTags.item(i);
202 if (current.getUserData("GSDEBUGFILENAME") == null)
203 {
204 Element xslParent = (Element) current.getParentNode();
[25381]205
[26055]206 while (xslParent.getNamespaceURI() != GSXML.XSL_NAMESPACE && !xslParent.getNodeName().startsWith("xsl:"))
[25656]207 {
208 xslParent = (Element) xslParent.getParentNode();
209 }
[25381]210
[25656]211 System.err.println("ADDING FILE NAME " + fileName);
212 current.setUserData("GSDEBUGFILENAME", fileName, null);
213 current.setUserData("GSDEBUGXML", xslParent.cloneNode(true), null);
214 }
215 else
216 {
217 System.err.println("ALREADY SET!");
218 }
219 }
220 }
221
[26055]222 public static void inlineImportAndIncludeFiles(Document doc, String pathExtra, String site, String collection, String interface_name, ArrayList<String> base_interfaces)
[25656]223 {
[26055]224 inlineImportAndIncludeFilesDebug(doc, pathExtra, false, null, site, collection, interface_name, base_interfaces);
[25656]225 }
226
[26055]227 public static void inlineImportAndIncludeFilesDebug(Document doc, String pathExtra, boolean debug, String docFileName, String site, String collection, String interface_name, ArrayList<String> base_interfaces)
[25656]228 {
229 XMLConverter converter = new XMLConverter();
230
[25914]231 String path = (pathExtra == null) ? "" : pathExtra;
[25656]232
[26055]233 NodeList importList = doc.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "import");
234 NodeList includeList = doc.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "include");
[25656]235
236 for (int i = 0; i < importList.getLength() + includeList.getLength(); i++)
237 {
238 Element current = (Element) ((i < importList.getLength()) ? importList.item(i) : includeList.item(i - importList.getLength()));
239 String href = current.getAttribute("href");
[25988]240 //String filePath = GSFile.interfaceHome(GlobalProperties.getGSDL3Home(), interface_name) + File.separator + "transform" + File.separator + path.replace("/", File.separator) + href.replace("/", File.separator);
[25987]241 //String filePath = GSFile.stylesheetFile(GlobalProperties.getGSDL3Home(), site_name, collection, interface_name, base_interfaces,
[25656]242
243 try
244 {
[26055]245 //Document inlineDoc = converter.getDOM(new File(filePath), "UTF-8");
246 Document inlineDoc = mergedXSLTDocumentCascade(path + href, site, collection, interface_name, base_interfaces, debug);
[25656]247 String newPath = path;
248 int lastSepIndex = href.lastIndexOf("/");
249 if (lastSepIndex != -1)
250 {
251 newPath += href.substring(0, lastSepIndex + 1);
252 }
253
254 //Do this recursively
[26055]255 inlineImportAndIncludeFilesDebug(inlineDoc, newPath, debug, "merged " + href/* filePath */, site, collection, interface_name, base_interfaces);
[25914]256
[26055]257 GSXSLT.mergeStylesheetsDebug(doc, inlineDoc.getDocumentElement(), false, debug, docFileName, /* filePath */"merged " + href);
[25656]258 }
259 catch (Exception ex)
260 {
261 ex.printStackTrace();
262 return;
263 }
264 }
265
266 while (importList.getLength() > 0)
267 {
268 Element importElem = (Element) importList.item(0);
269 importElem.getParentNode().removeChild(importElem);
270 }
271 while (includeList.getLength() > 0)
272 {
273 Element includeElem = (Element) includeList.item(0);
274 includeElem.getParentNode().removeChild(includeElem);
275 }
276 }
277
[26055]278 public static Document mergedXSLTDocumentCascade(String xslt_filename, String site, String collection, String this_interface, ArrayList<String> base_interfaces, boolean debug)
279 {
280 XMLConverter converter = new XMLConverter();
281 // find the list of stylesheets with this name
282 ArrayList<File> stylesheets = GSFile.getStylesheetFiles(GlobalProperties.getGSDL3Home(), site, collection, this_interface, base_interfaces, xslt_filename);
[25987]283 if (stylesheets.size() == 0)
284 {
285 logger.error(" Can't find stylesheet for " + xslt_filename);
286 return null;
287 }
288 logger.debug("Stylesheet: " + xslt_filename);
289
290 Document finalDoc = converter.getDOM(stylesheets.get(stylesheets.size() - 1), "UTF-8");
291 if (finalDoc == null)
292 {
293 return null;
294 }
295
296 for (int i = stylesheets.size() - 2; i >= 0; i--)
297 {
298 Document currentDoc = converter.getDOM(stylesheets.get(i), "UTF-8");
299 if (currentDoc == null)
300 {
301 return null;
302 }
303
304 if (debug)
305 {
306 GSXSLT.mergeStylesheetsDebug(finalDoc, currentDoc.getDocumentElement(), true, true, stylesheets.get(stylesheets.size() - 1).getAbsolutePath(), stylesheets.get(i).getAbsolutePath());
307 }
308 else
309 {
310 GSXSLT.mergeStylesheets(finalDoc, currentDoc.getDocumentElement(), true);
311 }
312 }
313
314 return finalDoc;
315 }
316
[25656]317 public static void modifyConfigFormatForDebug(Document doc, String fileName)
318 {
[26055]319 NodeList templateNodes = doc.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "template");
[25656]320 if (templateNodes.getLength() == 0)
321 {
322 templateNodes = doc.getElementsByTagName("xsl:template");
323 }
324
325 String debugElementString = "";
326 debugElementString += "<span class=\"configDebugSpan\" style=\"display:none;\">";
327 debugElementString += " \"filename\":\"" + fileName + "\",";
328 debugElementString += " \"xml\":\"<xsl:value-of select=\"util:xmlNodeToString(.)\"/>\""; //<xsl:copy><xsl:copy-of select=\"@*\"/></xsl:copy>
329 debugElementString += "</span>";
330
331 XMLConverter converter = new XMLConverter();
[26055]332 Element debugElement = (Element) converter.getDOM("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xslt:stylesheet version=\"1.0\" xmlns:xsl=\"" + GSXML.XSL_NAMESPACE + "\" xmlns:xslt=\"output.xsl\" xmlns:gsf=\"" + GSXML.GSF_NAMESPACE + "\">" + debugElementString + "</xslt:stylesheet>").getDocumentElement().getFirstChild();
[25656]333
334 for (int i = 0; i < templateNodes.getLength(); i++)
335 {
336 Element currentTemplate = (Element) templateNodes.item(i);
337 if (currentTemplate.getAttribute("match") != null && (currentTemplate.getAttribute("match").equals("gsf:metadata") || currentTemplate.getAttribute("match").equals("*") || currentTemplate.getAttribute("match").equals("format")))
338 {
339 continue;
340 }
341
342 if (currentTemplate.hasChildNodes())
343 {
344 currentTemplate.insertBefore(doc.importNode(debugElement.cloneNode(true), true), currentTemplate.getFirstChild());
345 }
346 else
347 {
348 currentTemplate.appendChild(doc.importNode(debugElement.cloneNode(true), true));
349 }
350 }
351 }
352
[24864]353 /**
354 * takes any import or include nodes, and creates absolute path names for
355 * the files
356 */
[25635]357 public static void absoluteIncludePaths(Document stylesheet, String gsdl3_home, String site_name, String collection, String interface_name, ArrayList<String> base_interfaces)
[24864]358 {
359 Element base_node = stylesheet.getDocumentElement();
360 if (base_node == null)
361 {
362 return;
363 }
364 Node child = base_node.getFirstChild();
365 while (child != null)
366 {
367 String name = child.getNodeName();
368 if (name.equals("xsl:import") || name.equals("xsl:include"))
369 {
370 ((Element) child).setAttribute("href", GSFile.stylesheetFile(gsdl3_home, site_name, collection, interface_name, base_interfaces, ((Element) child).getAttribute("href")));
371 }
372 child = child.getNextSibling();
373 }
[20139]374 }
[4079]375
[24864]376 /**
377 * looks through a stylesheet for <xxx:template match='template_name'>
378 * inside this template it looks for any <xxx:value-of
379 * select='metadataList/metadata[@name=yyy]> elements, and extracts the
380 * metadata names into a Vector
381 */
[25635]382 public static Vector<String> extractWantedMetadata(Document stylesheet, String template_name)
[24864]383 {
384
[25635]385 Vector<String> metadata = new Vector<String>();
[24864]386 Element base_node = stylesheet.getDocumentElement();
387 NodeList templates = base_node.getElementsByTagNameNS("*", "template");
388 for (int i = 0; i < templates.getLength(); i++)
389 {
390 Element template = (Element) templates.item(i);
391 String match_name = template.getAttribute("match");
392 if (!match_name.equals(template_name))
393 {
394 continue; // we're only looking for specific templates
395 }
396 String mode = template.getAttribute("mode");
397 if (!mode.equals(""))
398 {
399 continue; // we only want ones without modes - these are processing ones, not display ones
400 }
401 // we have one that we want to look through
402 NodeList values = template.getElementsByTagNameNS("*", "value-of");
403 for (int v = 0; v < values.getLength(); v++)
404 {
405 String select = ((Element) values.item(v)).getAttribute("select");
406 if (select.startsWith("metadataList/metadata[@name="))
407 {
408 String[] bits = select.split("'|\"");
409 // there should be two quotes in teh string, therefore 3 items, and the second one is teh one we want
410 String name = bits[1];
411 metadata.add(name);
412 }
413 }
[4079]414 }
[24864]415 return metadata;
[4079]416 }
417
[25982]418 public static void mergeFormatElements(Element mainFormat, Element secondaryFormat, boolean overwrite)
419 {
[26055]420 NodeList xslChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.XSL_NAMESPACE, "variable");
421 NodeList gsfChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.GSF_NAMESPACE, "variable");
[25982]422 for (int i = 0; i < xslChildren.getLength() + gsfChildren.getLength(); i++)
423 {
424 Element node = (Element) ((i < xslChildren.getLength()) ? xslChildren.item(i) : gsfChildren.item(i - xslChildren.getLength()));
425 if (GSXML.getNamedElementNS(mainFormat, "http://www.w3.org/1999/XSL/Transform", "variable", "name", node.getAttribute("name")) == null && GSXML.getNamedElementNS(mainFormat, "http://www.greenstone.org/greenstone3/schema/ConfigFormat", "variable", "name", node.getAttribute("name")) == null)
426 {
427 mainFormat.appendChild(node);
428 }
429 }
430
[26055]431 xslChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.XSL_NAMESPACE, "param");
432 gsfChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.GSF_NAMESPACE, "param");
[25982]433 for (int i = 0; i < xslChildren.getLength() + gsfChildren.getLength(); i++)
434 {
435 Element node = (Element) ((i < xslChildren.getLength()) ? xslChildren.item(i) : gsfChildren.item(i - xslChildren.getLength()));
436 if (GSXML.getNamedElementNS(mainFormat, "http://www.w3.org/1999/XSL/Transform", "param", "name", node.getAttribute("name")) == null && GSXML.getNamedElementNS(mainFormat, "http://www.greenstone.org/greenstone3/schema/ConfigFormat", "param", "name", node.getAttribute("name")) == null)
437 {
438 mainFormat.appendChild(node);
439 }
440 }
441
[26055]442 xslChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.XSL_NAMESPACE, "template");
443 gsfChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.GSF_NAMESPACE, "template");
[25982]444 for (int i = 0; i < xslChildren.getLength() + gsfChildren.getLength(); i++)
445 {
446 Element node = (Element) ((i < xslChildren.getLength()) ? xslChildren.item(i) : gsfChildren.item(i - xslChildren.getLength()));
447 // remove any previous occurrences of xsl:template with the same value for name or match
448 String template_match = node.getAttribute("match");
449 String template_name = node.getAttribute("name");
450 String template_mode = node.getAttribute("mode");
451
452 String[] attributeNames = new String[] { "name", "match", "mode" };
453 String[] attributeValues = new String[] { template_name, template_match, template_mode };
454
455 if (overwrite)
456 {
457 // if we have a name attribute, remove any other similarly named template
[26055]458 GSXML.removeElementsWithAttributesNS(mainFormat, GSXML.XSL_NAMESPACE, "template", attributeNames, attributeValues);
459 GSXML.removeElementsWithAttributesNS(mainFormat, GSXML.GSF_NAMESPACE, "template", attributeNames, attributeValues);
[25982]460
461 // now add our good template in
462 mainFormat.appendChild(node);
463 }
464 else
465 {
466 // if overwrite is false, then we only add in templates if they don't match something else.
467 // In this case (eg from expanding imported stylesheets)
468 // there can't be any duplicate named templates, so just look for matches
469 // we already have the one with highest import precedence (from the top most level) so don't add any more in
470 if (GSXML.getElementsWithAttributesNS(mainFormat, "http://www.w3.org/1999/XSL/Transform", "template", attributeNames, attributeValues).getLength() == 0 && GSXML.getElementsWithAttributesNS(mainFormat, "http://www.greenstone.org/greenstone3/schema/ConfigFormat", "template", attributeNames, attributeValues).getLength() == 0)
471 {
472 mainFormat.appendChild(node);
473 }
474 }
475 }
476
[26055]477 gsfChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.GSF_NAMESPACE, "option");
[25982]478 for (int i = 0; i < gsfChildren.getLength(); i++)
479 {
480 Element node = (Element) gsfChildren.item(i);
[26055]481 if (GSXML.getNamedElementNS(mainFormat, GSXML.GSF_NAMESPACE, "option", "name", node.getAttribute("name")) == null)
[25982]482 {
483 mainFormat.appendChild(node);
484 }
485 }
486 }
[3450]487}
Note: See TracBrowser for help on using the repository browser.