Skip to content
This repository has been archived by the owner on Mar 20, 2018. It is now read-only.

Commit

Permalink
Fix ZoneRulesGroup threading and parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
jodastephen committed Jun 21, 2010
1 parent 83410dc commit 3c6ae55
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 66 deletions.
14 changes: 13 additions & 1 deletion src/main/java/javax/time/calendar/format/ZonePrinterParser.java
Expand Up @@ -170,9 +170,21 @@ public int parse(DateTimeParseContext context, String parseText, int position) {
}

if (parsedZoneId != null && preparedIDs.contains(parsedZoneId)) {
// handle zone version
TimeZone zone = TimeZone.of(parsedZoneId);
int pos = position + parsedZoneId.length();
if (pos + 1 < length && parseText.charAt(pos) == '#') {
Set<String> versions = zone.getGroup().getAvailableVersionIDs();
for (String version : versions) {
if (parseText.regionMatches(pos + 1, version, 0, version.length())) {
zone = zone.withVersion(version);
pos += version.length() + 1;
break;
}
}
}
context.setParsed(TimeZone.rule(), zone);
return position + parsedZoneId.length();
return pos;
} else {
return ~position;
}
Expand Down
Expand Up @@ -149,7 +149,7 @@ private JarZoneRulesDataProvider(URL url) throws ClassNotFoundException, IOExcep
}
versionSet.add(new ResourceZoneRulesVersion(this, versionArray[i], versionRegionArray, versionRulesArray));
}
this.versions = Collections.unmodifiableSet(versionSet);
this.versions = versionSet;
// rules
int ruleCount = dis.readShort();
this.rules = new AtomicReferenceArray<Object>(ruleCount);
Expand Down Expand Up @@ -185,6 +185,11 @@ public Set<ZoneRulesVersion> getVersions() {
return versions;
}

/** {@inheritDoc} */
public Set<String> getRegionIDs() {
return regions;
}

//-------------------------------------------------------------------------
/**
* Loads the rule.
Expand All @@ -202,6 +207,12 @@ ZoneRules loadRule(short index) throws Exception {
return (ZoneRules) obj;
}

//-----------------------------------------------------------------------
@Override
public String toString() {
return groupID + ":#" + versions;
}

//-------------------------------------------------------------------------
/**
* Version.
Expand All @@ -215,8 +226,6 @@ static class ResourceZoneRulesVersion implements ZoneRulesVersion {
private final String[] regionArray;
/** Region IDs. */
private final short[] ruleIndices;
/** Region set. */
private volatile Set<String> regions;
/** Constructor. */
ResourceZoneRulesVersion(JarZoneRulesDataProvider provider, String versionID, String[] regions, short[] ruleIndices) {
this.provider = provider;
Expand All @@ -228,17 +237,10 @@ public String getVersionID() {
return versionID;
}
public boolean isRegionID(String regionID) {
if (regions == null && provider.regions.contains(regionID) == false) {
return false; // reduces need to setup region set for older versions
}
return getRegionIDs().contains(regionID);
return Arrays.binarySearch(regionArray, regionID) >= 0;
}
public Set<String> getRegionIDs() {
Set<String> set = regions;
if (set == null) {
regions = set = new HashSet<String>(Arrays.asList(regionArray));
}
return set;
return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(regionArray)));
}
public ZoneRules getZoneRules(String regionID) {
int index = Arrays.binarySearch(regionArray, regionID);
Expand All @@ -251,6 +253,10 @@ public ZoneRules getZoneRules(String regionID) {
throw new CalendricalException("Unable to load rules: " + provider.groupID + ':' + regionID + '#' + versionID, ex);
}
}
@Override
public String toString() {
return versionID;
}
}

}
Expand Up @@ -70,8 +70,15 @@ public interface ZoneRulesDataProvider {
/**
* Gets the provided rules, version by version.
*
* @return the provided rules, never null
* @return the provided rules, not to be modified, never null
*/
Set<ZoneRulesVersion> getVersions();

/**
* Gets the provided region IDs.
*
* @return the provided region IDs, not to be modified, never null
*/
Set<String> getRegionIDs();

}
74 changes: 31 additions & 43 deletions src/main/java/javax/time/calendar/zone/ZoneRulesGroup.java
Expand Up @@ -39,6 +39,7 @@
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;

