• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *******************************************************************************
3  * Copyright (C) 2004-2012, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  *
7  * Created on Jul 28, 2004
8  *
9  */
10 package org.unicode.cldr.util;
11 
12 import java.io.File;
13 import java.io.IOException;
14 import java.io.PrintWriter;
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 
21 import javax.xml.parsers.DocumentBuilder;
22 import javax.xml.parsers.DocumentBuilderFactory;
23 import javax.xml.transform.OutputKeys;
24 import javax.xml.transform.Transformer;
25 import javax.xml.transform.TransformerException;
26 import javax.xml.transform.TransformerFactory;
27 import javax.xml.transform.dom.DOMSource;
28 import javax.xml.transform.stream.StreamResult;
29 import javax.xml.xpath.XPath;
30 import javax.xml.xpath.XPathConstants;
31 import javax.xml.xpath.XPathExpression;
32 import javax.xml.xpath.XPathExpressionException;
33 import javax.xml.xpath.XPathFactory;
34 
35 import org.unicode.cldr.icu.LDMLConstants;
36 import org.w3c.dom.Document;
37 import org.w3c.dom.NamedNodeMap;
38 import org.w3c.dom.Node;
39 import org.w3c.dom.NodeList;
40 import org.xml.sax.ErrorHandler;
41 import org.xml.sax.InputSource;
42 import org.xml.sax.SAXException;
43 import org.xml.sax.SAXParseException;
44 
45 /**
46  * @author ram
47  *
48  *         TODO To change the template for this generated type comment go to
49  *         Window - Preferences - Java - Code Generation - Code and Comments
50  */
51 public class LDMLUtilities {
52 
53     public static final int XML = 0,
54         TXT = 1;
55     private static final boolean DEBUG = false;
56 
57     /**
58      * Creates a fully resolved locale starting with root and
59      *
60      * @param sourceDir
61      * @param locale
62      * @return
63      */
getFullyResolvedLDML(String sourceDir, String locale, boolean ignoreRoot, boolean ignoreUnavailable, boolean ignoreIfNoneAvailable, boolean ignoreDraft)64     public static Document getFullyResolvedLDML(String sourceDir, String locale,
65         boolean ignoreRoot, boolean ignoreUnavailable,
66         boolean ignoreIfNoneAvailable, boolean ignoreDraft) {
67         return getFullyResolvedLDML(sourceDir, locale, ignoreRoot, ignoreUnavailable, ignoreIfNoneAvailable,
68             ignoreDraft, null);
69     }
70 
getFullyResolvedLDML(String sourceDir, String locale, boolean ignoreRoot, boolean ignoreUnavailable, boolean ignoreIfNoneAvailable, boolean ignoreDraft, Map<String, String> stack)71     private static Document getFullyResolvedLDML(String sourceDir, String locale,
72         boolean ignoreRoot, boolean ignoreUnavailable,
73         boolean ignoreIfNoneAvailable, boolean ignoreDraft, Map<String, String> stack) {
74         Document full = null;
75         if (stack != null) {
76             // For guarding against cicular references
77             String key = "SRC:" + sourceDir + File.separator + locale + ".xml";
78             if (stack.get(key) != null) {
79                 System.err.println("Found circular aliases! " + key);
80                 System.exit(-1);
81             }
82             stack.put(key, "");
83         }
84         // System.err.println("In getFullyResolvedLDML "+sourceDir + " " + locale);
85         try {
86             full = parse(sourceDir + File.separator + "root.xml", ignoreRoot);
87             /*
88              * Debugging
89              *
90              * Node[] list = getNodeArray(full, LDMLConstants.ALIAS);
91              * if(list.length>0){
92              * System.err.println("Aliases not resolved!. list.getLength() returned "+ list.length);
93              * }
94              */
95 
96             if (DEBUG) {
97                 try {
98                     java.io.OutputStreamWriter writer = new java.io.OutputStreamWriter(
99                         new java.io.FileOutputStream("./" + File.separator
100                             + "root_debug.xml"),
101                         "UTF-8");
102                     LDMLUtilities.printDOMTree(full, new PrintWriter(writer),
103                         "http://www.unicode.org/cldr/dtd/1.3/ldml.dtd", null);
104                     writer.flush();
105                 } catch (IOException e) {
106                     // throw the exceptionaway .. this is for debugging
107                 }
108             }
109         } catch (RuntimeException ex) {
110             if (!ignoreRoot) {
111                 throw ex;
112             }
113         }
114         int index = locale.indexOf(".xml");
115         if (index > -1) {
116             locale = locale.substring(0, index);
117         }
118         if (locale.equals("root")) {
119             full = resolveAliases(full, sourceDir, locale, ignoreDraft, stack);
120             return full;
121         }
122         String[] constituents = locale.split("_");
123         String loc = null;
124         boolean isAvailable = false;
125         // String lastLoc = "root";
126         for (int i = 0; i < constituents.length; i++) {
127             if (loc == null) {
128                 loc = constituents[i];
129             } else {
130                 loc = loc + "_" + constituents[i];
131             }
132             Document doc = null;
133 
134             // Try cache
135             // doc = readMergeCache(sourceDir, lastLoc, loc);
136             // if(doc == null) { ..
137             String fileName = sourceDir + File.separator + loc + ".xml";
138             File file = new File(fileName);
139             if (file.exists()) {
140                 isAvailable = true;
141                 doc = parseAndResolveAlias(fileName, loc, ignoreUnavailable);
142 
143                 /*
144                  * Debugging
145                  *
146                  * Node[] list = getNodeArray(doc, LDMLConstants.ALIAS);
147                  * if(list.length>0){
148                  * System.err.println("Aliases not resolved!. list.getLength() returned "+ list.length);
149                  * }
150                  */
151                 if (full == null) {
152                     full = doc;
153                 } else {
154                     StringBuffer xpath = new StringBuffer();
155                     mergeLDMLDocuments(full, doc, xpath, loc, sourceDir, ignoreDraft, false);
156                     if (DEBUG) {
157                         try {
158                             java.io.OutputStreamWriter writer = new java.io.OutputStreamWriter(
159                                 new java.io.FileOutputStream("./" + File.separator + loc
160                                     + "_debug.xml"),
161                                 "UTF-8");
162                             LDMLUtilities.printDOMTree(full, new PrintWriter(writer),
163                                 "http://www.unicode.org/cldr/dtd/1.3/ldml.dtd", null);
164                             writer.flush();
165                         } catch (IOException e) {
166                             // throw the exceptionaway .. this is for debugging
167                         }
168                     }
169                 }
170                 /*
171                  * debugging
172                  *
173                  * Node ec = getNode(full, "//ldml/characters/exemplarCharacters");
174                  * if(ec==null){
175                  * System.err.println("Could not find exemplarCharacters");
176                  * }else{
177                  * System.out.println("The chars are: "+ getNodeValue(ec));
178                  * }
179                  */
180 
181                 // writeMergeCache(sourceDir, lastLoc, loc, full);
182                 // lastLoc = loc;
183             } else {
184                 if (!ignoreUnavailable) {
185                     throw new RuntimeException("Could not find: " + fileName);
186                 }
187             }
188             // TODO: investigate if we really need to revalidate the DOM tree!
189             // full = revalidate(full, locale);
190         }
191 
192         if (ignoreIfNoneAvailable == true && isAvailable == false) {
193             return null;
194         }
195 
196         if (DEBUG) {
197             try {
198                 java.io.OutputStreamWriter writer = new java.io.OutputStreamWriter(
199                     new java.io.FileOutputStream("./" + File.separator + locale
200                         + "_ba_debug.xml"),
201                     "UTF-8");
202                 LDMLUtilities.printDOMTree(full, new PrintWriter(writer),
203                     "http://www.unicode.org/cldr/dtd/1.3/ldml.dtd", null);
204                 writer.flush();
205             } catch (IOException e) {
206                 // throw the exceptionaway .. this is for debugging
207             }
208         }
209         // get the real locale name
210         locale = getLocaleName(full);
211         // Resolve the aliases once the data is built
212         full = resolveAliases(full, sourceDir, locale, ignoreDraft, stack);
213 
214         if (DEBUG) {
215             try {
216                 java.io.OutputStreamWriter writer = new java.io.OutputStreamWriter(
217                     new java.io.FileOutputStream("./" + File.separator + locale
218                         + "_aa_debug.xml"),
219                     "UTF-8");
220                 LDMLUtilities.printDOMTree(full, new PrintWriter(writer),
221                     "http://www.unicode.org/cldr/dtd/1.3/ldml.dtd", null);
222                 writer.flush();
223             } catch (IOException e) {
224                 // throw the exceptionaway .. this is for debugging
225             }
226         }
227         return full;
228     }
229 
getLocaleName(Document doc)230     public static String getLocaleName(Document doc) {
231 
232         Node ln = LDMLUtilities.getNode(doc, "//ldml/identity/language");
233         Node tn = LDMLUtilities.getNode(doc, "//ldml/identity/territory");
234         Node sn = LDMLUtilities.getNode(doc, "//ldml/identity/script");
235         Node vn = LDMLUtilities.getNode(doc, "//ldml/identity/variant");
236 
237         StringBuffer locName = new StringBuffer();
238         String lang = LDMLUtilities.getAttributeValue(ln, LDMLConstants.TYPE);
239         if (lang != null) {
240             locName.append(lang);
241         } else {
242             throw new IllegalArgumentException("Did not get any value for language node from identity.");
243         }
244         if (sn != null) {
245             String script = LDMLUtilities.getAttributeValue(sn, LDMLConstants.TYPE);
246             if (script != null) {
247                 locName.append("_");
248                 locName.append(script);
249             }
250         }
251         if (tn != null) {
252             String terr = LDMLUtilities.getAttributeValue(tn, LDMLConstants.TYPE);
253             if (terr != null) {
254                 locName.append("_");
255                 locName.append(terr);
256             }
257         }
258         if (vn != null) {
259             String variant = LDMLUtilities.getAttributeValue(vn, LDMLConstants.TYPE);
260             if (variant != null && tn != null) {
261                 locName.append("_");
262                 locName.append(variant);
263             }
264         }
265         return locName.toString();
266     }
267 
268     // revalidate wasn't called anywhere.
269     // TODO: if needed, reimplement using DOM level 3
270     /*
271      * public static Document revalidate(Document doc, String fileName){
272      * // what a waste!!
273      * // to revalidate an in-memory DOM tree we need to first
274      * // serialize it to byte array and read it back again.
275      * // in DOM level 3 implementation there is API to validate
276      * // in-memory DOM trees but the latest implementation of Xerces
277      * // can only validate against schemas not DTDs!!!
278      * try{
279      * // revalidate the document
280      * Serializer serializer = SerializerFactory.getSerializer(OutputProperties.getDefaultMethodProperties("xml"));
281      * ByteArrayOutputStream os = new ByteArrayOutputStream();
282      * serializer.setOutputStream(os);
283      * DOMSerializer ds = serializer.asDOMSerializer();
284      * //ds.serialize(doc);
285      * os.flush();
286      * ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
287      * doc = parse(new InputSource(is),"Fully resolved: "+fileName, false);
288      * return doc;
289      * }catch(IOException ex){
290      * throw new RuntimeException(ex);
291      * }
292      * }
293      */
294     @Deprecated
convertXPath2ICU(Node alias, Node namespaceNode, StringBuffer fullPath)295     public static String convertXPath2ICU(Node alias, Node namespaceNode, StringBuffer fullPath)
296         throws TransformerException {
297         StringBuilder sb = new StringBuilder(fullPath.toString());
298         return convertXPath2ICU(alias, namespaceNode, sb);
299     }
300 
convertXPath2ICU(Node alias, Node namespaceNode, StringBuilder fullPath)301     public static String convertXPath2ICU(Node alias, Node namespaceNode, StringBuilder fullPath)
302         throws TransformerException {
303         Node context = alias.getParentNode();
304         StringBuffer icu = new StringBuffer();
305         String source = getAttributeValue(alias, LDMLConstants.SOURCE);
306         String xpath = getAttributeValue(alias, LDMLConstants.PATH);
307 
308         // make sure that the xpaths are valid
309         if (namespaceNode == null) {
310             XPathAPI_eval(context, fullPath.toString());
311             if (xpath != null) {
312                 XPathAPI_eval(context, xpath);
313             }
314         } else {
315             XPathAPI_eval(context, fullPath.toString(), namespaceNode);
316             if (xpath != null) {
317                 XPathAPI_eval(context, xpath, namespaceNode);
318             }
319         }
320         if (source.equals(LDMLConstants.LOCALE)) {
321             icu.append("/");
322             icu.append(source.toUpperCase());
323         } else {
324             icu.append(source);
325         }
326         if (xpath != null) {
327             StringBuilder resolved = XPathTokenizer.relativeToAbsolute(xpath, fullPath);
328             // make sure that fullPath is not corrupted!
329             XPathAPI_eval(context, fullPath.toString());
330 
331             // TODO .. do the conversion
332             XPathTokenizer tokenizer = new XPathTokenizer(resolved.toString());
333 
334             String token = tokenizer.nextToken();
335             while (token != null) {
336                 if (!token.equals("ldml")) {
337                     String equiv = getICUEquivalent(token);
338                     if (equiv == null) {
339                         throw new IllegalArgumentException("Could not find ICU equivalent for token: " + token);
340                     }
341                     if (equiv.length() > 0) {
342                         icu.append("/");
343                         icu.append(equiv);
344                     }
345                 }
346                 token = tokenizer.nextToken();
347             }
348         }
349         return icu.toString();
350     }
351 
convertXPath2ICU(String source, String xpath, String basePath, String fullPath)352     public static String convertXPath2ICU(String source, String xpath, String basePath, String fullPath)
353         throws TransformerException {
354         // Node context = alias.getParentNode();
355         StringBuffer icu = new StringBuffer();
356 
357         // TODO: make sure that the xpaths are valid. How?
358 
359         if (source.equals(LDMLConstants.LOCALE)) {
360             icu.append("/");
361             icu.append(source.toUpperCase());
362         } else {
363             icu.append(source);
364         }
365 
366         if (xpath != null) {
367             StringBuilder fullPathBuffer = new StringBuilder(fullPath);
368             StringBuilder resolved = XPathTokenizer.relativeToAbsolute(xpath, fullPathBuffer);
369             // TODO: make sure that fullPath is not corrupted! How?
370             // XPathAPI.eval(context, fullPath.toString());
371 
372             // TODO .. do the conversion
373             XPathTokenizer tokenizer = new XPathTokenizer(resolved);
374 
375             String token = tokenizer.nextToken();
376             while (token != null) {
377                 if (!token.equals("ldml")) {
378                     String equiv = getICUEquivalent(token);
379                     if (equiv == null) {
380                         throw new IllegalArgumentException("Could not find ICU equivalent for token: " + token);
381                     }
382                     if (equiv.length() > 0) {
383                         icu.append("/");
384                         icu.append(equiv);
385                     }
386                 }
387                 token = tokenizer.nextToken();
388             }
389         }
390         return icu.toString();
391     }
392 
getDayIndexAsString(String type)393     public static String getDayIndexAsString(String type) {
394         if (type.equals("sun")) {
395             return "0";
396         } else if (type.equals("mon")) {
397             return "1";
398         } else if (type.equals("tue")) {
399             return "2";
400         } else if (type.equals("wed")) {
401             return "3";
402         } else if (type.equals("thu")) {
403             return "4";
404         } else if (type.equals("fri")) {
405             return "5";
406         } else if (type.equals("sat")) {
407             return "6";
408         } else {
409             throw new IllegalArgumentException("Unknown type: " + type);
410         }
411     }
412 
getMonthIndexAsString(String type)413     public static String getMonthIndexAsString(String type) {
414         return Integer.toString(Integer.parseInt(type) - 1);
415     }
416 
getICUEquivalent(String token)417     private static String getICUEquivalent(String token) {
418         int index = 0;
419         if (token.indexOf(LDMLConstants.LDN) > -1) {
420             return "";
421         } else if (token.indexOf(LDMLConstants.LANGUAGES) > -1) {
422             return "Languages";
423         } else if (token.indexOf(LDMLConstants.LANGUAGE) > -1) {
424             return getAttributeValue(token, LDMLConstants.TYPE);
425         } else if (token.indexOf(LDMLConstants.TERRITORIES) > -1) {
426             return "Countries";
427         } else if (token.indexOf(LDMLConstants.TERRITORY) > -1) {
428             return getAttributeValue(token, LDMLConstants.TYPE);
429         } else if (token.indexOf(LDMLConstants.SCRIPTS) > -1) {
430             return "Scripts";
431         } else if (token.indexOf(LDMLConstants.SCRIPT) > -1) {
432             return getAttributeValue(token, LDMLConstants.TYPE);
433         } else if (token.indexOf(LDMLConstants.VARIANTS) > -1) {
434             return "Variants";
435         } else if (token.indexOf(LDMLConstants.VARIANT) > -1) {
436             return getAttributeValue(token, LDMLConstants.TYPE);
437         } else if (token.indexOf(LDMLConstants.KEYS) > -1) {
438             return "Keys";
439         } else if (token.indexOf(LDMLConstants.KEY) > -1) {
440             return getAttributeValue(token, LDMLConstants.TYPE);
441         } else if (token.indexOf(LDMLConstants.TYPES) > -1) {
442             return "Types";
443         } else if ((index = token.indexOf(LDMLConstants.TYPE)) > -1 && token.charAt(index - 1) != '@') {
444             String type = getAttributeValue(token, LDMLConstants.TYPE);
445             String key = getAttributeValue(token, LDMLConstants.KEY);
446             return type + "/" + key;
447         } else if (token.indexOf(LDMLConstants.LAYOUT) > -1) {
448             return "Layout";
449         } else if (token.indexOf(LDMLConstants.ORIENTATION) > -1) {
450             // TODO fix this
451         } else if (token.indexOf(LDMLConstants.CONTEXT_TRANSFORMS) > -1) {
452             return "contextTransforms";
453         } else if (token.indexOf(LDMLConstants.CONTEXT_TRANSFORM_USAGE) > -1) {
454             return getAttributeValue(token, LDMLConstants.TYPE);
455         } else if (token.indexOf(LDMLConstants.CHARACTERS) > -1) {
456             return "";
457         } else if (token.indexOf(LDMLConstants.EXEMPLAR_CHARACTERS) > -1) {
458             return "ExemplarCharacters";
459         } else if (token.indexOf(LDMLConstants.MEASUREMENT) > -1) {
460             return "";
461         } else if (token.indexOf(LDMLConstants.MS) > -1) {
462             return "MeasurementSystem";
463         } else if (token.indexOf(LDMLConstants.PAPER_SIZE) > -1) {
464             return "PaperSize";
465         } else if (token.indexOf(LDMLConstants.HEIGHT) > -1) {
466             return "0";
467         } else if (token.indexOf(LDMLConstants.WIDTH) > -1) {
468             return "1";
469         } else if (token.indexOf(LDMLConstants.DATES) > -1) {
470             return "";
471         } else if (token.indexOf(LDMLConstants.LPC) > -1) {
472             return "localPatternCharacters";
473         } else if (token.indexOf(LDMLConstants.CALENDARS) > -1) {
474             return "calendar";
475         } else if (token.indexOf(LDMLConstants.DEFAULT) > -1) {
476             return "default";
477         } else if (token.indexOf(LDMLConstants.CALENDAR) > -1) {
478             return getAttributeValue(token, LDMLConstants.TYPE);
479         } else if (token.indexOf(LDMLConstants.ERAS) > -1) {
480             return "eras";
481         } else if (token.indexOf(LDMLConstants.ERAABBR) > -1) {
482             return "abbreviated";
483         } else if (token.indexOf(LDMLConstants.ERA) > -1) {
484             return getAttributeValue(token, LDMLConstants.TYPE);
485         } else if (token.indexOf(LDMLConstants.NUMBERS) > -1) {
486             // TODO fix this
487         } else if (token.indexOf(LDMLConstants.SYMBOLS) > -1) {
488             return "NumberElements";
489         } else if (token.indexOf(LDMLConstants.DATE_FORMATS) > -1) {
490             // TODO fix this
491         } else if (token.indexOf(LDMLConstants.DFL) > -1) {
492             // TODO fix this
493         } else if (token.indexOf(LDMLConstants.DATE_FORMAT) > -1) {
494             // TODO fix this
495         } else if (token.indexOf(LDMLConstants.TIME_FORMATS) > -1) {
496             // TODO fix this
497         } else if (token.indexOf(LDMLConstants.TFL) > -1) {
498             // TODO fix this
499         } else if (token.indexOf(LDMLConstants.TIME_FORMAT) > -1) {
500             // TODO fix this
501         } else if (token.indexOf(LDMLConstants.DATE_TIME_FORMATS) > -1) {
502             // TODO fix this
503             return "DateTimePatterns";
504         } else if (token.indexOf(LDMLConstants.INTVL_FMTS) > -1) {
505             return "intervalFormats";
506             // TODO fix this
507         } else if (token.indexOf(LDMLConstants.DTFL) > -1) {
508             // TODO fix this
509         } else if (token.indexOf(LDMLConstants.DATE_TIME_FORMAT) > -1) {
510             // TODO fix this
511         } else if (token.indexOf(LDMLConstants.INTVL_FMTS) > -1) {
512             // TODO fix this
513         } else if (token.indexOf(LDMLConstants.CYCLIC_NAME_SETS) > -1) {
514             return "cyclicNameSets";
515         } else if (token.indexOf(LDMLConstants.CYCLIC_NAME_SET) > -1) {
516             return getAttributeValue(token, LDMLConstants.TYPE);
517         } else if (token.indexOf(LDMLConstants.CYCLIC_NAME_CONTEXT) > -1) {
518             return getAttributeValue(token, LDMLConstants.TYPE);
519         } else if (token.indexOf(LDMLConstants.CYCLIC_NAME_WIDTH) > -1) {
520             return getAttributeValue(token, LDMLConstants.TYPE);
521         } else if (token.indexOf(LDMLConstants.CYCLIC_NAME) > -1) {
522             String valStr = getAttributeValue(token, LDMLConstants.TYPE);
523             return getMonthIndexAsString(valStr);
524         } else if (token.indexOf(LDMLConstants.MONTHS) > -1) {
525             return "monthNames";
526         } else if (token.indexOf(LDMLConstants.MONTH_PATTERNS) > -1) {
527             return "monthPatterns";
528         } else if (token.indexOf(LDMLConstants.MONTH_PATTERN_CONTEXT) > -1) {
529             return getAttributeValue(token, LDMLConstants.TYPE);
530         } else if (token.indexOf(LDMLConstants.MONTH_PATTERN_WIDTH) > -1) {
531             return getAttributeValue(token, LDMLConstants.TYPE);
532         } else if (token.indexOf(LDMLConstants.MONTH_PATTERN) > -1) {
533             return getAttributeValue(token, LDMLConstants.TYPE);
534         } else if (token.indexOf(LDMLConstants.MONTH_CONTEXT) > -1) {
535             return getAttributeValue(token, LDMLConstants.TYPE);
536         } else if (token.indexOf(LDMLConstants.MONTH_WIDTH) > -1) {
537             return getAttributeValue(token, LDMLConstants.TYPE);
538         } else if (token.indexOf(LDMLConstants.MONTH) > -1) {
539             String valStr = getAttributeValue(token, LDMLConstants.TYPE);
540             return getMonthIndexAsString(valStr);
541         } else if (token.indexOf(LDMLConstants.DAYPERIODS) > -1) {
542             return "dayPeriods";
543         } else if (token.indexOf(LDMLConstants.DAYS) > -1) {
544             return "dayNames";
545         } else if (token.indexOf(LDMLConstants.DAY_CONTEXT) > -1) {
546             return getAttributeValue(token, LDMLConstants.TYPE);
547         } else if (token.indexOf(LDMLConstants.DAY_WIDTH) > -1) {
548             return getAttributeValue(token, LDMLConstants.TYPE);
549         } else if (token.indexOf(LDMLConstants.DAY) > -1) {
550             String dayName = getAttributeValue(token, LDMLConstants.TYPE);
551             return getDayIndexAsString(dayName);
552         } else if (token.indexOf(LDMLConstants.QUARTER_WIDTH) > -1) {
553             return getAttributeValue(token, LDMLConstants.TYPE);
554         } else if (token.indexOf(LDMLConstants.QUARTER_CONTEXT) > -1) {
555             return getAttributeValue(token, LDMLConstants.TYPE);
556         } else if (token.indexOf(LDMLConstants.QUARTERS) > -1) {
557             return "quarters";
558         } else if (token.indexOf(LDMLConstants.QUARTER) > -1) {
559             String valStr = getAttributeValue(token, LDMLConstants.TYPE);
560             return getMonthIndexAsString(valStr);
561         } else if (token.indexOf(LDMLConstants.COLLATIONS) > -1) {
562             return "collations";
563         } else if (token.indexOf(LDMLConstants.COLLATION) > -1) {
564             return getAttributeValue(token, LDMLConstants.TYPE);
565         }
566 
567         // TODO: this method is not finished yet
568         // the conversion of Xpath to ICU alias path
569         // is not as straight forward as I thought
570         // need to cater to idiosynchracies of each
571         // element node :(
572         throw new IllegalArgumentException("Unknown Xpath fragment: " + token);
573     }
574 
575     /**
576      *
577      * @param token
578      *            XPath token fragment
579      * @param attrib
580      *            attribute whose value must be fetched
581      * @return
582      */
getAttributeValue(String token, String attrib)583     private static String getAttributeValue(String token, String attrib) {
584         int attribStart = token.indexOf(attrib);
585         int valStart = token.indexOf('=', attribStart) + 1/* skip past the separtor */;
586         int valEnd = token.indexOf('@', valStart);
587         if (valEnd < 0) {
588             valEnd = valStart + (token.length() - valStart - 1);
589         } else {
590             valEnd = token.length() - 1 /* valEnd should be index */;
591         }
592         String value = token.substring(valStart, valEnd);
593         int s = value.indexOf('\'');
594         if (s > -1) {
595             s++;
596             int e = value.lastIndexOf('\'');
597             return value.substring(s, e);
598         } else {
599             // also handle ""
600             s = value.indexOf('"');
601             if (s > -1) {
602                 s++;
603                 int e = value.lastIndexOf('"');
604                 return value.substring(s, e);
605             }
606         }
607         return value;
608     }
609 
610     @Deprecated
mergeLDMLDocuments(Document source, Node override, StringBuffer xpath, String thisName, String sourceDir, boolean ignoreDraft, boolean ignoreVersion)611     public static Node mergeLDMLDocuments(Document source, Node override, StringBuffer xpath,
612         String thisName, String sourceDir, boolean ignoreDraft,
613         boolean ignoreVersion) {
614         StringBuilder sb = new StringBuilder(xpath.toString());
615         return mergeLDMLDocuments(source, override, sb, thisName, sourceDir, ignoreDraft, ignoreVersion);
616     }
617 
618     /**
619      * Resolved Data File
620      * <p>
621      * To produce fully resolved locale data file from CLDR for a locale ID L, you start with root, and replace/add
622      * items from the child locales until you get down to L. More formally, this can be expressed as the following
623      * procedure.
624      * </p>
625      * <ol>
626      * <li>Let Result be an empty LDML file.</li>
627      *
628      * <li>For each Li in the locale chain for L
629      * <ol>
630      * <li>For each element pair P in the LDML file for Li:
631      * <ol>
632      * <li>If Result has an element pair Q with an equivalent element chain, remove Q.</li>
633      * <li>Add P to Result.</li>
634      * </ol>
635      * </li>
636      * </ol>
637      *
638      * </li>
639      * </ol>
640      * <p>
641      * Note: when adding an element pair to a result, it has to go in the right order for it to be valid according to
642      * the DTD.
643      * </p>
644      *
645      * @param source
646      * @param override
647      * @return the merged document
648      */
mergeLDMLDocuments(Document source, Node override, StringBuilder xpath, String thisName, String sourceDir, boolean ignoreDraft, boolean ignoreVersion)649     public static Node mergeLDMLDocuments(Document source, Node override, StringBuilder xpath,
650         String thisName, String sourceDir, boolean ignoreDraft,
651         boolean ignoreVersion) {
652         if (source == null) {
653             return override;
654         }
655         if (xpath.length() == 0) {
656             xpath.append("/");
657         }
658 
659         // boolean gotcha = false;
660         // String oldx = new String(xpath);
661         // if(override.getNodeName().equals("week")) {
662         // gotcha = true;
663         // System.out.println("SRC: " + getNode(source, xpath.toString()).toString());
664         // System.out.println("OVR: " + override.toString());
665         // }
666 
667         // we know that every child xml file either adds or
668         // overrides the elements in parent
669         // so we traverse the child, at every node check if
670         // if the node is present in the source,
671         // if (present)
672         // recurse to replace any nodes that need to be overridded
673         // else
674         // import the node into source
675         Node child = override.getFirstChild();
676         while (child != null) {
677             // we are only concerned with element nodes
678             if (child.getNodeType() != Node.ELEMENT_NODE) {
679                 child = child.getNextSibling();
680                 continue;
681             }
682             String childName = child.getNodeName();
683 
684             int savedLength = xpath.length();
685             xpath.append("/");
686             xpath.append(childName);
687             appendXPathAttribute(child, xpath, false, false);
688             Node nodeInSource = null;
689 
690             if (childName.indexOf(":") > -1) {
691                 nodeInSource = getNode(source, xpath.toString(), child);
692             } else {
693                 nodeInSource = getNode(source, xpath.toString());
694             }
695 
696             Node parentNodeInSource = null;
697             if (nodeInSource == null) {
698                 // the child xml has a new node
699                 // that should be added to parent
700                 String parentXpath = xpath.substring(0, savedLength);
701 
702                 if (childName.indexOf(":") > -1) {
703                     parentNodeInSource = getNode(source, parentXpath, child);
704                 } else {
705                     parentNodeInSource = getNode(source, parentXpath);
706                 }
707                 if (parentNodeInSource == null) {
708                     throw new RuntimeException("Internal Error");
709                 }
710 
711                 Node childToImport = source.importNode(child, true);
712                 parentNodeInSource.appendChild(childToImport);
713             } else if (childName.equals(LDMLConstants.IDENTITY)) {
714                 if (!ignoreVersion) {
715                     // replace the source doc
716                     // none of the elements under collations are inherited
717                     // only the node as a whole!!
718                     parentNodeInSource = nodeInSource.getParentNode();
719                     Node childToImport = source.importNode(child, true);
720                     parentNodeInSource.replaceChild(childToImport, nodeInSource);
721                 }
722             } else if (childName.equals(LDMLConstants.COLLATION)) {
723                 // replace the source doc
724                 // none of the elements under collations are inherited
725                 // only the node as a whole!!
726                 parentNodeInSource = nodeInSource.getParentNode();
727                 Node childToImport = source.importNode(child, true);
728                 parentNodeInSource.replaceChild(childToImport, nodeInSource);
729                 // override the validSubLocales attribute
730                 String val = LDMLUtilities.getAttributeValue(child.getParentNode(), LDMLConstants.VALID_SUBLOCALE);
731                 NamedNodeMap map = parentNodeInSource.getAttributes();
732                 Node vs = map.getNamedItem(LDMLConstants.VALID_SUBLOCALE);
733                 vs.setNodeValue(val);
734             } else {
735                 boolean childElementNodes = areChildrenElementNodes(child);
736                 boolean sourceElementNodes = areChildrenElementNodes(nodeInSource);
737                 // System.out.println(childName + ":" + childElementNodes + "/" + sourceElementNodes);
738                 if (childElementNodes && sourceElementNodes) {
739                     // recurse to pickup any children!
740                     mergeLDMLDocuments(source, child, xpath, thisName, sourceDir, ignoreDraft, ignoreVersion);
741                 } else {
742                     // we have reached a leaf node now get the
743                     // replace to the source doc
744                     parentNodeInSource = nodeInSource.getParentNode();
745                     Node childToImport = source.importNode(child, true);
746                     parentNodeInSource.replaceChild(childToImport, nodeInSource);
747                 }
748             }
749             xpath.delete(savedLength, xpath.length());
750             child = child.getNextSibling();
751         }
752         // if(gotcha==true) {
753         // System.out.println("Final: " + getNode(source, oldx).toString());
754         // }
755         return source;
756     }
757 
getNodeArray(Document doc, String tagName)758     private static Node[] getNodeArray(Document doc, String tagName) {
759         NodeList list = doc.getElementsByTagName(tagName);
760         // node list is dynamic .. if a node is deleted, then
761         // list is immidiately updated.
762         // so first cache the nodes returned and do stuff
763         Node[] array = new Node[list.getLength()];
764         for (int i = 0; i < list.getLength(); i++) {
765             array[i] = list.item(i);
766         }
767         return array;
768     }
769 
770     /**
771      * Utility to create abosolute Xpath from 1.1 style alias element
772      *
773      * @param node
774      * @param type
775      * @return
776      */
getAbsoluteXPath(Node node, String type)777     public static String getAbsoluteXPath(Node node, String type) {
778         StringBuffer xpath = new StringBuffer();
779         StringBuffer xpathFragment = new StringBuffer();
780         node = node.getParentNode(); // the node is alias node .. get its parent
781         if (node == null) {
782             throw new IllegalArgumentException("Alias node's parent is null!");
783         }
784         xpath.append(node.getNodeName());
785         if (type != null) {
786             xpath.append("[@type='" + type + "']"); // TODO: double quotes?
787         }
788         Node parent = node;
789         while ((parent = parent.getParentNode()) != null) {
790             xpathFragment.setLength(0);
791             xpathFragment.append(parent.getNodeName());
792             if (parent.getNodeType() != Node.DOCUMENT_NODE) {
793                 appendXPathAttribute(parent, xpathFragment);
794                 xpath.insert(0, "/");
795                 xpath.insert(0, xpathFragment);
796             }
797         }
798         xpath.insert(0, "//");
799         return xpath.toString();
800     }
801 
802     /**
803      *
804      * @param n1
805      * @param n2
806      *            preferred list
807      * @param xpath
808      * @return
809      */
mergeNodeLists(Object[] n1, Object[] n2)810     private static Node[] mergeNodeLists(Object[] n1, Object[] n2) {
811         StringBuffer xp1 = new StringBuffer();
812         StringBuffer xp2 = new StringBuffer();
813         int l1 = xp1.length(), l2 = xp2.length();
814         Map<String, Object> map = new HashMap<>();
815         if (n2 == null || n2.length == 0) {
816             Node[] na = new Node[n1.length];
817             for (int i = 0; i < n1.length; i++) {
818                 na[i] = (Node) n1[i];
819             }
820             return na;
821         }
822         for (int i = 0; i < n1.length; i++) {
823             xp1.append(((Node) n1[i]).getNodeName());
824             appendXPathAttribute((Node) n1[i], xp1);
825             map.put(xp1.toString(), n1[i]);
826             xp1.setLength(l1);
827         }
828         for (int i = 0; i < n2.length; i++) {
829             xp2.append(((Node) n2[i]).getNodeName());
830             appendXPathAttribute((Node) n2[i], xp2);
831             map.put(xp2.toString(), n2[i]);
832             xp2.setLength(l2);
833         }
834         Object[] arr = map.values().toArray();
835         Node[] na = new Node[arr.length];
836         for (int i = 0; i < arr.length; i++) {
837             na[i] = (Node) arr[i];
838         }
839         return na;
840     }
841 
842     /**
843      *
844      * @param fullyResolvedDoc
845      * @param sourceDir
846      * @param thisLocale
847      */
848     // TODO guard against circular aliases
resolveAliases(Document fullyResolvedDoc, String sourceDir, String thisLocale, boolean ignoreDraft, Map<String, String> stack)849     public static Document resolveAliases(Document fullyResolvedDoc, String sourceDir, String thisLocale,
850         boolean ignoreDraft, Map<String, String> stack) {
851         Node[] array = getNodeArray(fullyResolvedDoc, LDMLConstants.ALIAS);
852 
853         // resolve all the aliases by iterating over
854         // the list of nodes
855         Node[] replacementList = null;
856         Node parent = null;
857         String source = null;
858         String path = null;
859         String type = null;
860         for (int i = 0; i < array.length; i++) {
861             Node node = array[i];
862             /*
863              * //stop inherited aliases from overwriting valid locale data
864              * //ldml.dtd does not allow alias to have any sibling elements
865              * boolean bFoundSibling = false;
866              * Node n = node.getNextSibling();
867              * while (n != null)
868              * {
869              * if (n.getNodeType() == Node.ELEMENT_NODE)
870              * {
871              * // System.err.println ("it's an element node " + n.getNodeName () + "  " + n.getNodeValue());
872              * bFoundSibling = true;
873              * break;
874              * }
875              * n = n.getNextSibling();
876              * }
877              * if (bFoundSibling == true)
878              * continue;
879              */
880             // initialize the stack for every alias!
881             stack = new HashMap<>();
882             if (node == null) {
883                 System.err.println("list.item(" + i + ") returned null!. The list reports it's length as: "
884                     + array.length);
885                 continue;
886             }
887             parent = node.getParentNode();
888             // boolean isDraft = isNodeDraft(node);
889             source = getAttributeValue(node, LDMLConstants.SOURCE);
890             path = getAttributeValue(node, LDMLConstants.PATH);
891             type = getAttributeValue(parent, LDMLConstants.TYPE);
892             if (parent.getParentNode() == null) {
893                 // some of the nodes were orphaned by the previous alias resolution .. just continue
894                 continue;
895             }
896             if (source != null && path == null) {
897                 // this LDML 1.1 style alias parse it
898                 path = getAbsoluteXPath(node, type);
899             }
900             String key = "SRC:" + thisLocale + ";XPATH:" + getAbsoluteXPath(node, type);
901             if (stack.get(key) != null) {
902                 throw new IllegalStateException("Found circular aliases! " + key);
903 
904             }
905             stack.put(key, "");
906             if (source.equals(LDMLConstants.LOCALE)) {
907 
908                 Object[] aliasList = getChildNodeListAsArray(getNode(parent, path), false);
909                 Object[] childList = getChildNodeListAsArray(parent, true);
910                 replacementList = mergeNodeLists(aliasList, childList);
911             } else if (source != null && !source.equals(thisLocale)) {
912                 // if source is defined then path should not be
913                 // relative
914                 if (path.indexOf("..") > 0) {
915                     throw new IllegalArgumentException("Cannot parse relative xpath: " + path +
916                         " in locale: " + source +
917                         " from source locale: " + thisLocale);
918                 }
919                 // this is a is an absolute XPath
920                 Document newDoc = getFullyResolvedLDML(sourceDir, source, false, true, false, ignoreDraft, stack);
921                 replacementList = getNodeListAsArray(newDoc, path);
922             } else {
923                 // path attribute is referencing another node in this DOM tree
924                 replacementList = getNodeListAsArray(parent, path);
925             }
926             if (replacementList != null) {
927                 parent.removeChild(node);
928                 int listLen = replacementList.length;
929                 if (listLen > 1) {
930                     // check if the whole locale is aliased
931                     // if yes then remove the identity from
932                     // the current document!
933                     if (path != null && path.equals("//ldml/*")) {
934                         Node[] identity = getNodeArray(fullyResolvedDoc, LDMLConstants.IDENTICAL);
935                         for (int j = 0; j < identity.length; j++) {
936                             parent.removeChild(node);
937                         }
938                     } else {
939                         // remove all the children of the parent node
940                         removeChildNodes(parent);
941                     }
942                     for (int j = 0; j < listLen; j++) {
943                         // found an element node in the aliased resource
944                         // add to the source
945                         Node child = replacementList[j];
946                         Node childToImport = fullyResolvedDoc.importNode(child, true);
947                         // if(isDraft==true && childToImport.getNodeType() == Node.ELEMENT_NODE){
948                         // ((Element)childToImport).setAttribute("draft", "true");
949                         // }
950                         parent.appendChild(childToImport);
951                     }
952                 } else {
953                     Node replacement = replacementList[0];
954                     // remove all the children of the parent node
955                     removeChildNodes(parent);
956                     for (Node child = replacement.getFirstChild(); child != null; child = child.getNextSibling()) {
957                         // found an element node in the aliased resource
958                         // add to the source
959                         Node childToImport = fullyResolvedDoc.importNode(child, true);
960                         // if(isDraft==true && childToImport.getNodeType() == Node.ELEMENT_NODE){
961                         // ((Element)childToImport).setAttribute("draft", "true");
962                         // }
963                         parent.appendChild(childToImport);
964                     }
965                 }
966 
967             } else {
968                 throw new IllegalArgumentException("Could not find node for xpath: " + path +
969                     " in locale: " + source +
970                     " from source locale: " + thisLocale);
971 
972             }
973         }
974         return fullyResolvedDoc;
975     }
976 
removeChildNodes(Node parent)977     private static void removeChildNodes(Node parent) {
978         Node[] children = toNodeArray(parent.getChildNodes());
979         for (int j = 0; j < children.length; j++) {
980             parent.removeChild(children[j]);
981         }
982     }
983 
984     // TODO add funtions for fetching legitimate children
985     // for ICU
isParentDraft(Document fullyResolved, String xpath)986     public boolean isParentDraft(Document fullyResolved, String xpath) {
987         Node node = getNode(fullyResolved, xpath);
988         Node parentNode;
989         while ((parentNode = node.getParentNode()) != null) {
990             String draft = getAttributeValue(parentNode, LDMLConstants.DRAFT);
991             if (draft != null) {
992                 if (draft.equals("true") || draft.equals("provisional") || draft.equals("unconfirmed")) {
993                     return true;
994                 } else {
995                     return false;
996                 }
997             }
998         }
999         // the default value is false if none specified
1000         return false;
1001     }
1002 
isNodeDraft(Node node)1003     public static boolean isNodeDraft(Node node) {
1004         String draft = getAttributeValue(node, LDMLConstants.DRAFT);
1005         if (draft != null) {
1006             if (draft.equals("true") || draft.equals("provisional") || draft.equals("unconfirmed")) {
1007                 return true;
1008             } else {
1009                 return false;
1010             }
1011         }
1012         return false;
1013     }
1014 
isDraft(Node fullyResolved, StringBuffer xpath)1015     public static boolean isDraft(Node fullyResolved, StringBuffer xpath) {
1016         Node current = getNode(fullyResolved, xpath.toString());
1017         String draft = null;
1018         while (current != null && current.getNodeType() == Node.ELEMENT_NODE) {
1019             draft = getAttributeValue(current, LDMLConstants.DRAFT);
1020             if (draft != null) {
1021                 if (draft.equals("true") || draft.equals("provisional") || draft.equals("unconfirmed")) {
1022                     return true;
1023                 } else {
1024                     return false;
1025                 }
1026             }
1027             current = current.getParentNode();
1028         }
1029         return false;
1030     }
1031 
isSiblingDraft(Node root)1032     public static boolean isSiblingDraft(Node root) {
1033         Node current = root;
1034         String draft = null;
1035         while (current != null && current.getNodeType() == Node.ELEMENT_NODE) {
1036             draft = getAttributeValue(current, LDMLConstants.DRAFT);
1037             if (draft != null) {
1038                 if (draft.equals("true") || draft.equals("provisional") || draft.equals("unconfirmed")) {
1039                     return true;
1040                 } else {
1041                     return false;
1042                 }
1043             }
1044             current = current.getNextSibling();
1045         }
1046         return false;
1047     }
1048 
appendAllAttributes(Node node, StringBuffer xpath)1049     public static void appendAllAttributes(Node node, StringBuffer xpath) {
1050         NamedNodeMap attr = node.getAttributes();
1051         int len = attr.getLength();
1052         if (len > 0) {
1053             for (int i = 0; i < len; i++) {
1054                 Node item = attr.item(i);
1055                 xpath.append("[@");
1056                 xpath.append(item.getNodeName());
1057                 xpath.append("='");
1058                 xpath.append(item.getNodeValue());
1059                 xpath.append("']");
1060             }
1061         }
1062     }
1063 
areChildNodesElements(Node node)1064     private static boolean areChildNodesElements(Node node) {
1065         NodeList list = node.getChildNodes();
1066         for (int i = 0; i < list.getLength(); i++) {
1067             if (list.item(i).getNodeType() == Node.ELEMENT_NODE) {
1068                 return true;
1069             }
1070         }
1071         return false;
1072     }
1073 
areSiblingsOfNodeElements(Node node)1074     private static boolean areSiblingsOfNodeElements(Node node) {
1075         NodeList list = node.getParentNode().getChildNodes();
1076         int count = 0;
1077         for (int i = 0; i < list.getLength(); i++) {
1078             Node item = list.item(i);
1079             if (item.getNodeType() == Node.ELEMENT_NODE) {
1080                 count++;
1081             }
1082             // the first child node of type element of <ldml> should be <identity>
1083             // here we figure out if any additional elements are there
1084             if (count > 1) {
1085                 return true;
1086             }
1087         }
1088         return false;
1089     }
1090 
isLocaleAlias(Document doc)1091     public static boolean isLocaleAlias(Document doc) {
1092         return (getAliasNode(doc) != null);
1093     }
1094 
getAliasNode(Document doc)1095     private static Node getAliasNode(Document doc) {
1096         NodeList elements = doc.getElementsByTagName(LDMLConstants.IDENTITY);
1097         if (elements.getLength() == 1) {
1098             Node id = elements.item(0);
1099             Node sib = id;
1100             while ((sib = sib.getNextSibling()) != null) {
1101                 if (sib.getNodeType() != Node.ELEMENT_NODE) {
1102                     continue;
1103                 }
1104                 if (sib.getNodeName().equals(LDMLConstants.ALIAS)) {
1105                     return sib;
1106                 }
1107             }
1108         } else {
1109             System.out.println("Error: elements returned more than 1 identity element!");
1110         }
1111         return null;
1112     }
1113 
1114     /**
1115      * Determines if the whole locale is marked draft. To accomplish this
1116      * the method traverses all leaf nodes to determine if all nodes are marked draft
1117      */
1118     private static boolean seenElementsOtherThanIdentity = false;
1119 
isLocaleDraft(Node node)1120     public static final boolean isLocaleDraft(Node node) {
1121         boolean isDraft = true;
1122         // fast path to check if <ldml> element is draft
1123         if (isNodeDraft(node) == true) {
1124             return true;
1125         }
1126         for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
1127             if (child.getNodeType() != Node.ELEMENT_NODE) {
1128                 continue;
1129             }
1130             String name = child.getNodeName();
1131             // fast path to check if <ldml> element is draft
1132             if (name.equals(LDMLConstants.LDML) && isNodeDraft(child) == true) {
1133                 return true;
1134             }
1135             if (name.equals(LDMLConstants.IDENTITY)) {
1136                 seenElementsOtherThanIdentity = areSiblingsOfNodeElements(child);
1137                 continue;
1138             }
1139 
1140             if (child.hasChildNodes() && areChildNodesElements(child)) {
1141                 isDraft = isLocaleDraft(child);
1142             } else {
1143                 if (isNodeDraft(child) == false) {
1144                     isDraft = false;
1145                 }
1146             }
1147             if (isDraft == false) {
1148                 break;
1149             }
1150         }
1151         if (!seenElementsOtherThanIdentity) {
1152             return false;
1153         }
1154         return isDraft;
1155     }
1156 
1157     /**
1158      * Appends the attribute values that make differentiate 2 siblings
1159      * in LDML
1160      *
1161      * @param node
1162      * @param xpath
1163      * @deprecated - use version that takes StringBuilder instead
1164      */
1165     @Deprecated
appendXPathAttribute(Node node, StringBuffer xpath)1166     public static final void appendXPathAttribute(Node node, StringBuffer xpath) {
1167         appendXPathAttribute(node, xpath, false, false);
1168     }
1169 
1170     /**
1171      * @deprecated
1172      */
1173     @Deprecated
appendXPathAttribute(Node node, StringBuffer xpath, boolean ignoreAlt, boolean ignoreDraft)1174     public static void appendXPathAttribute(Node node, StringBuffer xpath, boolean ignoreAlt, boolean ignoreDraft) {
1175         StringBuilder sb = new StringBuilder(xpath.toString());
1176         appendXPathAttribute(node, sb, ignoreAlt, ignoreDraft);
1177     }
1178 
appendXPathAttribute(Node node, StringBuilder xpath)1179     public static final void appendXPathAttribute(Node node, StringBuilder xpath) {
1180         appendXPathAttribute(node, xpath, false, false);
1181     }
1182 
appendXPathAttribute(Node node, StringBuilder xpath, boolean ignoreAlt, boolean ignoreDraft)1183     public static void appendXPathAttribute(Node node, StringBuilder xpath, boolean ignoreAlt, boolean ignoreDraft) {
1184         boolean terminate = false;
1185         String val = getAttributeValue(node, LDMLConstants.TYPE);
1186         String and = "][";// " and ";
1187         boolean isStart = true;
1188         String name = node.getNodeName();
1189         if (val != null && !name.equals(LDMLConstants.DEFAULT) && !name.equals(LDMLConstants.MS)) {
1190             if (!(val.equals("standard") && name.equals(LDMLConstants.PATTERN))) {
1191 
1192                 if (isStart) {
1193                     xpath.append("[");
1194                     isStart = false;
1195                 }
1196                 xpath.append("@type='");
1197                 xpath.append(val);
1198                 xpath.append("'");
1199                 terminate = true;
1200             }
1201         }
1202         if (!ignoreAlt) {
1203             val = getAttributeValue(node, LDMLConstants.ALT);
1204             if (val != null) {
1205                 if (isStart) {
1206                     xpath.append("[");
1207                     isStart = false;
1208                 } else {
1209                     xpath.append(and);
1210                 }
1211                 xpath.append("@alt='");
1212                 xpath.append(val);
1213                 xpath.append("'");
1214                 terminate = true;
1215             }
1216 
1217         }
1218 
1219         if (!ignoreDraft) {
1220             val = getAttributeValue(node, LDMLConstants.DRAFT);
1221             if (val != null && !name.equals(LDMLConstants.LDML)) {
1222                 if (isStart) {
1223                     xpath.append("[");
1224                     isStart = false;
1225                 } else {
1226                     xpath.append(and);
1227                 }
1228                 xpath.append("@draft='");
1229                 xpath.append(val);
1230                 xpath.append("'");
1231                 terminate = true;
1232             }
1233 
1234         }
1235 
1236         val = getAttributeValue(node, LDMLConstants.KEY);
1237         if (val != null) {
1238             if (isStart) {
1239                 xpath.append("[");
1240                 isStart = false;
1241             } else {
1242                 xpath.append(and);
1243             }
1244             xpath.append("@key='");
1245             xpath.append(val);
1246             xpath.append("'");
1247             terminate = true;
1248         }
1249         val = getAttributeValue(node, LDMLConstants.REGISTRY);
1250         if (val != null) {
1251             if (isStart) {
1252                 xpath.append("[");
1253                 isStart = false;
1254             } else {
1255                 xpath.append(and);
1256             }
1257             xpath.append("@registry='");
1258             xpath.append(val);
1259             xpath.append("'");
1260             terminate = true;
1261         }
1262         val = getAttributeValue(node, LDMLConstants.ID);
1263         if (val != null) {
1264             if (isStart) {
1265                 xpath.append("[");
1266                 isStart = false;
1267             } else {
1268                 xpath.append(and);
1269             }
1270             xpath.append("@id='");
1271             xpath.append(val);
1272             xpath.append("'");
1273             terminate = true;
1274         }
1275         if (terminate) {
1276             xpath.append("]");
1277         }
1278     }
1279 
1280     /**
1281      * Ascertains if the children of the given node are element
1282      * nodes.
1283      *
1284      * @param node
1285      * @return
1286      */
areChildrenElementNodes(Node node)1287     public static boolean areChildrenElementNodes(Node node) {
1288         NodeList list = node.getChildNodes();
1289         for (int i = 0; i < list.getLength(); i++) {
1290             if (list.item(i).getNodeType() == Node.ELEMENT_NODE) {
1291                 return true;
1292             }
1293         }
1294         return false;
1295     }
1296 
getNodeListAsArray(Node doc, String xpath)1297     public static Node[] getNodeListAsArray(Node doc, String xpath) {
1298         try {
1299             NodeList list = XPathAPI_selectNodeList(doc, xpath);
1300             int length = list.getLength();
1301             if (length > 0) {
1302                 Node[] array = new Node[length];
1303                 for (int i = 0; i < length; i++) {
1304                     array[i] = list.item(i);
1305                 }
1306                 return array;
1307             }
1308             return null;
1309         } catch (TransformerException ex) {
1310             throw new RuntimeException(ex);
1311         }
1312     }
1313 
getChildNodeListAsArray(Node parent, boolean exceptAlias)1314     private static Object[] getChildNodeListAsArray(Node parent, boolean exceptAlias) {
1315 
1316         NodeList list = parent.getChildNodes();
1317         int length = list.getLength();
1318 
1319         List<Node> al = new ArrayList<>();
1320         for (int i = 0; i < length; i++) {
1321             Node item = list.item(i);
1322             if (item.getNodeType() != Node.ELEMENT_NODE) {
1323                 continue;
1324             }
1325             if (exceptAlias && item.getNodeName().equals(LDMLConstants.ALIAS)) {
1326                 continue;
1327             }
1328             al.add(item);
1329         }
1330         return al.toArray();
1331 
1332     }
1333 
toNodeArray(NodeList list)1334     public static Node[] toNodeArray(NodeList list) {
1335         int length = list.getLength();
1336         if (length > 0) {
1337             Node[] array = new Node[length];
1338             for (int i = 0; i < length; i++) {
1339                 array[i] = list.item(i);
1340             }
1341             return array;
1342         }
1343         return null;
1344     }
1345 
getElementsByTagName(Document doc, String tagName)1346     public static Node[] getElementsByTagName(Document doc, String tagName) {
1347         try {
1348             NodeList list = doc.getElementsByTagName(tagName);
1349             int length = list.getLength();
1350             if (length > 0) {
1351                 Node[] array = new Node[length];
1352                 for (int i = 0; i < length; i++) {
1353                     array[i] = list.item(i);
1354                 }
1355                 return array;
1356             }
1357             return null;
1358         } catch (Exception ex) {
1359             throw new RuntimeException(ex);
1360         }
1361     }
1362 
1363     /**
1364      * Fetches the list of nodes that match the given xpath
1365      *
1366      * @param doc
1367      * @param xpath
1368      * @return
1369      */
getNodeList(Document doc, String xpath)1370     public static NodeList getNodeList(Document doc, String xpath) {
1371         try {
1372             return XPathAPI_selectNodeList(doc, xpath);
1373 
1374         } catch (TransformerException ex) {
1375             throw new RuntimeException(ex);
1376         }
1377     }
1378 
isAlternate(Node node)1379     public static final boolean isAlternate(Node node) {
1380         NamedNodeMap attributes = node.getAttributes();
1381         Node attr = attributes.getNamedItem(LDMLConstants.ALT);
1382         if (attr != null) {
1383             return true;
1384         }
1385         return false;
1386     }
1387 
getNonAltNodeIfPossible(NodeList list)1388     private static final Node getNonAltNodeIfPossible(NodeList list) {
1389         // A nonalt node is one which .. does not have alternate
1390         // attribute set
1391         Node node = null;
1392         for (int i = 0; i < list.getLength(); i++) {
1393             node = list.item(i);
1394             if (/* !isDraft(node, xpath)&& */!isAlternate(node)) {
1395                 return node;
1396             }
1397         }
1398         if (list.getLength() > 0)
1399             return list.item(0); // if all have alt=.... then return the first one
1400         return null;
1401     }
1402 
getNonAltNodeLike(Node parent, Node child)1403     public static Node getNonAltNodeLike(Node parent, Node child) {
1404         StringBuffer childXpath = new StringBuffer(child.getNodeName());
1405         appendXPathAttribute(child, childXpath, true/* ignore alt */, true/* ignore draft */);
1406         String childXPathString = childXpath.toString();
1407         for (Node other = parent.getFirstChild(); other != null; other = other.getNextSibling()) {
1408             if ((other.getNodeType() != Node.ELEMENT_NODE) || (other == child)) {
1409                 continue;
1410             }
1411             StringBuffer otherXpath = new StringBuffer(other.getNodeName());
1412             appendXPathAttribute(other, otherXpath);
1413             // System.out.println("Compare: " + childXpath + " to " + otherXpath);
1414             if (childXPathString.equals(otherXpath.toString())) {
1415                 // System.out.println("Match!");
1416                 return other;
1417             }
1418         }
1419         return null;
1420     }
1421 
1422     /**
1423      * Fetches the node from the document that matches the given xpath.
1424      * The context namespace node is required if the xpath contains
1425      * namespace elments
1426      *
1427      * @param doc
1428      * @param xpath
1429      * @param namespaceNode
1430      * @return
1431      */
getNode(Document doc, String xpath, Node namespaceNode)1432     public static Node getNode(Document doc, String xpath, Node namespaceNode) {
1433         try {
1434             NodeList nl = XPathAPI_selectNodeList(doc, xpath, namespaceNode);
1435             int len = nl.getLength();
1436             // TODO watch for attribute "alt"
1437             if (len > 1) {
1438                 throw new IllegalArgumentException("The XPATH returned more than 1 node!. Check XPATH: " + xpath);
1439             }
1440             if (len == 0) {
1441                 return null;
1442             }
1443             return nl.item(0);
1444 
1445         } catch (TransformerException ex) {
1446             ex.printStackTrace();
1447             throw new RuntimeException(ex);
1448         }
1449     }
1450 
getNode(Node context, String resToFetch, Node namespaceNode)1451     public static Node getNode(Node context, String resToFetch, Node namespaceNode) {
1452         try {
1453             NodeList nl = XPathAPI_selectNodeList(context, "./" + resToFetch, namespaceNode);
1454             int len = nl.getLength();
1455             // TODO watch for attribute "alt"
1456             if (len > 1) {
1457                 throw new IllegalArgumentException("The XPATH returned more than 1 node!. Check XPATH: " + resToFetch);
1458             }
1459             if (len == 0) {
1460                 return null;
1461             }
1462             return nl.item(0);
1463 
1464         } catch (TransformerException ex) {
1465             throw new RuntimeException(ex);
1466         }
1467     }
1468 
1469     /**
1470      * Fetches the node from the document which matches the xpath
1471      *
1472      * @param node
1473      * @param xpath
1474      * @return
1475      */
getNode(Node node, String xpath)1476     public static Node getNode(Node node, String xpath) {
1477         try {
1478             NodeList nl = XPathAPI_selectNodeList(node, xpath);
1479             int len = nl.getLength();
1480             // TODO watch for attribute "alt"
1481             if (len > 1) {
1482                 // PN Node best = getNonAltNode(nl);
1483                 Node best = getNonAltNodeIfPossible(nl); // PN
1484                 if (best != null) {
1485                     // System.err.println("Chose best node from " + xpath);
1486                     return best;
1487                 }
1488                 /* else complain */
1489                 String all = "";
1490                 int i;
1491                 for (i = 0; i < len; i++) {
1492                     all = all + ", " + nl.item(i);
1493                 }
1494                 throw new IllegalArgumentException("The XPATH returned more than 1 node!. Check XPATH: " + xpath
1495                     + " = " + all);
1496             }
1497             if (len == 0) {
1498                 return null;
1499             }
1500             return nl.item(0);
1501 
1502         } catch (TransformerException ex) {
1503             throw new RuntimeException(ex);
1504         }
1505     }
1506 
getNode(Node node, String xpath, boolean preferDraft, boolean preferAlt)1507     public static Node getNode(Node node, String xpath, boolean preferDraft, boolean preferAlt) {
1508         try {
1509             NodeList nl = XPathAPI_selectNodeList(node, xpath);
1510             return getNode(nl, xpath, preferDraft, preferAlt);
1511 
1512         } catch (TransformerException ex) {
1513             throw new RuntimeException(ex);
1514         }
1515     }
1516 
getVettedNode(NodeList list, StringBuffer xpath, boolean ignoreDraft)1517     private static Node getVettedNode(NodeList list, StringBuffer xpath, boolean ignoreDraft) {
1518         // A vetted node is one which is not draft and does not have alternate
1519         // attribute set
1520         Node node = null;
1521         for (int i = 0; i < list.getLength(); i++) {
1522             node = list.item(i);
1523             if (isDraft(node, xpath) && !ignoreDraft) {
1524                 continue;
1525             }
1526             if (isAlternate(node)) {
1527                 continue;
1528             }
1529             return node;
1530         }
1531         return null;
1532     }
1533 
getVettedNode(Document fullyResolvedDoc, Node parent, String childName, StringBuffer xpath, boolean ignoreDraft)1534     public static Node getVettedNode(Document fullyResolvedDoc, Node parent, String childName, StringBuffer xpath,
1535         boolean ignoreDraft) {
1536         NodeList list = getNodeList(parent, childName, fullyResolvedDoc, xpath.toString());
1537         int oldLength = xpath.length();
1538         Node ret = null;
1539 
1540         if (list != null && list.getLength() > 0) {
1541             xpath.append("/");
1542             xpath.append(childName);
1543             ret = getVettedNode(list, xpath, ignoreDraft);
1544         }
1545         xpath.setLength(oldLength);
1546         return ret;
1547     }
1548 
getNode(NodeList nl, String xpath, boolean preferDraft, boolean preferAlt)1549     public static Node getNode(NodeList nl, String xpath, boolean preferDraft, boolean preferAlt) {
1550         int len = nl.getLength();
1551         // TODO watch for attribute "alt"
1552         if (len > 1) {
1553             Node best = null;
1554             for (int i = 0; i < len; i++) {
1555                 Node current = nl.item(i);
1556                 if (!preferDraft && !preferAlt) {
1557                     if (!isNodeDraft(current) && !isAlternate(current)) {
1558                         best = current;
1559                         break;
1560                     }
1561                     continue;
1562                 } else if (preferDraft && !preferAlt) {
1563                     if (isNodeDraft(current) && !isAlternate(current)) {
1564                         best = current;
1565                         break;
1566                     }
1567                     continue;
1568                 } else if (!preferDraft && preferAlt) {
1569                     if (!isNodeDraft(current) && isAlternate(current)) {
1570                         best = current;
1571                         break;
1572                     }
1573                     continue;
1574                 } else {
1575                     if (isNodeDraft(current) || isAlternate(current)) {
1576                         best = current;
1577                         break;
1578                     }
1579                     continue;
1580                 }
1581             }
1582             if (best == null && preferDraft == true) {
1583                 best = getVettedNode(nl, new StringBuffer(xpath), false);
1584             }
1585             if (best != null) {
1586                 return best;
1587             }
1588             /* else complain */
1589             String all = "";
1590             int i;
1591             for (i = 0; i < len; i++) {
1592                 all = all + ", " + nl.item(i);
1593             }
1594             throw new IllegalArgumentException("The XPATH returned more than 1 node!. Check XPATH: " + xpath + " = "
1595                 + all);
1596         }
1597         if (len == 0) {
1598             return null;
1599         }
1600         return nl.item(0);
1601 
1602     }
1603 
1604     /**
1605      *
1606      * @param context
1607      * @param resToFetch
1608      * @param fullyResolved
1609      * @param xpath
1610      * @return
1611      */
getNode(Node context, String resToFetch, Document fullyResolved, String xpath)1612     public static Node getNode(Node context, String resToFetch, Document fullyResolved, String xpath) {
1613         String ctx = "./" + resToFetch;
1614         Node node = getNode(context, ctx);
1615         if (node == null && fullyResolved != null) {
1616             // try from fully resolved
1617             String path = xpath + "/" + resToFetch;
1618             node = getNode(fullyResolved, path);
1619         }
1620         return node;
1621     }
1622 
1623     /**
1624      *
1625      * @param context
1626      * @param resToFetch
1627      * @return
1628      */
getChildNodes(Node context, String resToFetch)1629     public static NodeList getChildNodes(Node context, String resToFetch) {
1630         String ctx = "./" + resToFetch;
1631         NodeList list = getNodeList(context, ctx);
1632         return list;
1633     }
1634 
1635     /**
1636      * Fetches the node from the document that matches the given xpath.
1637      * The context namespace node is required if the xpath contains
1638      * namespace elments
1639      *
1640      * @param doc
1641      * @param xpath
1642      * @param namespaceNode
1643      * @return
1644      */
getNodeList(Document doc, String xpath, Node namespaceNode)1645     public static NodeList getNodeList(Document doc, String xpath, Node namespaceNode) {
1646         try {
1647             NodeList nl = XPathAPI_selectNodeList(doc, xpath, namespaceNode);
1648             if (nl.getLength() == 0) {
1649                 return null;
1650             }
1651             return nl;
1652 
1653         } catch (TransformerException ex) {
1654             throw new RuntimeException(ex);
1655         }
1656     }
1657 
1658     /**
1659      * Fetches the node from the document which matches the xpath
1660      *
1661      * @param node
1662      * @param xpath
1663      * @return
1664      */
getNodeList(Node node, String xpath)1665     public static NodeList getNodeList(Node node, String xpath) {
1666         try {
1667             NodeList nl = XPathAPI_selectNodeList(node, xpath);
1668             int len = nl.getLength();
1669             if (len == 0) {
1670                 return null;
1671             }
1672             return nl;
1673         } catch (TransformerException ex) {
1674             throw new RuntimeException(ex);
1675         }
1676     }
1677 
1678     /**
1679      * Fetches node list from the children of the context node.
1680      *
1681      * @param context
1682      * @param resToFetch
1683      * @param fullyResolved
1684      * @param xpath
1685      * @return
1686      */
getNodeList(Node context, String resToFetch, Document fullyResolved, String xpath)1687     public static NodeList getNodeList(Node context, String resToFetch, Document fullyResolved, String xpath) {
1688         String ctx = "./" + resToFetch;
1689         NodeList list = getNodeList(context, ctx);
1690         if ((list == null || list.getLength() > 0) && fullyResolved != null) {
1691             // try from fully resolved
1692             String path = xpath + "/" + resToFetch;
1693             list = getNodeList(fullyResolved, path);
1694         }
1695         return list;
1696     }
1697 
1698     /**
1699      * Decide if the node is text, and so must be handled specially
1700      *
1701      * @param n
1702      * @return
1703      */
getAttributeNode(Node sNode, String attribName)1704     public static Node getAttributeNode(Node sNode, String attribName) {
1705         NamedNodeMap attrs = sNode.getAttributes();
1706         if (attrs != null) {
1707             return attrs.getNamedItem(attribName);
1708         }
1709         return null;
1710     }
1711 
1712     /**
1713      * Utility method to fetch the attribute value from the given
1714      * element node
1715      *
1716      * @param sNode
1717      * @param attribName
1718      * @return
1719      */
getAttributeValue(Node sNode, String attribName)1720     public static String getAttributeValue(Node sNode, String attribName) {
1721         String value = null;
1722         NamedNodeMap attrs = sNode.getAttributes();
1723         if (attrs != null) {
1724             Node attr = attrs.getNamedItem(attribName);
1725             if (attr != null) {
1726                 value = attr.getNodeValue();
1727             }
1728         }
1729         return value;
1730     }
1731 
1732     /**
1733      * Utility method to set the attribute value on the given
1734      * element node
1735      *
1736      * @param sNode
1737      * @param attribName
1738      * @param val
1739      */
setAttributeValue(Node sNode, String attribName, String val)1740     public static void setAttributeValue(Node sNode, String attribName, String val) {
1741 
1742         Node attr = sNode.getAttributes().getNamedItem(attribName);
1743         if (attr != null) {
1744             attr.setNodeValue(val);
1745         } else {
1746             attr = sNode.getOwnerDocument().createAttribute(attribName);
1747             attr.setNodeValue(val);
1748             sNode.getAttributes().setNamedItem(attr);
1749         }
1750     }
1751 
1752     /**
1753      * Utility method to fetch the value of the element node
1754      *
1755      * @param node
1756      * @return
1757      */
getNodeValue(Node node)1758     public static String getNodeValue(Node node) {
1759         for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
1760             if (child.getNodeType() == Node.TEXT_NODE) {
1761                 return child.getNodeValue();
1762             }
1763         }
1764         return null;
1765     }
1766 
1767     /**
1768      * Parse & resolve file level alias
1769      */
parseAndResolveAlias(String filename, String locale, boolean ignoreError)1770     public static Document parseAndResolveAlias(String filename, String locale, boolean ignoreError)
1771         throws RuntimeException {
1772         // Force filerefs to be URI's if needed: note this is independent of any other files
1773         String docURI = filenameToURL(filename);
1774         Document doc = parse(new InputSource(docURI), filename, ignoreError);
1775         NodeList elements = doc.getElementsByTagName(LDMLConstants.IDENTITY);
1776         if (elements.getLength() == 1) {
1777             Node id = elements.item(0);
1778             Node sib = id;
1779             while ((sib = sib.getNextSibling()) != null) {
1780                 if (sib.getNodeType() != Node.ELEMENT_NODE) {
1781                     continue;
1782                 }
1783                 if (sib.getNodeName().equals(LDMLConstants.ALIAS)) {
1784                     resolveAliases(doc, filename.substring(0, filename.lastIndexOf(File.separator) + 1), locale, false,
1785                         null);
1786                 }
1787             }
1788         } else {
1789             System.out.println("Error: elements returned more than 1 identity element!");
1790         }
1791         if (DEBUG) {
1792             try {
1793                 java.io.OutputStreamWriter writer = new java.io.OutputStreamWriter(
1794                     new java.io.FileOutputStream("./" + File.separator + locale
1795                         + "_debug_1.xml"),
1796                     "UTF-8");
1797                 LDMLUtilities.printDOMTree(doc, new PrintWriter(writer),
1798                     "http://www.unicode.org/cldr/dtd/1.3/ldml.dtd", null);
1799                 writer.flush();
1800             } catch (IOException e) {
1801                 // throw the exceptionaway .. this is for debugging
1802             }
1803         }
1804         return doc;
1805     }
1806 
1807     /**
1808      * Simple worker method to parse filename to a Document.
1809      *
1810      * Attempts XML parse, then HTML parse (when parser available),
1811      * then just parses as text and sticks into a text node.
1812      *
1813      * @param filename
1814      *            to parse as a local path
1815      *
1816      * @return Document object with contents of the file;
1817      *         otherwise throws an unchecked RuntimeException if there
1818      *         is any fatal problem
1819      */
parse(String filename, boolean ignoreError)1820     public static Document parse(String filename, boolean ignoreError) throws RuntimeException {
1821         // Force filerefs to be URI's if needed: note this is independent of any other files
1822         String docURI = filenameToURL(filename);
1823         return parse(new InputSource(docURI), filename, ignoreError);
1824     }
1825 
parseAndResolveAliases(String locale, String sourceDir, boolean ignoreError, boolean ignoreDraft)1826     public static Document parseAndResolveAliases(String locale, String sourceDir, boolean ignoreError,
1827         boolean ignoreDraft) {
1828         try {
1829             Document full = parse(sourceDir + File.separator + locale, ignoreError);
1830             if (full != null) {
1831                 full = resolveAliases(full, sourceDir, locale, ignoreDraft, null);
1832             }
1833             /*
1834              * Debugging
1835              *
1836              * Node[] list = getNodeArray(full, LDMLConstants.ALIAS);
1837              * if(list.length>0){
1838              * System.err.println("Aliases not resolved!. list.getLength() returned "+ list.length);
1839              * }
1840              */
1841             return full;
1842         } catch (Exception ex) {
1843             if (!ignoreError) {
1844                 ex.printStackTrace();
1845                 throw new RuntimeException(ex);
1846             }
1847         }
1848         return null;
1849 
1850     }
1851 
getNullErrorHandler(final String filename2, final boolean ignoreError)1852     private static ErrorHandler getNullErrorHandler(final String filename2, final boolean ignoreError) {
1853         // Local class: cheap non-printing ErrorHandler
1854         // This is used to suppress validation warnings
1855         ErrorHandler nullHandler = new ErrorHandler() {
1856             @Override
1857             public void warning(SAXParseException e) throws SAXException {
1858                 int col = e.getColumnNumber();
1859                 String msg = (filename2 + ":" + e.getLineNumber()
1860                     + (col >= 0 ? ":" + col : "") + ": WARNING: "
1861                     + e.getMessage());
1862 
1863                 System.err.println(msg);
1864                 if (!ignoreError) {
1865                     throw new RuntimeException(msg);
1866                 }
1867             }
1868 
1869             @Override
1870             public void error(SAXParseException e) throws SAXException {
1871                 int col = e.getColumnNumber();
1872                 String msg = (filename2 + ":" + e.getLineNumber()
1873                     + (col >= 0 ? ":" + col : "") + ": ERROR: "
1874                     + e.getMessage());
1875                 System.err.println(msg);
1876                 if (!ignoreError) {
1877                     throw new RuntimeException(msg);
1878                 }
1879             }
1880 
1881             @Override
1882             public void fatalError(SAXParseException e) throws SAXException {
1883                 throw e;
1884             }
1885         };
1886         return nullHandler;
1887     }
1888 
newDocument()1889     public static Document newDocument() {
1890         return newDocumentBuilder(false).newDocument();
1891     }
1892 
newDocumentBuilder(boolean validating)1893     private static DocumentBuilder newDocumentBuilder(boolean validating) {
1894         try {
1895             DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
1896             // Always set namespaces on
1897             dfactory.setNamespaceAware(true);
1898             dfactory.setValidating(validating);
1899             dfactory.setIgnoringComments(false);
1900             dfactory.setExpandEntityReferences(true);
1901             // Set other attributes here as needed
1902             // applyAttributes(dfactory, attributes);
1903 
1904             DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
1905             return docBuilder;
1906         } catch (Throwable se) {
1907             System.err.println(": ERROR : trying to create documentBuilder: " + se.getMessage());
1908             se.printStackTrace();
1909             throw new RuntimeException(se);
1910         }
1911     }
1912 
parse(InputSource docSrc, String filename, boolean ignoreError)1913     public static Document parse(InputSource docSrc, String filename, boolean ignoreError) {
1914         Document doc = null;
1915         try {
1916             // First, attempt to parse as XML (preferred)...
1917             DocumentBuilder docBuilder = newDocumentBuilder(true);
1918             docBuilder.setErrorHandler(getNullErrorHandler(filename, ignoreError));
1919             doc = docBuilder.parse(docSrc);
1920         } catch (Throwable se) {
1921             // ... if we couldn't parse as XML, attempt parse as HTML...
1922             System.err.println(filename + ": ERROR :" + se.getMessage());
1923             se.printStackTrace();
1924             if (!ignoreError) {
1925                 throw new RuntimeException(se);
1926             }
1927         }
1928         return doc;
1929     } // end of parse()
1930 
1931     /*
1932      * Utility method to translate a String filename to URL.
1933      *
1934      * Note: This method is not necessarily proven to get the
1935      * correct URL for every possible kind of filename; it should
1936      * be improved. It handles the most common cases that we've
1937      * encountered when running Conformance tests on Xalan.
1938      * Also note, this method does not handle other non-file:
1939      * flavors of URLs at all.
1940      *
1941      * If the name is null, return null.
1942      * If the name starts with a common URI scheme (namely the ones
1943      * found in the examples of RFC2396), then simply return the
1944      * name as-is (the assumption is that it's already a URL)
1945      * Otherwise we attempt (cheaply) to convert to a file:/// URL.
1946      */
filenameToURL(String filename)1947     public static String filenameToURL(String filename) {
1948         // null begets null - something like the commutative property
1949         if (null == filename) {
1950             return null;
1951         }
1952 
1953         // Don't translate a string that already looks like a URL
1954         if (filename.startsWith("file:")
1955             || filename.startsWith("http:")
1956             || filename.startsWith("ftp:")
1957             || filename.startsWith("gopher:")
1958             || filename.startsWith("mailto:")
1959             || filename.startsWith("news:")
1960             || filename.startsWith("telnet:")) {
1961             return filename;
1962         }
1963 
1964         File f = new File(filename);
1965         String tmp = PathUtilities.getNormalizedPathString(f);
1966 
1967         // URLs must explicitly use only forward slashes
1968         if (File.separatorChar == '\\') {
1969             tmp = tmp.replace('\\', '/');
1970         }
1971         // Note the presumption that it's a file reference
1972         // Ensure we have the correct number of slashes at the
1973         // start: we always want 3 /// if it's absolute
1974         // (which we should have forced above)
1975         if (tmp.startsWith("/")) {
1976             return "file://" + tmp;
1977         } else {
1978             return "file:///" + tmp;
1979         }
1980     }
1981 
1982     /**
1983      * Debugging method for printing out the DOM Tree
1984      * Prints the specified node, recursively.
1985      *
1986      * @param node
1987      * @param out
1988      * @throws IOException
1989      */
printDOMTree(Node node, PrintWriter out, String docType, String copyright)1990     public static void printDOMTree(Node node, PrintWriter out, String docType, String copyright) throws IOException {
1991         try {
1992             Transformer transformer = TransformerFactory.newInstance().newTransformer();
1993             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
1994             transformer.setOutputProperty(OutputKeys.METHOD, "xml");
1995             transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
1996 
1997             transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
1998             out.print("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
1999             if (copyright != null) {
2000                 out.print(copyright);
2001             }
2002             if (docType != null) {
2003                 out.print(docType);
2004             }
2005 
2006             // transformer.setParameter(entityName, entityRef );
2007             // transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, XLIFF_PUBLIC_NAME);
2008             transformer.transform(new DOMSource(node), new StreamResult(out));
2009         } catch (TransformerException te) {
2010             throw new IOException(te.getMessage());
2011         }
2012 
2013         // out.close();
2014     } // printDOMTree(Node, PrintWriter)
2015 
2016     // Document readMergeCache(String sourceDir, String last, String loc)
2017     // {
2018     // File cacheName = getCacheName(String sourceDir, last, loc);
2019     // System.out.println(" M:  " + cacheName);
2020     // File cacheFile = new File(xCacheDir, cacheName + ".xml");
2021     // if(cacheFile.exists()) { // && is newer than last, loc
2022     // doc = parse(cacheFile.getPath(),ignoreUnavailable);
2023     // }
2024     // if(doc!=null) {
2025     // System.out.println("Cache hit for " + cacheName);
2026     // }
2027     // return doc;
2028     // }
2029 
2030     // void writeMergeCache(String sourceDir, String last, String loc, Document full)
2031     // {
2032     // try {
2033     // OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(cacheFile),"UTF-8");
2034     // PrintWriter pw = new PrintWriter(writer);
2035     // printDOMTree(full,pw);
2036     // long stop = System.currentTimeMillis();
2037     // long total = (stop-start);
2038     // double s = total/1000.;
2039     // System.out.println(" " + cacheName + " parse time: " + s + "s");
2040     // pw.println("<!-- " + cacheName + " parse time: " + s + "s -->");
2041     // writer.close();
2042     // } catch (Throwable t) {
2043     // System.err.println(t.toString() + " while trying to write cache file " + cacheName);
2044     // }
2045     // }
2046 
getFullPath(int fileType, String fName, String dir)2047     public static String getFullPath(int fileType, String fName, String dir) {
2048         String str = null;
2049         int lastIndex1 = fName.lastIndexOf(File.separator, fName.length()) + 1/* add 1 to skip past the separator */;
2050         int lastIndex2 = fName.lastIndexOf('.', fName.length());
2051         if (fileType == TXT) {
2052             if (lastIndex2 == -1) {
2053                 fName = fName.trim() + ".txt";
2054             } else {
2055                 if (!fName.substring(lastIndex2).equalsIgnoreCase(".txt")) {
2056                     fName = fName.substring(lastIndex1, lastIndex2) + ".txt";
2057                 }
2058             }
2059             if (dir != null && fName != null) {
2060                 str = dir + "/" + fName.trim();
2061             } else {
2062                 str = System.getProperty("user.dir") + "/" + fName.trim();
2063             }
2064         } else if (fileType == XML) {
2065             if (lastIndex2 == -1) {
2066                 fName = fName.trim() + ".xml";
2067             } else {
2068                 if (!fName.substring(lastIndex2).equalsIgnoreCase(".xml")
2069                     && fName.substring(lastIndex2).equalsIgnoreCase(".xlf")) {
2070                     fName = fName.substring(lastIndex1, lastIndex2) + ".xml";
2071                 }
2072             }
2073             if (dir != null && fName != null) {
2074                 str = dir + "/" + fName;
2075             } else if (lastIndex1 > 0) {
2076                 str = fName;
2077             } else {
2078                 str = System.getProperty("user.dir") + "/" + fName;
2079             }
2080         } else {
2081             System.err.println("Invalid file type.");
2082             System.exit(-1);
2083         }
2084         return str;
2085     }
2086 
2087     /**
2088      * split an alt= tag into pieces. Any piece can be missing (== null)
2089      * Piece 0: 'alt type'. null means this is the normal (non-alt) item. Possible values are 'alternate', 'colloquial',
2090      * etc.
2091      * Piece 1: 'proposed type'. If non-null, this is a string beginning with 'proposed' and containing arbitrary other
2092      * text.
2093      *
2094      * STRING 0 1
2095      * -------------------------------
2096      * ""/null null null
2097      * something something null
2098      * something-proposed something proposed
2099      * something-proposed3 something proposed3
2100      * proposed null proposed
2101      * somethingproposed somethingproposed null
2102      *
2103      *
2104      * @param alt
2105      *            the alt tag to parse
2106      * @return a 2-element array containing piece 0 and piece 1
2107      * @see formatAlt
2108      */
parseAlt(String alt)2109     public static String[] parseAlt(String alt) {
2110         String[] ret = new String[2];
2111         if (alt == null) {
2112             ret[0] = null;
2113             ret[1] = null;
2114         } else {
2115             int l = alt.indexOf(LDMLConstants.PROPOSED);
2116             if (l == -1) { /* no PROPOSED */
2117                 ret[0] = alt; // all alt,
2118                 ret[1] = null; // no kind
2119             } else if (l == 0) { /* begins with */
2120                 ret[0] = null; // all properties
2121                 ret[1] = alt;
2122             } else {
2123                 if (alt.charAt(l - 1) != '-') {
2124                     throw new InternalError("Expected '-' before " + LDMLConstants.PROPOSED + " in " + alt);
2125                 }
2126                 ret[0] = alt.substring(0, l - 1);
2127                 ret[1] = alt.substring(l);
2128 
2129                 if (ret[0].length() == 0) {
2130                     ret[0] = null;
2131                 }
2132                 if (ret[1].length() == 0) {
2133                     ret[1] = null;
2134                 }
2135             }
2136         }
2137         return ret;
2138     }
2139 
2140     /**
2141      * Format aan alt string from components.
2142      *
2143      * @param altType
2144      *            optional alternate type (i.e. 'alternate' or 'colloquial').
2145      * @param proposedType
2146      * @see parseAlt
2147      */
formatAlt(String altType, String proposedType)2148     public static String formatAlt(String altType, String proposedType) {
2149 
2150         if (((altType == null) || (altType.length() == 0)) &&
2151             ((proposedType == null) || (proposedType.length() == 0))) {
2152             return null;
2153         }
2154 
2155         if ((proposedType == null) || (proposedType.length() == 0)) {
2156             return altType; // no proposed type: 'alternate'
2157         } else if (!proposedType.startsWith(LDMLConstants.PROPOSED)) {
2158             throw new InternalError("proposedType must begin with " + LDMLConstants.PROPOSED);
2159         }
2160 
2161         if ((altType == null) || (altType.length() == 0)) {
2162             return proposedType; // Just a proposed type: "proposed" or "proposed-3"
2163         } else {
2164             return altType + "-" + proposedType; // 'alternate-proposed'
2165         }
2166     }
2167 
2168     /**
2169      * Compatibility.
2170      *
2171      * @param node
2172      * @param xpath
2173      * @return
2174      * @throws TransformerException
2175      */
XPathAPI_selectNodeList(Node node, String xpath)2176     private static NodeList XPathAPI_selectNodeList(Node node, String xpath) throws TransformerException {
2177         XPathFactory factory = XPathFactory.newInstance();
2178         XPath xPath = factory.newXPath();
2179         setNamespace(xPath, xpath);
2180         try {
2181             XPathExpression xPathExpression = xPath.compile(xpath);
2182             return (NodeList) xPathExpression.evaluate(node, XPathConstants.NODESET);
2183         } catch (XPathExpressionException e) {
2184             throw new TransformerException("Exception in XPathAPI_selectNodeList: " + xpath, e);
2185         }
2186     }
2187 
XPathAPI_selectNodeList(Document doc, String xpath, Node namespaceNode)2188     private static NodeList XPathAPI_selectNodeList(Document doc, String xpath,
2189         Node namespaceNode) throws TransformerException {
2190         XPathFactory factory = XPathFactory.newInstance();
2191         XPath xPath = factory.newXPath();
2192         setNamespace(xPath, xpath);
2193         try {
2194             XPathExpression xPathExpression = xPath.compile(xpath);
2195             return (NodeList) xPathExpression.evaluate(doc, XPathConstants.NODESET);
2196         } catch (XPathExpressionException e) {
2197             throw new TransformerException("Exception in XPathAPI_selectNodeList: " + xpath, e);
2198         }
2199     }
2200 
XPathAPI_selectNodeList(Node context, String xpath, Node namespaceNode)2201     private static NodeList XPathAPI_selectNodeList(Node context, String xpath,
2202         Node namespaceNode) throws TransformerException {
2203         XPathFactory factory = XPathFactory.newInstance();
2204         XPath xPath = factory.newXPath();
2205         setNamespace(xPath, xpath);
2206         try {
2207             XPathExpression xPathExpression = xPath.compile(xpath);
2208             return (NodeList) xPathExpression.evaluate(context, XPathConstants.NODESET);
2209         } catch (XPathExpressionException e) {
2210             throw new TransformerException("Exception in XPathAPI_selectNodeList: " + xpath, e);
2211         }
2212     }
2213 
XPathAPI_eval(Node context, String string, Node namespaceNode)2214     private static void XPathAPI_eval(Node context, String string,
2215         Node namespaceNode) throws TransformerException {
2216         XPathAPI_selectNodeList(context, string, namespaceNode);
2217     }
2218 
XPathAPI_eval(Node context, String string)2219     private static void XPathAPI_eval(Node context, String string) throws TransformerException {
2220         XPathAPI_selectNodeList(context, string);
2221     }
2222 
setNamespace(XPath xpath, String string)2223     private static void setNamespace(XPath xpath, String string) {
2224         if (string.contains("special/icu:")) {
2225             xpath.setNamespaceContext(new javax.xml.namespace.NamespaceContext() {
2226                 @Override
2227                 public String getNamespaceURI(String prefix) {
2228                     if (prefix.equals("icu")) {
2229                         return "http://www.icu-project.org";
2230                     }
2231                     return null;
2232                 }
2233 
2234                 @Override
2235                 public String getPrefix(String namespaceURI) {
2236                     return null;
2237                 }
2238 
2239                 @Override
2240                 public Iterator<String> getPrefixes(String namespaceURI) {
2241                     return null;
2242                 }
2243             });
2244         }
2245     }
2246 }
2247