source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/XSLTUtil.java@ 36508

Last change on this file since 36508 was 36508, checked in by kjdon, 20 months ago

new method getCollectionDisplayItemText - you pass in a node containing a list of displayItem elements, and it will choose the bestone that matches the current lang. displayItems can use dictionary keys instead of text

  • Property svn:keywords set to Author Date Id Revision
File size: 29.0 KB
Line 
1/*
2 * XSLTUtil.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 */
19
20/**
21 * Note (cstephen):
22 * Use of org.apache.commons.lang3.StringUtils can likely be removed when compiling
23 * for Java 1.13+, due to performance improvements in the standard library.
24 * https://stackoverflow.com/questions/16228992/commons-lang-stringutils-replace-performance-vs-string-replace
25 */
26
27package org.greenstone.gsdl3.util;
28
29import java.io.ByteArrayInputStream;
30import java.io.File;
31import java.io.UnsupportedEncodingException;
32import java.net.URLEncoder;
33import java.text.DateFormat;
34import java.text.SimpleDateFormat;
35import java.util.ArrayList;
36import java.util.Date;
37import java.util.Enumeration;
38import java.util.HashMap;
39import java.util.HashSet;
40import java.util.Locale;
41
42import javax.xml.parsers.DocumentBuilder;
43import javax.xml.parsers.DocumentBuilderFactory;
44
45import net.tanesha.recaptcha.ReCaptcha;
46import net.tanesha.recaptcha.ReCaptchaFactory;
47
48import org.apache.commons.lang3.StringUtils;
49import org.apache.log4j.Logger;
50import org.greenstone.util.GlobalProperties;
51import org.w3c.dom.Node;
52import org.w3c.dom.Element;
53import org.w3c.dom.Document;
54
55/**
56 * a class to contain various static methods that are used by the xslt
57 * stylesheets
58 */
59public class XSLTUtil
60{
61 protected static HashMap<String, ArrayList<String>> _foundTableValues = new HashMap<String, ArrayList<String>>();
62 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.XSLTUtil.class.getName());
63 protected static HashMap<String, String> _stringVariables = new HashMap<String, String>();
64
65 public static void storeString(String name, String value)
66 {
67 _stringVariables.put(name, value);
68 }
69
70 public static String getString(String name)
71 {
72 return _stringVariables.get(name);
73 }
74
75 /* some tests */
76 public static boolean equals(String s1, String s2)
77 {
78 return s1.equals(s2);
79 }
80
81 public static boolean notEquals(String s1, String s2)
82 {
83 return !s1.equals(s2);
84 }
85
86 public static boolean exists(String s1, String s2)
87 {
88 return !s1.equals("");
89 }
90
91 public static boolean contains(String s1, String s2)
92 {
93 return (s1.indexOf(s2) != -1);
94 }
95
96 public static boolean startsWith(String s1, String s2)
97 {
98 return s1.startsWith(s2);
99 }
100
101 public static boolean endsWith(String s1, String s2)
102 {
103 return s1.endsWith(s2);
104 }
105
106 public static boolean lessThan(String s1, String s2)
107 {
108 return (s1.compareTo(s2) < 0);
109 }
110
111 public static boolean lessThanOrEquals(String s1, String s2)
112 {
113 return (s1.compareTo(s2) <= 0);
114 }
115
116 public static boolean greaterThan(String s1, String s2)
117 {
118 return (s1.compareTo(s2) > 0);
119 }
120
121 public static boolean greaterThanOrEquals(String s1, String s2)
122 {
123 return (s1.compareTo(s2) >= 0);
124 }
125
126 public static boolean csvContains(String user_groups, String this_group)
127 {
128 String[] groups_array = user_groups.split(",");
129 for (int i=0; i<groups_array.length; i++) {
130 if (groups_array[i].equals(this_group)) {
131 return true;
132 }
133 }
134 return false;
135
136 }
137 public static boolean oidIsMatchOrParent(String first, String second)
138 {
139 if (first.equals(second))
140 {
141 return true;
142 }
143
144 String[] firstParts = first.split(".");
145 String[] secondParts = second.split(".");
146
147 if (firstParts.length >= secondParts.length)
148 {
149 return false;
150 }
151
152 for (int i = 0; i < firstParts.length; i++)
153 {
154 if (!firstParts[i].equals(secondParts[i]))
155 {
156 return false;
157 }
158 }
159
160 return true;
161 }
162
163 public static String oidDocumentRoot(String oid)
164 {
165 String[] oidParts = oid.split("\\.");
166
167 return oidParts[0];
168 }
169
170 public static String replace(String orig, String match, String replacement)
171 {
172 return orig.replace(match, replacement);
173 }
174
175 public static String getNumberedItem(String list, int number)
176 {
177 String[] items = list.split(",", -1); //String[] items = StringUtils.split(list, ",", -1);
178 // Using StringUtils.split() causes an off-by-one error for the boolean operators (fqk)
179 // where boolean operators combining rows in multiforms are shifted up by 1 row.
180
181 if (items.length > number)
182 {
183 return items[number];
184 }
185 return ""; // index out of bounds
186 }
187
188 public static String getFormattedCCSSelection(String collections, String groups) {
189
190 String result = ",";
191 if (collections != null && !collections.equals("")) {
192 result += collections+",";
193 }
194 if (groups != null && !groups.equals("")) {
195 String[] gps = groups.split(",");
196 for (int i=0; i<gps.length; i++) {
197 result += "group."+gps[i].replace('/','.') +",";
198 }
199 }
200 return result;
201 }
202 /**
203 * Generates links to equivalent documents for a document with a default
204 * document icon/type. Links are generated from the parameters: a list of
205 * document icons which are each in turn embedded in the matching starting
206 * link tag in the list of docStartLinks (these starting links link to the
207 * equivalent documents in another format). Each link's start tag is closed
208 * with the corresponding closing tag in the docEndLinks list. Parameter
209 * token is the list separator. Parameter divider is the string that should
210 * separate each final link generated from the next. Returns a string that
211 * represents a sequence of links to equivalent documents, where the anchor
212 * is a document icon.
213 */
214 public static String getEquivDocLinks(String token, String docIconsString, String docStartLinksString, String docEndLinksString, String divider)
215 {
216 String[] docIcons = StringUtils.split(docIconsString, token, -1);
217 String[] startLinks = StringUtils.split(docStartLinksString, token, -1);
218 String[] endLinks = StringUtils.split(docEndLinksString, token, -1);
219
220 StringBuffer buffer = new StringBuffer();
221 for (int i = 0; i < docIcons.length; i++)
222 {
223 if (i > 0)
224 {
225 buffer.append(divider);
226 }
227 buffer.append(startLinks[i] + docIcons[i] + endLinks[i]);
228 }
229
230 return buffer.toString();
231 }
232
233 public static String getInterfaceText(String interface_name, String lang, String key)
234 {
235 return getInterfaceTextWithArgs(interface_name, lang, key, null);
236 }
237
238 public static String getInterfaceText(String interface_name, String lang, String key, String args_str)
239 {
240 String[] args = null;
241 if (args_str != null && !args_str.equals(""))
242 {
243 args = StringUtils.split(args_str, ";");
244 }
245 return getInterfaceTextWithArgs(interface_name, lang, key, args);
246 }
247
248 public static String getInterfaceText(String interface_name, String dictionary_name, String lang, String key, String args_str)
249 {
250 // now we allow looking for files in the interface's resources folder
251 CustomClassLoader my_loader = new CustomClassLoader(XSLTUtil.class.getClassLoader(), GSFile.interfaceResourceDir(GlobalProperties.getGSDL3Home(), interface_name));
252 String[] args = null;
253 if (args_str != null && !args_str.equals(""))
254 {
255 args = StringUtils.split(args_str, ";");
256 }
257
258 // try the specified dictionary first
259 Dictionary dict = new Dictionary(dictionary_name, lang, my_loader);
260 String result = dict.get(key, args);
261 if (result == null) {
262 // fall back to a general interface text search
263 return getInterfaceTextWithArgs(interface_name, lang, key, args);
264 }
265 result = result.replaceAll("__INTERFACE_NAME__", interface_name); // do we need to so this here?
266 return result;
267 }
268
269 public static String getInterfaceTextWithDOM(String interface_name, String lang, String key, Node arg_node)
270 {
271 return getInterfaceTextWithDOMMulti(interface_name, lang, key, arg_node);
272 }
273
274 public static String getInterfaceTextWithDOM(String interface_name, String lang, String key, Node arg1_node, Node arg2_node)
275 {
276 return getInterfaceTextWithDOMMulti(interface_name, lang, key, arg1_node, arg2_node);
277 }
278
279 public static String getInterfaceTextWithDOMMulti(String interface_name, String lang, String key, Node... nodes)
280 {
281 int num_nodes = nodes.length;
282 String[] args = null;
283 if (num_nodes != 0)
284 {
285 args = new String[num_nodes];
286
287 for (int i = 0; i < num_nodes; i++)
288 {
289 String node_str = XMLConverter.getString(nodes[i]);
290 args[i] = node_str;
291 }
292 }
293 return getInterfaceTextWithArgs(interface_name, lang, key, args);
294
295 }
296
297 /* This is the base method that actually does the work of looking up the various chain of dictionaries */
298 public static String getInterfaceTextWithArgs(String interface_name, String lang, String key, String[] args)
299 {
300 // now we allow looking for files in the interface's resources folder
301 CustomClassLoader my_loader = new CustomClassLoader(XSLTUtil.class.getClassLoader(), GSFile.interfaceResourceDir(GlobalProperties.getGSDL3Home(), interface_name));
302 Dictionary dict = new Dictionary("interface_" + interface_name, lang, my_loader);
303
304 String result = dict.get(key, args);
305 if (result == null)
306 {
307 //if not found, search a separate subdirectory named by the interface name. this is used for eg the flax interface. this could be replaced by new class loader option?
308 String sep_interface_dir = interface_name + File.separatorChar + lang + File.separatorChar + "interface";
309 dict = new Dictionary(sep_interface_dir, lang);
310 result = dict.get(key, args);
311 if (result != null)
312 {
313 return result;
314 }
315 }
316
317 if (result == null && !interface_name.startsWith("default")) {
318 // not found, try the default interface
319 if (interface_name.endsWith("2")) { // hack for interface_xxx2.properties
320 dict = new Dictionary("interface_default2", lang);
321 } else {
322 dict = new Dictionary("interface_default", lang);
323
324 }
325 result = dict.get(key, args);
326 }
327
328 if (result == null)
329 { // not found
330 return "?" + key + "?";
331 }
332 result = result.replaceAll("__INTERFACE_NAME__", interface_name);
333
334 return result;
335
336
337
338 }
339 public static String getInterfaceTextSubstituteArgs(String value, String args_str)
340 {
341 String[] args = null;
342 if (args_str != null && !args_str.equals("")) {
343 args = StringUtils.split(args_str, ";");
344 }
345
346 return Dictionary.processArgs(value,args);
347 }
348
349 public static Node getCollectionText(String collection, String site_name, String lang, String key)
350 {
351 return getCollectionTextWithArgs(collection, site_name, lang, key, null);
352 }
353
354 public static Node getCollectionText(String collection, String site_name, String lang, String key, String args_str)
355 {
356
357 String[] args = null;
358 if (args_str != null && !args_str.equals(""))
359 {
360 args = StringUtils.split(args_str, ";");
361 }
362
363 return getCollectionTextWithArgs(collection, site_name, lang, key, args);
364 }
365
366 // xslt didn't like calling the function with Node varargs, so have this hack for now
367 public static Node getCollectionTextWithDOM(String collection, String site_name, String lang, String key, Node n1)
368 {
369 return getCollectionTextWithDOMMulti(collection, site_name, lang, key, n1);
370 }
371
372 public static Node getCollectionTextWithDOM(String collection, String site_name, String lang, String key, Node n1, Node n2)
373 {
374 return getCollectionTextWithDOMMulti(collection, site_name, lang, key, n1, n2);
375 }
376
377 public static Node getCollectionTextWithDOM(String collection, String site_name, String lang, String key, Node n1, Node n2, Node n3)
378 {
379 return getCollectionTextWithDOMMulti(collection, site_name, lang, key, n1, n2, n3);
380 }
381
382 public static Node getCollectionTextWithDOM(String collection, String site_name, String lang, String key, Node n1, Node n2, Node n3, Node n4)
383 {
384 return getCollectionTextWithDOMMulti(collection, site_name, lang, key, n1, n2, n3, n4);
385 }
386
387 public static Node getCollectionTextWithDOMMulti(String collection, String site_name, String lang, String key, Node... nodes)
388 {
389 int num_nodes = nodes.length;
390 String[] args = null;
391 if (num_nodes != 0)
392 {
393 args = new String[num_nodes];
394
395 for (int i = 0; i < num_nodes; i++)
396 {
397 String node_str = XMLConverter.getString(nodes[i]);
398 args[i] = node_str;
399 }
400 }
401 return getCollectionTextWithArgs(collection, site_name, lang, key, args);
402 }
403
404 public static Node getCollectionTextWithArgs(String collection, String site_name, String lang, String key, String[] args)
405 {
406 try
407 {
408 DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
409
410 CustomClassLoader class_loader = new CustomClassLoader(XSLTUtil.class.getClassLoader(), GSFile.collectionResourceDir(GSFile.siteHome(GlobalProperties.getGSDL3Home(), site_name), collection));
411 Dictionary dict = new Dictionary("interface_custom", lang, class_loader);
412 String result = dict.get(key, args);
413 if (result != null)
414 {
415 return docBuilder.parse(new ByteArrayInputStream(("<fragment>" + result + "</fragment>").getBytes("UTF-8"))).getDocumentElement();
416 }
417 return docBuilder.parse(new ByteArrayInputStream(("<fragment>" + "text:" + collection + ":" + key + "</fragment>").getBytes())).getDocumentElement();
418 }
419 catch (Exception ex)
420 {
421 return null;
422 }
423 }
424
425 public static String getGenericText(String dictionary_name, String lang, String key) {
426 return getGenericTextWithArgs(dictionary_name, lang, key, null);
427 }
428
429 public String getGenericText(String dictionary_name, String lang, String key, String args_str) {
430 String[] args = null;
431 if (args_str != null && !args_str.equals("")) {
432 args = StringUtils.split(args_str, ";");
433 }
434 return getGenericTextWithArgs(dictionary_name, lang, key, args);
435 }
436
437 public static String getGenericTextWithArgs(String dictionary_name, String lang, String key, String[] args)
438 {
439 Dictionary dict = new Dictionary(dictionary_name, lang);
440 String result = dict.get(key, args);
441 if (result == null) {
442 return "_"+dictionary_name+"_"+key+"_";
443 }
444 return result;
445 }
446
447 /** handle displayItems from xslt */
448 public static String getCollectionDisplayItemText(Node display_item_list, String lang, String site_name, String collection) {
449
450 CustomClassLoader class_loader = new CustomClassLoader(XSLTUtil.class.getClassLoader(), GSFile.collectionResourceDir(GSFile.siteHome(GlobalProperties.getGSDL3Home(), site_name), collection));
451 Document doc = XMLConverter.newDOM();
452 Element di = DisplayItemUtil.chooseBestMatchDisplayItem(doc, (Element)display_item_list, lang, "en", class_loader) ;
453 if (di == null) {
454 return "";
455 }
456 return GSXML.getNodeText(di);
457
458 }
459
460 public static boolean isImage(String mimetype)
461 {
462 if (mimetype.startsWith("image/"))
463 {
464 return true;
465 }
466 return false;
467 }
468
469 // formatting /preprocessing functions
470 // some require a language, so we'll have a language param for all
471 public static String toLower(String orig, String lang)
472 {
473 return orig.toLowerCase();
474 }
475
476 public static String toUpper(String orig, String lang)
477 {
478 return orig.toUpperCase();
479 }
480
481 public static String tidyWhitespace(String original, String lang)
482 {
483
484 if (original == null || original.equals(""))
485 {
486 return original;
487 }
488 String new_s = original.replaceAll("\\s+", " ");
489 return new_s;
490 }
491
492 public static String stripWhitespace(String original, String lang)
493 {
494
495 if (original == null || original.equals(""))
496 {
497 return original;
498 }
499 String new_s = original.replaceAll("\\s+", "");
500 return new_s;
501 }
502
503 public static byte[] toUTF8(String orig, String lang)
504 {
505 try
506 {
507 byte[] utf8 = orig.getBytes("UTF-8");
508 return utf8;
509 }
510 catch (Exception e)
511 {
512 logger.error("unsupported encoding");
513 return orig.getBytes();
514 }
515 }
516
517 public static String formatDate(String date, String lang)
518 {
519 String in_pattern = "yyyyMMdd";
520 String out_pattern = "dd MMMM yyyy";
521 if (date.length() == 6)
522 {
523 in_pattern = "yyyyMM";
524 out_pattern = "MMMM yyyy";
525 }
526 // remove the 00
527 else if (date.length() == 8 && date.endsWith("00")) {
528 date = date.substring(0,6);
529 in_pattern = "yyyyMM";
530 out_pattern = "MMMM yyyy";
531 }
532
533 SimpleDateFormat formatter = new SimpleDateFormat(in_pattern, new Locale(lang));
534 try
535 {
536 Date d = formatter.parse(date);
537 formatter.applyPattern(out_pattern);
538 String new_date = formatter.format(d);
539 return new_date;
540 }
541 catch (Exception e)
542 {
543 return date;
544 }
545
546 }
547
548 public static final int TS_SECS = 0;
549 public static final int TS_MILLISECS = 1;
550 public static final int F_DATE = 0;
551 public static final int F_TIME = 1;
552 public static final int F_DATETIME = 2;
553 public static final int F_DAYSAGO = 3;
554
555 public static String formatTimeStamp(String timestamp, int ts_type, int format_type, String lang) {
556 try {
557 long ts = Long.parseLong(timestamp);
558 if (ts_type == TS_SECS) {
559 ts = ts * 1000;
560 }
561 if (format_type == F_DAYSAGO) {
562 long current_time = new Date().getTime();
563 long days = (current_time - ts)/86400000;
564 return String.valueOf(days);
565 }
566 Date d = new Date(ts);
567 DateFormat df;
568 switch (format_type) {
569 case F_DATE:
570 df = DateFormat.getDateInstance(DateFormat.DEFAULT, new Locale(lang));
571 break;
572 case F_TIME:
573 df = DateFormat.getTimeInstance(DateFormat.DEFAULT, new Locale(lang));
574 break;
575 case F_DATETIME:
576 df = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, new Locale(lang));
577 break;
578 default:
579 df = DateFormat.getDateInstance(DateFormat.DEFAULT, new Locale(lang));
580 break;
581 }
582
583 return df.format(d);
584 } catch (Exception e) {
585
586 return timestamp + e.getMessage();
587 }
588
589 }
590 public static String getDetailFromDate(String date, String detail, String lang)
591 {
592 String in_pattern = "yyyyMMdd";
593 if (date.length() == 6)
594 {
595 in_pattern = "yyyyMM";
596 }
597 // remove the 00
598 else if (date.length() == 8 && date.endsWith("00")) {
599 date = date.substring(0,6);
600 in_pattern = "yyyyMM";
601 }
602
603 SimpleDateFormat formatter = new SimpleDateFormat(in_pattern, new Locale(lang));
604 try
605 {
606 Date d = formatter.parse(date);
607 if (detail.toLowerCase().equals("day"))
608 {
609 formatter.applyPattern("dd");
610 }
611 else if (detail.toLowerCase().equals("month"))
612 {
613 formatter.applyPattern("MMMM");
614 }
615 else if (detail.toLowerCase().equals("year"))
616 {
617 formatter.applyPattern("yyyy");
618 }
619 else
620 {
621 return "";
622 }
623 return formatter.format(d);
624 }
625 catch (Exception ex)
626 {
627 return "";
628 }
629 }
630
631 public static String formatLanguage(String display_lang, String lang)
632 {
633
634 return new Locale(display_lang).getDisplayLanguage(new Locale(lang));
635 }
636
637 public static boolean checkFileExistence(String site_name, String filePath){
638
639 String gsdl3_home = GlobalProperties.getGSDL3Home();
640 //Remove leading file separator
641 filePath = filePath.replaceAll("^/+", "");
642 //Remove duplicates and replace by separator for current platform
643 filePath = filePath.replaceAll("/+", File.separator);
644 //Create full path to check
645 String fullPath = GSFile.siteHome(gsdl3_home, site_name) + File.separator + filePath;
646 File file = new File(fullPath);
647 if (file.exists() && file.isFile()) {
648 return true;
649 }
650 return false;
651 }
652
653 public static String uriEncode(String input)
654 {
655 String result = "";
656 try {
657 result = URLEncoder.encode(input, "UTF-8");
658 } catch (UnsupportedEncodingException e) {
659 e.printStackTrace();
660 }
661
662 return result;
663
664 }
665
666 public static String cgiSafe(String original, String lang)
667 {
668
669 original = original.replace('&', ' ');
670 original = original.replaceAll(" ", "%20");
671 return original;
672 }
673
674 public static String formatBigNumber(String num, String lang)
675 {
676
677 String num_str = num;
678 char[] num_chars = num_str.toCharArray();
679 String zero_str = "";
680 String formatted_str = "";
681
682 for (int i = num_chars.length - 4; i >= 0; i--)
683 {
684 zero_str += '0';
685 }
686
687 String sig_str = "";
688 for (int i = 0; i < 3 && i < num_chars.length; i++)
689 {
690 sig_str = sig_str + num_chars[i];
691 if (i == 1 && i + 1 < num_chars.length)
692 {
693 sig_str = sig_str + ".";
694 }
695 }
696
697 int sig_int = Math.round(Float.parseFloat(sig_str));
698 String new_sig_str = sig_int + "";
699 if (sig_str.length() > 2)
700 {
701 new_sig_str = sig_int + "0";
702 }
703
704 char[] final_chars = (new_sig_str + zero_str).toCharArray();
705 int count = 1;
706 for (int i = final_chars.length - 1; i >= 0; i--)
707 {
708 formatted_str = final_chars[i] + formatted_str;
709 if (count == 3 && i != 0)
710 {
711 formatted_str = "," + formatted_str;
712 count = 1;
713 }
714 else
715 {
716 count++;
717 }
718 }
719 return formatted_str;
720 }
721
722 public static String hashToSectionId(String hashString)
723 {
724 if (hashString == null || hashString.length() == 0)
725 {
726 return "";
727 }
728
729 int firstDotIndex = hashString.indexOf(".");
730 if (firstDotIndex == -1)
731 {
732 return "";
733 }
734
735 String sectionString = hashString.substring(firstDotIndex + 1);
736
737 return sectionString;
738 }
739
740 public static String hashToDepthClass(String hashString)
741 {
742 if (hashString == null || hashString.length() == 0)
743 {
744 return "";
745 }
746
747 String sectionString = hashToSectionId(hashString);
748
749 int count = sectionString.split("\\.").length;
750
751 if (sectionString.equals(""))
752 {
753 return "sectionHeaderDepthTitle";
754 }
755 else
756 {
757 return "sectionHeaderDepth" + count;
758 }
759 }
760
761 public static String escapeNewLines(String str)
762 {
763 if (str == null || str.length() < 1)
764 {
765 return null;
766 }
767 return str.replace("\n", "\\\n");
768 }
769
770 public static String escapeQuotes(String str)
771 {
772 if (str == null || str.length() < 1)
773 {
774 return null;
775 }
776 return str.replace("\"", "\\\"");
777 }
778 public static String escapeAngleBrackets(String str)
779 {
780 if (str == null || str.length() < 1)
781 {
782 return null;
783 }
784 return str.replace("<", "&lt;").replace(">", "&gt;");
785 }
786
787 public static String escapeNewLinesAndQuotes(String str)
788 {
789 if (str == null || str.length() < 1)
790 {
791 return null;
792 }
793 return escapeNewLines(escapeQuotes(str));
794 }
795
796 public static String escapeNewLinesQuotesAngleBracketsForJSString(String str)
797 {
798 // The \n and " becomes \\\n and \\\"
799 // but the <> are escaped/encoded for html, i.e. &gt; and &lt;
800 if (str == null || str.length() < 1)
801 {
802 return null;
803 }
804 return escapeAngleBrackets(escapeNewLines(escapeQuotes(str)));
805 }
806 public static String getGlobalProperty(String name)
807 {
808 return GlobalProperties.getProperty(name);
809 }
810
811 public static void clearMetadataStorage()
812 {
813 _foundTableValues.clear();
814 }
815
816 public static boolean checkMetadataNotDuplicate(String name, String value)
817 {
818 if (_foundTableValues.containsKey(name))
819 {
820 for (String mapValue : _foundTableValues.get(name))
821 {
822 if (mapValue.equals(value))
823 {
824 return false;
825 }
826 }
827 _foundTableValues.get(name).add(value);
828 return true;
829 }
830
831 ArrayList<String> newList = new ArrayList<String>();
832 newList.add(value);
833
834 _foundTableValues.put(name, newList);
835
836 return true;
837 }
838
839 public static String reCAPTCHAimage(String publicKey, String privateKey)
840 {
841 ReCaptcha c = ReCaptchaFactory.newReCaptcha(publicKey, privateKey, false);
842 return c.createRecaptchaHtml(null, null);
843 }
844
845 /**
846 * Generates a Javascript object graph to store language strings. Leaf nodes are the key, and their value the string.
847 * Further preceding nodes denote the prefix of the string within the language strings property file.
848 * Accessing a language string from the object graph can be done as such: 'const myString = gs.text.{prefix}.{key};'
849 * @param interfaceName The name of the interface to retrieve language strings for.
850 * @param lang The language to retrieve.
851 * @param prefix The prefix to to retrieve strings under. E.g. a value of 'atea.macroniser' will only retrieve strings prefixed with that value.
852 * @return Stringified Javascript code that will generate the language string object graph.
853 */
854 public static String getInterfaceStringsAsJavascript(String interfaceName, String lang, String prefix)
855 {
856 String prependToPrefix = "gs.text";
857 return XSLTUtil.getInterfaceStringsAsJavascript(interfaceName, lang, prefix, prependToPrefix);
858 }
859
860 /**
861 * Generates a Javascript object graph to store language strings. Leaf nodes are the key, and their value the string.
862 * Further preceding nodes denote the prefix of the string within the language strings property file.
863 * Accessing a language string from the object graph can be done as such: 'const myString = {prependToPrefix}.{prefix}.{key};'
864 * @param interfaceName The name of the interface to retrieve language strings for.
865 * @param lang The language to retrieve.
866 * @param prefix The prefix to to retrieve strings under. E.g. a value of 'atea.macroniser' will only retrieve strings prefixed with that value.
867 * @param prependToPrefix An accessor string to prepend to the generated JS object graph.
868 * @return Stringified Javascript code that will generate the language string object graph.
869 */
870 public static String getInterfaceStringsAsJavascript(String interfaceName, String lang, String prefix, String prependToPrefix)
871 {
872 // now we allow looking for files in the interface's resources folder
873 CustomClassLoader my_loader = new CustomClassLoader(
874 XSLTUtil.class.getClassLoader(),
875 GSFile.interfaceResourceDir(GlobalProperties.getGSDL3Home(), interfaceName)
876 );
877
878 StringBuffer outputStr = new StringBuffer();
879 HashSet<String> initialisedNodes = new HashSet<>();
880
881 // The dictionaries to pull keys from
882 String[] dictionaries = new String[] {
883 "interface_" + interfaceName,
884 "interface_default",
885 "interface_default2"
886 };
887
888 for (String dictName : dictionaries)
889 {
890 // get all the *keys* from the english dictionary as this is a complete set
891 Dictionary dict = new Dictionary(dictName, "en", my_loader);
892 Enumeration<String> keys = dict.getKeys();
893 if (keys == null) {
894 continue;
895 }
896
897 // Get all properties in the language-specific dictionary with the given key prefix
898 // Create Javascript strings of the form:
899 // prependToPrefix.key="value";\n
900 while (keys.hasMoreElements())
901 {
902 String key = keys.nextElement();
903 if (key.startsWith(prefix))
904 {
905 int lastDotIndex = StringUtils.lastIndexOf(key, '.');
906 // If this is true, the key is nested under nodes that we might have to construct
907 if (lastDotIndex > 0) {
908 // Builds the JS object structure we need to access the key.
909 // Also has the side effect of ensuring that any '.' characters in
910 // the key are valid once parsed by the JS engine.
911 buildJSObjectGraph(
912 outputStr,
913 prependToPrefix + "." + StringUtils.substring(key, 0, lastDotIndex), // Strip the actual key from the path
914 initialisedNodes
915 );
916 }
917
918 // get the language dependent value for the key. This will return the english if no value found for the given lang
919 String value = getInterfaceText(interfaceName, dictName, lang, key, null);
920 outputStr.append(prependToPrefix + "." + key + "=\"" + value + "\";\n");
921 }
922 }
923 }
924
925 return outputStr.toString();
926 }
927
928 /**
929 * Builds a string that will initialize an empty javascript object.
930 * I.e. 'gs.text??={};'.
931 * @param buffer The buffer to append the string to.
932 * @param objectPath The path to the JS object to initialize. E.g. gs.text.atea.
933 * @param isRootObject A value indicating whether the object is a root object. If not, a logical nullish assignment statement will be built in order to produce cleaner javascript.
934 */
935 private static void buildJSObjectInitializer(
936 StringBuffer buffer,
937 String objectPath,
938 Boolean isRootObject
939 )
940 {
941 if (!isRootObject) {
942 buffer.append(objectPath + "??={};\n");
943 return;
944 }
945
946 buffer.append("if(typeof " + objectPath + "===\"undefined\"){" + objectPath + "={};}\n");
947 }
948
949 /**
950 * Builds a string that will initialize a javascript object graph.
951 * I.e. the structure required to access the final property on the object graph 'gs.text.atea.asr'.
952 * @param buffer The buffer to append the string to.
953 * @param objectGraph The object graph to build.
954 * @param visitedNodes A map of previously built nodes. Updated to add nodes that are produced in this function.
955 */
956 private static void buildJSObjectGraph(
957 StringBuffer buffer,
958 String objectGraph,
959 HashSet<String> preBuiltNodes
960 )
961 {
962 if (objectGraph == null) {
963 return;
964 }
965
966 String[] nodes = StringUtils.split(objectGraph, '.');
967
968 if (!preBuiltNodes.contains(nodes[0])) {
969 buildJSObjectInitializer(buffer, nodes[0], true);
970 preBuiltNodes.add(nodes[0]);
971 }
972
973 if (nodes.length == 1) {
974 return;
975 }
976
977 String currentDepth = nodes[0];
978 for (int i = 1; i < nodes.length; i++) {
979 currentDepth += "." + nodes[i];
980 if (preBuiltNodes.contains(currentDepth)) {
981 continue;
982 }
983
984 buildJSObjectInitializer(buffer, currentDepth, false);
985 preBuiltNodes.add(currentDepth);
986 }
987 }
988
989 public static String xmlNodeToString(Node node)
990 {
991 return GSXML.xmlNodeToString(node);
992 }
993
994 // Test from cmdline with:
995 // java -classpath /research/ak19/gs3-svn/web/WEB-INF/lib/gsdl3.jar:/research/ak19/gs3-svn/web/WEB-INF/lib/log4j-1.2.8.jar:/research/ak19/gs3-svn/web/WEB-INF/classes/ org.greenstone.gsdl3.util.XSLTUtil
996 public static void main(String args[])
997 {
998 System.out.println("\n@@@@@\n" + XSLTUtil.getInterfaceStringsAsJavascript("default", "en", "dse", "gs.text") + "@@@@@\n");
999 }
1000}
Note: See TracBrowser for help on using the repository browser.