import javax.time.CalendricalException;
Expand Down Expand Up @@ -115,8 +116,9 @@ public final class ZoneRulesGroup {
/**
* The versions and rules.
*/
private volatile TreeMap<String, ZoneRulesVersion> versions =
new TreeMap<String, ZoneRulesVersion>(Collections.reverseOrder());
private AtomicReference<TreeMap<String, ZoneRulesVersion>> versions =
new AtomicReference<TreeMap<String, ZoneRulesVersion>>(
new TreeMap<String, ZoneRulesVersion>(Collections.reverseOrder()));

//-----------------------------------------------------------------------
/**
Expand Down Expand Up @@ -175,23 +177,17 @@ public static List<ZoneRulesGroup> getAvailableGroups() {
}

/**
* Gets a view of the complete set of parsable time-zone IDs.
* Gets a view of the complete set of parsable group:region IDs.
* <p>
* This returns the complete set of IDs that can be parsed.
* For each group and region, all the valid versions and the 'floating'
* version IDs will be included. Each 'TZDB' ID will be included twice
* as the 'TZDB:' prefix is optional in parsing.
* This returns the complete set of group:region IDs that can be parsed.
* The version is not included in the set for performance reasons.
* Each 'TZDB' ID will be included twice as the 'TZDB:' prefix is optional in parsing.
* For more detailed control, use the instance methods on this class.
* <p>
* For example, for the single time-zone of 'Europe/London' and two available
* versions, the set would contain:
* For example, for the single time-zone of 'Europe/London' would contain:
* <ul>
* <li>{@code Europe/London}</li>
* <li>{@code Europe/London#2009a}</li>
* <li>{@code Europe/London#2009b}</li>
* <li>{@code TZDB:Europe/London}</li>
* <li>{@code TZDB:Europe/London#2009a}</li>
* <li>{@code TZDB:Europe/London#2009b}</li>
* </ul>
* <p>
* The returned set is a view of underlying state that may be changed by another thread.
Expand All @@ -202,10 +198,10 @@ public static List<ZoneRulesGroup> getAvailableGroups() {
* This means that it the caller can cache the set and its current size to use
* as an indication as to whether the contents have changed.
*
* @return an unmodifiable set of parsable IDs, never null
* @return an unmodifiable set of parsable group:region IDs, never null
*/
public static Set<String> getParsableIDs() {
return Collections.unmodifiableSet(IDS.keySet()); // TODO: regex?
return Collections.unmodifiableSet(IDS.keySet());
}

//-----------------------------------------------------------------------
Expand Down Expand Up @@ -255,8 +251,9 @@ private ZoneRulesGroup(String groupID) {
* @param provider the provider to register, not null
*/
@SuppressWarnings("unchecked")
private synchronized void registerProvider0(ZoneRulesDataProvider provider) {
TreeMap<String, ZoneRulesVersion> newVersions = (TreeMap<String, ZoneRulesVersion>) versions.clone();
private void registerProvider0(ZoneRulesDataProvider provider) {
// synchronized by caller
TreeMap<String, ZoneRulesVersion> newVersions = (TreeMap<String, ZoneRulesVersion>) versions.get().clone();
for (ZoneRulesVersion version : provider.getVersions()) {
String versionID = version.getVersionID();
ZoneRules.checkNotNull(versionID, "Version ID must not be null");
Expand All @@ -269,7 +266,15 @@ private synchronized void registerProvider0(ZoneRulesDataProvider provider) {
}
newVersions.put(versionID, version);
}
versions = newVersions;
versions.set(newVersions);

Set<String> regionIDs = provider.getRegionIDs();
for (String regionID : regionIDs) {
IDS.put(groupID + ':' + regionID, "");
if (groupID.equals("TZDB")) {
IDS.put(regionID, "");
}
}
}

//-----------------------------------------------------------------------
Expand All @@ -294,7 +299,7 @@ public boolean isValidRules(String regionID, String versionID) {
if (regionID == null || versionID == null) {
return false;
}
ZoneRulesVersion version = versions.get(versionID);
ZoneRulesVersion version = versions.get().get(versionID);
return version != null && version.isRegionID(regionID);
}

Expand All @@ -309,7 +314,7 @@ public boolean isValidRules(String regionID, String versionID) {
public ZoneRules getRules(String regionID, String versionID) {
ZoneRules.checkNotNull(regionID, "Region ID must not be null");
ZoneRules.checkNotNull(versionID, "Version ID must not be null");
ZoneRulesVersion version = versions.get(versionID);
ZoneRulesVersion version = versions.get().get(versionID);
if (version == null) {
throw new CalendricalException("Unknown version for group: " + groupID + ':' + regionID + '#' + versionID);
}
Expand Down Expand Up @@ -371,7 +376,7 @@ public String getLatestVersionIDValidFor(String regionID, OffsetDateTime dateTim
ZoneRules.checkNotNull(regionID, "Region ID must not be null");
ZoneRules.checkNotNull(dateTime, "OffsetDateTime must not be null");
boolean foundRegion = false;
for (ZoneRulesVersion version : versions.values()) {
for (ZoneRulesVersion version : versions.get().values()) {
if (version.isRegionID(regionID)) {
foundRegion = true;
ZoneRules rules = version.getZoneRules(regionID); // not null if registered properly
Expand Down Expand Up @@ -403,7 +408,7 @@ public String getLatestVersionIDValidFor(String regionID, OffsetDateTime dateTim
* @throws CalendricalException if the region ID is not found
*/
public Set<String> getAvailableVersionIDs() {
return Collections.unmodifiableSet(versions.keySet());
return Collections.unmodifiableSet(versions.get().keySet());
}

/**
Expand All @@ -420,7 +425,7 @@ public Set<String> getAvailableVersionIDs() {
* @throws CalendricalException if the region ID is not found
*/
public String getLatestVersionID() {
return versions.firstKey(); // TODO: test, use as cache in TimeZone???
return versions.get().firstKey(); // TODO: test, use as cache in TimeZone???
}

/**
Expand All @@ -439,7 +444,7 @@ public String getLatestVersionID() {
*/
public String getLatestVersionID(String regionID) {
ZoneRules.checkNotNull(regionID, "Region ID must not be null");
for (ZoneRulesVersion version : versions.values()) {
for (ZoneRulesVersion version : versions.get().values()) {
if (version.isRegionID(regionID)) {
return version.getVersionID();
}
Expand All @@ -460,7 +465,7 @@ public String getLatestVersionID(String regionID) {
*/
public boolean isValidRegionID(String regionID) {
ZoneRules.checkNotNull(regionID, "Region ID must not be null");
for (ZoneRulesVersion version : versions.values()) {
for (ZoneRulesVersion version : versions.get().values()) {
if (version.isRegionID(regionID)) {
return true;
}
Expand All @@ -469,23 +474,6 @@ public boolean isValidRegionID(String regionID) {
}

//-----------------------------------------------------------------------
// /**
// * Gets the set of available region IDs for this group.
// * <p>
// * Each returned region will have at least one associated version.
// * <p>
// * The returned regions will remain available for the lifetime of the application as
// * there is no way to deregister time-zone information. More regions may be added during
// * the lifetime of the application, however the returned list will not be dynamically updated.
// *
// * @return an independent, modifiable list of available regions sorted alphabetically, never null
// */
// public List<String> getAvailableRegionIDs() {
// List<String> list = new ArrayList<String>(versions.keySet());
// Collections.sort(list);
// return list;
// }

/**
* Gets the set of available region IDs for this group that are valid for the specified version.
* <p>
Expand All @@ -503,7 +491,7 @@ public boolean isValidRegionID(String regionID) {
*/
public Set<String> getRegionIDs(String versionID) {
ZoneRules.checkNotNull(versionID, "Version ID must not be null");
ZoneRulesVersion version = versions.get(versionID);
ZoneRulesVersion version = versions.get().get(versionID);
if (version == null) {
throw new CalendricalException("Unknown time-zone version: " + groupID + '#' + versionID);
}
Expand Down

0 comments on commit 3c6ae55

Please sign in to comment.