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