EMMA Coverage Report (generated Tue Oct 28 00:01:11 GMT 2008)
[all classes][org.joda.time.tz]

COVERAGE SUMMARY FOR SOURCE FILE [ZoneInfoProvider.java]

nameclass, %method, %block, %line, %
ZoneInfoProvider.java100% (1/1)82%  (9/11)66%  (242/364)71%  (63.9/90)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ZoneInfoProvider100% (1/1)82%  (9/11)66%  (242/364)71%  (63.9/90)
ZoneInfoProvider (String, ClassLoader): void 0%   (0/1)0%   (0/6)0%   (0/2)
uncaughtException (Exception): void 0%   (0/1)0%   (0/8)0%   (0/3)
ZoneInfoProvider (File): void 100% (1/1)47%  (26/55)75%  (9/12)
openResource (String): InputStream 100% (1/1)52%  (32/61)70%  (7/10)
loadZoneData (String): DateTimeZone 100% (1/1)62%  (31/50)60%  (8.4/14)
getZone (String): DateTimeZone 100% (1/1)74%  (31/42)77%  (10/13)
loadZoneInfoMap (InputStream): Map 100% (1/1)74%  (26/35)65%  (6.5/10)
readZoneInfoMap (DataInputStream, Map): void 100% (1/1)88%  (42/48)82%  (9/11)
ZoneInfoProvider (String, ClassLoader, boolean): void 100% (1/1)89%  (41/46)92%  (11/12)
ZoneInfoProvider (String): void 100% (1/1)100% (6/6)100% (2/2)
getAvailableIDs (): Set 100% (1/1)100% (7/7)100% (1/1)

