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

Last change on this file since 25988 was 25988, checked in by kjdon, 12 years ago

inlineImport... now calls mergedXSLTDocumentCascade to find all copies of a particular stylesheet and merge them, before inlining it. needs lots more arguments

  • Property svn:keywords set to Author Date Id Revision
File size: 20.6 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;
[25913]63 Element last_import = GSXML.getLastElementByTagNameNS(main, "http://www.w3.org/1999/XSL/Transform", "import");
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
74 NodeList children = extra_xsl.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "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
[25820]80 if (GSXML.getNamedElementNS(main, "http://www.w3.org/1999/XSL/Transform", "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??
92 Element last_include = GSXML.getLastElementByTagNameNS(main, "http://www.w3.org/1999/XSL/Transform", "include");
93 if (last_include != null)
94 {
95 insertion_point = last_include.getNextSibling();
96 }
97
[25820]98 // includes
[25397]99 children = extra_xsl.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "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>
[25913]107 if (GSXML.getNamedElementNS(main, "http://www.w3.org/1999/XSL/Transform", "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
[25914]114 if (main.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "output").getLength() == 0)
[25913]115 {
[25914]116 // outputs
117 children = extra_xsl.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "output");
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
130 children = GSXML.getChildrenByTagNameNS(extra_xsl, "http://www.w3.org/1999/XSL/Transform", "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
[25914]136 if (GSXML.getNamedElementNS(main, "http://www.w3.org/1999/XSL/Transform", "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
[25914]144 children = GSXML.getChildrenByTagNameNS(extra_xsl, "http://www.w3.org/1999/XSL/Transform", "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
[25914]150 if (GSXML.getNamedElementNS(main, "http://www.w3.org/1999/XSL/Transform", "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
[25389]158 children = extra_xsl.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "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
[25982]170 GSXML.removeElementsWithAttributesNS(main, "http://www.w3.org/1999/XSL/Transform", "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
[25982]181 if (GSXML.getElementsWithAttributesNS(main, "http://www.w3.org/1999/XSL/Transform", "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
[25656]206 while (xslParent.getNamespaceURI() != "http://www.w3.org/1999/XSL/Transform" && !xslParent.getNodeName().startsWith("xsl:"))
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
[25988]222 public static void inlineImportAndIncludeFiles(Document doc, String pathExtra, String site, String collection, String interface_name, ArrayList<String> base_interfaces)
[25656]223 {
[25988]224 inlineImportAndIncludeFilesDebug(doc, pathExtra, false, null, site, collection, interface_name, base_interfaces);
[25656]225 }
226
[25988]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
233 NodeList importList = doc.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "import");
234 NodeList includeList = doc.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "include");
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 {
[25988]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
[25988]255 inlineImportAndIncludeFilesDebug(inlineDoc, newPath, debug, "merged "+href/*filePath*/, site, collection, interface_name, base_interfaces);
[25914]256
[25988]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
[25987]278 public static Document mergedXSLTDocumentCascade(String xslt_filename, String site, String collection, String this_interface, ArrayList<String> base_interfaces, boolean debug) {
279 XMLConverter converter = new XMLConverter();
280 // find the list of stylesheets with this name
281 ArrayList<File> stylesheets = GSFile.getStylesheetFiles(GlobalProperties.getGSDL3Home(), site, collection, this_interface, base_interfaces, xslt_filename);
282 if (stylesheets.size() == 0)
283 {
284 logger.error(" Can't find stylesheet for " + xslt_filename);
285 return null;
286 }
287 logger.debug("Stylesheet: " + xslt_filename);
288
289 Document finalDoc = converter.getDOM(stylesheets.get(stylesheets.size() - 1), "UTF-8");
290 if (finalDoc == null)
291 {
292 return null;
293 }
294
295 for (int i = stylesheets.size() - 2; i >= 0; i--)
296 {
297 Document currentDoc = converter.getDOM(stylesheets.get(i), "UTF-8");
298 if (currentDoc == null)
299 {
300 return null;
301 }
302
303 if (debug)
304 {
305 GSXSLT.mergeStylesheetsDebug(finalDoc, currentDoc.getDocumentElement(), true, true, stylesheets.get(stylesheets.size() - 1).getAbsolutePath(), stylesheets.get(i).getAbsolutePath());
306 }
307 else
308 {
309 GSXSLT.mergeStylesheets(finalDoc, currentDoc.getDocumentElement(), true);
310 }
311 }
312
313 return finalDoc;
314 }
315
316
317
[25656]318 public static void modifyConfigFormatForDebug(Document doc, String fileName)
319 {
320 NodeList templateNodes = doc.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "template");
321 if (templateNodes.getLength() == 0)
322 {
323 templateNodes = doc.getElementsByTagName("xsl:template");
324 }
325
326 String debugElementString = "";
327 debugElementString += "<span class=\"configDebugSpan\" style=\"display:none;\">";
328 debugElementString += " \"filename\":\"" + fileName + "\",";
329 debugElementString += " \"xml\":\"<xsl:value-of select=\"util:xmlNodeToString(.)\"/>\""; //<xsl:copy><xsl:copy-of select=\"@*\"/></xsl:copy>
330 debugElementString += "</span>";
331
332 XMLConverter converter = new XMLConverter();
333 Element debugElement = (Element) converter.getDOM("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xslt:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:xslt=\"output.xsl\" xmlns:gsf=\"http://www.greenstone.org/greenstone3/schema/ConfigFormat\">" + debugElementString + "</xslt:stylesheet>").getDocumentElement().getFirstChild();
334
335 for (int i = 0; i < templateNodes.getLength(); i++)
336 {
337 Element currentTemplate = (Element) templateNodes.item(i);
338 if (currentTemplate.getAttribute("match") != null && (currentTemplate.getAttribute("match").equals("gsf:metadata") || currentTemplate.getAttribute("match").equals("*") || currentTemplate.getAttribute("match").equals("format")))
339 {
340 continue;
341 }
342
343 if (currentTemplate.hasChildNodes())
344 {
345 currentTemplate.insertBefore(doc.importNode(debugElement.cloneNode(true), true), currentTemplate.getFirstChild());
346 }
347 else
348 {
349 currentTemplate.appendChild(doc.importNode(debugElement.cloneNode(true), true));
350 }
351 }
352 }
353
[24864]354 /**
355 * takes any import or include nodes, and creates absolute path names for
356 * the files
357 */
[25635]358 public static void absoluteIncludePaths(Document stylesheet, String gsdl3_home, String site_name, String collection, String interface_name, ArrayList<String> base_interfaces)
[24864]359 {
360 Element base_node = stylesheet.getDocumentElement();
361 if (base_node == null)
362 {
363 return;
364 }
365 Node child = base_node.getFirstChild();
366 while (child != null)
367 {
368 String name = child.getNodeName();
369 if (name.equals("xsl:import") || name.equals("xsl:include"))
370 {
371 ((Element) child).setAttribute("href", GSFile.stylesheetFile(gsdl3_home, site_name, collection, interface_name, base_interfaces, ((Element) child).getAttribute("href")));
372 }
373 child = child.getNextSibling();
374 }
[20139]375 }
[4079]376
[24864]377 /**
378 * looks through a stylesheet for <xxx:template match='template_name'>
379 * inside this template it looks for any <xxx:value-of
380 * select='metadataList/metadata[@name=yyy]> elements, and extracts the
381 * metadata names into a Vector
382 */
[25635]383 public static Vector<String> extractWantedMetadata(Document stylesheet, String template_name)
[24864]384 {
385
[25635]386 Vector<String> metadata = new Vector<String>();
[24864]387 Element base_node = stylesheet.getDocumentElement();
388 NodeList templates = base_node.getElementsByTagNameNS("*", "template");
389 for (int i = 0; i < templates.getLength(); i++)
390 {
391 Element template = (Element) templates.item(i);
392 String match_name = template.getAttribute("match");
393 if (!match_name.equals(template_name))
394 {
395 continue; // we're only looking for specific templates
396 }
397 String mode = template.getAttribute("mode");
398 if (!mode.equals(""))
399 {
400 continue; // we only want ones without modes - these are processing ones, not display ones
401 }
402 // we have one that we want to look through
403 NodeList values = template.getElementsByTagNameNS("*", "value-of");
404 for (int v = 0; v < values.getLength(); v++)
405 {
406 String select = ((Element) values.item(v)).getAttribute("select");
407 if (select.startsWith("metadataList/metadata[@name="))
408 {
409 String[] bits = select.split("'|\"");
410 // there should be two quotes in teh string, therefore 3 items, and the second one is teh one we want
411 String name = bits[1];
412 metadata.add(name);
413 }
414 }
[4079]415 }
[24864]416 return metadata;
[4079]417 }
418
[25982]419 public static void mergeFormatElements(Element mainFormat, Element secondaryFormat, boolean overwrite)
420 {
421 NodeList xslChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, "http://www.w3.org/1999/XSL/Transform", "variable");
422 NodeList gsfChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, "http://www.greenstone.org/greenstone3/schema/ConfigFormat", "variable");
423 for (int i = 0; i < xslChildren.getLength() + gsfChildren.getLength(); i++)
424 {
425 Element node = (Element) ((i < xslChildren.getLength()) ? xslChildren.item(i) : gsfChildren.item(i - xslChildren.getLength()));
426 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)
427 {
428 mainFormat.appendChild(node);
429 }
430 }
431
432 xslChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, "http://www.w3.org/1999/XSL/Transform", "param");
433 gsfChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, "http://www.greenstone.org/greenstone3/schema/ConfigFormat", "param");
434 for (int i = 0; i < xslChildren.getLength() + gsfChildren.getLength(); i++)
435 {
436 Element node = (Element) ((i < xslChildren.getLength()) ? xslChildren.item(i) : gsfChildren.item(i - xslChildren.getLength()));
437 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)
438 {
439 mainFormat.appendChild(node);
440 }
441 }
442
443 xslChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, "http://www.w3.org/1999/XSL/Transform", "template");
444 gsfChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, "http://www.greenstone.org/greenstone3/schema/ConfigFormat", "template");
445 for (int i = 0; i < xslChildren.getLength() + gsfChildren.getLength(); i++)
446 {
447 Element node = (Element) ((i < xslChildren.getLength()) ? xslChildren.item(i) : gsfChildren.item(i - xslChildren.getLength()));
448 // remove any previous occurrences of xsl:template with the same value for name or match
449 String template_match = node.getAttribute("match");
450 String template_name = node.getAttribute("name");
451 String template_mode = node.getAttribute("mode");
452
453 String[] attributeNames = new String[] { "name", "match", "mode" };
454 String[] attributeValues = new String[] { template_name, template_match, template_mode };
455
456 if (overwrite)
457 {
458 // if we have a name attribute, remove any other similarly named template
459 GSXML.removeElementsWithAttributesNS(mainFormat, "http://www.w3.org/1999/XSL/Transform", "template", attributeNames, attributeValues);
460 GSXML.removeElementsWithAttributesNS(mainFormat, "http://www.greenstone.org/greenstone3/schema/ConfigFormat", "template", attributeNames, attributeValues);
461
462 // now add our good template in
463 mainFormat.appendChild(node);
464 }
465 else
466 {
467 // if overwrite is false, then we only add in templates if they don't match something else.
468 // In this case (eg from expanding imported stylesheets)
469 // there can't be any duplicate named templates, so just look for matches
470 // we already have the one with highest import precedence (from the top most level) so don't add any more in
471 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)
472 {
473 mainFormat.appendChild(node);
474 }
475 }
476 }
477
478 gsfChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, "http://www.greenstone.org/greenstone3/schema/ConfigFormat", "option");
479 for (int i = 0; i < gsfChildren.getLength(); i++)
480 {
481 Element node = (Element) gsfChildren.item(i);
482 if (GSXML.getNamedElementNS(mainFormat, "http://www.greenstone.org/greenstone3/schema/ConfigFormat", "option", "name", node.getAttribute("name")) == null)
483 {
484 mainFormat.appendChild(node);
485 }
486 }
487 }
[3450]488}
Note: See TracBrowser for help on using the repository browser.