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