diff -r cc93757aeca3 -r 6dcaece8ec41 hack/model/TimeZoneRegistryImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hack/model/TimeZoneRegistryImpl.java Fri Feb 13 23:45:37 2015 +0100 @@ -0,0 +1,220 @@ +/** + * Copyright (c) 2012, Ben Fortuna + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * o Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * o Neither the name of Ben Fortuna nor the names of any other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.fortuna.ical4j.model; + +import java.io.IOException; +import java.net.URL; +import java.util.Map; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.fortuna.ical4j.data.CalendarBuilder; +import net.fortuna.ical4j.data.ParserException; +import net.fortuna.ical4j.model.component.VTimeZone; +import net.fortuna.ical4j.model.property.TzUrl; +import net.fortuna.ical4j.util.CompatibilityHints; +import net.fortuna.ical4j.util.Configurator; +import net.fortuna.ical4j.util.ResourceLoader; + +import android.util.Log; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * $Id$ + * + * Created on 18/09/2005 + * + * The default implementation of a TimeZoneRegistry. This implementation will search the classpath for + * applicable VTimeZone definitions used to back the provided TimeZone instances. + * @author Ben Fortuna + */ +public class TimeZoneRegistryImpl implements TimeZoneRegistry { + + private static final String DEFAULT_RESOURCE_PREFIX = "zoneinfo/"; + + private static final Pattern TZ_ID_SUFFIX = Pattern.compile("(?<=/)[^/]*/[^/]*$"); + + private static final String UPDATE_ENABLED = "net.fortuna.ical4j.timezone.update.enabled"; + + private static final Map DEFAULT_TIMEZONES = new ConcurrentHashMap(); + + private static final Properties ALIASES = new Properties(); + static { + try { + ALIASES.load(ResourceLoader.getResourceAsStream("tz.alias")); + } + catch (IOException ioe) { + Log.w("MSvB-Hack", "Error loading timezone aliases: " + ioe.getMessage()); + } + try { + ALIASES.load(ResourceLoader.getResourceAsStream("/tz.alias")); + } + catch (Exception e) { + Log.d("MSvB-Hack", "Error loading custom timezone aliases: " + e.getMessage()); + } + } + + private Map timezones; + + private String resourcePrefix; + + /** + * Default constructor. + */ + public TimeZoneRegistryImpl() { + this(DEFAULT_RESOURCE_PREFIX); + } + + /** + * Creates a new instance using the specified resource prefix. + * @param resourcePrefix a prefix prepended to classpath resource lookups for default timezones + */ + public TimeZoneRegistryImpl(final String resourcePrefix) { + this.resourcePrefix = resourcePrefix; + timezones = new ConcurrentHashMap(); + } + + /** + * {@inheritDoc} + */ + public final void register(final TimeZone timezone) { + // for now we only apply updates to included definitions by default.. + register(timezone, false); + } + + /** + * {@inheritDoc} + */ + public final void register(final TimeZone timezone, boolean update) { + if (update) { + // load any available updates for the timezone.. + timezones.put(timezone.getID(), new TimeZone(updateDefinition(timezone.getVTimeZone()))); + } + else { + timezones.put(timezone.getID(), timezone); + } + } + + /** + * {@inheritDoc} + */ + public final void clear() { + timezones.clear(); + } + + /** + * {@inheritDoc} + */ + public final TimeZone getTimeZone(final String id) { + TimeZone timezone = (TimeZone) timezones.get(id); + if (timezone == null) { + timezone = (TimeZone) DEFAULT_TIMEZONES.get(id); + if (timezone == null) { + // if timezone not found with identifier, try loading an alias.. + final String alias = ALIASES.getProperty(id); + if (alias != null) { + return getTimeZone(alias); + } + else { + synchronized (DEFAULT_TIMEZONES) { + // check again as it may be loaded now.. + timezone = (TimeZone) DEFAULT_TIMEZONES.get(id); + if (timezone == null) { + try { + final VTimeZone vTimeZone = loadVTimeZone(id); + if (vTimeZone != null) { + // XXX: temporary kludge.. + // ((TzId) vTimeZone.getProperties().getProperty(Property.TZID)).setValue(id); + timezone = new TimeZone(vTimeZone); + DEFAULT_TIMEZONES.put(timezone.getID(), timezone); + } + else if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) { + // strip global part of id and match on default tz.. + Matcher matcher = TZ_ID_SUFFIX.matcher(id); + if (matcher.find()) { + return getTimeZone(matcher.group()); + } + } + } + catch (Exception e) { + Log.w("MSvB-Hack", "Error occurred loading VTimeZone", e); + } + } + } + } + } + } + return timezone; + } + + /** + * Loads an existing VTimeZone from the classpath corresponding to the specified Java timezone. + */ + private VTimeZone loadVTimeZone(final String id) throws IOException, ParserException { + final URL resource = ResourceLoader.getResource(resourcePrefix + id + ".ics"); + if (resource != null) { + final CalendarBuilder builder = new CalendarBuilder(); + final Calendar calendar = builder.build(resource.openStream()); + final VTimeZone vTimeZone = (VTimeZone) calendar.getComponent(Component.VTIMEZONE); + // load any available updates for the timezone.. can be explicility disabled via configuration + if (!"false".equals(Configurator.getProperty(UPDATE_ENABLED))) { + return updateDefinition(vTimeZone); + } + return vTimeZone; + } + return null; + } + + /** + * @param vTimeZone + * @return + */ + private VTimeZone updateDefinition(VTimeZone vTimeZone) { + final TzUrl tzUrl = vTimeZone.getTimeZoneUrl(); + if (tzUrl != null) { + try { + final CalendarBuilder builder = new CalendarBuilder(); + final Calendar calendar = builder.build(tzUrl.getUri().toURL().openStream()); + final VTimeZone updatedVTimeZone = (VTimeZone) calendar.getComponent(Component.VTIMEZONE); + if (updatedVTimeZone != null) { + return updatedVTimeZone; + } + } + catch (Exception e) { + Log.w("MSvB-Hack", "Unable to retrieve updates for timezone: " + vTimeZone.getTimeZoneId().getValue(), e); + } + } + return vTimeZone; + } +}