--- /dev/null 2013-01-18 16:17:08.886776012 -0800 +++ new/test/sun/util/calendar/zi/Zoneinfo.java 2013-02-06 22:33:53.000000000 -0800 @@ -0,0 +1,567 @@ +/* + * Copyright (c) 2000, 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.FileReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * Zoneinfo provides javazic compiler front-end functionality. + * @since 1.4 + */ +class Zoneinfo { + + private static final int minYear = 1900; + private static final int maxYear = 2037; + private static final long minTime = Time.getLocalTime(minYear, Month.JANUARY, 1, 0); + private static int startYear = minYear; + private static int endYear = maxYear; + + /** + * True if javazic should generate a list of SimpleTimeZone + * instances for the SimpleTimeZone-based time zone support. + */ + static boolean isYearForTimeZoneDataSpecified = false; + + /** + * Zone name to Zone mappings + */ + private Map zones; + + /** + * Rule name to Rule mappings + */ + private Map rules; + + /** + * Alias name to real name mappings + */ + private Map aliases; + + /** + * Constracts a Zoneinfo. + */ + Zoneinfo() { + zones = new HashMap(); + rules = new HashMap(); + aliases = new HashMap(); + } + + /** + * Adds the given zone to the list of Zones. + * @param zone Zone to be added to the list. + */ + void add(Zone zone) { + String name = zone.getName(); + zones.put(name, zone); + } + + /** + * Adds the given rule to the list of Rules. + * @param rule Rule to be added to the list. + */ + void add(Rule rule) { + String name = rule.getName(); + rules.put(name, rule); + } + + /** + * Puts the specifid name pair to the alias table. + * @param name1 an alias time zone name + * @param name2 the real time zone of the alias name + */ + void putAlias(String name1, String name2) { + aliases.put(name1, name2); + } + + /** + * Sets the given year for SimpleTimeZone list output. + * This method is called when the -S option is specified. + * @param year the year for which SimpleTimeZone list should be generated + */ + static void setYear(int year) { + setStartYear(year); + setEndYear(year); + isYearForTimeZoneDataSpecified = true; + } + + /** + * Sets the start year. + * @param year the start year value + * @throws IllegalArgumentException if the specified year value is + * smaller than the minimum year or greater than the end year. + */ + static void setStartYear(int year) { + if (year < minYear || year > endYear) { + throw new IllegalArgumentException("invalid start year specified: " + year); + } + startYear = year; + } + + /** + * @return the start year value + */ + static int getStartYear() { + return startYear; + } + + /** + * Sets the end year. + * @param year the end year value + * @throws IllegalArgumentException if the specified year value is + * smaller than the start year or greater than the maximum year. + */ + static void setEndYear(int year) { + if (year < startYear || year > maxYear) { + throw new IllegalArgumentException(); + } + endYear = year; + } + + /** + * @return the end year value + */ + static int getEndYear() { + return endYear; + } + + /** + * @return the minimum year value + */ + static int getMinYear() { + return minYear; + } + + /** + * @return the maximum year value + */ + static int getMaxYear() { + return maxYear; + } + + /** + * @return the alias table + */ + Map getAliases() { + return aliases; + } + + /** + * @return the Zone list + */ + Map getZones() { + return zones; + } + + /** + * @return a Zone specified by name. + * @param name a zone name + */ + Zone getZone(String name) { + return zones.get(name); + } + + /** + * @return a Rule specified by name. + * @param name a rule name + */ + Rule getRule(String name) { + return rules.get(name); + } + + private static String line; + + private static int lineNum; + + /** + * Parses the specified time zone data file and creates a Zoneinfo + * that has all Rules, Zones and Links (aliases) information. + * @param fname the time zone data file name + * @return a Zoneinfo object + */ + static Zoneinfo parse(String fname) { + BufferedReader in = null; + try { + FileReader fr = new FileReader(fname); + in = new BufferedReader(fr); + } catch (FileNotFoundException e) { + panic("can't open file: "+fname); + } + Zoneinfo zi = new Zoneinfo(); + boolean continued = false; + Zone zone = null; + String l; + lineNum = 0; + + try { + while ((line = in.readLine()) != null) { + lineNum++; + // skip blank and comment lines + if (line.length() == 0 || line.charAt(0) == '#') { + continue; + } + + // trim trailing comments + int rindex = line.lastIndexOf('#'); + if (rindex != -1) { + // take the data part of the line + l = line.substring(0, rindex); + } else { + l = line; + } + + StringTokenizer tokens = new StringTokenizer(l); + if (!tokens.hasMoreTokens()) { + continue; + } + String token = tokens.nextToken(); + + if (continued || "Zone".equals(token)) { + if (zone == null) { + if (!tokens.hasMoreTokens()) { + panic("syntax error: zone no more token"); + } + token = tokens.nextToken(); + // if the zone name is in "GMT+hh" or "GMT-hh" + // format, ignore it due to spec conflict. + if (token.startsWith("GMT+") || token.startsWith("GMT-")) { + continue; + } + zone = new Zone(token); + } else { + // no way to push the current token back... + tokens = new StringTokenizer(l); + } + + ZoneRec zrec = ZoneRec.parse(tokens); + zrec.setLine(line); + zone.add(zrec); + if ((continued = zrec.hasUntil()) == false) { + if (Zone.isTargetZone(zone.getName())) { + // zone.resolve(zi); + zi.add(zone); + } + zone = null; + } + } else if ("Rule".equals(token)) { + if (!tokens.hasMoreTokens()) { + panic("syntax error: rule no more token"); + } + token = tokens.nextToken(); + Rule rule = zi.getRule(token); + if (rule == null) { + rule = new Rule(token); + zi.add(rule); + } + RuleRec rrec = RuleRec.parse(tokens); + rrec.setLine(line); + rule.add(rrec); + } else if ("Link".equals(token)) { + // Link + try { + String name1 = tokens.nextToken(); + String name2 = tokens.nextToken(); + + // if the zone name is in "GMT+hh" or "GMT-hh" + // format, ignore it due to spec conflict with + // custom time zones. Also, ignore "ROC" for + // PC-ness. + if (name2.startsWith("GMT+") || name2.startsWith("GMT-") + || "ROC".equals(name2)) { + continue; + } + zi.putAlias(name2, name1); + } catch (Exception e) { + panic("syntax error: no more token for Link"); + } + } + } + in.close(); + } catch (IOException ex) { + panic("IO error: " + ex.getMessage()); + } + + return zi; + } + + /** + * Interprets a zone and constructs a Timezone object that + * contains enough information on GMT offsets and DST schedules to + * generate a zone info database. + * + * @param zoneName the zone name for which a Timezone object is + * constructed. + * + * @return a Timezone object that contains all GMT offsets and DST + * rules information. + */ + Timezone phase2(String zoneName) { + Timezone tz = new Timezone(zoneName); + Zone zone = getZone(zoneName); + zone.resolve(this); + + // TODO: merge phase2's for the regular and SimpleTimeZone ones. + if (isYearForTimeZoneDataSpecified) { + ZoneRec zrec = zone.get(zone.size()-1); + tz.setLastZoneRec(zrec); + tz.setRawOffset(zrec.getGmtOffset()); + if (zrec.hasRuleReference()) { + /* + * This part assumes that the specified year is covered by + * the rules referred to by the last zone record. + */ + List rrecs = zrec.getRuleRef().getRules(startYear); + + if (rrecs.size() == 2) { + // make sure that one is a start rule and the other is + // an end rule. + RuleRec r0 = rrecs.get(0); + RuleRec r1 = rrecs.get(1); + if (r0.getSave() == 0 && r1.getSave() > 0) { + rrecs.set(0, r1); + rrecs.set(1, r0); + } else if (!(r0.getSave() > 0 && r1.getSave() == 0)) { + rrecs = null; + Main.error(zoneName + ": rules for " + startYear + " not found."); + } + } else { + rrecs = null; + } + if (rrecs != null) { + tz.setLastRules(rrecs); + } + } + return tz; + } + + int gmtOffset; + int year = minYear; + int fromYear = year; + long fromTime = Time.getLocalTime(startYear, + Month.JANUARY, + 1, 0); + + // take the index 0 for the GMT offset of the last zone record + ZoneRec zrec = zone.get(zone.size()-1); + tz.getOffsetIndex(zrec.getGmtOffset()); + + int currentSave = 0; + boolean usedZone; + for (int zindex = 0; zindex < zone.size(); zindex++) { + zrec = zone.get(zindex); + usedZone = false; + gmtOffset = zrec.getGmtOffset(); + int stdOffset = zrec.getDirectSave(); + + // If this is the last zone record, take the last rule info. + if (!zrec.hasUntil()) { + tz.setRawOffset(gmtOffset, fromTime); + if (zrec.hasRuleReference()) { + tz.setLastRules(zrec.getRuleRef().getLastRules()); + } else if (stdOffset != 0) { + // in case the last rule is all year round DST-only + // (Asia/Amman once announced this rule.) + tz.setLastDSTSaving(stdOffset); + } + } + if (!zrec.hasRuleReference()) { + if (!zrec.hasUntil() || zrec.getUntilTime(stdOffset) >= fromTime) { + tz.addTransition(fromTime, + tz.getOffsetIndex(gmtOffset+stdOffset), + tz.getDstOffsetIndex(stdOffset)); + usedZone = true; + } + currentSave = stdOffset; + // optimization in case the last rule is fixed. + if (!zrec.hasUntil()) { + if (tz.getNTransitions() > 0) { + if (stdOffset == 0) { + tz.setDSTType(Timezone.X_DST); + } else { + tz.setDSTType(Timezone.LAST_DST); + } + long time = Time.getLocalTime(maxYear, + Month.JANUARY, 1, 0); + time -= zrec.getGmtOffset(); + tz.addTransition(time, + tz.getOffsetIndex(gmtOffset+stdOffset), + tz.getDstOffsetIndex(stdOffset)); + tz.addUsedRec(zrec); + } else { + tz.setDSTType(Timezone.NO_DST); + } + break; + } + } else { + Rule rule = zrec.getRuleRef(); + boolean fromTimeUsed = false; + currentSave = 0; + year_loop: + for (year = getMinYear(); year <= endYear; year++) { + if (zrec.hasUntil() && year > zrec.getUntilYear()) { + break; + } + List rules = rule.getRules(year); + if (rules.size() > 0) { + for (int i = 0; i < rules.size(); i++) { + RuleRec rrec = rules.get(i); + long transition = rrec.getTransitionTime(year, + gmtOffset, + currentSave); + if (zrec.hasUntil()) { + if (transition >= zrec.getUntilTime(currentSave)) { + break year_loop; + } + } + + if (fromTimeUsed == false) { + if (fromTime <= transition) { + fromTimeUsed = true; + + if (fromTime != minTime) { + int prevsave; + + ZoneRec prevzrec = zone.get(zindex - 1); + + // See if until time in the previous + // ZoneRec is the same thing as the + // local time in the next rule. + // (examples are Asia/Ashkhabad in 1991, + // Europe/Riga in 1989) + + if (i > 0) { + prevsave = rules.get(i-1).getSave(); + } else { + List prevrules = rule.getRules(year-1); + + if (prevrules.size() > 0) { + prevsave = prevrules.get(prevrules.size()-1).getSave(); + } else { + prevsave = 0; + } + } + + if (rrec.isSameTransition(prevzrec, prevsave, gmtOffset)) { + currentSave = rrec.getSave(); + tz.addTransition(fromTime, + tz.getOffsetIndex(gmtOffset+currentSave), + tz.getDstOffsetIndex(currentSave)); + tz.addUsedRec(rrec); + usedZone = true; + continue; + } + if (!prevzrec.hasRuleReference() + || rule != prevzrec.getRuleRef() + || (rule == prevzrec.getRuleRef() + && gmtOffset != prevzrec.getGmtOffset())) { + int save = (fromTime == transition) ? rrec.getSave() : currentSave; + tz.addTransition(fromTime, + tz.getOffsetIndex(gmtOffset+save), + tz.getDstOffsetIndex(save)); + tz.addUsedRec(rrec); + usedZone = true; + } + } else { // fromTime == minTime + int save = rrec.getSave(); + tz.addTransition(minTime, + tz.getOffsetIndex(gmtOffset), + tz.getDstOffsetIndex(0)); + + tz.addTransition(transition, + tz.getOffsetIndex(gmtOffset+save), + tz.getDstOffsetIndex(save)); + + tz.addUsedRec(rrec); + usedZone = true; + } + } else if (year == fromYear && i == rules.size()-1) { + int save = rrec.getSave(); + tz.addTransition(fromTime, + tz.getOffsetIndex(gmtOffset+save), + tz.getDstOffsetIndex(save)); + } + } + + currentSave = rrec.getSave(); + if (fromTime < transition) { + tz.addTransition(transition, + tz.getOffsetIndex(gmtOffset+currentSave), + tz.getDstOffsetIndex(currentSave)); + tz.addUsedRec(rrec); + usedZone = true; + } + } + } else { + if (year == fromYear) { + tz.addTransition(fromTime, + tz.getOffsetIndex(gmtOffset+currentSave), + tz.getDstOffsetIndex(currentSave)); + fromTimeUsed = true; + } + if (year == endYear && !zrec.hasUntil()) { + if (tz.getNTransitions() > 0) { + // Assume that this Zone stopped DST + tz.setDSTType(Timezone.X_DST); + long time = Time.getLocalTime(maxYear, Month.JANUARY, + 1, 0); + time -= zrec.getGmtOffset(); + tz.addTransition(time, + tz.getOffsetIndex(gmtOffset), + tz.getDstOffsetIndex(0)); + usedZone = true; + } else { + tz.setDSTType(Timezone.NO_DST); + } + } + } + } + } + if (usedZone) { + tz.addUsedRec(zrec); + } + if (zrec.hasUntil() && zrec.getUntilTime(currentSave) > fromTime) { + fromTime = zrec.getUntilTime(currentSave); + fromYear = zrec.getUntilYear(); + year = zrec.getUntilYear(); + } + } + + if (tz.getDSTType() == Timezone.UNDEF_DST) { + tz.setDSTType(Timezone.DST); + } + tz.optimize(); + tz.checksum(); + return tz; + } + + private static void panic(String msg) { + Main.panic(msg); + } +}