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
Line 
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 */
19package org.greenstone.gsdl3.util;
20
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;
28import org.w3c.dom.Node;
29import org.w3c.dom.NodeList;
30
31import org.apache.log4j.*;
32
33/** various functions for manipulating Greenstone xslt */
34public class GSXSLT
35{
36
37 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.GSXSLT.class.getName());
38
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
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
47 * just added to the end of the stylesheet
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
52 */
53 public static void mergeStylesheetsDebug(Document main_xsl, Element extra_xsl, boolean overwrite, boolean debug, String firstDocFileName, String secondDocFileName)
54 {
55 if (debug)
56 {
57 System.err.println("ADDING DEBUG ELEMENTS WITH FILE NAME " + firstDocFileName);
58 insertDebugElements(main_xsl, firstDocFileName);
59 }
60
61 Element main = main_xsl.getDocumentElement();
62 Node insertion_point = null;
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
73 // imports
74 NodeList children = extra_xsl.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "import");
75 for (int i = 0; i < children.getLength(); i++)
76 {
77 Element node = (Element) children.item(i);
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
80 if (GSXML.getNamedElementNS(main, "http://www.w3.org/1999/XSL/Transform", "import", "href", node.getAttribute("href")) == null)
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>
86 //main.insertBefore(main_xsl.importNode(node, true), main.getFirstChild());
87 main.insertBefore(main_xsl.importNode(node, true), insertion_point);
88 }
89 }
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
98 // includes
99 children = extra_xsl.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "include");
100 for (int i = 0; i < children.getLength(); i++)
101 {
102 Element node = (Element) children.item(i);
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>
107 if (GSXML.getNamedElementNS(main, "http://www.w3.org/1999/XSL/Transform", "include", "href", node.getAttribute("href")) == null)
108 {
109 //main.appendChild(main_xsl.importNode(node, true));
110 main.insertBefore(main_xsl.importNode(node, true), insertion_point);
111 }
112 } // for each include
113
114 if (main.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "output").getLength() == 0)
115 {
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 }
126 }
127
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");
131 for (int i = 0; i < children.getLength(); i++)
132 {
133 Element node = (Element) children.item(i);
134 // If the new xsl:import element is identical (in terms of href attr value)
135 // to any in the merged document, don't copy it over
136 if (GSXML.getNamedElementNS(main, "http://www.w3.org/1999/XSL/Transform", "variable", "name", node.getAttribute("name")) == null)
137 {
138 main.appendChild(main_xsl.importNode(node, true));
139 }
140 }
141
142 // params - only top level ones!!
143 // append to end of document
144 children = GSXML.getChildrenByTagNameNS(extra_xsl, "http://www.w3.org/1999/XSL/Transform", "param");
145 for (int i = 0; i < children.getLength(); i++)
146 {
147 Element node = (Element) children.item(i);
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
150 if (GSXML.getNamedElementNS(main, "http://www.w3.org/1999/XSL/Transform", "param", "name", node.getAttribute("name")) == null)
151 {
152 main.appendChild(main_xsl.importNode(node, true));
153 }
154 }
155
156 // templates
157 // append to end of document
158 children = extra_xsl.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "template");
159 for (int i = 0; i < children.getLength(); i++)
160 {
161 Element node = (Element) children.item(i);
162 // remove any previous occurrences of xsl:template with the same value for name or match
163 String template_match = node.getAttribute("match");
164 String template_name = node.getAttribute("name");
165 String template_mode = node.getAttribute("mode");
166
167 if (overwrite)
168 {
169 // if we have a name attribute, remove any other similarly named template
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 });
171
172 // now add our good template in
173 main.appendChild(main_xsl.importNode(node, true));
174 }
175 else
176 {
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
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)
182 {
183 main.appendChild(main_xsl.importNode(node, true));
184 }
185 }
186 }
187
188 if (debug)
189 {
190 System.err.println("ADDING DEBUG ELEMENTS WITH FILE NAME " + secondDocFileName);
191 insertDebugElements(main_xsl, secondDocFileName);
192 }
193 }
194
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();
205
206 while (xslParent.getNamespaceURI() != "http://www.w3.org/1999/XSL/Transform" && !xslParent.getNodeName().startsWith("xsl:"))
207 {
208 xslParent = (Element) xslParent.getParentNode();
209 }
210
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
222 public static void inlineImportAndIncludeFiles(Document doc, String pathExtra, String site, String collection, String interface_name, ArrayList<String> base_interfaces)
223 {
224 inlineImportAndIncludeFilesDebug(doc, pathExtra, false, null, site, collection, interface_name, base_interfaces);
225 }
226
227 public static void inlineImportAndIncludeFilesDebug(Document doc, String pathExtra, boolean debug, String docFileName, String site, String collection, String interface_name, ArrayList<String> base_interfaces)
228 {
229 XMLConverter converter = new XMLConverter();
230
231 String path = (pathExtra == null) ? "" : pathExtra;
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");
240 //String filePath = GSFile.interfaceHome(GlobalProperties.getGSDL3Home(), interface_name) + File.separator + "transform" + File.separator + path.replace("/", File.separator) + href.replace("/", File.separator);
241 //String filePath = GSFile.stylesheetFile(GlobalProperties.getGSDL3Home(), site_name, collection, interface_name, base_interfaces,
242
243 try
244 {
245 //Document inlineDoc = converter.getDOM(new File(filePath), "UTF-8");
246 Document inlineDoc = mergedXSLTDocumentCascade(path+href, site, collection, interface_name, base_interfaces, debug);
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
255 inlineImportAndIncludeFilesDebug(inlineDoc, newPath, debug, "merged "+href/*filePath*/, site, collection, interface_name, base_interfaces);
256
257 GSXSLT.mergeStylesheetsDebug(doc, inlineDoc.getDocumentElement(), false, debug, docFileName, /*filePath*/"merged "+href);
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
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
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
354 /**
355 * takes any import or include nodes, and creates absolute path names for
356 * the files
357 */
358 public static void absoluteIncludePaths(Document stylesheet, String gsdl3_home, String site_name, String collection, String interface_name, ArrayList<String> base_interfaces)
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 }
375 }
376
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 */
383 public static Vector<String> extractWantedMetadata(Document stylesheet, String template_name)
384 {
385
386 Vector<String> metadata = new Vector<String>();
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 }
415 }
416 return metadata;
417 }
418
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 }
488}
Note: See TracBrowser for help on using the repository browser.