1 /*
   2  * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import  java.io.BufferedReader;
  25 import  java.io.BufferedWriter;
  26 import  java.io.File;
  27 import  java.io.FileReader;
  28 import  java.io.FileWriter;
  29 import  java.io.IOException;
  30 import  java.util.Date;
  31 import  java.util.HashMap;
  32 import  java.util.List;
  33 import  java.util.Map;
  34 import  java.util.Set;
  35 import  java.util.SortedMap;
  36 import  java.util.StringTokenizer;
  37 import  java.util.TreeMap;
  38 import  java.util.TreeSet;
  39 
  40 /**
  41  * <code>GenDoc</code> is one of back-end classes of javazic, and generates
  42  * index.html and other html files which prints the detailed time zone
  43  * information for each zone.
  44  */
  45 class GenDoc extends BackEnd {
  46 
  47     private static final String docDir = "doc";
  48 
  49     private static final String header1 =
  50         "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Frameset//EN\"" +
  51         "\"http://www.w3.org/TR/REC-html40/frameset.dtd\">\n" +
  52         "<HTML>\n<HEAD>\n<!-- Generated by javazic on ";
  53     private static final String header2 =
  54         "-->\n<TITLE>\n" +
  55         "Java Platform, Standard Edition - TimeZone information based on ";
  56     private static final String header3 =
  57         "-->\n<TITLE>\n" +
  58         "Java Platform, Standard Edition  TimeZone - ";
  59     private static final String header4 =
  60         "</TITLE>\n" +
  61         "</HEAD>\n\n";
  62 
  63     private static final String body1 =
  64         "<BODY BGCOLOR=\"white\">\n";
  65     private static final String body2 =
  66         "</BODY>\n";
  67 
  68     private static final String footer =
  69         "</HTML>\n";
  70 
  71 
  72     // list of time zone name and zonefile name/real time zone name
  73     //   e.g.
  74     //      key (String)       : value (String)
  75     //      "America/Denver"   : "America/Denver.html" (real time zone)
  76     //      "America/Shiprock" : "America/Denver"      (alias)
  77     TreeMap<String,String> timezoneList = new TreeMap<String,String>();
  78 
  79     // list of time zone's display name and time zone name
  80     //   e.g.
  81     //      key (String)                : value (String)
  82     //      "Tokyo, Asia"               : "Asia/Tokyo"
  83     //      "Marengo, Indiana, America" : "America/Indiana/Marengo"
  84     //          (aliases included)
  85     TreeMap<String,String> displayNameList = new TreeMap<String,String>();
  86 
  87     // list of top level regions
  88     //   e.g.
  89     //      key (String) : value (String)
  90     //      "America"    : "America.html"
  91     //          (including entries in America/Indiana/, America/Kentucky/, ...)
  92     TreeMap<String,String> regionList = new TreeMap<String,String>();
  93 
  94     // mapping list from zone name to latitude & longitude
  95     //   This list is generated from zone.tab.
  96     //   e.g.
  97     //      key (String) : value (LatitudeAndLongitude object)
  98     //      "Asia/Tokyo" : latitude=35.3916, longitude=13.9444
  99     //          (aliases not included)
 100     HashMap<String,LatitudeAndLongitude> mapList = null;
 101 
 102     // SortedMap of zone IDs sorted by their GMT offsets. If zone's GMT
 103     // offset will change in the future, its last known offset is
 104     // used.
 105     SortedMap<Integer, Set<String>> zonesByOffset = new TreeMap<Integer, Set<String>>();
 106 
 107     /**
 108      * Generates HTML document for each zone.
 109      * @param Timezone
 110      * @return 0 if no errors, or 1 if error occurred.
 111      */
 112     int processZoneinfo(Timezone tz) {
 113         try {
 114             int size;
 115             int index;
 116             String outputDir = Main.getOutputDir();
 117             String zonename = tz.getName();
 118             String zonefile = ZoneInfoFile.getFileName(zonename) + ".html";
 119             List<RuleRec> stz = tz.getLastRules();
 120             timezoneList.put(zonename, zonefile);
 121             displayNameList.put(transform(zonename), zonename);
 122 
 123             // Populate zonesByOffset. (Zones that will change their
 124             // GMT offsets are also added to zonesByOffset here.)
 125             int lastKnownOffset = tz.getRawOffset();
 126             Set<String> set = zonesByOffset.get(lastKnownOffset);
 127             if (set == null) {
 128                 set = new TreeSet<String>();
 129                 zonesByOffset.put(lastKnownOffset, set);
 130             }
 131             set.add(zonename);
 132 
 133             /* If outputDir doesn't end with file-separator, adds it. */
 134             if (!outputDir.endsWith(File.separator)) {
 135                 outputDir += File.separatorChar;
 136             }
 137             outputDir += docDir + File.separatorChar;
 138 
 139             index = zonename.indexOf('/');
 140             if (index != -1) {
 141                 regionList.put(zonename.substring(0, index),
 142                                zonename.substring(0, index) + ".html");
 143             }
 144 
 145             /* If zonefile includes file-separator, it's treated as part of
 146              * pathname. And make directory if necessary.
 147              */
 148             index = zonefile.lastIndexOf('/');
 149             if (index != -1) {
 150                 zonefile.replace('/', File.separatorChar);
 151                 outputDir += zonefile.substring(0, index+1);
 152             }
 153             File outD = new File(outputDir);
 154             outD.mkdirs();
 155 
 156             /* If mapfile is available, add a link to the appropriate map */
 157             if ((mapList == null) && (Main.getMapFile() != null)) {
 158                 FileReader fr = new FileReader(Main.getMapFile());
 159                 BufferedReader in = new BufferedReader(fr);
 160                 mapList = new HashMap<String,LatitudeAndLongitude>();
 161                 String line;
 162                 while ((line = in.readLine()) != null) {
 163                     // skip blank and comment lines
 164                     if (line.length() == 0 || line.charAt(0) == '#') {
 165                         continue;
 166                     }
 167                     StringTokenizer tokens = new StringTokenizer(line);
 168                     String token = tokens.nextToken();  /* We don't use the first token. */
 169                     token = tokens.nextToken();
 170                     LatitudeAndLongitude location = new LatitudeAndLongitude(token);
 171                     token = tokens.nextToken();
 172                     mapList.put(token, location);
 173                 }
 174                 in.close();
 175             }
 176 
 177             /* Open zoneinfo file to write. */
 178             FileWriter fw = new FileWriter(outputDir + zonefile.substring(index+1));
 179             BufferedWriter out = new BufferedWriter(fw);
 180 
 181             out.write(header1 + new Date() + header3 + zonename + header4);
 182             out.write(body1 + "<FONT size=\"+2\"><B>" + zonename + "</B></FONT>");
 183             LatitudeAndLongitude location = mapList.get(zonename);
 184             if (location != null) {
 185                 int deg, min, sec;
 186 
 187                 deg = location.getLatDeg();
 188                 min = location.getLatMin();
 189                 sec = location.getLatSec();
 190                 if (deg < 0) {
 191                     min = -min;
 192                     sec = -sec;
 193                 } else if (min < 0) {
 194                     sec = -sec;
 195                 }
 196                 out.write("&nbsp;&nbsp;&nbsp;" +
 197                           "<A HREF=\"http://www.mapquest.com/maps/map.adp?" +
 198                           "latlongtype=degrees" +
 199                           "&latdeg=" + deg +
 200                           "&latmin=" + min +
 201                           "&latsec=" + sec);
 202 
 203                 deg = location.getLongDeg();
 204                 min = location.getLongMin();
 205                 sec = location.getLongSec();
 206                 if (deg < 0) {
 207                     min = -min;
 208                     sec = -sec;
 209                 } else if (min < 0) {
 210                     sec = -sec;
 211                 }
 212                 out.write("&longdeg=" + deg +
 213                           "&longmin=" + min +
 214                           "&longsec=" + sec +
 215                           "\" target=\"_blank\">[map]</A>");
 216             }
 217             out.write("\n<P>\n");
 218 
 219             List<ZoneRec> zone = tz.getZones();
 220             List<RuleRec> rule = tz.getRules();
 221             if (rule != null && zone != null) {
 222                 out.write("<TABLE BORDER=\"0\" WIDTH=\"100%\" CELLPADDING=\"1\" CELLSPACING=\"0\">\n" +
 223                           "<TR>\n" +
 224                           "<TD BGCOLOR=\"#EEEEFF\" WIDTH=\"50%\" ALIGN=\"CENTER\"><BR>" +
 225                           "<A HREF=\"#Rules\">Rules</A><BR></TD>\n" +
 226                           "<TD BGCOLOR=\"#EEEEFF\" WIDTH=\"50%\" ALIGN=\"CENTER\">" +
 227                           "<A HREF=\"#Zone\"><BR>Zone<BR></A></TD>\n" +
 228                           "</TR>\n</TABLE>\n");
 229             }
 230 
 231             /* Output Rule records. */
 232             if (rule != null) {
 233                 size = rule.size();
 234                 out.write("<P>\n<A NAME=\"Rules\">" +
 235                           "<FONT SIZE=\"+1\"><B>Rules</B></FONT></A>\n" +
 236                           "<TABLE BORDER=\"1\" WIDTH=\"100%\" CELLPADDING=\"3\" CELLSPACING=\"0\">\n" +
 237                           "<TR BGCOLOR=\"#CCCCFF\">\n" +
 238                           "<TD>NAME</TD><TD>FROM</TD><TD>TO</TD><TD>TYPE</TD>" +
 239                           "<TD>IN</TD><TD>ON</TD><TD>AT</TD><TD>SAVE</TD>" +
 240                           "<TD>LETTER/S</TD><TD>NOTES</TD>\n</TR>\n");
 241                 for (int i = 0; i < size; i++) {
 242                     out.write("<TR BGCOLOR=\"#FFFFFF\">\n");
 243                     StringTokenizer st = new StringTokenizer(rule.get(i).getLine());
 244                     String s;
 245                     if (st.hasMoreTokens()) {   /* RULE - truncated */
 246                         st.nextToken();
 247                     }
 248                     if (st.hasMoreTokens()) {   /* NAME */
 249                         out.write("<TD>" + st.nextToken() + "</TD>");
 250                     }
 251                     if (st.hasMoreTokens()) {   /* FROM */
 252                         out.write("<TD>" + st.nextToken() + "</TD>");
 253                     }
 254                     if (st.hasMoreTokens()) {   /* TO */
 255                         s = st.nextToken();
 256                         if (s.equals("min") || s.equals("max")) {
 257                             out.write("<TD><FONT COLOR=\"red\">" + s + "</FONT></TD>");
 258                         } else {
 259                             out.write("<TD>" + s + "</TD>");
 260                         }
 261                     }
 262                     if (st.hasMoreTokens()) {   /* TYPE */
 263                         out.write("<TD>" + st.nextToken() + "</TD>");
 264                     }
 265                     if (st.hasMoreTokens()) {   /* IN */
 266                         out.write("<TD>" + st.nextToken() + "</TD>");
 267                     }
 268                     if (st.hasMoreTokens()) {   /* ON */
 269                         out.write("<TD>" + st.nextToken() + "</TD>");
 270                     }
 271                     if (st.hasMoreTokens()) {   /* AT */
 272                         out.write("<TD>" + st.nextToken() + "</TD>");
 273                     }
 274                     if (st.hasMoreTokens()) {   /* SAVE */
 275                         out.write("<TD>" + st.nextToken() + "</TD>");
 276                     }
 277                     if (st.hasMoreTokens()) {   /* LETTER/S */
 278                         out.write("<TD>" + st.nextToken() + "</TD>");
 279                     }
 280                     if (st.hasMoreTokens()) {   /* NOTES */
 281                         s = st.nextToken();
 282                         while (st.hasMoreTokens()) {
 283                             s += " " + st.nextToken();
 284                         }
 285                         index = s.indexOf('#');
 286                         out.write("<TD>" + s.substring(index+1) + "</TD>\n");
 287                     } else {
 288                         out.write("<TD>&nbsp;</TD>\n");
 289                     }
 290                     out.write("</TR>\n");
 291                 }
 292                 out.write("</TABLE>\n<P>&nbsp;<P>\n");
 293             }
 294 
 295             /* Output Zone records. */
 296             if (zone != null) {
 297                 size = zone.size();
 298                 out.write("<P>\n<A NAME=\"Zone\">" +
 299                           "<FONT SIZE=\"+1\"><B>Zone</B></FONT></A>\n" +
 300                           "<TABLE BORDER=\"1\" WIDTH=\"100%\" CELLPADDING=\"3\" CELLSPACING=\"0\">\n" +
 301                           "<TR BGCOLOR=\"#CCCCFF\">\n<TD>GMTOFF</TD>" +
 302                           "<TD>RULES</TD><TD>FORMAT</TD><TD>UNTIL</TD>" +
 303                           "<TD>NOTES</TD>\n</TR>\n");
 304                 for (int i = 0; i < size; i++) {
 305                     out.write("<TR>\n");
 306                     StringTokenizer st = new StringTokenizer(zone.get(i).getLine());
 307                     String s = st.nextToken();
 308                     if (s.equals("Zone")) {     /* NAME */
 309                         s = st.nextToken();
 310                         s = st.nextToken();
 311                     }
 312                     out.write("<TD>" + s + "</TD>");    /* GMTOFFSET */
 313                     if (st.hasMoreTokens()) {   /* RULES */
 314                         out.write("<TD>" + st.nextToken() + "</TD>");
 315                     }
 316                     if (st.hasMoreTokens()) {   /* FORMAT */
 317                         s = st.nextToken();
 318                         index = s.indexOf('#');
 319                         if (index != -1) {
 320                             if (index != 0) {
 321                                 out.write("<TD>" + s.substring(0, index-1) +
 322                                           "</TD>");     /* FORMAT */
 323                                 s = s.substring(index+1);
 324                             } else {
 325                                 out.write("<TD>&nbsp;</TD>");   /* FORMAT */
 326                             }
 327                             while (st.hasMoreTokens()) {
 328                                 s += " " + st.nextToken();
 329                             }
 330                             out.write("<TD>&nbsp;</TD>");       /* UNTIL */
 331                             out.write("<TD>" + s + "</TD>\n</TR>\n");   /* NOTES */
 332                             continue;
 333                         } else {
 334                             out.write("<TD>" + s + "</TD>");    /* FORMAT */
 335                         }
 336                     }
 337 
 338                     if (st.hasMoreTokens()) {   /* UNTIL */
 339                         s = st.nextToken();
 340                         while (st.hasMoreTokens()) {
 341                             s += " " + st.nextToken();
 342                         }
 343                         index = s.indexOf('#');
 344                         if (index != -1) {
 345                             if (index != 0) {
 346                                 out.write("<TD>" + s.substring(0, index-1) +
 347                                           "</TD>");     /* UNTIL */
 348                             } else {
 349                                 out.write("<TD>&nbsp;</TD>");   /* UNTIL */
 350                             }
 351                             out.write("<TD>" + s.substring(index+1) +
 352                                       "</TD>\n");       /* NOTES */
 353                         } else {
 354                             out.write("<TD>" + s + "</TD>");    /* UNTIL */
 355                             out.write("<TD>&nbsp;</TD>\n");     /* NOTES */
 356                         }
 357                     } else {
 358                         out.write("<TD>&nbsp;</TD>");           /* UNTIL */
 359                         out.write("<TD>&nbsp;</TD>\n");         /* NOTES */
 360                     }
 361                     out.write("</TR>\n");
 362                 }
 363                 out.write("</TABLE>\n");
 364             }
 365             out.write(body2 + footer);
 366 
 367             out.close();
 368             fw.close();
 369         } catch(IOException e) {
 370             Main.panic("IO error: "+e.getMessage());
 371             return 1;
 372         }
 373 
 374         return 0;
 375     }
 376 
 377     /**
 378      * Generates index.html and other top-level frame files.
 379      * @param Mappings
 380      * @return 0 if no errors, or 1 if error occurred.
 381      */
 382     int generateSrc(Mappings map) {
 383         try {
 384             int len;
 385             Object o[];
 386             String outputDir = Main.getOutputDir();
 387             FileWriter fw1, fw2;
 388             BufferedWriter out1, out2;
 389 
 390             /* Whether alias list exists or not. */
 391             Map<String,String> a = map.getAliases();
 392             if (a == null) {
 393                 Main.panic("Data not exist. (aliases)");
 394                 return 1;
 395             }
 396 
 397             timezoneList.putAll(a);
 398 
 399             /* If outputDir doesn't end with file-separator, adds it. */
 400             if (!outputDir.endsWith(File.separator)) {
 401                 outputDir += File.separatorChar;
 402             }
 403             outputDir += docDir + File.separatorChar;
 404 
 405             File outD = new File(outputDir);
 406             outD.mkdirs();
 407 
 408             /* Creates index.html */
 409             fw1 = new FileWriter(outputDir + "index.html", false);
 410             out1 = new BufferedWriter(fw1);
 411 
 412             out1.write(header1 + new Date() + header2 + Main.getVersionName() +
 413                        header4 +
 414                        "<FRAMESET cols=\"20%,80%\">\n" +
 415                        "<FRAMESET rows=\"30%,70%\">\n" +
 416                        "<FRAME src=\"overview-frame.html\" name=\"TimeZoneListFrame\">\n" +
 417                        "<FRAME src=\"allTimeZone-frame1.html\" name=\"allTimeZoneFrame\">\n" +
 418                        "</FRAMESET>" +
 419                        "<FRAME src=\"overview-summary.html\" name=\"rightFrame\">\n" +
 420                        "</FRAMESET>\n" +
 421                        "<NOFRAMES>\n" +
 422                        "<H2>\nFrame Alert\n</H2>\n\n" +
 423                        "<P>\n\n" +
 424                        "This document is designed to be viewed using the frames feature. If you see this\n" +
 425                        "message, you are using a non-frame-capable web client.\n" +
 426                        "<BR>\n" +
 427                        "Link to<A HREF=\"overview-summary.html\">Non-frame version.</A>\n" +
 428                        "</NOFRAMES>\n" + footer);
 429 
 430             out1.close();
 431             fw1.close();
 432 
 433 
 434             /* Creates overview-frame.html */
 435             fw1 = new FileWriter(outputDir + "overview-frame.html", false);
 436             out1 = new BufferedWriter(fw1);
 437 
 438             out1.write(header1 + new Date() + header2 + Main.getVersionName() +
 439                        header4 + body1 +
 440                        "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n<TR>\n" +
 441                        "<TD NOWRAP><FONT size=\"+1\">\n" +
 442                        "<B>Java<sup><font size=-2>TM</font></sup>&nbsp;Platform<br>Standard&nbsp;Ed.</B></FONT></TD>\n" +
 443                        "</TR>\n</TABLE>\n\n" +
 444                        "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n<TR>\n<TD NOWRAP>" +
 445                        "<P>\n<FONT size=\"+1\">\nAll Time Zones Sorted By:</FONT>\n<BR>\n" +
 446                        "&nbsp;&nbsp;<A HREF=\"allTimeZone-frame1.html\" TARGET=\"allTimeZoneFrame\">GMT offsets</A></FONT>\n<BR>\n" +
 447                        "&nbsp;&nbsp;<A HREF=\"allTimeZone-frame2.html\" TARGET=\"allTimeZoneFrame\">Zone names</A></FONT>\n<BR>" +
 448                        "&nbsp;&nbsp;<A HREF=\"allTimeZone-frame3.html\" TARGET=\"allTimeZoneFrame\">City names</A></FONT>\n" +
 449                        "<P>\n<FONT size=\"+1\">\nContinents and Oceans</FONT>\n<BR>\n");
 450 
 451             for (String regionKey : regionList.keySet()) {
 452                 out1.write("&nbsp;&nbsp;<A HREF=\"" + regionList.get(regionKey) +
 453                            "\" TARGET=\"allTimeZoneFrame\">" + regionKey +
 454                            "</A><BR>\n");
 455 
 456                 fw2 = new FileWriter(outputDir + regionList.get(regionKey),
 457                                      false);
 458                 out2 = new BufferedWriter(fw2);
 459 
 460                 out2.write(header1 + new Date() + header3 + regionKey +
 461                            header4 + body1 + "<FONT size=\"+1\"><B>" +
 462                            regionKey + "</B></FONT>\n<BR>\n<TABLE>\n<TR>\n<TD>");
 463 
 464                 boolean found = false;
 465                 for (String timezoneKey : timezoneList.keySet()) {
 466                     int regionIndex = timezoneKey.indexOf('/');
 467                     if (regionIndex == -1 ||
 468                         !regionKey.equals(timezoneKey.substring(0, regionIndex))) {
 469                         if (found) {
 470                             break;
 471                         } else {
 472                             continue;
 473                         }
 474                     }
 475 
 476                     found = true;
 477                     if (a.containsKey(timezoneKey)) {
 478                         Object realName = a.get(timezoneKey);
 479                         while (a.containsKey(realName)) {
 480                             realName = a.get(realName);
 481                         }
 482                         out2.write(timezoneKey +
 483                                    " (alias for " + "<A HREF=\"" +
 484                                    timezoneList.get(realName) +
 485                                    "\" TARGET=\"rightFrame\">" +
 486                                    realName + "</A>)");
 487                     } else {
 488                         out2.write("<A HREF=\"" + timezoneList.get(timezoneKey) +
 489                                    "\" TARGET=\"rightFrame\">" + timezoneKey +
 490                                    "</A>");
 491                     }
 492                     out2.write("<BR>\n");
 493                 }
 494                 out2.write("</TD>\n</TR>\n</TABLE>\n" + body2 + footer);
 495 
 496                 out2.close();
 497                 fw2.close();
 498             }
 499             out1.write("</FONT></TD>\n</TR></TABLE>\n" + body2 + footer);
 500 
 501             out1.close();
 502             fw1.close();
 503 
 504 
 505             /* Creates allTimeZone-frame1.html (Sorted by GMT offsets) */
 506             fw1 = new FileWriter(outputDir + "allTimeZone-frame1.html", false);
 507             out1 = new BufferedWriter(fw1);
 508 
 509             out1.write(header1 + new Date() + header2 + Main.getVersionName() +
 510                        header4 + body1 +
 511                        "<FONT size=\"+1\"><B>Sorted by GMT offsets</B></FONT>\n" +
 512                        "<BR>\n\n" + "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n" +
 513                        "<TR>\n<TD NOWRAP>\n");
 514 
 515             List<Integer> roi = map.getRawOffsetsIndex();
 516             List<Set<String>> roit = map.getRawOffsetsIndexTable();
 517 
 518             int index = 0;
 519             for (Integer offset : zonesByOffset.keySet()) {
 520                 int off = roi.get(index);
 521                 Set<String> perRO = zonesByOffset.get(offset);
 522                 if (offset == off) {
 523                     // Merge aliases into zonesByOffset
 524                     perRO.addAll(roit.get(index));
 525                 }
 526                 index++;
 527 
 528                 for (String timezoneKey : perRO) {
 529                     out1.write("<TR>\n<TD><FONT SIZE=\"-1\">(" +
 530                                Time.toGMTFormat(offset.toString()) +
 531                                ")</FONT></TD>\n<TD>");
 532 
 533                     if (a.containsKey(timezoneKey)) {
 534                         Object realName = a.get(timezoneKey);
 535                         while (a.containsKey(realName)) {
 536                             realName = a.get(realName);
 537                         }
 538                         out1.write(timezoneKey +
 539                                    " (alias for " + "<A HREF=\"" +
 540                                    timezoneList.get(realName) +
 541                                    "\" TARGET=\"rightFrame\">" + realName +
 542                                    "</A>)");
 543                     } else {
 544                         out1.write("<A HREF=\"" + timezoneList.get(timezoneKey) +
 545                                    "\" TARGET=\"rightFrame\">" + timezoneKey +
 546                                    "</A>");
 547                     }
 548                     out1.write("</TD>\n</TR>\n");
 549                 }
 550             }
 551             out1.write("</FONT></TD>\n</TR>\n</TABLE>\n" + body2 + footer);
 552 
 553             out1.close();
 554             fw1.close();
 555 
 556 
 557             /* Creates allTimeZone-frame2.html (Sorted by zone names) */
 558             fw1 = new FileWriter(outputDir + "allTimeZone-frame2.html", false);
 559             out1 = new BufferedWriter(fw1);
 560 
 561             out1.write(header1 + new Date() + header2 + Main.getVersionName() +
 562                        header4 + body1 +
 563                        "<FONT size=\"+1\"><B>Sorted by zone names</B></FONT>\n" +
 564                        "<BR>\n\n" + "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n" +
 565                        "<TR>\n<TD NOWRAP>\n");
 566             o = timezoneList.keySet().toArray();
 567             len = timezoneList.size();
 568             for (int i = 0; i < len; i++) {
 569                 Object timezoneKey = o[i];
 570                 if (a.containsKey(timezoneKey)) {
 571                     Object realName = a.get(timezoneKey);
 572                     while (a.containsKey(realName)) {
 573                         realName = a.get(realName);
 574                     }
 575                     out1.write(timezoneKey +
 576                                " (alias for " +
 577                                "<A HREF=\"" + timezoneList.get(realName) +
 578                                "\" TARGET=\"rightFrame\">" + realName +
 579                                "</A>)");
 580                 } else {
 581                     out1.write("<A HREF=\"" + timezoneList.get(timezoneKey) +
 582                                "\" TARGET=\"rightFrame\">" + timezoneKey +
 583                                "</A>");
 584                 }
 585                 out1.write("<BR> \n");
 586             }
 587             out1.write("</FONT></TD>\n</TR>\n</TABLE>\n" + body2 + footer);
 588 
 589             out1.close();
 590             fw1.close();
 591 
 592             /* Creates allTimeZone-frame3.html (Sorted by city names) */
 593             fw1 = new FileWriter(outputDir + "allTimeZone-frame3.html", false);
 594             out1 = new BufferedWriter(fw1);
 595 
 596             out1.write(header1 + new Date() + header2 + Main.getVersionName() +
 597                        header4 + body1 +
 598                        "<FONT size=\"+1\"><B>Sorted by city names</B></FONT>\n" +
 599                        "<BR>\n\n" + "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n" +
 600                        "<TR>\n<TD NOWRAP>\n");
 601 
 602             Set<String> aliasSet = a.keySet();
 603             len = aliasSet.size();
 604             String aliasNames[] = aliasSet.toArray(new String[0]);
 605             for (int i = 0; i < len; i++) {
 606                 displayNameList.put(transform(aliasNames[i]),
 607                                     aliasNames[i]);
 608             }
 609 
 610             o = displayNameList.keySet().toArray();
 611             len = displayNameList.size();
 612             for (int i = 0; i < len; i++) {
 613                 Object displayName = o[i];
 614                 Object timezoneKey = displayNameList.get(o[i]);
 615                 if (a.containsKey(timezoneKey)) {
 616                     Object realName = a.get(timezoneKey);
 617                     while (a.containsKey(realName)) {
 618                         realName = a.get(realName);
 619                     }
 620                     out1.write(displayName +
 621                                " (alias for " +
 622                                "<A HREF=\"" + timezoneList.get(realName) +
 623                                "\" TARGET=\"rightFrame\">" + realName +
 624                                "</A>)");
 625                 } else {
 626                     out1.write("<A HREF=\"" + timezoneList.get(timezoneKey) +
 627                                "\" TARGET=\"rightFrame\">" + displayName +
 628                                "</A>");
 629                 }
 630                 out1.write("<BR> \n");
 631             }
 632 
 633             out1.write("</FONT></TD>\n</TR>\n</TABLE>\n" + body2 + footer);
 634 
 635             out1.close();
 636             fw1.close();
 637 
 638             /* Creates overview-summary.html */
 639             fw1 = new FileWriter(outputDir + "overview-summary.html", false);
 640             out1 = new BufferedWriter(fw1);
 641 
 642             out1.write(header1 + new Date() + header2 + Main.getVersionName() +
 643                        header4 + body1 +
 644                        "<p>This is the list of time zones generated from <B>" +
 645                        Main.getVersionName() + "</B> for Java Platform, " +
 646                        "Standard Edition. The source code can be obtained " +
 647                        "from ftp site <a href=\"ftp://elsie.nci.nih.gov/pub/\">" +
 648                        "ftp://elsie.nci.nih.gov/pub/</a>. A total of <B>" +
 649                        len +
 650                        "</B> time zones and aliases are supported " +
 651                        "in this edition. For the " +
 652                        "format of rules and zones, refer to the zic " +
 653                        "(zoneinfo compiler) man page on " +
 654                        "Solaris or Linux.</p>\n" +
 655                        "<p>Note that the time zone data is not " +
 656                        "a public interface of the Java Platform. No " +
 657                        "applications should rely on the time zone data of " +
 658                        "this document. Time zone names and data " +
 659                        "may change without any prior notice.</p>\n" +
 660                        body2 + footer);
 661 
 662             out1.close();
 663             fw1.close();
 664         } catch(IOException e) {
 665             Main.panic("IO error: "+e.getMessage());
 666             return 1;
 667         }
 668 
 669         return 0;
 670     }
 671 
 672     String transform(String s) {
 673         int index = s.lastIndexOf("/");
 674 
 675         /* If the string doesn't include any delimiter, return */
 676         if (index == -1) {
 677             return s;
 678         }
 679 
 680         int lastIndex = index;
 681         String str = s.substring(index+1);
 682         do {
 683             index = s.substring(0, lastIndex).lastIndexOf('/');
 684             str += ", " + s.substring(index+1, lastIndex);
 685             lastIndex = index;
 686         } while (index > -1);
 687 
 688         return str;
 689     }
 690 
 691     static class LatitudeAndLongitude {
 692 
 693         private int latDeg, latMin, latSec, longDeg, longMin, longSec;
 694 
 695         LatitudeAndLongitude(String s) {
 696             try {
 697                 // First of all, check the string has the correct format:
 698                 //    either +-DDMM+-DDDMM or +-DDMMSS+-DDDMMSS
 699 
 700                 if (!s.startsWith("+") && !s.startsWith("-")) {
 701                     Main.warning("Wrong latitude&longitude data: " + s);
 702                     return;
 703                 }
 704                 int index;
 705                 if (((index = s.lastIndexOf("+")) <= 0) &&
 706                     ((index = s.lastIndexOf("-")) <= 0)) {
 707                     Main.warning("Wrong latitude&longitude data: " + s);
 708                     return;
 709                 }
 710 
 711                 if (index == 5) {
 712                     latDeg = Integer.parseInt(s.substring(1, 3));
 713                     latMin = Integer.parseInt(s.substring(3, 5));
 714                     latSec = 0;
 715                 } else if (index == 7) {
 716                     latDeg = Integer.parseInt(s.substring(1, 3));
 717                     latMin = Integer.parseInt(s.substring(3, 5));
 718                     latSec = Integer.parseInt(s.substring(5, 7));
 719                 } else {
 720                     Main.warning("Wrong latitude&longitude data: " + s);
 721                     return;
 722                 }
 723                 if (s.startsWith("-")){
 724                         latDeg = -latDeg;
 725                         latMin = -latMin;
 726                         latSec = -latSec;
 727                 }
 728 
 729                 int len = s.length();
 730                 if (index == 5 && len == 11) {
 731                     longDeg = Integer.parseInt(s.substring(index+1, index+4));
 732                     longMin = Integer.parseInt(s.substring(index+4, index+6));
 733                     longSec = 0;
 734                 } else if (index == 7 && len == 15) {
 735                     longDeg = Integer.parseInt(s.substring(index+1, index+4));
 736                     longMin = Integer.parseInt(s.substring(index+4, index+6));
 737                     longSec = Integer.parseInt(s.substring(index+6, index+8));
 738                 } else {
 739                     Main.warning("Wrong latitude&longitude data: " + s);
 740                     return;
 741                 }
 742                 if (s.charAt(index) == '-'){
 743                         longDeg = -longDeg;
 744                         longMin = -longMin;
 745                         longSec = -longSec;
 746                 }
 747             } catch(Exception e) {
 748                 Main.warning("LatitudeAndLongitude() Parse error: " + s);
 749             }
 750         }
 751 
 752         int getLatDeg() {
 753             return latDeg;
 754         }
 755 
 756         int getLatMin() {
 757             return latMin;
 758         }
 759 
 760         int getLatSec() {
 761             return latSec;
 762         }
 763 
 764         int getLongDeg() {
 765             return longDeg;
 766         }
 767 
 768         int getLongMin() {
 769             return longMin;
 770         }
 771 
 772         int getLongSec() {
 773             return longSec;
 774         }
 775     }
 776 }