Tue, 10 Feb 2015 19:58:00 +0100
Upgrade the upgraded ical4j component to use org.apache.commons.lang3.
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;
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;
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;
49 import org.apache.commons.logging.Log;
50 import org.apache.commons.logging.LogFactory;
52 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
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 {
65 private static final String DEFAULT_RESOURCE_PREFIX = "zoneinfo/";
67 private static final Pattern TZ_ID_SUFFIX = Pattern.compile("(?<=/)[^/]*/[^/]*$");
69 private static final String UPDATE_ENABLED = "net.fortuna.ical4j.timezone.update.enabled";
71 private static final Map DEFAULT_TIMEZONES = new ConcurrentHashMap();
73 private static final Properties ALIASES = new Properties();
74 static {
75 try {
76 ALIASES.load(ResourceLoader.getResourceAsStream("net/fortuna/ical4j/model/tz.alias"));
77 }
78 catch (IOException ioe) {
79 LogFactory.getLog(TimeZoneRegistryImpl.class).warn(
80 "Error loading timezone aliases: " + ioe.getMessage());
81 }
82 try {
83 ALIASES.load(ResourceLoader.getResourceAsStream("tz.alias"));
84 }
85 catch (Exception e) {
86 LogFactory.getLog(TimeZoneRegistryImpl.class).debug(
87 "Error loading custom timezone aliases: " + e.getMessage());
88 }
89 }
91 private Map timezones;
93 private String resourcePrefix;
95 /**
96 * Default constructor.
97 */
98 public TimeZoneRegistryImpl() {
99 this(DEFAULT_RESOURCE_PREFIX);
100 }
102 /**
103 * Creates a new instance using the specified resource prefix.
104 * @param resourcePrefix a prefix prepended to classpath resource lookups for default timezones
105 */
106 public TimeZoneRegistryImpl(final String resourcePrefix) {
107 this.resourcePrefix = resourcePrefix;
108 timezones = new ConcurrentHashMap();
109 }
111 /**
112 * {@inheritDoc}
113 */
114 public final void register(final TimeZone timezone) {
115 // for now we only apply updates to included definitions by default..
116 register(timezone, false);
117 }
119 /**
120 * {@inheritDoc}
121 */
122 public final void register(final TimeZone timezone, boolean update) {
123 if (update) {
124 // load any available updates for the timezone..
125 timezones.put(timezone.getID(), new TimeZone(updateDefinition(timezone.getVTimeZone())));
126 }
127 else {
128 timezones.put(timezone.getID(), timezone);
129 }
130 }
132 /**
133 * {@inheritDoc}
134 */
135 public final void clear() {
136 timezones.clear();
137 }
139 /**
140 * {@inheritDoc}
141 */
142 public final TimeZone getTimeZone(final String id) {
143 TimeZone timezone = (TimeZone) timezones.get(id);
144 if (timezone == null) {
145 timezone = (TimeZone) DEFAULT_TIMEZONES.get(id);
146 if (timezone == null) {
147 // if timezone not found with identifier, try loading an alias..
148 final String alias = ALIASES.getProperty(id);
149 if (alias != null) {
150 return getTimeZone(alias);
151 }
152 else {
153 synchronized (DEFAULT_TIMEZONES) {
154 // check again as it may be loaded now..
155 timezone = (TimeZone) DEFAULT_TIMEZONES.get(id);
156 if (timezone == null) {
157 try {
158 final VTimeZone vTimeZone = loadVTimeZone(id);
159 if (vTimeZone != null) {
160 // XXX: temporary kludge..
161 // ((TzId) vTimeZone.getProperties().getProperty(Property.TZID)).setValue(id);
162 timezone = new TimeZone(vTimeZone);
163 DEFAULT_TIMEZONES.put(timezone.getID(), timezone);
164 }
165 else if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) {
166 // strip global part of id and match on default tz..
167 Matcher matcher = TZ_ID_SUFFIX.matcher(id);
168 if (matcher.find()) {
169 return getTimeZone(matcher.group());
170 }
171 }
172 }
173 catch (Exception e) {
174 Log log = LogFactory.getLog(TimeZoneRegistryImpl.class);
175 log.warn("Error occurred loading VTimeZone", e);
176 }
177 }
178 }
179 }
180 }
181 }
182 return timezone;
183 }
185 /**
186 * Loads an existing VTimeZone from the classpath corresponding to the specified Java timezone.
187 */
188 private VTimeZone loadVTimeZone(final String id) throws IOException, ParserException {
189 final URL resource = ResourceLoader.getResource(resourcePrefix + id + ".ics");
190 if (resource != null) {
191 final CalendarBuilder builder = new CalendarBuilder();
192 final Calendar calendar = builder.build(resource.openStream());
193 final VTimeZone vTimeZone = (VTimeZone) calendar.getComponent(Component.VTIMEZONE);
194 // load any available updates for the timezone.. can be explicility disabled via configuration
195 if (!"false".equals(Configurator.getProperty(UPDATE_ENABLED))) {
196 return updateDefinition(vTimeZone);
197 }
198 return vTimeZone;
199 }
200 return null;
201 }
203 /**
204 * @param vTimeZone
205 * @return
206 */
207 private VTimeZone updateDefinition(VTimeZone vTimeZone) {
208 final TzUrl tzUrl = vTimeZone.getTimeZoneUrl();
209 if (tzUrl != null) {
210 try {
211 final CalendarBuilder builder = new CalendarBuilder();
212 final Calendar calendar = builder.build(tzUrl.getUri().toURL().openStream());
213 final VTimeZone updatedVTimeZone = (VTimeZone) calendar.getComponent(Component.VTIMEZONE);
214 if (updatedVTimeZone != null) {
215 return updatedVTimeZone;
216 }
217 }
218 catch (Exception e) {
219 Log log = LogFactory.getLog(TimeZoneRegistryImpl.class);
220 log.warn("Unable to retrieve updates for timezone: " + vTimeZone.getTimeZoneId().getValue(), e);
221 }
222 }
223 return vTimeZone;
224 }
225 }