jdk/test/sun/util/calendar/zi/GenDoc.java
changeset 15658 55b829ca2334
child 23010 6dadb192ad81
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/util/calendar/zi/GenDoc.java	Tue Feb 12 09:25:43 2013 -0800
@@ -0,0 +1,778 @@
+/*
+ * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import  java.io.BufferedReader;
+import  java.io.BufferedWriter;
+import  java.io.File;
+import  java.io.FileReader;
+import  java.io.FileWriter;
+import  java.io.IOException;
+import  java.util.Date;
+import  java.util.HashMap;
+import  java.util.List;
+import  java.util.Map;
+import  java.util.Set;
+import  java.util.SortedMap;
+import  java.util.StringTokenizer;
+import  java.util.TreeMap;
+import  java.util.TreeSet;
+
+/**
+ * <code>GenDoc</code> is one of back-end classes of javazic, and generates
+ * index.html and other html files which prints the detailed time zone
+ * information for each zone.
+ */
+class GenDoc extends BackEnd {
+
+    private static final String docDir = "doc";
+
+    private static final String header1 =
+        "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Frameset//EN\"" +
+        "\"http://www.w3.org/TR/REC-html40/frameset.dtd\">\n" +
+        "<HTML>\n<HEAD>\n<!-- Generated by javazic on ";
+    private static final String header2 =
+        "-->\n<TITLE>\n" +
+        "Java Platform, Standard Edition - TimeZone information based on ";
+    private static final String header3 =
+        "-->\n<TITLE>\n" +
+        "Java Platform, Standard Edition  TimeZone - ";
+    private static final String header4 =
+        "</TITLE>\n" +
+        "</HEAD>\n\n";
+
+    private static final String body1 =
+        "<BODY BGCOLOR=\"white\">\n";
+    private static final String body2 =
+        "</BODY>\n";
+
+    private static final String footer =
+        "</HTML>\n";
+
+
+    // list of time zone name and zonefile name/real time zone name
+    //   e.g.
+    //      key (String)       : value (String)
+    //      "America/Denver"   : "America/Denver.html" (real time zone)
+    //      "America/Shiprock" : "America/Denver"      (alias)
+    TreeMap<String,String> timezoneList = new TreeMap<String,String>();
+
+    // list of time zone's display name and time zone name
+    //   e.g.
+    //      key (String)                : value (String)
+    //      "Tokyo, Asia"               : "Asia/Tokyo"
+    //      "Marengo, Indiana, America" : "America/Indiana/Marengo"
+    //          (aliases included)
+    TreeMap<String,String> displayNameList = new TreeMap<String,String>();
+
+    // list of top level regions
+    //   e.g.
+    //      key (String) : value (String)
+    //      "America"    : "America.html"
+    //          (including entries in America/Indiana/, America/Kentucky/, ...)
+    TreeMap<String,String> regionList = new TreeMap<String,String>();
+
+    // mapping list from zone name to latitude & longitude
+    //   This list is generated from zone.tab.
+    //   e.g.
+    //      key (String) : value (LatitudeAndLongitude object)
+    //      "Asia/Tokyo" : latitude=35.3916, longitude=13.9444
+    //          (aliases not included)
+    HashMap<String,LatitudeAndLongitude> mapList = null;
+
+    // SortedMap of zone IDs sorted by their GMT offsets. If zone's GMT
+    // offset will change in the future, its last known offset is
+    // used.
+    SortedMap<Integer, Set<String>> zonesByOffset = new TreeMap<Integer, Set<String>>();
+
+    /**
+     * Generates HTML document for each zone.
+     * @param Timezone
+     * @return 0 if no errors, or 1 if error occurred.
+     */
+    int processZoneinfo(Timezone tz) {
+        try {
+            int size;
+            int index;
+            String outputDir = Main.getOutputDir();
+            String zonename = tz.getName();
+            String zonefile = ZoneInfoFile.getFileName(zonename) + ".html";
+            List<RuleRec> stz = tz.getLastRules();
+            timezoneList.put(zonename, zonefile);
+            displayNameList.put(transform(zonename), zonename);
+
+            // Populate zonesByOffset. (Zones that will change their
+            // GMT offsets are also added to zonesByOffset here.)
+            int lastKnownOffset = tz.getRawOffset();
+            Set<String> set = zonesByOffset.get(lastKnownOffset);
+            if (set == null) {
+                set = new TreeSet<String>();
+                zonesByOffset.put(lastKnownOffset, set);
+            }
+            set.add(zonename);
+
+            /* If outputDir doesn't end with file-separator, adds it. */
+            if (!outputDir.endsWith(File.separator)) {
+                outputDir += File.separatorChar;
+            }
+            outputDir += docDir + File.separatorChar;
+
+            index = zonename.indexOf('/');
+            if (index != -1) {
+                regionList.put(zonename.substring(0, index),
+                               zonename.substring(0, index) + ".html");
+            }
+
+            /* If zonefile includes file-separator, it's treated as part of
+             * pathname. And make directory if necessary.
+             */
+            index = zonefile.lastIndexOf('/');
+            if (index != -1) {
+                zonefile.replace('/', File.separatorChar);
+                outputDir += zonefile.substring(0, index+1);
+            }
+            File outD = new File(outputDir);
+            outD.mkdirs();
+
+            /* If mapfile is available, add a link to the appropriate map */
+            if ((mapList == null) && (Main.getMapFile() != null)) {
+                FileReader fr = new FileReader(Main.getMapFile());
+                BufferedReader in = new BufferedReader(fr);
+                mapList = new HashMap<String,LatitudeAndLongitude>();
+                String line;
+                while ((line = in.readLine()) != null) {
+                    // skip blank and comment lines
+                    if (line.length() == 0 || line.charAt(0) == '#') {
+                        continue;
+                    }
+                    StringTokenizer tokens = new StringTokenizer(line);
+                    String token = tokens.nextToken();  /* We don't use the first token. */
+                    token = tokens.nextToken();
+                    LatitudeAndLongitude location = new LatitudeAndLongitude(token);
+                    token = tokens.nextToken();
+                    mapList.put(token, location);
+                }
+                in.close();
+            }
+
+            /* Open zoneinfo file to write. */
+            FileWriter fw = new FileWriter(outputDir + zonefile.substring(index+1));
+            BufferedWriter out = new BufferedWriter(fw);
+
+            out.write(header1 + new Date() + header3 + zonename + header4);
+            out.write(body1 + "<FONT size=\"+2\"><B>" + zonename + "</B></FONT>");
+            LatitudeAndLongitude location = mapList.get(zonename);
+            if (location != null) {
+                int deg, min, sec;
+
+                deg = location.getLatDeg();
+                min = location.getLatMin();
+                sec = location.getLatSec();
+                if (deg < 0) {
+                    min = -min;
+                    sec = -sec;
+                } else if (min < 0) {
+                    sec = -sec;
+                }
+                out.write("&nbsp;&nbsp;&nbsp;" +
+                          "<A HREF=\"http://www.mapquest.com/maps/map.adp?" +
+                          "latlongtype=degrees" +
+                          "&latdeg=" + deg +
+                          "&latmin=" + min +
+                          "&latsec=" + sec);
+
+                deg = location.getLongDeg();
+                min = location.getLongMin();
+                sec = location.getLongSec();
+                if (deg < 0) {
+                    min = -min;
+                    sec = -sec;
+                } else if (min < 0) {
+                    sec = -sec;
+                }
+                out.write("&longdeg=" + deg +
+                          "&longmin=" + min +
+                          "&longsec=" + sec +
+                          "\" target=\"_blank\">[map]</A>");
+            }
+            out.write("\n<P>\n");
+
+            List<ZoneRec> zone = tz.getZones();
+            List<RuleRec> rule = tz.getRules();
+            if (rule != null && zone != null) {
+                out.write("<TABLE BORDER=\"0\" WIDTH=\"100%\" CELLPADDING=\"1\" CELLSPACING=\"0\">\n" +
+                          "<TR>\n" +
+                          "<TD BGCOLOR=\"#EEEEFF\" WIDTH=\"50%\" ALIGN=\"CENTER\"><BR>" +
+                          "<A HREF=\"#Rules\">Rules</A><BR></TD>\n" +
+                          "<TD BGCOLOR=\"#EEEEFF\" WIDTH=\"50%\" ALIGN=\"CENTER\">" +
+                          "<A HREF=\"#Zone\"><BR>Zone<BR></A></TD>\n" +
+                          "</TR>\n</TABLE>\n");
+            }
+
+            /* Output Rule records. */
+            if (rule != null) {
+                size = rule.size();
+                out.write("<P>\n<A NAME=\"Rules\">" +
+                          "<FONT SIZE=\"+1\"><B>Rules</B></FONT></A>\n" +
+                          "<TABLE BORDER=\"1\" WIDTH=\"100%\" CELLPADDING=\"3\" CELLSPACING=\"0\">\n" +
+                          "<TR BGCOLOR=\"#CCCCFF\">\n" +
+                          "<TD>NAME</TD><TD>FROM</TD><TD>TO</TD><TD>TYPE</TD>" +
+                          "<TD>IN</TD><TD>ON</TD><TD>AT</TD><TD>SAVE</TD>" +
+                          "<TD>LETTER/S</TD><TD>NOTES</TD>\n</TR>\n");
+                for (int i = 0; i < size; i++) {
+                    out.write("<TR BGCOLOR=\"#FFFFFF\">\n");
+                    StringTokenizer st = new StringTokenizer(rule.get(i).getLine());
+                    String s;
+                    if (st.hasMoreTokens()) {   /* RULE - truncated */
+                        st.nextToken();
+                    }
+                    if (st.hasMoreTokens()) {   /* NAME */
+                        out.write("<TD>" + st.nextToken() + "</TD>");
+                    }
+                    if (st.hasMoreTokens()) {   /* FROM */
+                        out.write("<TD>" + st.nextToken() + "</TD>");
+                    }
+                    if (st.hasMoreTokens()) {   /* TO */
+                        s = st.nextToken();
+                        if (s.equals("min") || s.equals("max")) {
+                            out.write("<TD><FONT COLOR=\"red\">" + s + "</FONT></TD>");
+                        } else {
+                            out.write("<TD>" + s + "</TD>");
+                        }
+                    }
+                    if (st.hasMoreTokens()) {   /* TYPE */
+                        out.write("<TD>" + st.nextToken() + "</TD>");
+                    }
+                    if (st.hasMoreTokens()) {   /* IN */
+                        out.write("<TD>" + st.nextToken() + "</TD>");
+                    }
+                    if (st.hasMoreTokens()) {   /* ON */
+                        out.write("<TD>" + st.nextToken() + "</TD>");
+                    }
+                    if (st.hasMoreTokens()) {   /* AT */
+                        out.write("<TD>" + st.nextToken() + "</TD>");
+                    }
+                    if (st.hasMoreTokens()) {   /* SAVE */
+                        out.write("<TD>" + st.nextToken() + "</TD>");
+                    }
+                    if (st.hasMoreTokens()) {   /* LETTER/S */
+                        out.write("<TD>" + st.nextToken() + "</TD>");
+                    }
+                    if (st.hasMoreTokens()) {   /* NOTES */
+                        s = st.nextToken();
+                        while (st.hasMoreTokens()) {
+                            s += " " + st.nextToken();
+                        }
+                        index = s.indexOf('#');
+                        out.write("<TD>" + s.substring(index+1) + "</TD>\n");
+                    } else {
+                        out.write("<TD>&nbsp;</TD>\n");
+                    }
+                    out.write("</TR>\n");
+                }
+                out.write("</TABLE>\n<P>&nbsp;<P>\n");
+            }
+
+            /* Output Zone records. */
+            if (zone != null) {
+                size = zone.size();
+                out.write("<P>\n<A NAME=\"Zone\">" +
+                          "<FONT SIZE=\"+1\"><B>Zone</B></FONT></A>\n" +
+                          "<TABLE BORDER=\"1\" WIDTH=\"100%\" CELLPADDING=\"3\" CELLSPACING=\"0\">\n" +
+                          "<TR BGCOLOR=\"#CCCCFF\">\n<TD>GMTOFF</TD>" +
+                          "<TD>RULES</TD><TD>FORMAT</TD><TD>UNTIL</TD>" +
+                          "<TD>NOTES</TD>\n</TR>\n");
+                for (int i = 0; i < size; i++) {
+                    out.write("<TR>\n");
+                    StringTokenizer st = new StringTokenizer(zone.get(i).getLine());
+                    String s = st.nextToken();
+                    if (s.equals("Zone")) {     /* NAME */
+                        s = st.nextToken();
+                        s = st.nextToken();
+                    }
+                    out.write("<TD>" + s + "</TD>");    /* GMTOFFSET */
+                    if (st.hasMoreTokens()) {   /* RULES */
+                        out.write("<TD>" + st.nextToken() + "</TD>");
+                    }
+                    if (st.hasMoreTokens()) {   /* FORMAT */
+                        s = st.nextToken();
+                        index = s.indexOf('#');
+                        if (index != -1) {
+                            if (index != 0) {
+                                out.write("<TD>" + s.substring(0, index-1) +
+                                          "</TD>");     /* FORMAT */
+                                s = s.substring(index+1);
+                            } else {
+                                out.write("<TD>&nbsp;</TD>");   /* FORMAT */
+                            }
+                            while (st.hasMoreTokens()) {
+                                s += " " + st.nextToken();
+                            }
+                            out.write("<TD>&nbsp;</TD>");       /* UNTIL */
+                            out.write("<TD>" + s + "</TD>\n</TR>\n");   /* NOTES */
+                            continue;
+                        } else {
+                            out.write("<TD>" + s + "</TD>");    /* FORMAT */
+                        }
+                    }
+
+                    if (st.hasMoreTokens()) {   /* UNTIL */
+                        s = st.nextToken();
+                        while (st.hasMoreTokens()) {
+                            s += " " + st.nextToken();
+                        }
+                        index = s.indexOf('#');
+                        if (index != -1) {
+                            if (index != 0) {
+                                out.write("<TD>" + s.substring(0, index-1) +
+                                          "</TD>");     /* UNTIL */
+                            } else {
+                                out.write("<TD>&nbsp;</TD>");   /* UNTIL */
+                            }
+                            out.write("<TD>" + s.substring(index+1) +
+                                      "</TD>\n");       /* NOTES */
+                        } else {
+                            out.write("<TD>" + s + "</TD>");    /* UNTIL */
+                            out.write("<TD>&nbsp;</TD>\n");     /* NOTES */
+                        }
+                    } else {
+                        out.write("<TD>&nbsp;</TD>");           /* UNTIL */
+                        out.write("<TD>&nbsp;</TD>\n");         /* NOTES */
+                    }
+                    out.write("</TR>\n");
+                }
+                out.write("</TABLE>\n");
+            }
+            out.write(body2 + footer);
+
+            out.close();
+            fw.close();
+        } catch(IOException e) {
+            Main.panic("IO error: "+e.getMessage());
+            return 1;
+        }
+
+        return 0;
+    }
+
+    /**
+     * Generates index.html and other top-level frame files.
+     * @param Mappings
+     * @return 0 if no errors, or 1 if error occurred.
+     */
+    int generateSrc(Mappings map) {
+        try {
+            int len;
+            Object o[];
+            String outputDir = Main.getOutputDir();
+            FileWriter fw1, fw2;
+            BufferedWriter out1, out2;
+
+            /* Whether alias list exists or not. */
+            Map<String,String> a = map.getAliases();
+            if (a == null) {
+                Main.panic("Data not exist. (aliases)");
+                return 1;
+            }
+
+            timezoneList.putAll(a);
+
+            /* If outputDir doesn't end with file-separator, adds it. */
+            if (!outputDir.endsWith(File.separator)) {
+                outputDir += File.separatorChar;
+            }
+            outputDir += docDir + File.separatorChar;
+
+            File outD = new File(outputDir);
+            outD.mkdirs();
+
+            /* Creates index.html */
+            fw1 = new FileWriter(outputDir + "index.html", false);
+            out1 = new BufferedWriter(fw1);
+
+            out1.write(header1 + new Date() + header2 + Main.getVersionName() +
+                       header4 +
+                       "<FRAMESET cols=\"20%,80%\">\n" +
+                       "<FRAMESET rows=\"30%,70%\">\n" +
+                       "<FRAME src=\"overview-frame.html\" name=\"TimeZoneListFrame\">\n" +
+                       "<FRAME src=\"allTimeZone-frame1.html\" name=\"allTimeZoneFrame\">\n" +
+                       "</FRAMESET>" +
+                       "<FRAME src=\"overview-summary.html\" name=\"rightFrame\">\n" +
+                       "</FRAMESET>\n" +
+                       "<NOFRAMES>\n" +
+                       "<H2>\nFrame Alert\n</H2>\n\n" +
+                       "<P>\n\n" +
+                       "This document is designed to be viewed using the frames feature. If you see this\n" +
+                       "message, you are using a non-frame-capable web client.\n" +
+                       "<BR>\n" +
+                       "Link to<A HREF=\"overview-summary.html\">Non-frame version.</A>\n" +
+                       "</NOFRAMES>\n" + footer);
+
+            out1.close();
+            fw1.close();
+
+
+            /* Creates overview-frame.html */
+            fw1 = new FileWriter(outputDir + "overview-frame.html", false);
+            out1 = new BufferedWriter(fw1);
+
+            out1.write(header1 + new Date() + header2 + Main.getVersionName() +
+                       header4 + body1 +
+                       "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n<TR>\n" +
+                       "<TD NOWRAP><FONT size=\"+1\">\n" +
+                       "<B>Java<sup><font size=-2>TM</font></sup>&nbsp;Platform<br>Standard&nbsp;Ed.</B></FONT></TD>\n" +
+                       "</TR>\n</TABLE>\n\n" +
+                       "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n<TR>\n<TD NOWRAP>" +
+                       "<P>\n<FONT size=\"+1\">\nAll Time Zones Sorted By:</FONT>\n<BR>\n" +
+                       "&nbsp;&nbsp;<A HREF=\"allTimeZone-frame1.html\" TARGET=\"allTimeZoneFrame\">GMT offsets</A></FONT>\n<BR>\n" +
+                       "&nbsp;&nbsp;<A HREF=\"allTimeZone-frame2.html\" TARGET=\"allTimeZoneFrame\">Zone names</A></FONT>\n<BR>" +
+                       "&nbsp;&nbsp;<A HREF=\"allTimeZone-frame3.html\" TARGET=\"allTimeZoneFrame\">City names</A></FONT>\n" +
+                       "<P>\n<FONT size=\"+1\">\nContinents and Oceans</FONT>\n<BR>\n");
+
+            for (String regionKey : regionList.keySet()) {
+                out1.write("&nbsp;&nbsp;<A HREF=\"" + regionList.get(regionKey) +
+                           "\" TARGET=\"allTimeZoneFrame\">" + regionKey +
+                           "</A><BR>\n");
+
+                fw2 = new FileWriter(outputDir + regionList.get(regionKey),
+                                     false);
+                out2 = new BufferedWriter(fw2);
+
+                out2.write(header1 + new Date() + header3 + regionKey +
+                           header4 + body1 + "<FONT size=\"+1\"><B>" +
+                           regionKey + "</B></FONT>\n<BR>\n<TABLE>\n<TR>\n<TD>");
+
+                boolean found = false;
+                for (String timezoneKey : timezoneList.keySet()) {
+                    int regionIndex = timezoneKey.indexOf('/');
+                    if (regionIndex == -1 ||
+                        !regionKey.equals(timezoneKey.substring(0, regionIndex))) {
+                        if (found) {
+                            break;
+                        } else {
+                            continue;
+                        }
+                    }
+
+                    found = true;
+                    if (a.containsKey(timezoneKey)) {
+                        Object realName = a.get(timezoneKey);
+                        while (a.containsKey(realName)) {
+                            realName = a.get(realName);
+                        }
+                        out2.write(timezoneKey +
+                                   " (alias for " + "<A HREF=\"" +
+                                   timezoneList.get(realName) +
+                                   "\" TARGET=\"rightFrame\">" +
+                                   realName + "</A>)");
+                    } else {
+                        out2.write("<A HREF=\"" + timezoneList.get(timezoneKey) +
+                                   "\" TARGET=\"rightFrame\">" + timezoneKey +
+                                   "</A>");
+                    }
+                    out2.write("<BR>\n");
+                }
+                out2.write("</TD>\n</TR>\n</TABLE>\n" + body2 + footer);
+
+                out2.close();
+                fw2.close();
+            }
+            out1.write("</FONT></TD>\n</TR></TABLE>\n" + body2 + footer);
+
+            out1.close();
+            fw1.close();
+
+
+            /* Creates allTimeZone-frame1.html (Sorted by GMT offsets) */
+            fw1 = new FileWriter(outputDir + "allTimeZone-frame1.html", false);
+            out1 = new BufferedWriter(fw1);
+
+            out1.write(header1 + new Date() + header2 + Main.getVersionName() +
+                       header4 + body1 +
+                       "<FONT size=\"+1\"><B>Sorted by GMT offsets</B></FONT>\n" +
+                       "<BR>\n\n" + "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n" +
+                       "<TR>\n<TD NOWRAP>\n");
+
+            List<Integer> roi = map.getRawOffsetsIndex();
+            List<Set<String>> roit = map.getRawOffsetsIndexTable();
+
+            int index = 0;
+            for (Integer offset : zonesByOffset.keySet()) {
+                int off = roi.get(index);
+                Set<String> perRO = zonesByOffset.get(offset);
+                if (offset == off) {
+                    // Merge aliases into zonesByOffset
+                    perRO.addAll(roit.get(index));
+                }
+                index++;
+
+                for (String timezoneKey : perRO) {
+                    out1.write("<TR>\n<TD><FONT SIZE=\"-1\">(" +
+                               Time.toGMTFormat(offset.toString()) +
+                               ")</FONT></TD>\n<TD>");
+
+                    if (a.containsKey(timezoneKey)) {
+                        Object realName = a.get(timezoneKey);
+                        while (a.containsKey(realName)) {
+                            realName = a.get(realName);
+                        }
+                        out1.write(timezoneKey +
+                                   " (alias for " + "<A HREF=\"" +
+                                   timezoneList.get(realName) +
+                                   "\" TARGET=\"rightFrame\">" + realName +
+                                   "</A>)");
+                    } else {
+                        out1.write("<A HREF=\"" + timezoneList.get(timezoneKey) +
+                                   "\" TARGET=\"rightFrame\">" + timezoneKey +
+                                   "</A>");
+                    }
+                    out1.write("</TD>\n</TR>\n");
+                }
+            }
+            out1.write("</FONT></TD>\n</TR>\n</TABLE>\n" + body2 + footer);
+
+            out1.close();
+            fw1.close();
+
+
+            /* Creates allTimeZone-frame2.html (Sorted by zone names) */
+            fw1 = new FileWriter(outputDir + "allTimeZone-frame2.html", false);
+            out1 = new BufferedWriter(fw1);
+
+            out1.write(header1 + new Date() + header2 + Main.getVersionName() +
+                       header4 + body1 +
+                       "<FONT size=\"+1\"><B>Sorted by zone names</B></FONT>\n" +
+                       "<BR>\n\n" + "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n" +
+                       "<TR>\n<TD NOWRAP>\n");
+            o = timezoneList.keySet().toArray();
+            len = timezoneList.size();
+            for (int i = 0; i < len; i++) {
+                Object timezoneKey = o[i];
+                if (a.containsKey(timezoneKey)) {
+                    Object realName = a.get(timezoneKey);
+                    while (a.containsKey(realName)) {
+                        realName = a.get(realName);
+                    }
+                    out1.write(timezoneKey +
+                               " (alias for " +
+                               "<A HREF=\"" + timezoneList.get(realName) +
+                               "\" TARGET=\"rightFrame\">" + realName +
+                               "</A>)");
+                } else {
+                    out1.write("<A HREF=\"" + timezoneList.get(timezoneKey) +
+                               "\" TARGET=\"rightFrame\">" + timezoneKey +
+                               "</A>");
+                }
+                out1.write("<BR> \n");
+            }
+            out1.write("</FONT></TD>\n</TR>\n</TABLE>\n" + body2 + footer);
+
+            out1.close();
+            fw1.close();
+
+            /* Creates allTimeZone-frame3.html (Sorted by city names) */
+            fw1 = new FileWriter(outputDir + "allTimeZone-frame3.html", false);
+            out1 = new BufferedWriter(fw1);
+
+            out1.write(header1 + new Date() + header2 + Main.getVersionName() +
+                       header4 + body1 +
+                       "<FONT size=\"+1\"><B>Sorted by city names</B></FONT>\n" +
+                       "<BR>\n\n" + "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n" +
+                       "<TR>\n<TD NOWRAP>\n");
+
+            Set<String> aliasSet = a.keySet();
+            len = aliasSet.size();
+            String aliasNames[] = aliasSet.toArray(new String[0]);
+            for (int i = 0; i < len; i++) {
+                displayNameList.put(transform(aliasNames[i]),
+                                    aliasNames[i]);
+            }
+
+            o = displayNameList.keySet().toArray();
+            len = displayNameList.size();
+            for (int i = 0; i < len; i++) {
+                Object displayName = o[i];
+                Object timezoneKey = displayNameList.get(o[i]);
+                if (a.containsKey(timezoneKey)) {
+                    Object realName = a.get(timezoneKey);
+                    while (a.containsKey(realName)) {
+                        realName = a.get(realName);
+                    }
+                    out1.write(displayName +
+                               " (alias for " +
+                               "<A HREF=\"" + timezoneList.get(realName) +
+                               "\" TARGET=\"rightFrame\">" + realName +
+                               "</A>)");
+                } else {
+                    out1.write("<A HREF=\"" + timezoneList.get(timezoneKey) +
+                               "\" TARGET=\"rightFrame\">" + displayName +
+                               "</A>");
+                }
+                out1.write("<BR> \n");
+            }
+
+            out1.write("</FONT></TD>\n</TR>\n</TABLE>\n" + body2 + footer);
+
+            out1.close();
+            fw1.close();
+
+            /* Creates overview-summary.html */
+            fw1 = new FileWriter(outputDir + "overview-summary.html", false);
+            out1 = new BufferedWriter(fw1);
+
+            out1.write(header1 + new Date() + header2 + Main.getVersionName() +
+                       header4 + body1 +
+                       "<p>This is the list of time zones generated from <B>" +
+                       Main.getVersionName() + "</B> for Java Platform, " +
+                       "Standard Edition. The source code can be obtained " +
+                       "from ftp site <a href=\"ftp://elsie.nci.nih.gov/pub/\">" +
+                       "ftp://elsie.nci.nih.gov/pub/</a>. A total of <B>" +
+                       len +
+                       "</B> time zones and aliases are supported " +
+                       "in this edition. For the " +
+                       "format of rules and zones, refer to the zic " +
+                       "(zoneinfo compiler) man page on " +
+                       "Solaris or Linux.</p>\n" +
+                       "<p>Note that the time zone data is not " +
+                       "a public interface of the Java Platform. No " +
+                       "applications should rely on the time zone data of " +
+                       "this document. Time zone names and data " +
+                       "may change without any prior notice.</p>\n" +
+                       body2 + footer);
+
+            out1.close();
+            fw1.close();
+        } catch(IOException e) {
+            Main.panic("IO error: "+e.getMessage());
+            return 1;
+        }
+
+        return 0;
+    }
+
+    String transform(String s) {
+        int index = s.lastIndexOf("/");
+
+        /* If the string doesn't include any delimiter, return */
+        if (index == -1) {
+            return s;
+        }
+
+        int lastIndex = index;
+        String str = s.substring(index+1);
+        do {
+            index = s.substring(0, lastIndex).lastIndexOf('/');
+            str += ", " + s.substring(index+1, lastIndex);
+            lastIndex = index;
+        } while (index > -1);
+
+        return str;
+    }
+
+    static class LatitudeAndLongitude {
+
+        private int latDeg, latMin, latSec, longDeg, longMin, longSec;
+
+        LatitudeAndLongitude(String s) {
+            try {
+                // First of all, check the string has the correct format:
+                //    either +-DDMM+-DDDMM or +-DDMMSS+-DDDMMSS
+
+                if (!s.startsWith("+") && !s.startsWith("-")) {
+                    Main.warning("Wrong latitude&longitude data: " + s);
+                    return;
+                }
+                int index;
+                if (((index = s.lastIndexOf("+")) <= 0) &&
+                    ((index = s.lastIndexOf("-")) <= 0)) {
+                    Main.warning("Wrong latitude&longitude data: " + s);
+                    return;
+                }
+
+                if (index == 5) {
+                    latDeg = Integer.parseInt(s.substring(1, 3));
+                    latMin = Integer.parseInt(s.substring(3, 5));
+                    latSec = 0;
+                } else if (index == 7) {
+                    latDeg = Integer.parseInt(s.substring(1, 3));
+                    latMin = Integer.parseInt(s.substring(3, 5));
+                    latSec = Integer.parseInt(s.substring(5, 7));
+                } else {
+                    Main.warning("Wrong latitude&longitude data: " + s);
+                    return;
+                }
+                if (s.startsWith("-")){
+                        latDeg = -latDeg;
+                        latMin = -latMin;
+                        latSec = -latSec;
+                }
+
+                int len = s.length();
+                if (index == 5 && len == 11) {
+                    longDeg = Integer.parseInt(s.substring(index+1, index+4));
+                    longMin = Integer.parseInt(s.substring(index+4, index+6));
+                    longSec = 0;
+                } else if (index == 7 && len == 15) {
+                    longDeg = Integer.parseInt(s.substring(index+1, index+4));
+                    longMin = Integer.parseInt(s.substring(index+4, index+6));
+                    longSec = Integer.parseInt(s.substring(index+6, index+8));
+                } else {
+                    Main.warning("Wrong latitude&longitude data: " + s);
+                    return;
+                }
+                if (s.charAt(index) == '-'){
+                        longDeg = -longDeg;
+                        longMin = -longMin;
+                        longSec = -longSec;
+                }
+            } catch(Exception e) {
+                Main.warning("LatitudeAndLongitude() Parse error: " + s);
+            }
+        }
+
+        int getLatDeg() {
+            return latDeg;
+        }
+
+        int getLatMin() {
+            return latMin;
+        }
+
+        int getLatSec() {
+            return latSec;
+        }
+
+        int getLongDeg() {
+            return longDeg;
+        }
+
+        int getLongMin() {
+            return longMin;
+        }
+
+        int getLongSec() {
+            return longSec;
+        }
+    }
+}