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

Last change on this file was 38153, checked in by kjdon, 7 months ago

when looking for interface files, in the web/interfaces/xxx/resources folder, we need to remove teh 2 from the name, if its there, eg interface_hnz2.properties will be found in web/interfaces/hnz/resources. also I think we had the list to look through in reverse order

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