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

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

modified Dictionary, so that if a lang fragment starts with [PENDING], then don't use it. This is to allow the addition of old strings which will help the translators, but which are too out of date to display in the interface. OTher classes use of Dictionary modified so that they go through getTextString, or createDictionaryAndGetString, thereby benifitting from the pending stuff

  • Property svn:keywords set to Author Date Id Revision
File size: 29.1 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 String result = Dictionary.createDictionaryAndGetString(dictionary_name, my_loader, key, lang, args);
260 if (result == null) {
261 // fall back to a general interface text search
262 return getInterfaceTextWithArgs(interface_name, lang, key, args);
263 }
264 result = result.replaceAll("__INTERFACE_NAME__", interface_name); // do we need to so this here?
265 return result;
266 }
267
268 public static String getInterfaceTextWithDOM(String interface_name, String lang, String key, Node arg_node)
269 {
270 return getInterfaceTextWithDOMMulti(interface_name, lang, key, arg_node);
271 }
272
273 public static String getInterfaceTextWithDOM(String interface_name, String lang, String key, Node arg1_node, Node arg2_node)
274 {
275 return getInterfaceTextWithDOMMulti(interface_name, lang, key, arg1_node, arg2_node);
276 }
277
278 public static String getInterfaceTextWithDOMMulti(String interface_name, String lang, String key, Node... nodes)
279 {
280 int num_nodes = nodes.length;
281 String[] args = null;
282 if (num_nodes != 0)
283 {
284 args = new String[num_nodes];
285
286 for (int i = 0; i < num_nodes; i++)
287 {
288 String node_str = XMLConverter.getString(nodes[i]);
289 args[i] = node_str;
290 }
291 }
292 return getInterfaceTextWithArgs(interface_name, lang, key, args);
293
294 }
295
296 /* This is the base method that actually does the work of looking up the various chain of dictionaries */
297 public static String getInterfaceTextWithArgs(String interface_name, String lang, String key, String[] args)
298 {
299 // now we allow looking for files in the interface's resources folder
300 CustomClassLoader my_loader = new CustomClassLoader(XSLTUtil.class.getClassLoader(), GSFile.interfaceResourceDir(GlobalProperties.getGSDL3Home(), interface_name));
301 String result = Dictionary.createDictionaryAndGetString("interface_"+interface_name, my_loader, key, lang, args);
302 if (result == null)
303 {
304 //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?
305 String sep_interface_dir = interface_name + File.separatorChar + lang + File.separatorChar + "interface";
306 result = Dictionary.createDictionaryAndGetString(sep_interface_dir, my_loader, key, lang, args);
307 if (result != null)
308 {
309 return result;
310 }
311 }
312
313 if (result == null && !interface_name.startsWith("default")) {
314 // not found, try the default interface
315 String dictionary_name;
316 if (interface_name.endsWith("2")) { // hack for interface_xxx2.properties
317 dictionary_name = "interface_default2";
318 } else {
319 dictionary_name = "interface_default";
320 }
321 result = Dictionary.createDictionaryAndGetString(dictionary_name, my_loader, key, lang, args);
322 }
323
324 if (result == null)
325 { // not found
326 return "?" + key + "?";
327 }
328 result = result.replaceAll("__INTERFACE_NAME__", interface_name);
329
330 return result;
331
332
333
334 }
335 public static String getInterfaceTextSubstituteArgs(String value, String args_str)
336 {
337 String[] args = null;
338 if (args_str != null && !args_str.equals("")) {
339 args = StringUtils.split(args_str, ";");
340 }
341
342 return Dictionary.processArgs(value,args);
343 }
344
345 public static Node getCollectionText(String collection, String site_name, String lang, String key)
346 {
347 return getCollectionTextWithArgs(collection, site_name, lang, key, null);
348 }
349
350 public static Node getCollectionText(String collection, String site_name, String lang, String key, String args_str)
351 {
352
353 String[] args = null;
354 if (args_str != null && !args_str.equals(""))
355 {
356 args = StringUtils.split(args_str, ";");
357 }
358
359 return getCollectionTextWithArgs(collection, site_name, lang, key, args);
360 }
361
362 // xslt didn't like calling the function with Node varargs, so have this hack for now
363 public static Node getCollectionTextWithDOM(String collection, String site_name, String lang, String key, Node n1)
364 {
365 return getCollectionTextWithDOMMulti(collection, site_name, lang, key, n1);
366 }
367
368 public static Node getCollectionTextWithDOM(String collection, String site_name, String lang, String key, Node n1, Node n2)
369 {
370 return getCollectionTextWithDOMMulti(collection, site_name, lang, key, n1, n2);
371 }
372
373 public static Node getCollectionTextWithDOM(String collection, String site_name, String lang, String key, Node n1, Node n2, Node n3)
374 {
375 return getCollectionTextWithDOMMulti(collection, site_name, lang, key, n1, n2, n3);
376 }
377
378 public static Node getCollectionTextWithDOM(String collection, String site_name, String lang, String key, Node n1, Node n2, Node n3, Node n4)
379 {
380 return getCollectionTextWithDOMMulti(collection, site_name, lang, key, n1, n2, n3, n4);
381 }
382
383 public static Node getCollectionTextWithDOMMulti(String collection, String site_name, String lang, String key, Node... nodes)
384 {
385 int num_nodes = nodes.length;
386 String[] args = null;
387 if (num_nodes != 0)
388 {
389 args = new String[num_nodes];
390
391 for (int i = 0; i < num_nodes; i++)
392 {
393 String node_str = XMLConverter.getString(nodes[i]);
394 args[i] = node_str;
395 }
396 }
397 return getCollectionTextWithArgs(collection, site_name, lang, key, args);
398 }
399
400 public static Node getCollectionTextWithArgs(String collection, String site_name, String lang, String key, String[] args)
401 {
402 try
403 {
404 DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
405
406 CustomClassLoader class_loader = new CustomClassLoader(XSLTUtil.class.getClassLoader(), GSFile.collectionResourceDir(GSFile.siteHome(GlobalProperties.getGSDL3Home(), site_name), collection));
407 String result = Dictionary.createDictionaryAndGetString("interface_custom", class_loader, key, lang, args);
408 if (result != null)
409 {
410 return docBuilder.parse(new ByteArrayInputStream(("<fragment>" + result + "</fragment>").getBytes("UTF-8"))).getDocumentElement();
411 }
412 return docBuilder.parse(new ByteArrayInputStream(("<fragment>" + "text:" + collection + ":" + key + "</fragment>").getBytes())).getDocumentElement();
413 }
414 catch (Exception ex)
415 {
416 return null;
417 }
418 }
419
420 public static String getGenericText(String dictionary_name, String lang, String key) {
421 return getGenericTextWithArgs(dictionary_name, lang, key, null);
422 }
423
424 public String getGenericText(String dictionary_name, String lang, String key, String args_str) {
425 String[] args = null;
426 if (args_str != null && !args_str.equals("")) {
427 args = StringUtils.split(args_str, ";");
428 }
429 return getGenericTextWithArgs(dictionary_name, lang, key, args);
430 }
431
432 public static String getGenericTextWithArgs(String dictionary_name, String lang, String key, String[] args)
433 {
434 String result = Dictionary.createDictionaryAndGetString(dictionary_name, null, key, lang, args);
435 if (result == null) {
436 return "_"+dictionary_name+"_"+key+"_";
437 }
438 return result;
439 }
440
441 /** handle displayItems from xslt */
442 public static String getCollectionDisplayItemText(Node display_item_list, String lang, String site_name, String collection) {
443
444 CustomClassLoader class_loader = new CustomClassLoader(XSLTUtil.class.getClassLoader(), GSFile.collectionResourceDir(GSFile.siteHome(GlobalProperties.getGSDL3Home(), site_name), collection));
445 Document doc = XMLConverter.newDOM();
446 Element di = DisplayItemUtil.chooseBestMatchDisplayItem(doc, (Element)display_item_list, lang, "en", class_loader) ;
447 if (di == null) {
448 return "";
449 }
450 return GSXML.getNodeText(di);
451
452 }
453
454 public static boolean isImage(String mimetype)
455 {
456 if (mimetype.startsWith("image/"))
457 {
458 return true;
459 }
460 return false;
461 }
462
463 // formatting /preprocessing functions
464 // some require a language, so we'll have a language param for all
465 public static String toLower(String orig, String lang)
466 {
467 return orig.toLowerCase();
468 }
469
470 public static String toUpper(String orig, String lang)
471 {
472 return orig.toUpperCase();
473 }
474
475 public static String tidyWhitespace(String original, String lang)
476 {
477
478 if (original == null || original.equals(""))
479 {
480 return original;
481 }
482 String new_s = original.replaceAll("\\s+", " ");
483 return new_s;
484 }
485
486 public static String stripWhitespace(String original, String lang)
487 {
488
489 if (original == null || original.equals(""))
490 {
491 return original;
492 }
493 String new_s = original.replaceAll("\\s+", "");
494 return new_s;
495 }
496
497 public static byte[] toUTF8(String orig, String lang)
498 {
499 try
500 {
501 byte[] utf8 = orig.getBytes("UTF-8");
502 return utf8;
503 }
504 catch (Exception e)
505 {
506 logger.error("unsupported encoding");
507 return orig.getBytes();
508 }
509 }
510
511 public static String formatDate(String date, String lang)
512 {
513 String in_pattern = "yyyyMMdd";
514 String out_pattern = "dd MMMM yyyy";
515 if (date.length() == 6)
516 {
517 in_pattern = "yyyyMM";
518 out_pattern = "MMMM yyyy";
519 }
520 // remove the 00
521 else if (date.length() == 8 && date.endsWith("00")) {
522 date = date.substring(0,6);
523 in_pattern = "yyyyMM";
524 out_pattern = "MMMM yyyy";
525 }
526
527 SimpleDateFormat formatter = new SimpleDateFormat(in_pattern, new Locale(lang));
528 try
529 {
530 Date d = formatter.parse(date);
531 formatter.applyPattern(out_pattern);
532 String new_date = formatter.format(d);
533 return new_date;
534 }
535 catch (Exception e)
536 {
537 return date;
538 }
539
540 }
541
542 public static final int TS_SECS = 0;
543 public static final int TS_MILLISECS = 1;
544 public static final int F_DATE = 0;
545 public static final int F_TIME = 1;
546 public static final int F_DATETIME = 2;
547 public static final int F_DAYSAGO = 3;
548
549 public static String formatTimeStamp(String timestamp, int ts_type, int format_type, String lang) {
550 try {
551 long ts = Long.parseLong(timestamp);
552 if (ts_type == TS_SECS) {
553 ts = ts * 1000;
554 }
555 if (format_type == F_DAYSAGO) {
556 long current_time = new Date().getTime();
557 long days = (current_time - ts)/86400000;
558 return String.valueOf(days);
559 }
560 Date d = new Date(ts);
561 DateFormat df;
562 switch (format_type) {
563 case F_DATE:
564 df = DateFormat.getDateInstance(DateFormat.DEFAULT, new Locale(lang));
565 break;
566 case F_TIME:
567 df = DateFormat.getTimeInstance(DateFormat.DEFAULT, new Locale(lang));
568 break;
569 case F_DATETIME:
570 df = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, new Locale(lang));
571 break;
572 default:
573 df = DateFormat.getDateInstance(DateFormat.DEFAULT, new Locale(lang));
574 break;
575 }
576
577 return df.format(d);
578 } catch (Exception e) {
579
580 return timestamp + e.getMessage();
581 }
582
583 }
584 public static String getDetailFromDate(String date, String detail, String lang)
585 {
586 String in_pattern = "yyyyMMdd";
587 if (date.length() == 6)
588 {
589 in_pattern = "yyyyMM";
590 }
591 // remove the 00
592 else if (date.length() == 8 && date.endsWith("00")) {
593 date = date.substring(0,6);
594 in_pattern = "yyyyMM";
595 }
596
597 SimpleDateFormat formatter = new SimpleDateFormat(in_pattern, new Locale(lang));
598 try
599 {
600 Date d = formatter.parse(date);
601 if (detail.toLowerCase().equals("day"))
602 {
603 formatter.applyPattern("dd");
604 }
605 else if (detail.toLowerCase().equals("month"))
606 {
607 formatter.applyPattern("MMMM");
608 }
609 else if (detail.toLowerCase().equals("year"))
610 {
611 formatter.applyPattern("yyyy");
612 }
613 else
614 {
615 return "";
616 }
617 return formatter.format(d);
618 }
619 catch (Exception ex)
620 {
621 return "";
622 }
623 }
624
625 public static String formatLanguage(String display_lang, String lang)
626 {
627
628 return new Locale(display_lang).getDisplayLanguage(new Locale(lang));
629 }
630
631 public static boolean checkFileExistence(String site_name, String filePath){
632
633 String gsdl3_home = GlobalProperties.getGSDL3Home();
634 //Remove leading file separator
635 filePath = filePath.replaceAll("^/+", "");
636 //Remove duplicates and replace by separator for current platform
637 filePath = filePath.replaceAll("/+", File.separator);
638 //Create full path to check
639 String fullPath = GSFile.siteHome(gsdl3_home, site_name) + File.separator + filePath;
640 File file = new File(fullPath);
641 if (file.exists() && file.isFile()) {
642 return true;
643 }
644 return false;
645 }
646
647 public static String uriEncode(String input)
648 {
649 String result = "";
650 try {
651 result = URLEncoder.encode(input, "UTF-8");
652 } catch (UnsupportedEncodingException e) {
653 e.printStackTrace();
654 }
655
656 return result;
657
658 }
659
660 public static String cgiSafe(String original, String lang)
661 {
662
663 original = original.replace('&', ' ');
664 original = original.replaceAll(" ", "%20");
665 return original;
666 }
667
668 public static String formatBigNumber(String num, String lang)
669 {
670
671 String num_str = num;
672 char[] num_chars = num_str.toCharArray();
673 String zero_str = "";
674 String formatted_str = "";
675
676 for (int i = num_chars.length - 4; i >= 0; i--)
677 {
678 zero_str += '0';
679 }
680
681 String sig_str = "";
682 for (int i = 0; i < 3 && i < num_chars.length; i++)
683 {
684 sig_str = sig_str + num_chars[i];
685 if (i == 1 && i + 1 < num_chars.length)
686 {
687 sig_str = sig_str + ".";
688 }
689 }
690
691 int sig_int = Math.round(Float.parseFloat(sig_str));
692 String new_sig_str = sig_int + "";
693 if (sig_str.length() > 2)
694 {
695 new_sig_str = sig_int + "0";
696 }
697
698 char[] final_chars = (new_sig_str + zero_str).toCharArray();
699 int count = 1;
700 for (int i = final_chars.length - 1; i >= 0; i--)
701 {
702 formatted_str = final_chars[i] + formatted_str;
703 if (count == 3 && i != 0)
704 {
705 formatted_str = "," + formatted_str;
706 count = 1;
707 }
708 else
709 {
710 count++;
711 }
712 }
713 return formatted_str;
714 }
715
716 public static String hashToSectionId(String hashString)
717 {
718 if (hashString == null || hashString.length() == 0)
719 {
720 return "";
721 }
722
723 int firstDotIndex = hashString.indexOf(".");
724 if (firstDotIndex == -1)
725 {
726 return "";
727 }
728
729 String sectionString = hashString.substring(firstDotIndex + 1);
730
731 return sectionString;
732 }
733
734 public static String hashToDepthClass(String hashString)
735 {
736 if (hashString == null || hashString.length() == 0)
737 {
738 return "";
739 }
740
741 String sectionString = hashToSectionId(hashString);
742
743 int count = sectionString.split("\\.").length;
744
745 if (sectionString.equals(""))
746 {
747 return "sectionHeaderDepthTitle";
748 }
749 else
750 {
751 return "sectionHeaderDepth" + count;
752 }
753 }
754
755 public static String escapeNewLines(String str)
756 {
757 if (str == null || str.length() < 1)
758 {
759 return null;
760 }
761 return str.replace("\n", "\\\n");
762 }
763
764 public static String escapeQuotes(String str)
765 {
766 if (str == null || str.length() < 1)
767 {
768 return null;
769 }
770 return str.replace("\"", "\\\"");
771 }
772 public static String escapeAngleBrackets(String str)
773 {
774 if (str == null || str.length() < 1)
775 {
776 return null;
777 }
778 return str.replace("<", "&lt;").replace(">", "&gt;");
779 }
780
781 public static String escapeNewLinesAndQuotes(String str)
782 {
783 if (str == null || str.length() < 1)
784 {
785 return null;
786 }
787 return escapeNewLines(escapeQuotes(str));
788 }
789
790 public static String escapeNewLinesQuotesAngleBracketsForJSString(String str)
791 {
792 // The \n and " becomes \\\n and \\\"
793 // but the <> are escaped/encoded for html, i.e. &gt; and &lt;
794 if (str == null || str.length() < 1)
795 {
796 return null;
797 }
798 return escapeAngleBrackets(escapeNewLines(escapeQuotes(str)));
799 }
800 public static String getGlobalProperty(String name)
801 {
802 return GlobalProperties.getProperty(name);
803 }
804
805 public static void clearMetadataStorage()
806 {
807 _foundTableValues.clear();
808 }
809
810 public static boolean checkMetadataNotDuplicate(String name, String value)
811 {
812 if (_foundTableValues.containsKey(name))
813 {
814 for (String mapValue : _foundTableValues.get(name))
815 {
816 if (mapValue.equals(value))
817 {
818 return false;
819 }
820 }
821 _foundTableValues.get(name).add(value);
822 return true;
823 }
824
825 ArrayList<String> newList = new ArrayList<String>();
826 newList.add(value);
827
828 _foundTableValues.put(name, newList);
829
830 return true;
831 }
832
833 public static String reCAPTCHAimage(String publicKey, String privateKey)
834 {
835 ReCaptcha c = ReCaptchaFactory.newReCaptcha(publicKey, privateKey, false);
836 return c.createRecaptchaHtml(null, null);
837 }
838
839 /**
840 * Generates a Javascript object graph to store language strings. Leaf nodes are the key, and their value the string.
841 * Further preceding nodes denote the prefix of the string within the language strings property file.
842 * Accessing a language string from the object graph can be done as such: 'const myString = gs.text.{prefix}.{key};'
843 * @param interfaceName The name of the interface to retrieve language strings for.
844 * @param lang The language to retrieve.
845 * @param prefix The prefix to to retrieve strings under. E.g. a value of 'atea.macroniser' will only retrieve strings prefixed with that value.
846 * @return Stringified Javascript code that will generate the language string object graph.
847 */
848 public static String getInterfaceStringsAsJavascript(String interfaceName, String lang, String prefix)
849 {
850 String prependToPrefix = "gs.text";
851 return XSLTUtil.getInterfaceStringsAsJavascript(interfaceName, lang, prefix, prependToPrefix);
852 }
853
854 /**
855 * Generates a Javascript object graph to store language strings. Leaf nodes are the key, and their value the string.
856 * Further preceding nodes denote the prefix of the string within the language strings property file.
857 * Accessing a language string from the object graph can be done as such: 'const myString = {prependToPrefix}.{prefix}.{key};'
858 * @param interfaceName The name of the interface to retrieve language strings for.
859 * @param lang The language to retrieve.
860 * @param prefix The prefix to to retrieve strings under. E.g. a value of 'atea.macroniser' will only retrieve strings prefixed with that value.
861 * @param prependToPrefix An accessor string to prepend to the generated JS object graph.
862 * @return Stringified Javascript code that will generate the language string object graph.
863 */
864 public static String getInterfaceStringsAsJavascript(String interfaceName, String lang, String prefix, String prependToPrefix)
865 {
866 // now we allow looking for files in the interface's resources folder
867 CustomClassLoader my_loader = new CustomClassLoader(
868 XSLTUtil.class.getClassLoader(),
869 GSFile.interfaceResourceDir(GlobalProperties.getGSDL3Home(), interfaceName)
870 );
871
872 StringBuffer outputStr = new StringBuffer();
873 HashSet<String> initialisedNodes = new HashSet<>();
874
875 // The dictionaries to pull keys from
876 String[] dictionaries = new String[] {
877 "interface_" + interfaceName,
878 "interface_default",
879 "interface_default2"
880 };
881
882 for (String dictName : dictionaries)
883 {
884 // get all the *keys* from the english dictionary as this is a complete set
885 Dictionary dict = new Dictionary(dictName, "en", my_loader);
886 Enumeration<String> keys = dict.getKeys();
887 if (keys == null) {
888 continue;
889 }
890
891 // Get all properties in the language-specific dictionary with the given key prefix
892 // Create Javascript strings of the form:
893 // prependToPrefix.key="value";\n
894 while (keys.hasMoreElements())
895 {
896 String key = keys.nextElement();
897 if (key.startsWith(prefix))
898 {
899 int lastDotIndex = StringUtils.lastIndexOf(key, '.');
900 // If this is true, the key is nested under nodes that we might have to construct
901 if (lastDotIndex > 0) {
902 // Builds the JS object structure we need to access the key.
903 // Also has the side effect of ensuring that any '.' characters in
904 // the key are valid once parsed by the JS engine.
905 buildJSObjectGraph(
906 outputStr,
907 prependToPrefix + "." + StringUtils.substring(key, 0, lastDotIndex), // Strip the actual key from the path
908 initialisedNodes
909 );
910 }
911
912 // get the language dependent value for the key. This will return the english if no value found for the given lang
913 String value = getInterfaceText(interfaceName, dictName, lang, key, null);
914 outputStr.append(prependToPrefix + "." + key + "=\"" + value + "\";\n");
915 }
916 }
917 }
918
919 return outputStr.toString();
920 }
921
922 /**
923 * Builds a string that will initialize an empty javascript object.
924 * I.e. 'gs.text??={};'.
925 * @param buffer The buffer to append the string to.
926 * @param objectPath The path to the JS object to initialize. E.g. gs.text.atea.
927 * @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.
928 */
929 private static void buildJSObjectInitializer(
930 StringBuffer buffer,
931 String objectPath,
932 Boolean isRootObject
933 )
934 {
935 if (!isRootObject) {
936 buffer.append(objectPath + "??={};\n");
937 return;
938 }
939
940 buffer.append("if(typeof " + objectPath + "===\"undefined\"){" + objectPath + "={};}\n");
941 }
942
943 /**
944 * Builds a string that will initialize a javascript object graph.
945 * I.e. the structure required to access the final property on the object graph 'gs.text.atea.asr'.
946 * @param buffer The buffer to append the string to.
947 * @param objectGraph The object graph to build.
948 * @param visitedNodes A map of previously built nodes. Updated to add nodes that are produced in this function.
949 */
950 private static void buildJSObjectGraph(
951 StringBuffer buffer,
952 String objectGraph,
953 HashSet<String> preBuiltNodes
954 )
955 {
956 if (objectGraph == null) {
957 return;
958 }
959
960 String[] nodes = StringUtils.split(objectGraph, '.');
961
962 if (!preBuiltNodes.contains(nodes[0])) {
963 buildJSObjectInitializer(buffer, nodes[0], true);
964 preBuiltNodes.add(nodes[0]);
965 }
966
967 if (nodes.length == 1) {
968 return;
969 }
970
971 String currentDepth = nodes[0];
972 for (int i = 1; i < nodes.length; i++) {
973 currentDepth += "." + nodes[i];
974 if (preBuiltNodes.contains(currentDepth)) {
975 continue;
976 }
977
978 buildJSObjectInitializer(buffer, currentDepth, false);
979 preBuiltNodes.add(currentDepth);
980 }
981 }
982
983 public static String xmlNodeToString(Node node)
984 {
985 return GSXML.xmlNodeToString(node);
986 }
987
988 // Test from cmdline with:
989 // 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
990 public static void main(String args[])
991 {
992 System.out.println("\n@@@@@\n" + XSLTUtil.getInterfaceStringsAsJavascript("default", "en", "dse", "gs.text") + "@@@@@\n");
993 }
994}
Note: See TracBrowser for help on using the repository browser.