• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *******************************************************************************
3  * Copyright (C) 2003-2012, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7 /**
8  * @author Ram Viswanadha
9  */
10 package org.unicode.cldr.icu;
11 
12 import java.io.File;
13 import java.io.FileOutputStream;
14 import java.io.OutputStreamWriter;
15 import java.io.PrintWriter;
16 import java.util.Calendar;
17 import java.util.Enumeration;
18 import java.util.Hashtable;
19 import java.util.Iterator;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.TreeMap;
23 import java.util.TreeSet;
24 import java.util.Vector;
25 
26 import org.unicode.cldr.util.LDMLUtilities;
27 import org.w3c.dom.Document;
28 import org.w3c.dom.NamedNodeMap;
29 import org.w3c.dom.Node;
30 import org.w3c.dom.NodeList;
31 
32 import com.ibm.icu.lang.UCharacter;
33 import com.ibm.icu.text.Collator;
34 import com.ibm.icu.text.DecimalFormat;
35 import com.ibm.icu.text.Normalizer;
36 import com.ibm.icu.text.RuleBasedCollator;
37 import com.ibm.icu.util.ULocale;
38 
39 public class LDMLComparator {
40     /*
41      * This application will compare different locale data xml files
42      * conforming to localeElements.dtd and produces an xml file file
43      * in the format
44      */
45     private static final int OPT_DIFF = 0x4000; /* 2exp15 */// PN
46     private static final int OPT_DIFF_REF_COMMON = 0x8000; /* 2exp16 */// PN
47     private static final int OPT_BULK = 0x00010000; // PN
48     private static final int OPT_CASE_SENSITIVE = 0x00020000; // PN
49     private static final int OPT_VETTING = 0x00040000;
50     private static final int OPT_UNKNOWN = 0x00080000;
51 
52     private static final String COMMON = "common";
53     private static final String ICU = "icu";
54     private static final String IBM_TOR = "ibm";
55     private static final String WINDOWS = "windows";
56     private static final String SUNJDK = "sunjdk";
57     private static final String IBMJDK = "ibmjdk";
58     private static final String HPUX = "hp";
59     private static final String APPLE = "apple";
60     private static final String SOLARIS = "solaris";
61     private static final String OPEN_OFFICE = "open_office";
62     private static final String AIX = "aix";
63     private static final String LINUX = "linux";
64 
65     private static final String ALTERNATE_TITLE = "(Original)";
66     private static final String ALT_COLOR = "#DDDDFD";
67     // PN added
68     private static final String DIFF = "diff";
69     private static final String DIFF_REF_COMMON = "diff_ref_common";
70     private static final String BULK = "bulk";
71     private static final String CASE_SENSITIVE = "case_sensitive";
72     private static final String VETTING = "vetting";
73     private static final String[] PLATFORM_PRINT_ORDER = {
74         COMMON,
75         ICU,
76         WINDOWS,
77         SUNJDK,
78         IBMJDK,
79         IBM_TOR,
80         APPLE,
81         SOLARIS,
82         OPEN_OFFICE,
83         AIX,
84         LINUX,
85         HPUX,
86     };
87 
88     private static final String USER_OPTIONS[] = {
89         "-" + COMMON,
90         "-" + ICU,
91         "-" + IBM_TOR,
92         "-" + WINDOWS,
93         "-" + SUNJDK,
94         "-" + IBMJDK,
95         "-" + HPUX,
96         "-" + APPLE,
97         "-" + SOLARIS,
98         "-" + OPEN_OFFICE,
99         "-" + AIX,
100         "-" + LINUX,
101         "-s",
102         "-d",
103         "-" + DIFF, // PN added, indicates that only differing elements/attributes to be written to html
104         "-" + DIFF_REF_COMMON, // PN added, same as diff only common is excluded from diff but gets printed to html for
105         // reference purposes
106         "-" + BULK, // do a bulk comparison of folder contents
107         "-" + CASE_SENSITIVE, // do case sensitive matching (by default it's not case sensitive)
108         "-" + VETTING // go into Vetting mode. (show draft, etc)
109     };
110 
main(String[] args)111     public static void main(String[] args) {
112         LDMLComparator comparator = new LDMLComparator();
113         comparator.processArgs(args);
114     }
115 
getDefaultCollation()116     static Collator getDefaultCollation() {
117         // if (DEFAULT_COLLATION != null) return DEFAULT_COLLATION;
118         RuleBasedCollator temp = (RuleBasedCollator) Collator.getInstance(ULocale.ENGLISH);
119         temp.setStrength(Collator.IDENTICAL);
120         temp.setNumericCollation(true);
121         // DEFAULT_COLLATION = temp;
122         return temp;
123     }
124 
125     Hashtable<String, String> optionTable = new Hashtable<>();
126     private String destFolder = ".";
127     private String localeStr;
128     private Calendar cal = Calendar.getInstance();
129     private Hashtable<String, String> colorHash = new Hashtable<>();
130     private String goldFileName;
131     private String goldKey;
132     private int serialNumber = 0;
133     private Map<String, Object> compareMap = new TreeMap<>(getDefaultCollation());
134     private Hashtable<String, String> doesNotExist = new Hashtable<>();
135     private Hashtable<String, String> requested = new Hashtable<>();
136     private Hashtable<String, String> deprecatedLanguageCodes = new Hashtable<>();
137     private Hashtable<String, String> deprecatedCountryCodes = new Hashtable<>();
138     private Set<String> vettingSet = new TreeSet<>();
139     private String encoding = "UTF-8"; // default encoding
140 
141     // PN added
142     private Vector<String> m_PlatformVect = new Vector<>(); // holds names of platforms
143     private Vector<String> m_PlatformFolderVect = new Vector<>(); // holds names of folders containing locale data
144     // for each platform
145     private int m_iOptions;
146     private Map<String, AccumulatedResults> m_AccumulatedResultsMap = new TreeMap<>();
147     private int m_iTotalConflictingElements = 0;
148     private int m_iTotalNonConflictingElements = 0;
149     private Map<String, SummaryData> m_LocaleSummaryDataMap = new TreeMap<>(); // key = localename,
150     // data = summary info
151     private boolean m_Vetting = false;
152 
153     private int m_totalCount = 0;
154     private int m_diffcount = 0;
155     private String m_Messages = "";
156 
157     private class CompareElement {
158         String node;
159         String index;
160         String parentNode;
161         Hashtable<String, String> platformData = new Hashtable<>();
162         String referenceUrl;
163     }
164 
165     // PN added
166     // used for bulk comparisons
167     // holds the locales where the element identified by node,index and parentNode conflict
168     // for at least 2 of the platforms tested
169     // holds the locales where the element identified by node,index and parentNode don't
170     // for at all the platforms tested
171     private class AccumulatedResults {
172         String node;
173         String index;
174         String parentNode;
175         Vector<String> localeVectDiff = new Vector<>(); // holds locales where a conflict in data was found
176         Vector<String> localeVectSame = new Vector<>(); // holds locales where a no conflict in data was found
177     }
178 
179     private class SummaryData {
180         String m_szPlatforms;
181         int m_iNumConflictingElements;
182     }
183 
LDMLComparator()184     LDMLComparator() {
185         // initialize the color hash
186         colorHash.put(COMMON, "#AD989D");
187         colorHash.put(ICU, "#CCFF00");
188         colorHash.put(IBM_TOR, "#FF7777");
189         colorHash.put(WINDOWS, "#98FB98");
190         colorHash.put(SUNJDK, "#FF6633");
191         colorHash.put(IBMJDK, "#CCFFFF");
192         colorHash.put(HPUX, "#FFE4B5");
193         colorHash.put(APPLE, "#FFBBBB");
194         colorHash.put(SOLARIS, "#CC9966");
195         colorHash.put(OPEN_OFFICE, "#FFFF33");
196         colorHash.put(AIX, "#EB97FE");
197         colorHash.put(LINUX, "#1191F1");
198         // TODO - use deprecatedMap instead.
199         // deprecatedLanguageCodes.put("sh", "what ever the new one is");
200         deprecatedLanguageCodes.put("iw", "he");
201         deprecatedLanguageCodes.put("in", "id");
202         deprecatedLanguageCodes.put("ji", "yi");
203         deprecatedLanguageCodes.put("jw", "jv"); // this does not even exist, JDK thinks jw is javanese!!
204 
205         // country codes
206         deprecatedCountryCodes.put("TP", "TL");
207         deprecatedCountryCodes.put("ZR", "CD");
208     }
209 
processArgs(String[] args)210     private void processArgs(String[] args) {
211         m_iOptions = identifyOptions(args);
212         if ((args.length < 2) || ((m_iOptions & OPT_UNKNOWN) != 0)) {
213             printUsage();
214             return;
215         }
216         boolean warning[] = new boolean[1];
217         warning[0] = false;
218         Enumeration<String> en = optionTable.keys();
219 
220         try {
221             // check for bulk operation
222             if ((m_iOptions & OPT_BULK) != 0) {
223                 doBulkComparison();
224             } else {
225                 localeStr = goldFileName.substring(goldFileName.lastIndexOf(File.separatorChar) + 1,
226                     goldFileName.lastIndexOf('.'));
227 
228                 String fileName = destFolder + File.separator + localeStr + ".html";
229                 m_totalCount = 0;
230                 m_diffcount = 0;
231                 if ((m_iOptions & OPT_VETTING) != 0) {
232                     m_Vetting = true;
233                     addVettable(goldFileName, goldKey);
234                 } else {
235                     addToCompareMap(goldFileName, goldKey);
236                 }
237                 for (; en.hasMoreElements();) {
238                     String key = en.nextElement();
239                     String compFile = optionTable.get(key);
240                     if ((m_iOptions & OPT_VETTING) != 0) {
241                         addVettable(goldFileName, goldKey);
242                     } else {
243                         addToCompareMap(compFile, key);
244                     }
245                 }
246                 if ((m_totalCount == 0) && m_Vetting) { // only optional for vetting.
247                     // System.out.println("INFO:  no file created (nothing to write..) " + fileName);
248                 } else {
249                     OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(fileName), encoding);
250                     System.out.println("INFO: Writing: " + fileName + "\t(" + m_totalCount + " items)");
251                     PrintWriter writer = new PrintWriter(os);
252                     printHTML(writer, localeStr);
253                     {
254                         ULocale ourLocale = new ULocale(localeStr);
255                         String idxFileName = destFolder + File.separator + ourLocale.getDisplayLanguage() + "_"
256                             + ourLocale.getDisplayCountry() + "_" + localeStr + ".idx";
257                         OutputStreamWriter is = new OutputStreamWriter(new FileOutputStream(idxFileName), "utf-8");
258                         PrintWriter indexwriter = new PrintWriter(is);
259                         indexwriter.println("<tr>");
260                         indexwriter.println(" <td>" +
261                             localeStr +
262                             "</td>");
263                         indexwriter.println(" <td><a href=\"" + localeStr + ".html" + "\">" +
264                             ourLocale.getDisplayName() + "</a></td>");
265                         indexwriter.println(" <td>" + m_totalCount + "</td>");
266                         if (!m_Vetting) {
267                             indexwriter.println(" <td>" + m_diffcount + "</td>");
268                         }
269                         indexwriter.println("<td></td>"); // TODO: need to calculate full path (i.e. seed/main/ssy.xml) and append to CLDRURLS.something
270                         indexwriter.println("</tr>");
271                         is.close();
272                     }
273                     // TODO: handle vettingSet;
274                 }
275             }
276         } catch (Exception e) {
277             e.printStackTrace();
278         }
279 
280     }
281 
printUsage()282     private void printUsage() {
283         System.err.println("Usage: LDMLComparator [<option>:<gold>] filename1 [option] filename2 ... \n" +
284             " LDMLComparator [-common:<gold>] filename [-icu] filename" +
285             " [-ibmjdk] filename [-windows] filename" +
286             " [-hpux]  filename [-solaris] filename" +
287             " [-ibmtor] filename [-apple] filename" +
288             " [-sunjdk]  filename [-open_office] filename" +
289             " [-aix] filename [-linux] filename" +
290             " [-diff / -diff_ref_common] [-bulk]" +
291             " [-case_sensitive (only active if -diff of -diff-ref-common option selected)]");
292         System.err.println("\nExample 1: \n " +
293             "LDMLComparator -solaris:gold foldername1 -sunjdk foldername2 -common foldername3 -diff-ref-common -bulk\n"
294             +
295             "\t\t will do a bulk comparison of the locales in folder1 and folder2 \n " +
296             "\t\t and print the values of any differing elements plus the \n" +
297             "\t\t corresponding element's value in folder3 to bulk.html \n" +
298             "\t\t as well as a summary to bulk_summary.html \n");
299         System.err.println("Example 2: \n" +
300             "LDMLComparator -common:gold filename1 -sunjdk filename2 -diff \n" +
301             "\t\t will do a comparison of the locales specified by filename1 and \n" +
302             "\t\t filename2 and print the values of any differing elements \n" +
303             "\t\t to a file called filename1.html in the current directory \n");
304     }
305 
identifyOptions(String[] options)306     private int identifyOptions(String[] options) {
307         int result = 0;
308         for (int j = 0; j < options.length; j++) {
309             String option = options[j];
310             boolean isGold = false;
311             if (option.startsWith("-")) {
312                 if (option.indexOf(":gold") > 0) {
313                     option = option.substring(0, option.indexOf(":"));
314                     isGold = true;
315                 }
316                 boolean optionRecognized = false;
317                 for (int i = 0; i < USER_OPTIONS.length; i++) {
318 
319                     if (USER_OPTIONS[i].equals(option)) {
320                         result |= 1 << i; // calculate option bit value
321                         optionRecognized = true;
322                         if (USER_OPTIONS[i].equals("-s")) {
323                         } else if (USER_OPTIONS[i].equals("-d")) {
324                             destFolder = options[++j];
325                         } else if (USER_OPTIONS[i].equals("-" + DIFF)) {
326 
327                         } else if (USER_OPTIONS[i].equals("-" + DIFF_REF_COMMON)) {
328 
329                         } else if (USER_OPTIONS[i].equals("-" + BULK)) {
330 
331                         } else if (USER_OPTIONS[i].equals("-" + VETTING)) {
332                             m_Vetting = true;
333                         } else if (USER_OPTIONS[i].equals("-" + CASE_SENSITIVE)) {
334                         } else {
335                             if (!isGold) {
336                                 optionTable.put(option.substring(1, option.length()), options[++j]);
337 
338                             } else {
339                                 goldFileName = options[++j];
340                                 goldKey = option.substring(1, option.length());
341                             }
342                             // PN added
343                             m_PlatformVect.add(option.substring(1, option.length()));
344                             m_PlatformFolderVect.add(options[j]);
345                         }
346                         break;
347                     }
348                 }
349                 if (!optionRecognized) {
350                     result |= OPT_UNKNOWN;
351                 }
352             } else {
353                 if (m_Vetting == true) {
354                     vettingSet.add(option);
355                 }
356             }
357         }
358 
359         return result;
360     }
361 
printTableHeader(PrintWriter writer)362     private void printTableHeader(PrintWriter writer) {
363 
364         writer.print("            <tr>\n" +
365             "                <th>N.</th>\n" +
366             "                <th>ParentNode</th>\n" +
367             "                <th>Name</th>\n" +
368             "                <th>ID</th>\n");
369 
370         for (int i = 0; i < PLATFORM_PRINT_ORDER.length && PLATFORM_PRINT_ORDER[i] != null; i++) {
371             String name = PLATFORM_PRINT_ORDER[i];
372             String folder;
373 
374             Object obj = requested.get(name);
375             if (obj != null && doesNotExist.get(name) == null) {
376                 folder = name + "/main/";
377                 if (name.equals("icu") || name.equals("common") || name.indexOf("jdk") >= 0) {
378                     int index = localeStr.indexOf("_");
379                     String parent = "";
380                     if (index > -1) {
381                         parent = localeStr.substring(0, index);
382                     }
383                     writer.print("                <th bgcolor=\"" +
384                         colorHash.get(name) + "\">" +
385                         name.toUpperCase() +
386                         " (<a href=\"../../" + folder + localeStr + ".xml\">" + localeStr + "</a>," +
387                         " <a href=\"../../" + folder + parent + ".xml\">" + parent + "</a>," +
388                         " <a href=\"../../" + folder + "root.xml\">root</a>)" +
389                         "</th>\n");
390                 } else {
391                     writer.print("                <th bgcolor=\"" +
392                         colorHash.get(name) + "\">" +
393                         name.toUpperCase() +
394                         " (<a href=\"../../" + folder + localeStr + ".xml\">" + localeStr + "</a>)" +
395                         "</th>\n");
396                 }
397             }
398         }
399         if (m_Vetting) {
400             writer.print("<th bgcolor=\"" + ALT_COLOR + "\">" + ALTERNATE_TITLE + "</th>");
401         }
402         writer.print("            </tr>\n");
403     }
404 
405     // PN added
printTableHeaderForDifferences(PrintWriter writer)406     private void printTableHeaderForDifferences(PrintWriter writer) {
407 
408         writer.print("            <tr>\n" +
409             "                <th width=10%>N.</th>\n" +
410             "                <th width=10%>ParentNode</th>\n" +
411             "                <th width=10%>Name</th>\n" +
412             "                <th width=10%>ID</th>\n");
413 
414         for (int i = 0; i < m_PlatformVect.size(); i++) {
415             String name = m_PlatformVect.elementAt(i);
416             String folder;
417 
418             // Object obj = requested.get(name);
419             // if(obj!=null && doesNotExist.get(name)==null )
420             // {
421             folder = name + "/xml/";
422             writer.print("                <th bgcolor=\"" +
423                 colorHash.get(name) + "\">" +
424                 name.toUpperCase() +
425                 " (<a href=\"../" + folder + localeStr + ".xml\">xml</a>)" +
426                 "</th>\n");
427             // not used numPlatforms++;
428 
429             // }
430         }
431         if (m_Vetting) {
432             writer.print("<th>" + ALTERNATE_TITLE + "</th>");
433         }
434         writer.print("            </tr>\n");
435     }
436 
437     // PN added
438     // method to print differing elements/attributes only to HTML
439     // returns false if a difference found otherwise true
printDifferentValues(CompareElement element, PrintWriter writer)440     private boolean printDifferentValues(CompareElement element, PrintWriter writer) {
441         boolean isEqual = true;
442         // following don't count
443         if ((element.node.compareTo("generation") == 0)
444             || (element.node.compareTo("version") == 0)) {
445             return isEqual;
446         }
447 
448         String compareTo = null;
449         boolean bFoundFirst = false;
450         for (int i = 0; i < m_PlatformVect.size(); i++) {
451             String value = element.platformData.get(m_PlatformVect.elementAt(i));
452             if (value == null)
453                 continue;
454             // loop until non null value is found, this is the reference for comparison
455             if (bFoundFirst == false) {
456                 compareTo = value;
457                 bFoundFirst = true;
458             } else { // we have something to compare this element to
459                 if (Normalizer.compare(compareTo, value, 0) == 0) {
460                     isEqual = true;
461                 } else if (Normalizer.compare(compareTo, value, Normalizer.COMPARE_IGNORE_CASE) == 0) {
462                     if ((m_iOptions & OPT_CASE_SENSITIVE) == 0) { // it's not a case sensitive search so this is a match
463                         isEqual = true;
464                     } else {
465                         isEqual = false;
466                         break; // we have found a difference therefore break out of loop , we will print full row
467                     }
468                 } else {
469                     isEqual = false;
470                     break; // we have found a difference therefore break out of loop , we will print full row
471                 }
472             } // end if
473         } // end while
474 
475         // if any differences found then print all non null values
476         if (isEqual == false) {
477             writer.print("            <tr>\n");
478             writer.print("                <td><a NAME=\"" + serialNumber + "\" href=\"#" + serialNumber + "\">"
479                 + serialNumber + "</a></td>\n");
480             writer.print("                <td>" + mapToAbbr(element.parentNode) + "</td>\n");
481             writer.print("                <td>" + mapToAbbr(element.node) + "</td>\n");
482             writer.print("                <td>" + element.index + "</td>\n");
483 
484             for (int i = 0; i < m_PlatformVect.size(); i++) {
485                 String val = element.platformData.get(m_PlatformVect.elementAt(i));
486                 if (val != null) {
487                     writer.print("                <td>" + val + "</td>\n");
488                 } else {
489                     writer.print("                <td>&nbsp;</td>\n");
490                 }
491             } // end while
492 
493             writer.print("            </tr>\n");
494             serialNumber++;
495         } // endif
496         return isEqual;
497     }
498 
499     // PN added
500     // method to print differing elements/attributes only to HTML excluding Common from diff
501     // only if the other platfroms differ amongst themselves will the Common data be printed
502     // returns false if a difference found otherwise true
printDifferentValuesWithRef(CompareElement element, PrintWriter writer)503     private boolean printDifferentValuesWithRef(CompareElement element, PrintWriter writer) {
504         boolean isEqual = true;
505         // following don't count
506         if ((element.node.compareTo("generation") == 0)
507             || (element.node.compareTo("version") == 0)) {
508             return isEqual;
509         }
510 
511         String compareTo = null;
512         boolean bFoundFirst = false;
513         for (int i = 0; i < m_PlatformVect.size(); i++) {
514             // excluding Common from diff
515             String platform = m_PlatformVect.elementAt(i);
516             if (platform.compareTo(COMMON) == 0)
517                 continue;
518 
519             String value = element.platformData.get(platform);
520             if (value == null)
521                 continue;
522 
523             // loop until non null value is found, this is the reference for comparison
524             if (bFoundFirst == false) {
525                 compareTo = value;
526                 bFoundFirst = true;
527             } else { // we have something to compare this element to
528                 if (Normalizer.compare(compareTo, value, 0) == 0) {
529                     isEqual = true;
530                 } else if (Normalizer.compare(compareTo, value, Normalizer.COMPARE_IGNORE_CASE) == 0) {
531                     // case difference on date and time format doesn't matter
532                     if ((element.parentNode.compareTo("timeFormat") == 0)
533                         || (element.parentNode.compareTo("dateFormat") == 0)) {
534                         isEqual = true;
535                     } else {
536                         if ((m_iOptions & OPT_CASE_SENSITIVE) == 0) { // it's not a case sensitive search so this is a match
537                             isEqual = true;
538                         } else {
539                             isEqual = false;
540                             break; // we have found a difference therefore break out of loop , we will print full row
541                         }
542                     }
543                 } else {
544                     isEqual = false;
545                     break; // we have found a difference therefore break out of loop , we will print full row
546                 }
547             } // end if
548         } // end while
549 
550         // if any differences found then print all non null values
551         if (isEqual == false) {
552             writer.print("            <tr>\n");
553             writer.print("                <td><a NAME=\"" + serialNumber + "\" href=\"#" + serialNumber + "\">"
554                 + serialNumber + "</a></td>\n");
555             writer.print("                <td>" + mapToAbbr(element.parentNode) + "</td>\n");
556             writer.print("                <td>" + mapToAbbr(element.node) + "</td>\n");
557             writer.print("                <td>" + element.index + "</td>\n");
558 
559             for (int i = 0; i < m_PlatformVect.size(); i++) {
560                 String val = element.platformData.get(m_PlatformVect.elementAt(i));
561                 if (val != null) {
562                     writer.print("                <td>" + val + "</td>\n");
563                 } else {
564                     writer.print("                <td>&nbsp;</td>\n");
565                 }
566             } // end while
567 
568             writer.print("            </tr>\n");
569             serialNumber++;
570         } // endif
571 
572         return isEqual;
573     }
574 
printValue(CompareElement element, PrintWriter writer)575     private void printValue(CompareElement element, PrintWriter writer) {
576 
577         writer.print("            <tr>\n");
578         writer.print("                <td><a NAME=\"" + serialNumber + "\" href=\"#" + serialNumber + "\">"
579             + serialNumber + "</a></td>\n");
580         writer.print("                <td>" + mapToAbbr(element.parentNode) + "</td>\n");
581         writer.print("                <td>" + mapToAbbr(element.node) + "</td>\n");
582         writer.print("                <td>" + element.index + "</td>\n");
583         serialNumber++;
584 
585         for (int i = 0; i < PLATFORM_PRINT_ORDER.length; i++) {
586             String value = element.platformData.get(PLATFORM_PRINT_ORDER[i]);
587             String color = colorHash.get(PLATFORM_PRINT_ORDER[i]);
588             boolean caseDiff = false;
589             boolean isEqual = false;
590             // the locale exists for the given platform but there is no data
591             // so just write non breaking space and continue
592             // else the object contains value to be written .. so write it
593             if (value == null) {
594                 if (requested.get(PLATFORM_PRINT_ORDER[i]) != null && doesNotExist.get(PLATFORM_PRINT_ORDER[i]) == null) {
595                     writer.print("                <td>&nbsp;</td>\n");
596                 }
597             } else {
598                 // pick the correct color
599                 for (int j = 0; j < i; j++) {
600                     String compareTo = element.platformData.get(PLATFORM_PRINT_ORDER[j]);
601                     if (compareTo == null) {
602                         continue;
603                     } else if (value.equals("")) {
604                         color = "#FFFFFF";
605                         break;
606                     } else if (element.parentNode.indexOf("decimalFormat") > -1
607                         || element.parentNode.indexOf("currencyFormat") > -1) {
608                         if (comparePatterns(compareTo, value)) {
609                             color = colorHash.get(PLATFORM_PRINT_ORDER[j]);
610                             isEqual = true;
611                             break;
612                         }
613                     } else if (Normalizer.compare(compareTo, value, 0) == 0) {
614                         color = colorHash.get(PLATFORM_PRINT_ORDER[j]);
615                         isEqual = true;
616                         break;
617                     } else if (Normalizer.compare(compareTo, value, Normalizer.COMPARE_IGNORE_CASE) == 0) {
618                         caseDiff = true;
619                         color = colorHash.get(PLATFORM_PRINT_ORDER[j]);
620                         break;
621                     }
622                 }
623                 if (isEqual) {
624                     value = "=";
625                 } else {
626                     if (i > 0) { // not the first platform
627                         if ((element.node.compareTo("generation") == 0)
628                             || (element.node.compareTo("version") == 0)) {
629                             // ignored
630                         } else {
631                             m_diffcount++;
632                         }
633                     }
634                 }
635                 if (m_Vetting) {
636                     String altText = element.platformData.get("ALT");
637                     writer.print("<td>" + value);
638                     String parName = mapToAbbr(element.parentNode);
639                     if ((parName.indexOf("_dateFormat") != -1)
640                         || (parName.indexOf("_timeFormat") != -1)) {
641                         writer.print("<form method=\"POST\" action=\"http://oss.software.ibm.com/cgi-bin/icu/lx/\">" +
642                             "<input type=hidden name=\"_\" value=\"" + localeStr + "\"/>" +
643                             "<input type=hidden name=\"x\" value=\"" + "dat" + "\"/>" +
644                             "<input type=hidden name=\"str\" value=\"" + value + "\"/>" +
645                             "<input type=submit value=\"" + "Test" + "\"/>" +
646                             "</form>");
647                     }
648                     if (/* m_Vetting && */element.referenceUrl != null) {
649                         writer.print("<br><div align='right'><a href=\"" + element.referenceUrl
650                             + "\"><i>(Ref)</i></a></div>");
651                     }
652                     writer.print("</td>");
653                     if (altText != null) {
654                         writer.print("        <td bgcolor=" + ALT_COLOR + ">" + altText);
655                         writer.print("</td>\n");
656                     }
657                 } else {
658                     if (caseDiff == true) {
659                         writer.print("                <td bgcolor=" + color + ">" + value + "&#x2020;");
660                     } else {
661                         writer.print("                <td bgcolor=" + color + ">" + value);
662                     }
663                     writer.print("</td>\n");
664                 }
665             }
666         }
667         writer.print("            </tr>\n");
668     }
669 
mapToAbbr(String source)670     private String mapToAbbr(String source) {
671         if (source.equals("icu:ruleBasedNumberFormat")) {
672             return "icu:rbnf";
673         }
674         if (source.equals("icu:ruleBasedNumberFormats")) {
675             return "icu:rbnfs";
676         }
677         if (source.equals("exemplarCharacters")) {
678             return "exemplarC";
679         }
680         if (source.equals("localizedPatternChars")) {
681             return "lpc";
682         }
683         return source;
684     }
685 
printHTML(PrintWriter writer, String localeStr)686     private void printHTML(PrintWriter writer, String localeStr) {
687         // System.out.println("INFO: Creating the comparison chart ");
688         ULocale locale = new ULocale(localeStr);
689         String displayName = localeStr + " (" + locale.getDisplayName() + ") ";
690         if ((m_iOptions & OPT_DIFF_REF_COMMON) != 0)
691             writer.print("<p>   Common data shown for reference purposes only</p>\n");
692 
693         if (!m_Vetting) {
694             writer.print("<html>\n" +
695                 "    <head>\n" +
696                 "        <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n" +
697                 "        <title>" + localeStr + "</title>\n" +
698                 "    </head>\n" +
699                 "    <style>\n" +
700                 "         <!--\n" +
701                 "         table        { border-spacing: 0; border-collapse: collapse; width: 100%; \n" +
702                 "                        border: 1px solid black }\n" +
703                 "         td, th       { width: 10%; border-spacing: 0; border-collapse: collapse; color: black; \n" +
704                 "                        vertical-align: top; border: 1px solid black }\n" +
705                 "         -->\n" +
706                 "     </style>" +
707                 "     <body bgcolor=\"#FFFFFF\">\n" +
708                 "        <p><b>" + displayName +
709                 "<a href=\"http://oss.software.ibm.com/cgi-bin/icu/lx/en/?_=" + localeStr + "\">Demo</a>, " +
710                 "<a href=\"../../comparison_charts.html\">Cover Page</a>, " +
711                 "<a href=\"./index.html\">Index</a>, " +
712                 "<a href=\"../collation/" + localeStr + ".html\">Collation</a> " +
713                 "</b></p>\n" +
714                 "        <table>\n");
715         } else {
716             writer.print("<html>\n" +
717                 "    <head>\n" +
718                 "        <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n" +
719                 "        <title>Draft/Alt: " + localeStr + "</title>\n" +
720                 "    </head>\n" +
721                 "    <style>\n" +
722                 "         <!--\n" +
723                 "         table        { border-spacing: 0; border-collapse: collapse;  \n" +
724                 "                        border: 1px solid black }\n" +
725                 "         td, th       { border-spacing: 0; border-collapse: collapse;  color: black; \n" +
726                 "                        vertical-align: top; border: 1px solid black }\n" +
727                 "         -->\n" +
728                 "     </style>" +
729                 "     <body bgcolor=\"#FFFFFF\">\n" +
730                 "        <p><b>" + displayName +
731                 "<a href=\"http://oss.software.ibm.com/cgi-bin/icu/lx/en/?_=" + localeStr + "\">Demo</a>, " +
732                 "<a href=\"./index.html\">Main and About</a>, " +
733                 "</b></p>\n");
734             writer.print("        <table>\n");
735         }
736 
737         // PN added
738         if (((m_iOptions & OPT_DIFF) != 0)
739             || ((m_iOptions & OPT_DIFF_REF_COMMON) != 0)) {
740             printTableHeaderForDifferences(writer);
741         } else {
742             printTableHeader(writer);
743         }
744 
745         // walk down the compare map and print the data
746         for (Object obj : compareMap.keySet()) {
747             CompareElement element;
748             if (obj != null) {
749                 Object value = compareMap.get(obj);
750                 if (value instanceof CompareElement) {
751                     element = (CompareElement) value;
752                 } else {
753                     throw new RuntimeException(
754                         "The object stored in the compare map is not an instance of CompareElement");
755                 }
756                 // PN added
757                 if ((m_iOptions & OPT_DIFF) != 0) {
758                     printDifferentValues(element, writer); // only print differences
759                 } else if ((m_iOptions & OPT_DIFF_REF_COMMON) != 0) {
760                     printDifferentValuesWithRef(element, writer);
761                 } else {
762                     printValue(element, writer);
763                 }
764             } else {
765                 throw new RuntimeException("No objects stored in the compare map!");
766             }
767 
768         }
769         writer.print("        </table>\n");
770 
771         if (m_Vetting) {
772             if (m_Messages.length() > 0) {
773                 writer.print("<table bgcolor=\"#FFBBBB\" border=3><tr><th>Warnings (please see source LDML)</th></tr>" +
774                     "<tr><td>" + m_Messages + "</td></tr></table><p/><p/>\n");
775             }
776             writer.print("<i>Interim page - subject to change</i> (<a href=\"./index.html\">Help</a>)<br/>");
777         }
778 
779         writer.print("        <p>Created on: " + cal.getTime() + "</p>\n" +
780             "    </body>\n" +
781             "</html>\n");
782         writer.flush();
783 
784         writer.flush();
785     }
786 
getFullyResolvedLocale(String localeName, String fileName)787     private Document getFullyResolvedLocale(String localeName, String fileName) {
788         // here we assume that "_" is the delimiter
789         int index = fileName.lastIndexOf(File.separatorChar);
790         String sourceDir = fileName.substring(0, index + 1);
791         String locale = fileName.substring(index + 1, fileName.lastIndexOf("."));
792         System.out.println("INFO: Creating fully resolved tree for : " + fileName);
793 
794         Document doc = LDMLUtilities.getFullyResolvedLDML(sourceDir, locale, true, true, true, true);
795         /*
796          * debugging code
797          *
798          * try{
799          * OutputStreamWriter writer = new OutputStreamWriter(new
800          * FileOutputStream(destFolder+File.separator+localeName+"_debug.xml"),encoding);
801          * LDMLUtilities.printDOMTree(doc,new PrintWriter(writer));
802          * writer.flush();
803          * }catch( Exception e){
804          * //throw the exception away .. this is for debugging
805          * }
806          */
807         return doc;
808     }
809 
addToCompareMap(String fileName, String key)810     private boolean addToCompareMap(String fileName, String key) {
811         // parse the test doc only if gold doc was parsed OK
812         Document testDoc = getFullyResolvedLocale(key, fileName);
813         requested.put(key, "");
814         if (null == testDoc) {
815             doesNotExist.put(key, "");
816             return false;
817         }
818         return extractMergeData(testDoc, key, false);
819 
820     }
821 
getParsedLocale(String localeName, String fileName)822     private Document getParsedLocale(String localeName, String fileName) {
823         // here we assume that "_" is the delimiter
824         System.out.println("INFO: Parsing " + fileName);
825         Document doc = LDMLUtilities.parse(fileName, true); // ?
826 
827         return doc;
828     }
829 
addVettable(String fileName, String key)830     private boolean addVettable(String fileName, String key) {
831         // parse the test doc only if gold doc was parsed OK
832         Document testDoc = getParsedLocale(key, fileName);
833         requested.put(key, "");
834         if (null == testDoc) {
835             doesNotExist.put(key, "");
836             return false;
837         }
838         return extractMergeData(testDoc, key, false);
839 
840     }
841 
comparePatterns(String pat1, String pat2)842     private boolean comparePatterns(String pat1, String pat2) {
843         // TODO: just return for now .. this is useful only
844         // when comparing data from toronto
845         try {
846             double args1 = 10000000000.00;
847             double args2 = -10000000000.00;
848 
849             DecimalFormat fmt = new DecimalFormat();
850 
851             fmt.applyPattern(pat1);
852             String s1 = fmt.format(args1);
853             String s3 = fmt.format(args2);
854             fmt.applyPattern(pat2);
855             String s2 = fmt.format(args1);
856             String s4 = fmt.format(args2);
857             if (s1.equals(s2) && s3.equals(s4)) {
858                 return true;
859             }
860         } catch (Exception e) {
861             // throw away the exception
862         }
863         return false;
864 
865         // return true;
866     }
867 
trim(String source)868     private String trim(String source) {
869         char[] src = source.toCharArray();
870         char[] dest = new char[src.length];
871 
872         int start = 0;
873         while (start < (src.length) && (UCharacter.isWhitespace(src[start]))) {
874             start++;
875         }
876         int stop = src.length - 1;
877         while (stop > 0 && (UCharacter.isWhitespace(src[stop]) || (src[stop] == 0xA0))) {
878             stop--;
879         }
880         if (stop != -1 && start != src.length) {
881             System.arraycopy(src, start, dest, 0, (stop - start) + 1);
882             return new String(dest, 0, (stop - start) + 1);
883         } else {
884             return new String();
885         }
886 
887     }
888 
addElement(String childNode, String parentNode, String id, String index, String platformValue, String platformName)889     private final void addElement(String childNode, String parentNode, String id, String index,
890         String platformValue, String platformName) {
891         addElement(childNode, parentNode, id, index, platformValue, platformName, null);
892     }
893 
addElement(String childNode, String parentNode, String id, String index, String platformValue, String platformName, String referenceUrl)894     private void addElement(String childNode, String parentNode, String id, String index,
895         String platformValue, String platformName, String referenceUrl) {
896         m_totalCount++;
897         Object obj = compareMap.get(id);
898         CompareElement element;
899         if (obj == null) {
900             element = new CompareElement();
901             // initialize the values
902             element.index = index;
903             element.parentNode = parentNode;
904             element.node = childNode;
905             element.referenceUrl = referenceUrl;
906             // add the element to the compare map
907             compareMap.put(id, element);
908         } else {
909             if (obj instanceof CompareElement) {
910                 element = (CompareElement) obj;
911             } else {
912                 throw new RuntimeException("The object stored in the compareMap is not a CompareElement object!");
913             }
914         }
915 
916         if ((!element.index.equals(index)) ||
917             (!element.node.equals(childNode)) ||
918             (!element.parentNode.equals(parentNode))) {
919             throw new RuntimeException("The retrieved object is not the same as the one trying to be saved, id is "
920                 + id);
921         }
922 
923         element.platformData.put(platformName, platformValue);
924     }
925 
childrenAreElements(Node node)926     private boolean childrenAreElements(Node node) {
927         NodeList list = node.getChildNodes();
928         for (int i = 0; i < list.getLength(); i++) {
929             if (list.item(i).getNodeType() == Node.ELEMENT_NODE) {
930                 return true;
931             }
932         }
933         return false;
934     }
935 
getTag(String childNodeName, String index)936     private String getTag(String childNodeName, String index) {
937 
938         // for months make index a,b,c,d etc
939         if (childNodeName.indexOf("month") > -1) {
940             int i = Integer.parseInt(index);
941             StringBuffer temp = new StringBuffer();
942             temp.append((char) ('a' + i));
943             return temp.toString();
944         } else if (childNodeName.indexOf("day") > -1) {
945             if (index.equals("sun")) {
946                 return "a";
947             } else if (index.equals("mon")) {
948                 return "b";
949             } else if (index.equals("tue")) {
950                 return "c";
951             } else if (index.equals("wed")) {
952                 return "d";
953             } else if (index.equals("thu")) {
954                 return "e";
955             } else if (index.equals("fri")) {
956                 return "f";
957             } else if (index.equals("sat")) {
958                 return "g";
959             }
960         } else {
961             return index;
962         }
963         return "";
964     }
965 
extractMergeData(Node node, String key, boolean parentDraft)966     private boolean extractMergeData(Node node, String key, boolean parentDraft) {
967         Node childOfSource;
968         for (childOfSource = node.getFirstChild(); childOfSource != null; childOfSource = childOfSource
969             .getNextSibling()) {
970             if (childOfSource.getNodeType() != Node.ELEMENT_NODE) {
971                 continue;
972             }
973             String altText = null;
974             // String altReferenceUrl = null;
975             Node altForChild = null;
976             boolean subDraft = parentDraft;
977             String childOfSourceName = childOfSource.getNodeName();
978             // Ignore collation and special tags
979             if (childOfSourceName.equals("collations") || childOfSource.equals("special")
980                 || childOfSourceName.indexOf(":") > -1) {
981                 continue;
982             }
983 
984             if (m_Vetting && LDMLUtilities.isNodeDraft(childOfSource)) {
985                 if (!subDraft) {
986                     subDraft = true;
987                 }
988             }
989             String referenceUrl = null;
990             if (m_Vetting) {
991                 referenceUrl = LDMLUtilities.getAttributeValue(childOfSource, LDMLConstants.REFERENCES);
992                 if ((referenceUrl != null) && (referenceUrl.length() == 0)) {
993                     referenceUrl = null;
994                 }
995             }
996 
997             if (m_Vetting) { /* Should this be always checked? */
998                 String alt = LDMLUtilities.getAttributeValue(childOfSource, LDMLConstants.ALT);
999                 if (alt != null) {
1000                     if (alt.equals(LDMLConstants.PROPOSED)) {
1001                         if (subDraft == false) {
1002                             throw new IllegalArgumentException("***** ERROR Proposed but not draft? "
1003                                 + childOfSource.toString());
1004                             // NOTREACHED
1005                         }
1006                         altForChild = LDMLUtilities.getNonAltNodeLike(node, childOfSource);
1007                         if (altForChild == null) {
1008                             System.out.println("WARNING: can't find a node like this one: " + childOfSource.toString()
1009                                 + " - consider removing the alt=\"proposed\" attribute.");
1010                             alt = null;
1011                         }
1012                         // altReferenceUrl = LDMLUtilities.getAttributeValue(altForChild, LDMLConstants.REFERENCES);
1013                         // if((altReferenceUrl!=null)&&(altReferenceUrl.length()==0)) {
1014                         // altReferenceUrl = null;
1015                         // }
1016                     } else if (subDraft) { /* don't care about nondraft */
1017                         String type = LDMLUtilities.getAttributeValue(childOfSource, LDMLConstants.TYPE);
1018                         if (type == null) {
1019                             type = "";
1020                         }
1021                         m_Messages = m_Messages + " <br> UNKNOWN alt type '" + alt + "' for " +
1022                             node.getNodeName() + "/" + childOfSourceName + "/" + type;
1023                         System.err.println("Warning: unknown alt type '" + alt + "'  - *IGNORING*. "
1024                             + childOfSource.toString());
1025                         continue;
1026                     }
1027                 }
1028             }
1029 
1030             if (childrenAreElements(childOfSource) == false) {
1031                 NamedNodeMap attr = childOfSource.getAttributes();
1032                 Node typeNode = attr.getNamedItem("type");
1033                 Node altNode = attr.getNamedItem("alt");
1034                 String index = "";
1035                 if (typeNode != null) {
1036                     /*
1037                      * if(childOfSource.getNodeName().equals("era")&&!key.equals("common")){
1038                      * //remap type for comparison purpose
1039                      * // TODO remove this hack
1040                      * int j = Integer.parseInt(typeNode.getNodeValue());
1041                      * if(j>0){
1042                      * j--;
1043                      * }
1044                      * typeNode.setNodeValue(Integer.toString(j));
1045                      * }
1046                      */
1047                     String temp = typeNode.getNodeValue();
1048 
1049                     if (!temp.equals("standard")) {
1050                         index = temp;
1051                     }
1052 
1053                 }
1054                 String alt = null;
1055                 if (altNode != null) {
1056                     alt = altNode.getNodeValue();
1057                 }
1058                 if (m_Vetting) { // TODO: all?
1059                     Node keyNode = attr.getNamedItem("key");
1060                     if (keyNode != null) {
1061                         String temp = keyNode.getNodeValue();
1062                         index = index + " (" + temp + ")";
1063                     }
1064                 }
1065                 String nodeValue = "";
1066                 Node valueNode = childOfSource.getFirstChild();
1067                 if (valueNode != null) {
1068                     String temp = trim(valueNode.getNodeValue());
1069                     if (!temp.equals("standard")) {
1070                         nodeValue = temp;
1071                     }
1072                 }
1073                 if (altForChild != null) {
1074                     Node valueNode2 = altForChild.getFirstChild();
1075                     if (valueNode2 != null) {
1076                         String temp = trim(valueNode2.getNodeValue());
1077                         if (!temp.equals("standard")) {
1078                             altText = temp;
1079                         } else {
1080                             altText = "??? alt=standard";
1081                         }
1082                     } else {
1083                         altText = "??? alt has no value";
1084                     }
1085                 }
1086                 Node parentNode = childOfSource.getParentNode();
1087                 String parentNodeName = trim(parentNode.getNodeName());
1088                 String childNodeName = trim(childOfSource.getNodeName());
1089                 Node grandParentNode = childOfSource.getParentNode().getParentNode();
1090                 String grandParentNodeName = grandParentNode.getNodeName();
1091                 NamedNodeMap parentAttrib = parentNode.getAttributes();
1092                 String type = "";
1093                 if (parentAttrib != null) {
1094                     Node mytypeNode = parentAttrib.getNamedItem("type");
1095                     if (mytypeNode != null) {
1096                         String mytype = mytypeNode.getNodeValue();
1097                         if (!mytype.equals("standard")) {
1098                             if (!parentNodeName.equals("calendar")) {
1099                                 type = mytype;
1100                             } else {
1101                                 parentNodeName = mytype;
1102                             }
1103                         }
1104                     }
1105 
1106                 }
1107                 if (grandParentNodeName.equals("eras")) {
1108                     Node calendar = grandParentNode.getParentNode();
1109                     NamedNodeMap gpa = calendar.getAttributes();
1110                     Node gptNode = gpa.getNamedItem("type");
1111                     if (gptNode != null) {
1112                         String gptType = gptNode.getNodeValue();
1113                         if (!gptType.equals("standard")) {
1114                             grandParentNodeName = gptType;
1115                         }
1116                     }
1117                     parentNodeName = grandParentNodeName + "\u200b_" + parentNodeName;
1118                 }
1119                 if (grandParentNodeName.equals("calendar")) {
1120                     NamedNodeMap gpa = grandParentNode.getAttributes();
1121                     Node gptNode = gpa.getNamedItem("type");
1122                     if (gptNode != null) {
1123                         String gptType = gptNode.getNodeValue();
1124                         if (!gptType.equals("standard")) {
1125                             grandParentNodeName = gptType;
1126                         }
1127                     }
1128                     parentNodeName = grandParentNodeName + "\u200b_" + parentNodeName;
1129                 }
1130                 if (grandParentNodeName.equals("monthContext") || grandParentNodeName.equals("dayContext") ||
1131                     grandParentNodeName.equals("dateFormatLength") || grandParentNodeName.equals("timeFormatLength") ||
1132                     grandParentNodeName.equals("dateTimeFormatLength")) {
1133 
1134                     Node calendar = grandParentNode.getParentNode().getParentNode();
1135                     NamedNodeMap ggpa = calendar.getAttributes();
1136                     Node ggptNode = ggpa.getNamedItem("type");
1137                     if (ggptNode != null) {
1138                         String ggptType = ggptNode.getNodeValue();
1139                         if (!ggptType.equals("standard")) {
1140                             grandParentNodeName = ggptType;
1141                             parentNodeName = ggptType + "\u200b_" + parentNodeName;
1142                         }
1143                     }
1144                     NamedNodeMap gpa = grandParentNode.getAttributes();
1145                     Node gptNode = gpa.getNamedItem("type");
1146                     if (gptNode != null) {
1147                         String gptType = gptNode.getNodeValue();
1148                         if (!gptType.equals("standard")) {
1149                             parentNodeName = parentNodeName + "\u200b_" + gptType;
1150                         }
1151                     }
1152                     NamedNodeMap pa = parentNode.getAttributes();
1153                     Node ptNode = pa.getNamedItem("type");
1154                     if (ptNode != null) {
1155                         String ptType = ptNode.getNodeValue();
1156                         if (!ptType.equals("standard")) {
1157                             parentNodeName = parentNodeName + "\u200b_" + ptType;
1158                         }
1159                     }
1160 
1161                 }
1162                 if (childNodeName.equals("pattern") || grandParentNodeName.equals("zone")) {
1163                     if (parentNodeName.indexOf("date") == -1 && parentNodeName.indexOf("time") == -1) {
1164                         NamedNodeMap at = grandParentNode.getAttributes();
1165                         Node mytypeNode = at.getNamedItem("type");
1166                         if (mytypeNode != null) {
1167                             String mytype = mytypeNode.getNodeValue();
1168                             if (!mytype.equals("standard")) {
1169                                 if (type.equals("")) {
1170                                     type = mytype;
1171                                 } else {
1172                                     type = type + "\u200b_" + mytype;
1173                                 }
1174 
1175                             }
1176                         }
1177                     }
1178                 }
1179                 if (grandParentNodeName.equals("special") || parentNodeName.equals("special")
1180                     || childNodeName.equals("special")
1181                     || grandParentNodeName.indexOf(":") > 0) {
1182                     continue;
1183                 }
1184                 if (!nodeValue.equals("") &&
1185                     !childOfSource.getNodeName().equals("version")) {
1186 
1187                     // for country codes and language codes
1188                     // replace the deprecated codes with the latest ones
1189                     if (childNodeName.equals("language")) {
1190                         String temp = deprecatedLanguageCodes.get(index);
1191                         if (temp != null) {
1192                             index = temp;
1193                         }
1194                     } else if (childNodeName.equals("territory")) {
1195                         String temp = deprecatedCountryCodes.get(index);
1196                         if (temp != null) {
1197                             index = temp;
1198                         }
1199                         if (index != null && alt != null) {
1200                             index = index + "_" + alt;
1201                         }
1202                     }
1203                     String id = "";
1204                     if (!type.equals("")) {
1205                         id = parentNodeName + "_" + childNodeName + "_" + type + "_" + getTag(childNodeName, index)
1206                             + "_" + grandParentNodeName;
1207                     } else {
1208                         id = parentNodeName + "_" + childNodeName + "_" + getTag(childNodeName, index) + "_"
1209                             + grandParentNodeName;
1210                     }
1211                     if (!index.equals("")) {
1212                         if (!index.equals(nodeValue) && !index.equals("Fallback")) {
1213                             if (!m_Vetting || subDraft) {
1214                                 addElement(childNodeName, parentNodeName, id, index, nodeValue, key, referenceUrl);
1215                                 if (altText != null) {
1216                                     addElement(childNodeName, parentNodeName, id, index, altText, "ALT", null /* altReferenceUrl */);
1217                                 }
1218                             }
1219                         }
1220                     } else {
1221                         if (!type.equals(nodeValue) && !type.equals("Fallback")) {
1222                             if (!m_Vetting || subDraft) {
1223                                 addElement(childNodeName, parentNodeName, id, type, nodeValue, key, referenceUrl);
1224                                 if (altText != null) {
1225                                     addElement(childNodeName, parentNodeName, id, index, altText, "ALT", null /* altReferenceUrl */);
1226                                 }
1227                             }
1228                         }
1229                     }
1230                 }
1231                 if (attr.getLength() > 0 && typeNode == null) { // TODO: make this a fcn
1232                     // add an element for each attribute different for each attribute
1233                     if (!m_Vetting || subDraft) {
1234                         for (int i = 0; i < attr.getLength(); i++) {
1235                             Node item = attr.item(i);
1236                             String attrName = item.getNodeName();
1237                             if (attrName.equals("type")) {
1238                                 continue;
1239                             }
1240                             if (attrName.equals("alt")) {
1241                                 continue;
1242                             }
1243                             if (attrName.equals("draft")) {
1244                                 continue;
1245                             }
1246                             if (grandParentNodeName.equals("zone")) {
1247                                 parentNodeName = grandParentNodeName + "\u200b_" + parentNodeName;
1248                             }
1249                             String id = grandParentNodeName + "_" + parentNodeName + "_" + childNodeName + "_" + type
1250                                 + "_" + attrName;
1251                             String subNodeValue = item.getNodeValue();
1252                             if (altForChild != null) {
1253                                 System.err.println(parentNodeName + "/" + childNodeName + " alt?? : " + altText);
1254                                 throw new IllegalArgumentException("UNKNOWN ALT SUBTAG + " + parentNodeName + "/"
1255                                     + childNodeName + " alt?? : " + altText + " not " + subNodeValue);
1256                             }
1257                             if (!index.equals("")) {
1258                                 addElement(childNodeName, parentNodeName, id, index, subNodeValue, key);
1259                             } else if (!type.equals("")) {
1260                                 addElement(childNodeName, parentNodeName, id, type, subNodeValue, key);
1261                             } else {
1262                                 if (!attrName.equals("draft")) {
1263                                     addElement(childNodeName, parentNodeName, id, attrName, subNodeValue, key);
1264                                 }
1265                             }
1266                         }
1267                     }
1268                 }
1269             } else {
1270                 // the element has more children .. recurse to pick them all
1271                 extractMergeData(childOfSource, key, subDraft);
1272             }
1273         }
1274         return true;
1275     }
1276 
1277     // *****************************************************************************************************
1278     // method writes the differences between xml files all to one HTML file
1279     // added by PN
1280     // *****************************************************************************************************
doBulkComparison()1281     private void doBulkComparison() {
1282         // get the output file name
1283         String fileName = destFolder + "/" + "Bulk.html";
1284         System.out.println("INFO: Creating file named: " + fileName);
1285         String fileName_summary = destFolder + "/" + "Bulk_summary.html";
1286         System.out.println("INFO: Creating file named: " + fileName_summary);
1287 
1288         try {
1289             OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(fileName), encoding);
1290             OutputStreamWriter os_summary = new OutputStreamWriter(new FileOutputStream(fileName_summary), encoding);
1291 
1292             // write the beginning of HTML page
1293             PrintWriter writer = new PrintWriter(os);
1294             PrintWriter writer_summary = new PrintWriter(os_summary);
1295             printHTMLStart(writer);
1296             printInfo(writer);
1297             printHTMLStart(writer_summary);
1298 
1299             // not all platforms have files for all locales so first build a locale superset
1300             // loop thru locale files from each folder, each folder contains a certain number of locales
1301             // build a HashSet superset
1302             File localeDir = null;
1303             String[] fileList;
1304             Set<String> localeTreeSet = new TreeSet<>(); // use TreeSet for locales in alphabetical order
1305             for (int i = 0; i < m_PlatformFolderVect.size(); i++) {
1306                 localeDir = new File(m_PlatformFolderVect.elementAt(i));
1307                 fileList = localeDir.list();
1308                 for (int j = 0; j < fileList.length; j++) {
1309                     if (fileList[j].endsWith(".xml")) {
1310                         // need to exclude root.xml and supplementalData.xml
1311                         if ((fileList[j].compareTo("root.xml") == 0)
1312                             || (fileList[j].compareTo("supplementalData.xml") == 0))
1313                             continue;
1314 
1315                         // exclude common if -diff_ref_common option chosen by user
1316                         // as common will only be shown as a reference if there are differences between locales for
1317                         // other platforms
1318                         if ((m_iOptions & OPT_DIFF_REF_COMMON) != 0) {
1319                             String platform = m_PlatformVect.elementAt(i);
1320                             if (platform.compareTo(COMMON) == 0)
1321                                 continue;
1322                         }
1323 
1324                         // entries are only added to TreeSets if not already there
1325                         localeTreeSet.add(fileList[j]);
1326                         // System.out.println (j + " adding " + fileList[j] + " to super set for platform " + (String)
1327                         // m_PlatformFolderVect.elementAt(i) );
1328                     }
1329                 }
1330             }
1331 
1332             // System.out.println(" size of locale set = " + localeTreeSet.size());
1333             // System.out.println(" number of platforms = " + m_PlatformFolderVect.size() + "(" + m_PlatformVect.size()
1334             // + ")");
1335 
1336             // loop thru all locales
1337             Object[] localeArray = localeTreeSet.toArray();
1338             int i = 0;
1339             for (i = 0; i < localeArray.length; i++) {
1340                 String platforms_with_this_locale = "";
1341 
1342                 String localeFile = (String) localeArray[i]; // locale file name without path
1343                 // class member localeStr used for writing to html
1344                 localeStr = localeFile.substring(0, localeFile.indexOf('.'));
1345                 System.out.println("INFO: locale : " + localeStr);
1346 
1347                 // add entry to CompareMap for any platforms having an xml file for the locale in question
1348                 for (int j = 0; j < m_PlatformFolderVect.size(); j++) {
1349                     localeDir = new File(m_PlatformFolderVect.elementAt(j));
1350                     fileList = localeDir.list();
1351                     for (int k = 0; k < fileList.length; k++) {
1352                         if (fileList[k].compareTo(localeFile) == 0) // test for 2 matching xml filenames
1353                         {
1354                             String key = m_PlatformVect.elementAt(j); // should use hashtable to link
1355                             // m_PlatformVect and
1356                             // m_PlatformFolderVect
1357                             String xmlFileName = localeDir + "/" + localeArray[i];
1358                             // System.out.println(i + " " + j + " " + k + " adding " + xmlFileName +
1359                             // " to compareMap at key " + key);
1360                             addToCompareMap(xmlFileName, key);
1361 
1362                             if (!(((m_iOptions & OPT_DIFF_REF_COMMON) != 0)
1363                                 && (key.compareTo(COMMON) == 0))) {
1364                                 platforms_with_this_locale += key;
1365                                 platforms_with_this_locale += ",  ";
1366                             }
1367                         }
1368                     }
1369                 }
1370                 // System.out.println("size of compareMap " + compareMap.size());
1371 
1372                 // print locale info and table header for this locale
1373                 printHTMLLocaleStart(writer, i, platforms_with_this_locale);
1374                 printTableHeaderForDifferences(writer);
1375 
1376                 // now do the comparison for a specific locale
1377                 walkCompareMap(writer, localeStr, platforms_with_this_locale);
1378 
1379                 // clear the compareMap before starting next locale
1380                 compareMap.clear();
1381 
1382                 // finish html table
1383                 printHTMLLocaleEnd(writer);
1384 
1385             } // end outer for loop on locales
1386 
1387             // print summary data to html summary file
1388             printLocaleSummaryToHTML(writer_summary);
1389             printAccumulatedResultsToHTML(writer_summary);
1390 
1391             printHTMLEnd(writer, i);
1392             printHTMLEnd(writer_summary, i);
1393         } catch (Exception e) {
1394             e.printStackTrace();
1395         }
1396         System.out.println("INFO: Finished writing file named: " + fileName);
1397         System.out.println("INFO: Finished writing file named: " + fileName_summary);
1398     }
1399 
1400     // added by PN
printHTMLStart(PrintWriter writer)1401     private void printHTMLStart(PrintWriter writer) {
1402         // System.out.println("INFO: Creating the comparison chart ");
1403 
1404         writer.print("<html>\n" +
1405             "    <head>\n" +
1406             "        <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n" +
1407             "    </head>\n" +
1408             "    <style>\n" +
1409             "         <!--\n" +
1410             "         table        { border-spacing: 0; border-collapse: collapse; width:100%; \n" +
1411             "                        border: 1px solid black }\n" +
1412             "         td, th       { border-spacing: 0; border-collapse: collapse; color: black; \n" +
1413             "                        vertical-align: top; border: 1px solid black }\n" +
1414             "         -->\n" +
1415             "     </style>" +
1416             "     <body bgcolor=\"#FFFFFF\"> \n" +
1417             "        <p><b>LOCALE DATA AUDIT</b></p>");
1418 
1419         writer.print("        <p>Created on: " + cal.getTime() + "</p>\n");
1420     }
1421 
printInfo(PrintWriter writer)1422     private void printInfo(PrintWriter writer) {
1423         if (((m_iOptions & OPT_DIFF_REF_COMMON) != 0)
1424             || ((m_iOptions & OPT_DIFF) != 0)) {
1425             writer
1426                 .print("        <p>locale elements where there is a difference between at least two platforms are shown. \n"
1427                     +
1428                     "If a locale element is the same across all platforms it is not shown </p>");
1429         }
1430 
1431         if ((m_iOptions & OPT_DIFF_REF_COMMON) != 0)
1432             writer
1433                 .print("<p>   Common data is shown for reference purposes only and is not part of the comparison</p>\n");
1434 
1435     }
1436 
1437     // added by PN
printHTMLEnd(PrintWriter writer, int iTotalNumLocales)1438     private void printHTMLEnd(PrintWriter writer, int iTotalNumLocales) {
1439         writer.print("<p>&nbsp;</p>");
1440         writer.print("<p>&nbsp;</p>");
1441         writer.print("          <p><b>SUMMARY : </b></p>");
1442         String platforms = "";
1443         for (int i = 0; i < m_PlatformVect.size(); i++) {
1444             if (((m_iOptions & OPT_DIFF_REF_COMMON) != 0)
1445                 && (m_PlatformVect.elementAt(i).equals(COMMON))) {
1446                 continue;
1447             }
1448             platforms += m_PlatformVect.elementAt(i);
1449             platforms += ", ";
1450         }
1451 
1452         writer.print("          <p><b>Platforms compared : " + platforms + "</b></p>");
1453         writer
1454             .print("          <p><b>Total Number of locales audited : "
1455                 + iTotalNumLocales
1456                 + "</b></p>"
1457                 +
1458                 "           <p><b>Total Number of conflicting locale data across all locales : "
1459                 + serialNumber
1460                 + "</b></p>"
1461                 +
1462                 "           <p><b>Number of locale elements where a conflict was found for at least one locale : "
1463                 + m_iTotalConflictingElements
1464                 + "</b></p>"
1465                 +
1466                 "           <p><b>Number of locale elements where no conflicts were found for any locale having this element : "
1467                 + m_iTotalNonConflictingElements + "</b></p>" +
1468                 "    </body>\n" +
1469                 "</html>\n");
1470         writer.flush();
1471 
1472         writer.flush();
1473     }
1474 
1475     // added by PN
printHTMLLocaleStart(PrintWriter writer, int iLocaleCounter, String platforms_with_this_locale)1476     private void printHTMLLocaleStart(PrintWriter writer, int iLocaleCounter, String platforms_with_this_locale) {
1477         ULocale locale = new ULocale(localeStr);
1478         String displayLang = locale.getDisplayLanguage();
1479         String dispCountry = locale.getDisplayCountry();
1480         String dispVariant = locale.getDisplayVariant();
1481         String displayName = localeStr + " (" + displayLang + "_" + dispCountry;
1482         if (dispVariant.length() > 0) {
1483             displayName += "_" + dispVariant + ") ";
1484         } else {
1485             displayName += ") ";
1486         }
1487 
1488         writer.print(
1489             "        <p><b>" + iLocaleCounter + "&nbsp;&nbsp;&nbsp;" + displayName +
1490             // "<a href=\"http://oss.software.ibm.com/cgi-bin/icu/lx/en/?_="+localeStr+"\">Demo</a>, "+
1491             // "<a href=\"../comparison_charts.html\">Cover Page</a>, "+
1492             // "<a href=\"./index.html\">Index</a>, "+
1493             // "<a href=\"../collation_diff/"+localeStr+"_collation.html\">Collation</a> "+
1494                 "</b>" +
1495                 "<b>&nbsp;&nbsp;&nbsp; platforms with this locale : " + platforms_with_this_locale + "</b></p>\n" +
1496                 "        <table>\n");
1497     }
1498 
1499     // added by PN
printHTMLLocaleEnd(PrintWriter writer)1500     private void printHTMLLocaleEnd(PrintWriter writer) {
1501         writer.print("        </table>\n");
1502     }
1503 
1504     // added by PN
walkCompareMap(PrintWriter writer, String locale, String platforms)1505     private void walkCompareMap(PrintWriter writer, String locale, String platforms) {
1506         SummaryData summData = new SummaryData();
1507 
1508         // walk down the compare map and print the data
1509         Iterator<String> iter = compareMap.keySet().iterator();
1510         while (iter.hasNext()) {
1511             Object obj = iter.next();
1512             CompareElement element;
1513             if (obj != null) {
1514                 Object value = compareMap.get(obj);
1515                 if (value instanceof CompareElement) {
1516                     element = (CompareElement) value;
1517                 } else {
1518                     throw new RuntimeException(
1519                         "The object stored in the compare map is not an instance of CompareElement");
1520                 }
1521 
1522                 boolean bIsEqual = true;
1523                 if ((m_iOptions & OPT_DIFF) != 0) {
1524                     bIsEqual = printDifferentValues(element, writer);
1525                     AddToAccumulatedResultsMap((String) obj, element, localeStr, bIsEqual);
1526                 } else if ((m_iOptions & OPT_DIFF_REF_COMMON) != 0) {
1527                     bIsEqual = printDifferentValuesWithRef(element, writer);
1528                     AddToAccumulatedResultsMap((String) obj, element, localeStr, bIsEqual);
1529                 } else {
1530                     printValue(element, writer);
1531                 }
1532 
1533                 if (bIsEqual == false)
1534                     summData.m_iNumConflictingElements++;
1535 
1536             } else {
1537                 throw new RuntimeException("No objects stored in the compare map!");
1538             }
1539         }
1540         summData.m_szPlatforms = platforms;
1541         m_LocaleSummaryDataMap.put(locale, summData);
1542 
1543     }
1544 
1545     // PN added
AddToAccumulatedResultsMap(String id, CompareElement element, String locale, boolean bIsEqual)1546     private void AddToAccumulatedResultsMap(String id, CompareElement element, String locale, boolean bIsEqual) {
1547         if (element == null)
1548             return;
1549 
1550         AccumulatedResults ad = m_AccumulatedResultsMap.get(id);
1551         if (ad == null) {
1552             // System.out.println("id = " + id);
1553 
1554             // add a new entry, there's none there with this key
1555             ad = new AccumulatedResults();
1556             ad.index = element.index;
1557             ad.node = element.node;
1558             ad.parentNode = element.parentNode;
1559             if (bIsEqual == false)
1560                 ad.localeVectDiff.add(locale);
1561             else
1562                 ad.localeVectSame.add(locale);
1563             m_AccumulatedResultsMap.put(id, ad);
1564         } else {
1565             if ((!ad.index.equals(element.index)) ||
1566                 (!ad.node.equals(element.node)) ||
1567                 (!ad.parentNode.equals(element.parentNode))) // ||
1568             // (!ad.type.equals(element.type))) type can be null so don't ceck its value
1569             {
1570                 throw new RuntimeException(
1571                     "The retrieved AccumulatedResults is not the same as the one trying to be saved - " + id);
1572             } else {
1573                 if (bIsEqual == false)
1574                     ad.localeVectDiff.add(locale);
1575                 else
1576                     ad.localeVectSame.add(locale);
1577             }
1578         }
1579     }
1580 
printAccumulatedResultsToHTML(PrintWriter writer)1581     private void printAccumulatedResultsToHTML(PrintWriter writer) {
1582         writer.print("<p>&nbsp;</p>");
1583         writer.print("<p>&nbsp;</p>");
1584         writer
1585             .print("<p><b>Table below shows the number of locales where conflicts did and didn't occur on a per locale element basis");
1586         writer
1587             .print("&nbsp;&nbsp; (For brevity, locale elements where no conflicts were detected for any locale are not shown) </b></p>");
1588         writer.print("<p></p>");
1589         writer.print("      <table width=\"700\">\n");
1590         writer.print("            <tr>\n" +
1591             "                <th width=5%>N.</th>\n" +
1592             "                <th width=10%>ParentNode</th>\n" +
1593             "                <th width=10%>Name</th>\n" +
1594             "                <th width=10%>ID</th>\n" +
1595             "                <th width=10%># of non-conflicting locales</th>" +
1596             "                <th width=10%># of conflicting locales</th>" +
1597             "                <th width=45%>Locales where conflicts were found</th>" +
1598             "            </tr>\n");
1599 
1600         // walk down the cm_AccumulateDifferenceMap and print the data
1601         Iterator<String> iter = m_AccumulatedResultsMap.keySet().iterator();
1602         // System.out.println ("size = " + m_AccumulateDifferenceMap.size());
1603 
1604         int iCounter = 0;
1605         while (iter.hasNext()) {
1606             Object obj = iter.next();
1607             AccumulatedResults ad;
1608             if (obj != null) {
1609                 Object value = m_AccumulatedResultsMap.get(obj);
1610                 if (value instanceof AccumulatedResults) {
1611                     ad = (AccumulatedResults) value;
1612                 } else {
1613                     throw new RuntimeException(
1614                         "The object stored in the AccumulateDifferencesMap is not an instance of AccumulateDifferences");
1615                 }
1616 
1617                 // only print locale elements where differences occurred
1618                 if (ad.localeVectDiff.size() > 0) {
1619                     m_iTotalConflictingElements++;
1620                     writer.print("            <tr>\n");
1621                     writer.print("                <td>" + (iCounter++) + "</td>\n");
1622                     writer.print("                <td>" + ad.parentNode + "</td>\n");
1623                     writer.print("                <td>" + ad.node + "</td>\n");
1624                     writer.print("                <td>" + ad.index + "</td>\n");
1625                     writer.print("                <td>" + ad.localeVectSame.size() + "</td>\n");
1626                     writer.print("                <td>" + ad.localeVectDiff.size() + "</td>\n");
1627                     String locales = "";
1628                     for (int i = 0; i < ad.localeVectDiff.size(); i++) {
1629                         locales += ad.localeVectDiff.elementAt(i);
1630                         locales += ", ";
1631                     }
1632                     writer.print("                <td>" + locales + "</td>\n");
1633                     writer.print("            </tr>\n");
1634                 } else {
1635                     m_iTotalNonConflictingElements++;
1636                 }
1637 
1638             } else {
1639                 throw new RuntimeException("No objects stored in the AccumulateDifferencesMap!");
1640             }
1641         }
1642 
1643         writer.print("      </table>\n");
1644 
1645     }
1646 
printLocaleSummaryToHTML(PrintWriter writer)1647     private void printLocaleSummaryToHTML(PrintWriter writer) {
1648         writer.print("<p>&nbsp;</p>");
1649         writer.print("<p>&nbsp;</p>");
1650         writer.print("<p><b>Table below shows the number of conflicting elements on a per locale basis\n</b></p>");
1651         writer.print("<p></p>");
1652         writer.print("      <table width=\"700\">\n");
1653         writer.print("            <tr>\n" +
1654             "                <th width=5%>N.</th>\n" +
1655             "                <th width=20%>Locale</th>\n" +
1656             "                <th width=40%>Platforms With This Locale</th>\n" +
1657             "                <th width=35%># of elements where a conflict was found</th>\n" +
1658             "            </tr>\n");
1659 
1660         // walk down the cm_AccumulateDifferenceMap and print the data
1661         Iterator<String> iter = m_LocaleSummaryDataMap.keySet().iterator();
1662         int iCounter = 0;
1663         while (iter.hasNext()) {
1664             Object obj = iter.next();
1665             SummaryData summData;
1666             if (obj != null) {
1667                 Object value = m_LocaleSummaryDataMap.get(obj);
1668                 if (value instanceof SummaryData) {
1669                     summData = (SummaryData) value;
1670                 } else {
1671                     throw new RuntimeException(
1672                         "The object stored in the AccumulateDifferencesMap is not an instance of AccumulateDifferences");
1673                 }
1674 
1675                 writer.print("            <tr>\n");
1676                 writer.print("                <td>" + (iCounter++) + "</td>\n");
1677                 writer.print("                <td>" + (String) obj + "</td>\n");
1678                 writer.print("                <td>" + summData.m_szPlatforms + "</td>\n");
1679                 writer.print("                <td>" + summData.m_iNumConflictingElements + "</td>\n");
1680                 writer.print("            </tr>\n");
1681             } else {
1682                 throw new RuntimeException("No objects stored in the AccumulateDifferencesMap!");
1683             }
1684         }
1685 
1686         writer.print("      </table>\n");
1687     }
1688 
1689 } // end of class definition/declaration
1690