src/net/fortuna/ical4j/model/TimeZoneRegistryImpl.java

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

mercurial