0N/A/*
3909N/A * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage build.tools.javazic;
0N/A
0N/Aimport java.io.BufferedReader;
0N/Aimport java.io.FileReader;
0N/Aimport java.io.FileNotFoundException;
0N/Aimport java.io.IOException;
0N/Aimport java.util.ArrayList;
0N/Aimport java.util.HashMap;
0N/Aimport java.util.Iterator;
0N/Aimport java.util.List;
0N/Aimport java.util.Map;
0N/Aimport java.util.StringTokenizer;
0N/A
0N/A/**
0N/A * Zoneinfo provides javazic compiler front-end functionality.
0N/A * @since 1.4
0N/A */
0N/Aclass Zoneinfo {
0N/A
0N/A private static final int minYear = 1900;
0N/A private static final int maxYear = 2037;
0N/A private static final long minTime = Time.getLocalTime(minYear, Month.JANUARY, 1, 0);
0N/A private static int startYear = minYear;
0N/A private static int endYear = maxYear;
0N/A
0N/A /**
0N/A * True if javazic should generate a list of SimpleTimeZone
0N/A * instances for the SimpleTimeZone-based time zone support.
0N/A */
0N/A static boolean isYearForTimeZoneDataSpecified = false;
0N/A
0N/A /**
0N/A * Zone name to Zone mappings
0N/A */
0N/A private Map<String,Zone> zones;
0N/A
0N/A /**
0N/A * Rule name to Rule mappings
0N/A */
0N/A private Map<String,Rule> rules;
0N/A
0N/A /**
0N/A * Alias name to real name mappings
0N/A */
0N/A private Map<String,String> aliases;
0N/A
0N/A /**
0N/A * Constracts a Zoneinfo.
0N/A */
0N/A Zoneinfo() {
0N/A zones = new HashMap<String,Zone>();
0N/A rules = new HashMap<String,Rule>();
0N/A aliases = new HashMap<String,String>();
0N/A }
0N/A
0N/A /**
0N/A * Adds the given zone to the list of Zones.
0N/A * @param zone Zone to be added to the list.
0N/A */
0N/A void add(Zone zone) {
0N/A String name = zone.getName();
0N/A zones.put(name, zone);
0N/A }
0N/A
0N/A /**
0N/A * Adds the given rule to the list of Rules.
0N/A * @param rule Rule to be added to the list.
0N/A */
0N/A void add(Rule rule) {
0N/A String name = rule.getName();
0N/A rules.put(name, rule);
0N/A }
0N/A
0N/A /**
0N/A * Puts the specifid name pair to the alias table.
0N/A * @param name1 an alias time zone name
0N/A * @param name2 the real time zone of the alias name
0N/A */
0N/A void putAlias(String name1, String name2) {
0N/A aliases.put(name1, name2);
0N/A }
0N/A
0N/A /**
0N/A * Sets the given year for SimpleTimeZone list output.
0N/A * This method is called when the -S option is specified.
0N/A * @param year the year for which SimpleTimeZone list should be generated
0N/A */
0N/A static void setYear(int year) {
0N/A setStartYear(year);
0N/A setEndYear(year);
0N/A isYearForTimeZoneDataSpecified = true;
0N/A }
0N/A
0N/A /**
0N/A * Sets the start year.
0N/A * @param year the start year value
0N/A * @throws IllegalArgumentException if the specified year value is
0N/A * smaller than the minimum year or greater than the end year.
0N/A */
0N/A static void setStartYear(int year) {
0N/A if (year < minYear || year > endYear) {
0N/A throw new IllegalArgumentException("invalid start year specified: " + year);
0N/A }
0N/A startYear = year;
0N/A }
0N/A
0N/A /**
0N/A * @return the start year value
0N/A */
0N/A static int getStartYear() {
0N/A return startYear;
0N/A }
0N/A
0N/A /**
0N/A * Sets the end year.
0N/A * @param year the end year value
0N/A * @throws IllegalArgumentException if the specified year value is
0N/A * smaller than the start year or greater than the maximum year.
0N/A */
0N/A static void setEndYear(int year) {
0N/A if (year < startYear || year > maxYear) {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A endYear = year;
0N/A }
0N/A
0N/A /**
0N/A * @return the end year value
0N/A */
0N/A static int getEndYear() {
0N/A return endYear;
0N/A }
0N/A
0N/A /**
0N/A * @return the minimum year value
0N/A */
0N/A static int getMinYear() {
0N/A return minYear;
0N/A }
0N/A
0N/A /**
0N/A * @return the maximum year value
0N/A */
0N/A static int getMaxYear() {
0N/A return maxYear;
0N/A }
0N/A
0N/A /**
0N/A * @return the alias table
0N/A */
0N/A Map<String,String> getAliases() {
0N/A return aliases;
0N/A }
0N/A
0N/A /**
0N/A * @return the Zone list
0N/A */
0N/A Map<String,Zone> getZones() {
0N/A return zones;
0N/A }
0N/A
0N/A /**
0N/A * @return a Zone specified by name.
0N/A * @param name a zone name
0N/A */
0N/A Zone getZone(String name) {
0N/A return zones.get(name);
0N/A }
0N/A
0N/A /**
0N/A * @return a Rule specified by name.
0N/A * @param name a rule name
0N/A */
0N/A Rule getRule(String name) {
0N/A return rules.get(name);
0N/A }
0N/A
0N/A private static String line;
0N/A
0N/A private static int lineNum;
0N/A
0N/A /**
0N/A * Parses the specified time zone data file and creates a Zoneinfo
0N/A * that has all Rules, Zones and Links (aliases) information.
0N/A * @param fname the time zone data file name
0N/A * @return a Zoneinfo object
0N/A */
0N/A static Zoneinfo parse(String fname) {
0N/A BufferedReader in = null;
0N/A try {
0N/A FileReader fr = new FileReader(fname);
0N/A in = new BufferedReader(fr);
0N/A } catch (FileNotFoundException e) {
0N/A panic("can't open file: "+fname);
0N/A }
0N/A Zoneinfo zi = new Zoneinfo();
0N/A boolean continued = false;
0N/A Zone zone = null;
0N/A String l;
3302N/A lineNum = 0;
0N/A
0N/A try {
0N/A while ((line = in.readLine()) != null) {
0N/A lineNum++;
0N/A // skip blank and comment lines
0N/A if (line.length() == 0 || line.charAt(0) == '#') {
0N/A continue;
0N/A }
0N/A
0N/A // trim trailing comments
0N/A int rindex = line.lastIndexOf('#');
0N/A if (rindex != -1) {
0N/A // take the data part of the line
0N/A l = line.substring(0, rindex);
0N/A } else {
0N/A l = line;
0N/A }
0N/A
0N/A StringTokenizer tokens = new StringTokenizer(l);
0N/A if (!tokens.hasMoreTokens()) {
0N/A continue;
0N/A }
0N/A String token = tokens.nextToken();
0N/A
0N/A if (continued || "Zone".equals(token)) {
0N/A if (zone == null) {
0N/A if (!tokens.hasMoreTokens()) {
0N/A panic("syntax error: zone no more token");
0N/A }
0N/A token = tokens.nextToken();
0N/A // if the zone name is in "GMT+hh" or "GMT-hh"
0N/A // format, ignore it due to spec conflict.
0N/A if (token.startsWith("GMT+") || token.startsWith("GMT-")) {
0N/A continue;
0N/A }
0N/A zone = new Zone(token);
0N/A } else {
0N/A // no way to push the current token back...
0N/A tokens = new StringTokenizer(l);
0N/A }
0N/A
0N/A ZoneRec zrec = ZoneRec.parse(tokens);
0N/A zrec.setLine(line);
0N/A zone.add(zrec);
0N/A if ((continued = zrec.hasUntil()) == false) {
0N/A if (Zone.isTargetZone(zone.getName())) {
0N/A // zone.resolve(zi);
0N/A zi.add(zone);
0N/A }
0N/A zone = null;
0N/A }
0N/A } else if ("Rule".equals(token)) {
0N/A if (!tokens.hasMoreTokens()) {
0N/A panic("syntax error: rule no more token");
0N/A }
0N/A token = tokens.nextToken();
0N/A Rule rule = zi.getRule(token);
0N/A if (rule == null) {
0N/A rule = new Rule(token);
0N/A zi.add(rule);
0N/A }
0N/A RuleRec rrec = RuleRec.parse(tokens);
0N/A rrec.setLine(line);
0N/A rule.add(rrec);
0N/A } else if ("Link".equals(token)) {
0N/A // Link <newname> <oldname>
0N/A try {
0N/A String name1 = tokens.nextToken();
0N/A String name2 = tokens.nextToken();
0N/A
0N/A // if the zone name is in "GMT+hh" or "GMT-hh"
0N/A // format, ignore it due to spec conflict with
0N/A // custom time zones. Also, ignore "ROC" for
0N/A // PC-ness.
0N/A if (name2.startsWith("GMT+") || name2.startsWith("GMT-")
0N/A || "ROC".equals(name2)) {
0N/A continue;
0N/A }
0N/A zi.putAlias(name2, name1);
0N/A } catch (Exception e) {
0N/A panic("syntax error: no more token for Link");
0N/A }
0N/A }
0N/A }
0N/A in.close();
0N/A } catch (IOException ex) {
0N/A panic("IO error: " + ex.getMessage());
0N/A }
0N/A
0N/A return zi;
0N/A }
0N/A
0N/A /**
0N/A * Interprets a zone and constructs a Timezone object that
0N/A * contains enough information on GMT offsets and DST schedules to
0N/A * generate a zone info database.
0N/A *
0N/A * @param zoneName the zone name for which a Timezone object is
0N/A * constructed.
0N/A *
0N/A * @return a Timezone object that contains all GMT offsets and DST
0N/A * rules information.
0N/A */
0N/A Timezone phase2(String zoneName) {
0N/A Timezone tz = new Timezone(zoneName);
0N/A Zone zone = getZone(zoneName);
0N/A zone.resolve(this);
0N/A
0N/A // TODO: merge phase2's for the regular and SimpleTimeZone ones.
0N/A if (isYearForTimeZoneDataSpecified) {
0N/A ZoneRec zrec = zone.get(zone.size()-1);
0N/A tz.setLastZoneRec(zrec);
0N/A tz.setRawOffset(zrec.getGmtOffset());
0N/A if (zrec.hasRuleReference()) {
0N/A /*
0N/A * This part assumes that the specified year is covered by
0N/A * the rules referred to by the last zone record.
0N/A */
0N/A List<RuleRec> rrecs = zrec.getRuleRef().getRules(startYear);
0N/A
0N/A if (rrecs.size() == 2) {
0N/A // make sure that one is a start rule and the other is
0N/A // an end rule.
0N/A RuleRec r0 = rrecs.get(0);
0N/A RuleRec r1 = rrecs.get(1);
0N/A if (r0.getSave() == 0 && r1.getSave() > 0) {
0N/A rrecs.set(0, r1);
0N/A rrecs.set(1, r0);
0N/A } else if (!(r0.getSave() > 0 && r1.getSave() == 0)) {
0N/A rrecs = null;
0N/A Main.error(zoneName + ": rules for " + startYear + " not found.");
0N/A }
0N/A } else {
0N/A rrecs = null;
0N/A }
0N/A if (rrecs != null) {
0N/A tz.setLastRules(rrecs);
0N/A }
0N/A }
0N/A return tz;
0N/A }
0N/A
0N/A int gmtOffset;
0N/A int year = minYear;
0N/A int fromYear = year;
0N/A long fromTime = Time.getLocalTime(startYear,
0N/A Month.JANUARY,
0N/A 1, 0);
0N/A
0N/A // take the index 0 for the GMT offset of the last zone record
0N/A ZoneRec zrec = zone.get(zone.size()-1);
0N/A tz.getOffsetIndex(zrec.getGmtOffset());
0N/A
0N/A int currentSave = 0;
0N/A boolean usedZone;
0N/A for (int zindex = 0; zindex < zone.size(); zindex++) {
0N/A zrec = zone.get(zindex);
0N/A usedZone = false;
0N/A gmtOffset = zrec.getGmtOffset();
0N/A int stdOffset = zrec.getDirectSave();
0N/A
0N/A // If this is the last zone record, take the last rule info.
0N/A if (!zrec.hasUntil()) {
0N/A tz.setRawOffset(gmtOffset, fromTime);
0N/A if (zrec.hasRuleReference()) {
0N/A tz.setLastRules(zrec.getRuleRef().getLastRules());
0N/A } else if (stdOffset != 0) {
0N/A // in case the last rule is all year round DST-only
0N/A // (Asia/Amman once announced this rule.)
0N/A tz.setLastDSTSaving(stdOffset);
0N/A }
0N/A }
0N/A if (!zrec.hasRuleReference()) {
0N/A if (!zrec.hasUntil() || zrec.getUntilTime(stdOffset) >= fromTime) {
0N/A tz.addTransition(fromTime,
0N/A tz.getOffsetIndex(gmtOffset+stdOffset),
0N/A tz.getDstOffsetIndex(stdOffset));
0N/A usedZone = true;
0N/A }
0N/A currentSave = stdOffset;
0N/A // optimization in case the last rule is fixed.
0N/A if (!zrec.hasUntil()) {
0N/A if (tz.getNTransitions() > 0) {
0N/A if (stdOffset == 0) {
0N/A tz.setDSTType(tz.X_DST);
0N/A } else {
0N/A tz.setDSTType(tz.LAST_DST);
0N/A }
0N/A long time = Time.getLocalTime(maxYear,
0N/A Month.JANUARY, 1, 0);
0N/A time -= zrec.getGmtOffset();
0N/A tz.addTransition(time,
0N/A tz.getOffsetIndex(gmtOffset+stdOffset),
0N/A tz.getDstOffsetIndex(stdOffset));
0N/A tz.addUsedRec(zrec);
0N/A } else {
0N/A tz.setDSTType(tz.NO_DST);
0N/A }
0N/A break;
0N/A }
0N/A } else {
0N/A Rule rule = zrec.getRuleRef();
0N/A boolean fromTimeUsed = false;
0N/A currentSave = 0;
0N/A year_loop:
0N/A for (year = getMinYear(); year <= endYear; year++) {
0N/A if (zrec.hasUntil() && year > zrec.getUntilYear()) {
0N/A break;
0N/A }
0N/A List<RuleRec> rules = rule.getRules(year);
0N/A if (rules.size() > 0) {
0N/A for (int i = 0; i < rules.size(); i++) {
0N/A RuleRec rrec = rules.get(i);
0N/A long transition = rrec.getTransitionTime(year,
0N/A gmtOffset,
0N/A currentSave);
0N/A if (zrec.hasUntil()) {
0N/A if (transition >= zrec.getUntilTime(currentSave)) {
0N/A break year_loop;
0N/A }
0N/A }
0N/A
0N/A if (fromTimeUsed == false) {
0N/A if (fromTime <= transition) {
0N/A fromTimeUsed = true;
0N/A
0N/A if (fromTime != minTime) {
0N/A int prevsave;
0N/A
0N/A ZoneRec prevzrec = zone.get(zindex - 1);
0N/A
0N/A // See if until time in the previous
0N/A // ZoneRec is the same thing as the
0N/A // local time in the next rule.
0N/A // (examples are Asia/Ashkhabad in 1991,
0N/A // Europe/Riga in 1989)
0N/A
0N/A if (i > 0) {
0N/A prevsave = rules.get(i-1).getSave();
0N/A } else {
0N/A List<RuleRec> prevrules = rule.getRules(year-1);
0N/A
0N/A if (prevrules.size() > 0) {
0N/A prevsave = prevrules.get(prevrules.size()-1).getSave();
0N/A } else {
0N/A prevsave = 0;
0N/A }
0N/A }
0N/A
0N/A if (rrec.isSameTransition(prevzrec, prevsave, gmtOffset)) {
0N/A currentSave = rrec.getSave();
0N/A tz.addTransition(fromTime,
0N/A tz.getOffsetIndex(gmtOffset+currentSave),
0N/A tz.getDstOffsetIndex(currentSave));
0N/A tz.addUsedRec(rrec);
0N/A usedZone = true;
0N/A continue;
0N/A }
0N/A if (!prevzrec.hasRuleReference()
0N/A || rule != prevzrec.getRuleRef()
0N/A || (rule == prevzrec.getRuleRef()
0N/A && gmtOffset != prevzrec.getGmtOffset())) {
0N/A int save = (fromTime == transition) ? rrec.getSave() : currentSave;
0N/A tz.addTransition(fromTime,
0N/A tz.getOffsetIndex(gmtOffset+save),
0N/A tz.getDstOffsetIndex(save));
0N/A tz.addUsedRec(rrec);
0N/A usedZone = true;
0N/A }
0N/A } else {
0N/A int save = rrec.getSave();
0N/A tz.addTransition(fromTime,
0N/A tz.getOffsetIndex(gmtOffset+save),
0N/A tz.getDstOffsetIndex(save));
0N/A tz.addUsedRec(rrec);
0N/A usedZone = true;
0N/A }
0N/A } else if (year == fromYear && i == rules.size()-1) {
0N/A int save = rrec.getSave();
0N/A tz.addTransition(fromTime,
0N/A tz.getOffsetIndex(gmtOffset+save),
0N/A tz.getDstOffsetIndex(save));
0N/A }
0N/A }
0N/A
0N/A currentSave = rrec.getSave();
0N/A if (fromTime < transition) {
0N/A tz.addTransition(transition,
0N/A tz.getOffsetIndex(gmtOffset+currentSave),
0N/A tz.getDstOffsetIndex(currentSave));
0N/A tz.addUsedRec(rrec);
0N/A usedZone = true;
0N/A }
0N/A }
0N/A } else {
0N/A if (year == fromYear) {
0N/A tz.addTransition(fromTime,
0N/A tz.getOffsetIndex(gmtOffset+currentSave),
0N/A tz.getDstOffsetIndex(currentSave));
0N/A fromTimeUsed = true;
0N/A }
0N/A if (year == endYear && !zrec.hasUntil()) {
0N/A if (tz.getNTransitions() > 0) {
0N/A // Assume that this Zone stopped DST
0N/A tz.setDSTType(tz.X_DST);
0N/A long time = Time.getLocalTime(maxYear, Month.JANUARY,
0N/A 1, 0);
0N/A time -= zrec.getGmtOffset();
0N/A tz.addTransition(time,
0N/A tz.getOffsetIndex(gmtOffset),
0N/A tz.getDstOffsetIndex(0));
0N/A usedZone = true;
0N/A } else {
0N/A tz.setDSTType(tz.NO_DST);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A if (usedZone) {
0N/A tz.addUsedRec(zrec);
0N/A }
0N/A if (zrec.hasUntil() && zrec.getUntilTime(currentSave) > fromTime) {
0N/A fromTime = zrec.getUntilTime(currentSave);
0N/A fromYear = zrec.getUntilYear();
0N/A year = zrec.getUntilYear();
0N/A }
0N/A }
0N/A
0N/A if (tz.getDSTType() == tz.UNDEF_DST) {
0N/A tz.setDSTType(tz.DST);
0N/A }
0N/A tz.optimize();
0N/A tz.checksum();
0N/A return tz;
0N/A }
0N/A
0N/A private static void panic(String msg) {
0N/A Main.panic(msg);
0N/A }
0N/A}