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

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

pass interface name to inlineImport... method, don't hard code default. Added new method, GSXLST.mergedXSLTDocumentCascade, which looks for a named stylesheet in collection/site/interface/base-interfaces, then merges them all together and returns the Document. Code copied from getXSLTDocument in TransformingReceptionist, which now calls this method

  • Property svn:keywords set to Author Date Id Revision
File size: 20.2 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 interface_name)
223 {
224 inlineImportAndIncludeFilesDebug(doc, pathExtra, false, null, interface_name);
225 }
226
227 public static void inlineImportAndIncludeFilesDebug(Document doc, String pathExtra, boolean debug, String docFileName, String interface_name)
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
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, filePath, interface_name);
256
257 GSXSLT.mergeStylesheetsDebug(doc, inlineDoc.getDocumentElement(), false, debug, docFileName, filePath);
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.