• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  *******************************************************************************
3  * Copyright (C) 2001-2015, International Business Machines Corporation and
4  * others. All Rights Reserved.
5  *******************************************************************************
6  */
7 
8 package com.ibm.icu.impl.data;
9 
10 import java.io.BufferedReader;
11 import java.io.Closeable;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.io.InputStreamReader;
15 import java.io.UnsupportedEncodingException;
16 
17 import com.ibm.icu.impl.ICUData;
18 import com.ibm.icu.impl.PatternProps;
19 
20 /**
21  * A reader for text resource data in the current package or the package
22  * of a given class object.  The
23  * resource data is loaded through the class loader, so it will
24  * typically be a file in the same directory as the *.class files, or
25  * a file within a JAR file in the corresponding subdirectory.  The
26  * file must be a text file in one of the supported encodings; when the
27  * resource is opened by constructing a <code>ResourceReader</code>
28  * object the encoding is specified.
29  *
30  * <p>2015-sep-03 TODO: Only used in com.ibm.icu.dev.test.format, move there.
31  *
32  * @author Alan Liu
33  */
34 public class ResourceReader implements Closeable {
35     private BufferedReader reader = null;
36     private String resourceName;
37     private String encoding; // null for default encoding
38     private Class<?> root;
39 
40     /**
41      * The one-based line number. Has the special value -1 before the
42      * object is initialized. Has the special value 0 after initialization
43      * but before the first line is read.
44      */
45     private int lineNo;
46 
47     /**
48      * Construct a reader object for the text file of the given name
49      * in this package, using the given encoding.
50      * @param resourceName the name of the text file located in this
51      * package's ".data" subpackage.
52      * @param encoding the encoding of the text file; if unsupported
53      * an exception is thrown
54      * @exception UnsupportedEncodingException if
55      * <code>encoding</code> is not supported by the JDK.
56      */
ResourceReader(String resourceName, String encoding)57     public ResourceReader(String resourceName, String encoding)
58         throws UnsupportedEncodingException {
59         this(ICUData.class, "data/" + resourceName, encoding);
60     }
61 
62     /**
63      * Construct a reader object for the text file of the given name
64      * in this package, using the default encoding.
65      * @param resourceName the name of the text file located in this
66      * package's ".data" subpackage.
67      */
ResourceReader(String resourceName)68     public ResourceReader(String resourceName) {
69         this(ICUData.class, "data/" + resourceName);
70     }
71 
72     /**
73      * Construct a reader object for the text file of the given name
74      * in the given class's package, using the given encoding.
75      * @param resourceName the name of the text file located in the
76      * given class's package.
77      * @param encoding the encoding of the text file; if unsupported
78      * an exception is thrown
79      * @exception UnsupportedEncodingException if
80      * <code>encoding</code> is not supported by the JDK.
81      */
ResourceReader(Class<?> rootClass, String resourceName, String encoding)82     public ResourceReader(Class<?> rootClass, String resourceName, String encoding)
83         throws UnsupportedEncodingException {
84         this.root = rootClass;
85         this.resourceName = resourceName;
86         this.encoding = encoding;
87         lineNo = -1;
88         _reset();
89     }
90 
91          /**
92           * Construct a reader object for the input stream associated with
93           * the given resource name.
94           * @param is the input stream of the resource
95           * @param resourceName the name of the resource
96           */
ResourceReader(InputStream is, String resourceName, String encoding)97           public ResourceReader(InputStream is, String resourceName, String encoding) {
98                    this.root = null;
99          this.resourceName = resourceName;
100          this.encoding = encoding;
101 
102          this.lineNo = -1;
103          try {
104              InputStreamReader isr = (encoding == null)
105                  ? new InputStreamReader(is)
106                  : new InputStreamReader(is, encoding);
107 
108              this.reader = new BufferedReader(isr);
109              this.lineNo= 0;
110          }
111          catch (UnsupportedEncodingException e) {
112          }
113      }
114 
115           /**
116            * Construct a reader object for the input stream associated with
117            * the given resource name.
118            * @param is the input stream of the resource
119            * @param resourceName the name of the resource
120            */
ResourceReader(InputStream is, String resourceName)121           public ResourceReader(InputStream is, String resourceName) {
122               this(is, resourceName, null);
123           }
124 
125     /**
126      * Construct a reader object for the text file of the given name
127      * in the given class's package, using the default encoding.
128      * @param resourceName the name of the text file located in the
129      * given class's package.
130      */
ResourceReader(Class<?> rootClass, String resourceName)131     public ResourceReader(Class<?> rootClass, String resourceName) {
132         this.root = rootClass;
133         this.resourceName = resourceName;
134         this.encoding = null;
135         lineNo = -1;
136         try {
137             _reset();
138         } catch (UnsupportedEncodingException e) {}
139     }
140 
141     /**
142      * Read and return the next line of the file or <code>null</code>
143      * if the end of the file has been reached.
144      */
readLine()145     public String readLine() throws IOException {
146         if (lineNo == 0) {
147             // Remove BOMs
148             ++lineNo;
149             String line = reader.readLine();
150             if (line != null && (line.charAt(0) == '\uFFEF' ||
151                                  line.charAt(0) == '\uFEFF')) {
152                 line = line.substring(1);
153             }
154             return line;
155         }
156         ++lineNo;
157         return reader.readLine();
158     }
159 
160     /**
161      * Read a line, ignoring blank lines and lines that start with
162      * '#'.
163      * @param trim if true then trim leading Pattern_White_Space.
164      */
readLineSkippingComments(boolean trim)165     public String readLineSkippingComments(boolean trim) throws IOException {
166         for (;;) {
167             String line = readLine();
168             if (line == null) {
169                 return line;
170             }
171             // Skip over white space
172             int pos = PatternProps.skipWhiteSpace(line, 0);
173             // Ignore blank lines and comment lines
174             if (pos == line.length() || line.charAt(pos) == '#') {
175                 continue;
176             }
177             // Process line
178             if (trim) line = line.substring(pos);
179             return line;
180         }
181     }
182 
183 
184     /**
185      * Read a line, ignoring blank lines and lines that start with
186      * '#'. Do not trim leading Pattern_White_Space.
187      */
readLineSkippingComments()188     public String readLineSkippingComments() throws IOException {
189         return readLineSkippingComments(false);
190     }
191 
192     /**
193      * Return the one-based line number of the last line returned by
194      * readLine() or readLineSkippingComments(). Should only be called
195      * after a call to one of these methods; otherwise the return
196      * value is undefined.
197      */
getLineNumber()198     public int getLineNumber() {
199         return lineNo;
200     }
201 
202     /**
203      * Return a string description of the position of the last line
204      * returned by readLine() or readLineSkippingComments().
205      */
describePosition()206     public String describePosition() {
207         return resourceName + ':' + lineNo;
208     }
209 
210     /**
211      * Reset this reader so that the next call to
212      * <code>readLine()</code> returns the first line of the file
213      * again.  This is a somewhat expensive call, however, calling
214      * <code>reset()</code> after calling it the first time does
215      * nothing if <code>readLine()</code> has not been called in
216      * between.
217      */
reset()218     public void reset() {
219         try {
220             _reset();
221         } catch (UnsupportedEncodingException e) {}
222         // We swallow this exception, if there is one.  If the encoding is
223         // invalid, the constructor will have thrown this exception already and
224         // the caller shouldn't use the object afterwards.
225     }
226 
227     /**
228      * Reset to the start by reconstructing the stream and readers.
229      * We could also use mark() and reset() on the stream or reader,
230      * but that would cause them to keep the stream data around in
231      * memory.  We don't want that because some of the resource files
232      * are large, e.g., 400k.
233      */
_reset()234     private void _reset() throws UnsupportedEncodingException {
235         try {
236             close();
237         } catch (IOException e) {}
238         if (lineNo == 0) {
239             return;
240         }
241         InputStream is = ICUData.getStream(root, resourceName);
242         if (is == null) {
243             throw new IllegalArgumentException("Can't open " + resourceName);
244         }
245 
246         InputStreamReader isr =
247             (encoding == null) ? new InputStreamReader(is) :
248                                  new InputStreamReader(is, encoding);
249         reader = new BufferedReader(isr);
250         lineNo = 0;
251     }
252 
253     /**
254      * Closes the underlying reader and releases any system resources
255      * associated with it. If the stream is already closed then invoking
256      * this method has no effect.
257      */
close()258     public void close() throws IOException {
259         if (reader != null) {
260             reader.close();
261             reader = null;
262         }
263     }
264 }
265