1/*
2 *  Copyright 2001-2005 Stephen Colebourne
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 *  you may not use this file except in compliance with the License.
6 *  You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *  Unless required by applicable law or agreed to in writing, software
11 *  distributed under the License is distributed on an "AS IS" BASIS,
12 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *  See the License for the specific language governing permissions and
14 *  limitations under the License.
15 */
16package org.joda.time.tz;
17 
18import java.io.DataInputStream;
19import java.io.File;
20import java.io.FileInputStream;
21import java.io.IOException;
22import java.io.InputStream;
23import java.lang.ref.SoftReference;
24import java.util.Map;
25import java.util.Set;
26import java.util.TreeMap;
27import java.util.TreeSet;
28 
29import org.joda.time.DateTimeZone;
30 
31/**
32 * ZoneInfoProvider loads compiled data files as generated by
33 * {@link ZoneInfoCompiler}.
34 * <p>
35 * ZoneInfoProvider is thread-safe and publicly immutable.
36 *
37 * @author Brian S O'Neill
38 * @since 1.0
39 */
40public class ZoneInfoProvider implements Provider {
41 
42    /** The directory where the files are held. */
43    private final File iFileDir;
44    /** The resource path. */
45    private final String iResourcePath;
46    /** The class loader to use. */
47    private final ClassLoader iLoader;
48    /** Maps ids to strings or SoftReferences to DateTimeZones. */
49    private final Map iZoneInfoMap;
50 
51    /**
52     * ZoneInfoProvider searches the given directory for compiled data files.
53     *
54     * @throws IOException if directory or map file cannot be read
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     * ZoneInfoProvider searches the given ClassLoader resource path for
76     * compiled data files. Resources are loaded from the ClassLoader that
77     * loaded this class.
78     *
79     * @throws IOException if directory or map file cannot be read
80     */
81    public ZoneInfoProvider(String resourcePath) throws IOException {
82        this(resourcePath, null, false);
83    }
84 
85    /**
86     * ZoneInfoProvider searches the given ClassLoader resource path for
87     * compiled data files.
88     *
89     * @param loader ClassLoader to load compiled data files from. If null,
90     * use system ClassLoader.
91     * @throws IOException if directory or map file cannot be read
92     */
93    public ZoneInfoProvider(String resourcePath, ClassLoader loader)
94        throws IOException
95    {
96        this(resourcePath, loader, true);
97    }
98 
99    /**
100     * @param favorSystemLoader when true, use the system class loader if
101     * loader null. When false, use the current class loader if loader is null.
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     * If an error is thrown while loading zone data, uncaughtException is
129     * called to log the error and null is returned for this and all future
130     * requests.
131     * 
132     * @param id  the id to load
133     * @return the loaded zone
134     */
135    public synchronized 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            // Load zone data for the first time.
147            return loadZoneData(id);
148        }
149 
150        if (obj instanceof SoftReference) {
151            DateTimeZone tz = (DateTimeZone)((SoftReference)obj).get();
152            if (tz != null) {
153                return tz;
154            }
155            // Reference cleared; load data again.
156            return loadZoneData(id);
157        }
158 
159        // If this point is reached, mapping must link to another.
160        return getZone((String)obj);
161    }
162 
163    /**
164     * Gets a list of all the available zone ids.
165     * 
166     * @return the zone ids
167     */
168    public synchronized Set getAvailableIDs() {
169        // Return a copy of the keys rather than an umodifiable collection.
170        // This prevents ConcurrentModificationExceptions from being thrown by
171        // some JVMs if zones are opened while this set is iterated over.
172        return new TreeSet(iZoneInfoMap.keySet());
173    }
174 
175    /**
176     * Called if an exception is thrown from getZone while loading zone data.
177     * 
178     * @param ex  the exception
179     */
180    protected void uncaughtException(Exception ex) {
181        Thread t = Thread.currentThread();
182        t.getThreadGroup().uncaughtException(t, ex);
183    }
184 
185    /**
186     * Opens a resource from file or classpath.
187     * 
188     * @param name  the name to open
189     * @return the input stream
190     * @throws IOException if an error occurs
191     */
192    private InputStream openResource(String name) throws IOException {
193        InputStream in;
194        if (iFileDir != null) {
195            in = new FileInputStream(new File(iFileDir, name));
196        } else {
197            String path = iResourcePath.concat(name);
198            if (iLoader != null) {
199                in = iLoader.getResourceAsStream(path);
200            } else {
201                in = ClassLoader.getSystemResourceAsStream(path);
202            }
203            if (in == null) {
204                StringBuffer buf = new StringBuffer(40)
205                    .append("Resource not found: \"")
206                    .append(path)
207                    .append("\" ClassLoader: ")
208                    .append(iLoader != null ? iLoader.toString() : "system");
209                throw new IOException(buf.toString());
210            }
211        }
212        return in;
213    }
214 
215    /**
216     * Loads the time zone data for one id.
217     * 
218     * @param id  the id to load
219     * @return the zone
220     */
221    private DateTimeZone loadZoneData(String id) {
222        InputStream in = null;
223        try {
224            in = openResource(id);
225            DateTimeZone tz = DateTimeZoneBuilder.readFrom(in, id);
226            iZoneInfoMap.put(id, new SoftReference(tz));
227            return tz;
228        } catch (IOException e) {
229            uncaughtException(e);
230            iZoneInfoMap.remove(id);
231            return null;
232        } finally {
233            try {
234                if (in != null) {
235                    in.close();
236                }
237            } catch (IOException e) {
238            }
239        }
240    }
241 
242    //-----------------------------------------------------------------------
243    /**
244     * Loads the zone info map.
245     * 
246     * @param in  the input stream
247     * @return the map
248     */
249    private static Map loadZoneInfoMap(InputStream in) throws IOException {
250        Map map = new TreeMap(String.CASE_INSENSITIVE_ORDER);
251        DataInputStream din = new DataInputStream(in);
252        try {
253            readZoneInfoMap(din, map);
254        } finally {
255            try {
256                din.close();
257            } catch (IOException e) {
258            }
259        }
260        map.put("UTC", new SoftReference(DateTimeZone.UTC));
261        return map;
262    }
263 
264    /**
265     * Reads the zone info map from file.
266     * 
267     * @param din  the input stream
268     * @param zimap  gets filled with string id to string id mappings
269     */
270    private static void readZoneInfoMap(DataInputStream din, Map zimap) throws IOException {
271        // Read the string pool.
272        int size = din.readUnsignedShort();
273        String[] pool = new String[size];
274        for (int i=0; i<size; i++) {
275            pool[i] = din.readUTF().intern();
276        }
277 
278        // Read the mappings.
279        size = din.readUnsignedShort();
280        for (int i=0; i<size; i++) {
281            try {
282                zimap.put(pool[din.readUnsignedShort()], pool[din.readUnsignedShort()]);
283            } catch (ArrayIndexOutOfBoundsException e) {
284                throw new IOException("Corrupt zone info map");
285            }
286        }
287    }
288 
289}

[all classes][org.joda.time.tz]
EMMA 2.0.5312 (C) Vladimir Roubtsov