hack/model/TimeZoneRegistryImpl.java

Fri, 13 Feb 2015 23:45:37 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 13 Feb 2015 23:45:37 +0100
branch
ICAL4J_EMBED_1
changeset 18
6dcaece8ec41
permissions
-rw-r--r--

Give up after a longwinded and unfruitful attempt to patch ical4j,
made difficult by the neverending graph of method calls leading up
to the NPE caused in getResource*() of the ResourceLoader class.

michael@18 1 /**
michael@18 2 * Copyright (c) 2012, Ben Fortuna
michael@18 3 * All rights reserved.
michael@18 4 *
michael@18 5 * Redistribution and use in source and binary forms, with or without
michael@18 6 * modification, are permitted provided that the following conditions
michael@18 7 * are met:
michael@18 8 *
michael@18 9 * o Redistributions of source code must retain the above copyright
michael@18 10 * notice, this list of conditions and the following disclaimer.
michael@18 11 *
michael@18 12 * o Redistributions in binary form must reproduce the above copyright
michael@18 13 * notice, this list of conditions and the following disclaimer in the
michael@18 14 * documentation and/or other materials provided with the distribution.
michael@18 15 *
michael@18 16 * o Neither the name of Ben Fortuna nor the names of any other contributors
michael@18 17 * may be used to endorse or promote products derived from this software
michael@18 18 * without specific prior written permission.
michael@18 19 *
michael@18 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@18 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@18 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@18 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
michael@18 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
michael@18 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
michael@18 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
michael@18 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
michael@18 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
michael@18 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
michael@18 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@18 31 */
michael@18 32 package net.fortuna.ical4j.model;
michael@18 33
michael@18 34 import java.io.IOException;
michael@18 35 import java.net.URL;
michael@18 36 import java.util.Map;
michael@18 37 import java.util.Properties;
michael@18 38 import java.util.regex.Matcher;
michael@18 39 import java.util.regex.Pattern;
michael@18 40
michael@18 41 import net.fortuna.ical4j.data.CalendarBuilder;
michael@18 42 import net.fortuna.ical4j.data.ParserException;
michael@18 43 import net.fortuna.ical4j.model.component.VTimeZone;
michael@18 44 import net.fortuna.ical4j.model.property.TzUrl;
michael@18 45 import net.fortuna.ical4j.util.CompatibilityHints;
michael@18 46 import net.fortuna.ical4j.util.Configurator;
michael@18 47 import net.fortuna.ical4j.util.ResourceLoader;
michael@18 48
michael@18 49 import android.util.Log;
michael@18 50
michael@18 51 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
michael@18 52
michael@18 53 /**
michael@18 54 * $Id$
michael@18 55 *
michael@18 56 * Created on 18/09/2005
michael@18 57 *
michael@18 58 * The default implementation of a <code>TimeZoneRegistry</code>. This implementation will search the classpath for
michael@18 59 * applicable VTimeZone definitions used to back the provided TimeZone instances.
michael@18 60 * @author Ben Fortuna
michael@18 61 */
michael@18 62 public class TimeZoneRegistryImpl implements TimeZoneRegistry {
michael@18 63
michael@18 64 private static final String DEFAULT_RESOURCE_PREFIX = "zoneinfo/";
michael@18 65
michael@18 66 private static final Pattern TZ_ID_SUFFIX = Pattern.compile("(?<=/)[^/]*/[^/]*$");
michael@18 67
michael@18 68 private static final String UPDATE_ENABLED = "net.fortuna.ical4j.timezone.update.enabled";
michael@18 69
michael@18 70 private static final Map DEFAULT_TIMEZONES = new ConcurrentHashMap();
michael@18 71
michael@18 72 private static final Properties ALIASES = new Properties();
michael@18 73 static {
michael@18 74 try {
michael@18 75 ALIASES.load(ResourceLoader.getResourceAsStream("tz.alias"));
michael@18 76 }
michael@18 77 catch (IOException ioe) {
michael@18 78 Log.w("MSvB-Hack", "Error loading timezone aliases: " + ioe.getMessage());
michael@18 79 }
michael@18 80 try {
michael@18 81 ALIASES.load(ResourceLoader.getResourceAsStream("/tz.alias"));
michael@18 82 }
michael@18 83 catch (Exception e) {
michael@18 84 Log.d("MSvB-Hack", "Error loading custom timezone aliases: " + e.getMessage());
michael@18 85 }
michael@18 86 }
michael@18 87
michael@18 88 private Map timezones;
michael@18 89
michael@18 90 private String resourcePrefix;
michael@18 91
michael@18 92 /**
michael@18 93 * Default constructor.
michael@18 94 */
michael@18 95 public TimeZoneRegistryImpl() {
michael@18 96 this(DEFAULT_RESOURCE_PREFIX);
michael@18 97 }
michael@18 98
michael@18 99 /**
michael@18 100 * Creates a new instance using the specified resource prefix.
michael@18 101 * @param resourcePrefix a prefix prepended to classpath resource lookups for default timezones
michael@18 102 */
michael@18 103 public TimeZoneRegistryImpl(final String resourcePrefix) {
michael@18 104 this.resourcePrefix = resourcePrefix;
michael@18 105 timezones = new ConcurrentHashMap();
michael@18 106 }
michael@18 107
michael@18 108 /**
michael@18 109 * {@inheritDoc}
michael@18 110 */
michael@18 111 public final void register(final TimeZone timezone) {
michael@18 112 // for now we only apply updates to included definitions by default..
michael@18 113 register(timezone, false);
michael@18 114 }
michael@18 115
michael@18 116 /**
michael@18 117 * {@inheritDoc}
michael@18 118 */
michael@18 119 public final void register(final TimeZone timezone, boolean update) {
michael@18 120 if (update) {
michael@18 121 // load any available updates for the timezone..
michael@18 122 timezones.put(timezone.getID(), new TimeZone(updateDefinition(timezone.getVTimeZone())));
michael@18 123 }
michael@18 124 else {
michael@18 125 timezones.put(timezone.getID(), timezone);
michael@18 126 }
michael@18 127 }
michael@18 128
michael@18 129 /**
michael@18 130 * {@inheritDoc}
michael@18 131 */
michael@18 132 public final void clear() {
michael@18 133 timezones.clear();
michael@18 134 }
michael@18 135
michael@18 136 /**
michael@18 137 * {@inheritDoc}
michael@18 138 */
michael@18 139 public final TimeZone getTimeZone(final String id) {
michael@18 140 TimeZone timezone = (TimeZone) timezones.get(id);
michael@18 141 if (timezone == null) {
michael@18 142 timezone = (TimeZone) DEFAULT_TIMEZONES.get(id);
michael@18 143 if (timezone == null) {
michael@18 144 // if timezone not found with identifier, try loading an alias..
michael@18 145 final String alias = ALIASES.getProperty(id);
michael@18 146 if (alias != null) {
michael@18 147 return getTimeZone(alias);
michael@18 148 }
michael@18 149 else {
michael@18 150 synchronized (DEFAULT_TIMEZONES) {
michael@18 151 // check again as it may be loaded now..
michael@18 152 timezone = (TimeZone) DEFAULT_TIMEZONES.get(id);
michael@18 153 if (timezone == null) {
michael@18 154 try {
michael@18 155 final VTimeZone vTimeZone = loadVTimeZone(id);
michael@18 156 if (vTimeZone != null) {
michael@18 157 // XXX: temporary kludge..
michael@18 158 // ((TzId) vTimeZone.getProperties().getProperty(Property.TZID)).setValue(id);
michael@18 159 timezone = new TimeZone(vTimeZone);
michael@18 160 DEFAULT_TIMEZONES.put(timezone.getID(), timezone);
michael@18 161 }
michael@18 162 else if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) {
michael@18 163 // strip global part of id and match on default tz..
michael@18 164 Matcher matcher = TZ_ID_SUFFIX.matcher(id);
michael@18 165 if (matcher.find()) {
michael@18 166 return getTimeZone(matcher.group());
michael@18 167 }
michael@18 168 }
michael@18 169 }
michael@18 170 catch (Exception e) {
michael@18 171 Log.w("MSvB-Hack", "Error occurred loading VTimeZone", e);
michael@18 172 }
michael@18 173 }
michael@18 174 }
michael@18 175 }
michael@18 176 }
michael@18 177 }
michael@18 178 return timezone;
michael@18 179 }
michael@18 180
michael@18 181 /**
michael@18 182 * Loads an existing VTimeZone from the classpath corresponding to the specified Java timezone.
michael@18 183 */
michael@18 184 private VTimeZone loadVTimeZone(final String id) throws IOException, ParserException {
michael@18 185 final URL resource = ResourceLoader.getResource(resourcePrefix + id + ".ics");
michael@18 186 if (resource != null) {
michael@18 187 final CalendarBuilder builder = new CalendarBuilder();
michael@18 188 final Calendar calendar = builder.build(resource.openStream());
michael@18 189 final VTimeZone vTimeZone = (VTimeZone) calendar.getComponent(Component.VTIMEZONE);
michael@18 190 // load any available updates for the timezone.. can be explicility disabled via configuration
michael@18 191 if (!"false".equals(Configurator.getProperty(UPDATE_ENABLED))) {
michael@18 192 return updateDefinition(vTimeZone);
michael@18 193 }
michael@18 194 return vTimeZone;
michael@18 195 }
michael@18 196 return null;
michael@18 197 }
michael@18 198
michael@18 199 /**
michael@18 200 * @param vTimeZone
michael@18 201 * @return
michael@18 202 */
michael@18 203 private VTimeZone updateDefinition(VTimeZone vTimeZone) {
michael@18 204 final TzUrl tzUrl = vTimeZone.getTimeZoneUrl();
michael@18 205 if (tzUrl != null) {
michael@18 206 try {
michael@18 207 final CalendarBuilder builder = new CalendarBuilder();
michael@18 208 final Calendar calendar = builder.build(tzUrl.getUri().toURL().openStream());
michael@18 209 final VTimeZone updatedVTimeZone = (VTimeZone) calendar.getComponent(Component.VTIMEZONE);
michael@18 210 if (updatedVTimeZone != null) {
michael@18 211 return updatedVTimeZone;
michael@18 212 }
michael@18 213 }
michael@18 214 catch (Exception e) {
michael@18 215 Log.w("MSvB-Hack", "Unable to retrieve updates for timezone: " + vTimeZone.getTimeZoneId().getValue(), e);
michael@18 216 }
michael@18 217 }
michael@18 218 return vTimeZone;
michael@18 219 }
michael@18 220 }

mercurial