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

Last change on this file since 26055 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
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, GSXML.XSL_NAMESPACE, "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(GSXML.XSL_NAMESPACE, "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, GSXML.XSL_NAMESPACE, "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, GSXML.XSL_NAMESPACE, "include");
93 if (last_include != null)
94 {
95 insertion_point = last_include.getNextSibling();
96 }
97
98 // includes
99 children = extra_xsl.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "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, GSXML.XSL_NAMESPACE, "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(GSXML.XSL_NAMESPACE, "output").getLength() == 0)
115 {
116 // outputs
117 children = extra_xsl.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "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, GSXML.XSL_NAMESPACE, "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, GSXML.XSL_NAMESPACE, "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, GSXML.XSL_NAMESPACE, "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, GSXML.XSL_NAMESPACE, "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(GSXML.XSL_NAMESPACE, "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, GSXML.XSL_NAMESPACE, "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, GSXML.XSL_NAMESPACE, "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() != GSXML.XSL_NAMESPACE && !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(GSXML.XSL_NAMESPACE, "import");
234 NodeList includeList = doc.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "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 {
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);
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
317 public static void modifyConfigFormatForDebug(Document doc, String fileName)
318 {
319 NodeList templateNodes = doc.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "template");
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();
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();
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
353 /**
354 * takes any import or include nodes, and creates absolute path names for
355 * the files
356 */
357 public static void absoluteIncludePaths(Document stylesheet, String gsdl3_home, String site_name, String collection, String interface_name, ArrayList<String> base_interfaces)
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 }
374 }
375
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 */
382 public static Vector<String> extractWantedMetadata(Document stylesheet, String template_name)
383 {
384
385 Vector<String> metadata = new Vector<String>();
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 }
414 }
415 return metadata;
416 }
417
418 public static void mergeFormatElements(Element mainFormat, Element secondaryFormat, boolean overwrite)
419 {
420 NodeList xslChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.XSL_NAMESPACE, "variable");
421 NodeList gsfChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.GSF_NAMESPACE, "variable");
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
431 xslChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.XSL_NAMESPACE, "param");
432 gsfChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.GSF_NAMESPACE, "param");
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
442 xslChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.XSL_NAMESPACE, "template");
443 gsfChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.GSF_NAMESPACE, "template");
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
458 GSXML.removeElementsWithAttributesNS(mainFormat, GSXML.XSL_NAMESPACE, "template", attributeNames, attributeValues);
459 GSXML.removeElementsWithAttributesNS(mainFormat, GSXML.GSF_NAMESPACE, "template", attributeNames, attributeValues);
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
477 gsfChildren = GSXML.getChildrenByTagNameNS(secondaryFormat, GSXML.GSF_NAMESPACE, "option");
478 for (int i = 0; i < gsfChildren.getLength(); i++)
479 {
480 Element node = (Element) gsfChildren.item(i);
481 if (GSXML.getNamedElementNS(mainFormat, GSXML.GSF_NAMESPACE, "option", "name", node.getAttribute("name")) == null)
482 {
483 mainFormat.appendChild(node);
484 }
485 }
486 }
487}
Note: See TracBrowser for help on using the repository browser.