1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.joda.time.tz;
17
18 import java.io.DataInputStream;
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.lang.ref.SoftReference;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.TreeSet;
27 import java.util.concurrent.ConcurrentHashMap;
28
29 import org.joda.time.DateTimeZone;
30
31
32
33
34
35
36
37
38
39
40 public class ZoneInfoProvider implements Provider {
41
42
43 private final File iFileDir;
44
45 private final String iResourcePath;
46
47 private final ClassLoader iLoader;
48
49 private final Map<String, Object> iZoneInfoMap;
50
51
52
53
54
55
56 public ZoneInfoProvider(File fileDir) throws IOException {
57 if (fileDir == null) {
58 throw new IllegalArgumentException("No file directory provided");
59 }
60 if (!fileDir.exists()) {
61 throw new IOException("File directory doesn't exist: " + fileDir);
62 }
63 if (!fileDir.isDirectory()) {
64 throw new IOException("File doesn't refer to a directory: " + fileDir);
65 }
66
67 iFileDir = fileDir;
68 iResourcePath = null;
69 iLoader = null;
70
71 iZoneInfoMap = loadZoneInfoMap(openResource("ZoneInfoMap"));
72 }
73
74
75
76
77
78
79
80
81 public ZoneInfoProvider(String resourcePath) throws IOException {
82 this(resourcePath, null, false);
83 }
84
85
86
87
88
89
90
91
92
93 public ZoneInfoProvider(String resourcePath, ClassLoader loader)
94 throws IOException
95 {
96 this(resourcePath, loader, true);
97 }
98
99
100
101
102
103 private ZoneInfoProvider(String resourcePath,
104 ClassLoader loader, boolean favorSystemLoader)
105 throws IOException
106 {
107 if (resourcePath == null) {
108 throw new IllegalArgumentException("No resource path provided");
109 }
110 if (!resourcePath.endsWith("/")) {
111 resourcePath += '/';
112 }
113
114 iFileDir = null;
115 iResourcePath = resourcePath;
116
117 if (loader == null && !favorSystemLoader) {
118 loader = getClass().getClassLoader();
119 }
120
121 iLoader = loader;
122
123 iZoneInfoMap = loadZoneInfoMap(openResource("ZoneInfoMap"));
124 }
125
126
127
128
129
130
131
132
133
134
135 public DateTimeZone getZone(String id) {
136 if (id == null) {
137 return null;
138 }
139
140 Object obj = iZoneInfoMap.get(id);
141 if (obj == null) {
142 return null;
143 }
144
145 if (id.equals(obj)) {
146
147 return loadZoneData(id);
148 }
149
150 if (obj instanceof SoftReference<?>) {
151 @SuppressWarnings("unchecked")
152 SoftReference<DateTimeZone> ref = (SoftReference<DateTimeZone>) obj;
153 DateTimeZone tz = ref.get();
154 if (tz != null) {
155 return tz;
156 }
157
158 return loadZoneData(id);
159 }
160
161
162 return getZone((String)obj);
163 }
164
165
166
167
168
169
170 public Set<String> getAvailableIDs() {
171
172
173
174 return new TreeSet<String>(iZoneInfoMap.keySet());
175 }
176
177
178
179
180
181
182 protected void uncaughtException(Exception ex) {
183 Thread t = Thread.currentThread();
184 t.getThreadGroup().uncaughtException(t, ex);
185 }
186
187
188
189
190
191
192
193
194 private InputStream openResource(String name) throws IOException {
195 InputStream in;
196 if (iFileDir != null) {
197 in = new FileInputStream(new File(iFileDir, name));
198 } else {
199 String path = iResourcePath.concat(name);
200 if (iLoader != null) {
201 in = iLoader.getResourceAsStream(path);
202 } else {
203 in = ClassLoader.getSystemResourceAsStream(path);
204 }
205 if (in == null) {
206 StringBuilder buf = new StringBuilder(40)
207 .append("Resource not found: \"")
208 .append(path)
209 .append("\" ClassLoader: ")
210 .append(iLoader != null ? iLoader.toString() : "system");
211 throw new IOException(buf.toString());
212 }
213 }
214 return in;
215 }
216
217
218
219
220
221
222
223 private DateTimeZone loadZoneData(String id) {
224 InputStream in = null;
225 try {
226 in = openResource(id);
227 DateTimeZone tz = DateTimeZoneBuilder.readFrom(in, id);
228 iZoneInfoMap.put(id, new SoftReference<DateTimeZone>(tz));
229 return tz;
230 } catch (IOException ex) {
231 uncaughtException(ex);
232 iZoneInfoMap.remove(id);
233 return null;
234 } finally {
235 try {
236 if (in != null) {
237 in.close();
238 }
239 } catch (IOException ex) {
240 }
241 }
242 }
243
244
245
246
247
248
249
250
251 private static Map<String, Object> loadZoneInfoMap(InputStream in) throws IOException {
252 Map<String, Object> map = new ConcurrentHashMap<String, Object>();
253 DataInputStream din = new DataInputStream(in);
254 try {
255 readZoneInfoMap(din, map);
256 } finally {
257 try {
258 din.close();
259 } catch (IOException ex) {
260 }
261 }
262 map.put("UTC", new SoftReference<DateTimeZone>(DateTimeZone.UTC));
263 return map;
264 }
265
266
267
268
269
270
271
272 private static void readZoneInfoMap(DataInputStream din, Map<String, Object> zimap) throws IOException {
273
274 int size = din.readUnsignedShort();
275 String[] pool = new String[size];
276 for (int i=0; i<size; i++) {
277 pool[i] = din.readUTF().intern();
278 }
279
280
281 size = din.readUnsignedShort();
282 for (int i=0; i<size; i++) {
283 try {
284 zimap.put(pool[din.readUnsignedShort()], pool[din.readUnsignedShort()]);
285 } catch (ArrayIndexOutOfBoundsException ex) {
286 throw new IOException("Corrupt zone info map");
287 }
288 }
289 }
290
291 }