001: /**
002: *******************************************************************************
003: * Copyright (C) 2001-2004, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */package com.ibm.icu.impl.data;
007:
008: import java.io.*;
009:
010: import com.ibm.icu.impl.ICUData;
011: import com.ibm.icu.impl.Utility;
012:
013: /**
014: * A reader for text resource data in the current package or the package
015: * of a given class object. The
016: * resource data is loaded through the class loader, so it will
017: * typically be a file in the same directory as the *.class files, or
018: * a file within a JAR file in the corresponding subdirectory. The
019: * file must be a text file in one of the supported encodings; when the
020: * resource is opened by constructing a <code>ResourceReader</code>
021: * object the encoding is specified.
022: *
023: * @author Alan Liu
024: */
025: public class ResourceReader {
026: private BufferedReader reader;
027: private String resourceName;
028: private String encoding; // null for default encoding
029: private Class root;
030:
031: /**
032: * The one-based line number. Has the special value -1 before the
033: * object is initialized. Has the special value 0 after initialization
034: * but before the first line is read.
035: */
036: private int lineNo;
037:
038: /**
039: * Construct a reader object for the text file of the given name
040: * in this package, using the given encoding.
041: * @param resourceName the name of the text file located in this
042: * package's ".data" subpackage.
043: * @param encoding the encoding of the text file; if unsupported
044: * an exception is thrown
045: * @exception UnsupportedEncodingException if
046: * <code>encoding</code> is not supported by the JDK.
047: */
048: public ResourceReader(String resourceName, String encoding)
049: throws UnsupportedEncodingException {
050: this (ICUData.class, "data/" + resourceName, encoding);
051: }
052:
053: /**
054: * Construct a reader object for the text file of the given name
055: * in this package, using the default encoding.
056: * @param resourceName the name of the text file located in this
057: * package's ".data" subpackage.
058: */
059: public ResourceReader(String resourceName) {
060: this (ICUData.class, "data/" + resourceName);
061: }
062:
063: /**
064: * Construct a reader object for the text file of the given name
065: * in the given class's package, using the given encoding.
066: * @param resourceName the name of the text file located in the
067: * given class's package.
068: * @param encoding the encoding of the text file; if unsupported
069: * an exception is thrown
070: * @exception UnsupportedEncodingException if
071: * <code>encoding</code> is not supported by the JDK.
072: */
073: public ResourceReader(Class rootClass, String resourceName,
074: String encoding) throws UnsupportedEncodingException {
075: this .root = rootClass;
076: this .resourceName = resourceName;
077: this .encoding = encoding;
078: lineNo = -1;
079: _reset();
080: }
081:
082: /**
083: * @internal
084: * @deprecated This API is ICU internal only.
085: */
086: public ResourceReader(InputStream is, String resourceName) {
087: this .root = null;
088: this .resourceName = resourceName;
089: this .encoding = null;
090:
091: this .lineNo = -1;
092: try {
093: InputStreamReader isr = (encoding == null) ? new InputStreamReader(
094: is)
095: : new InputStreamReader(is, encoding);
096:
097: this .reader = new BufferedReader(isr);
098: this .lineNo = 0;
099: } catch (UnsupportedEncodingException e) {
100: }
101: }
102:
103: /**
104: * Construct a reader object for the text file of the given name
105: * in the given class's package, using the default encoding.
106: * @param resourceName the name of the text file located in the
107: * given class's package.
108: */
109: public ResourceReader(Class rootClass, String resourceName) {
110: this .root = rootClass;
111: this .resourceName = resourceName;
112: this .encoding = null;
113: lineNo = -1;
114: try {
115: _reset();
116: } catch (UnsupportedEncodingException e) {
117: }
118: }
119:
120: /**
121: * Read and return the next line of the file or <code>null</code>
122: * if the end of the file has been reached.
123: */
124: public String readLine() throws IOException {
125: if (lineNo == 0) {
126: // Remove BOMs
127: ++lineNo;
128: String line = reader.readLine();
129: if (line.charAt(0) == '\uFFEF'
130: || line.charAt(0) == '\uFEFF') {
131: line = line.substring(1);
132: }
133: return line;
134: }
135: ++lineNo;
136: return reader.readLine();
137: }
138:
139: /**
140: * Read a line, ignoring blank lines and lines that start with
141: * '#'.
142: * @param trim if true then trim leading rule white space.
143: */
144: public String readLineSkippingComments(boolean trim)
145: throws IOException {
146: for (;;) {
147: String line = readLine();
148: if (line == null) {
149: return line;
150: }
151: // Skip over white space
152: int pos = Utility.skipWhitespace(line, 0);
153: // Ignore blank lines and comment lines
154: if (pos == line.length() || line.charAt(pos) == '#') {
155: continue;
156: }
157: // Process line
158: if (trim)
159: line = line.substring(pos);
160: return line;
161: }
162: }
163:
164: /**
165: * Read a line, ignoring blank lines and lines that start with
166: * '#'. Do not trim leading rule white space.
167: */
168: public String readLineSkippingComments() throws IOException {
169: return readLineSkippingComments(false);
170: }
171:
172: /**
173: * Return the one-based line number of the last line returned by
174: * readLine() or readLineSkippingComments(). Should only be called
175: * after a call to one of these methods; otherwise the return
176: * value is undefined.
177: */
178: public int getLineNumber() {
179: return lineNo;
180: }
181:
182: /**
183: * Return a string description of the position of the last line
184: * returned by readLine() or readLineSkippingComments().
185: */
186: public String describePosition() {
187: return resourceName + ':' + lineNo;
188: }
189:
190: /**
191: * Reset this reader so that the next call to
192: * <code>readLine()</code> returns the first line of the file
193: * again. This is a somewhat expensive call, however, calling
194: * <code>reset()</code> after calling it the first time does
195: * nothing if <code>readLine()</code> has not been called in
196: * between.
197: */
198: public void reset() {
199: try {
200: _reset();
201: } catch (UnsupportedEncodingException e) {
202: }
203: // We swallow this exception, if there is one. If the encoding is
204: // invalid, the constructor will have thrown this exception already and
205: // the caller shouldn't use the object afterwards.
206: }
207:
208: /**
209: * Reset to the start by reconstructing the stream and readers.
210: * We could also use mark() and reset() on the stream or reader,
211: * but that would cause them to keep the stream data around in
212: * memory. We don't want that because some of the resource files
213: * are large, e.g., 400k.
214: */
215: private void _reset() throws UnsupportedEncodingException {
216: if (lineNo == 0) {
217: return;
218: }
219: InputStream is = ICUData.getStream(root, resourceName);
220: if (is == null) {
221: throw new IllegalArgumentException("Can't open "
222: + resourceName);
223: }
224:
225: InputStreamReader isr = (encoding == null) ? new InputStreamReader(
226: is)
227: : new InputStreamReader(is, encoding);
228: reader = new BufferedReader(isr);
229: lineNo = 0;
230: }
231: }
|