| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| /** |
| ******************************************************************************* |
| * Copyright (C) 2001-2015, International Business Machines Corporation and |
| * others. All Rights Reserved. |
| ******************************************************************************* |
| */ |
| |
| package com.ibm.icu.impl.data; |
| |
| import java.io.BufferedReader; |
| import java.io.Closeable; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.UnsupportedEncodingException; |
| |
| import com.ibm.icu.impl.ICUData; |
| import com.ibm.icu.impl.PatternProps; |
| |
| /** |
| * A reader for text resource data in the current package or the package |
| * of a given class object. The |
| * resource data is loaded through the class loader, so it will |
| * typically be a file in the same directory as the *.class files, or |
| * a file within a JAR file in the corresponding subdirectory. The |
| * file must be a text file in one of the supported encodings; when the |
| * resource is opened by constructing a <code>ResourceReader</code> |
| * object the encoding is specified. |
| * |
| * <p>2015-sep-03 TODO: Only used in com.ibm.icu.dev.test.format, move there. |
| * |
| * @author Alan Liu |
| */ |
| public class ResourceReader implements Closeable { |
| private BufferedReader reader = null; |
| private String resourceName; |
| private String encoding; // null for default encoding |
| private Class<?> root; |
| |
| /** |
| * The one-based line number. Has the special value -1 before the |
| * object is initialized. Has the special value 0 after initialization |
| * but before the first line is read. |
| */ |
| private int lineNo; |
| |
| /** |
| * Construct a reader object for the text file of the given name |
| * in this package, using the given encoding. |
| * @param resourceName the name of the text file located in this |
| * package's ".data" subpackage. |
| * @param encoding the encoding of the text file; if unsupported |
| * an exception is thrown |
| * @exception UnsupportedEncodingException if |
| * <code>encoding</code> is not supported by the JDK. |
| */ |
| public ResourceReader(String resourceName, String encoding) |
| throws UnsupportedEncodingException { |
| this(ICUData.class, "data/" + resourceName, encoding); |
| } |
| |
| /** |
| * Construct a reader object for the text file of the given name |
| * in this package, using the default encoding. |
| * @param resourceName the name of the text file located in this |
| * package's ".data" subpackage. |
| */ |
| public ResourceReader(String resourceName) { |
| this(ICUData.class, "data/" + resourceName); |
| } |
| |
| /** |
| * Construct a reader object for the text file of the given name |
| * in the given class's package, using the given encoding. |
| * @param resourceName the name of the text file located in the |
| * given class's package. |
| * @param encoding the encoding of the text file; if unsupported |
| * an exception is thrown |
| * @exception UnsupportedEncodingException if |
| * <code>encoding</code> is not supported by the JDK. |
| */ |
| public ResourceReader(Class<?> rootClass, String resourceName, String encoding) |
| throws UnsupportedEncodingException { |
| this.root = rootClass; |
| this.resourceName = resourceName; |
| this.encoding = encoding; |
| lineNo = -1; |
| _reset(); |
| } |
| |
| /** |
| * Construct a reader object for the input stream associated with |
| * the given resource name. |
| * @param is the input stream of the resource |
| * @param resourceName the name of the resource |
| */ |
| public ResourceReader(InputStream is, String resourceName, String encoding) { |
| this.root = null; |
| this.resourceName = resourceName; |
| this.encoding = encoding; |
| |
| this.lineNo = -1; |
| try { |
| InputStreamReader isr = (encoding == null) |
| ? new InputStreamReader(is) |
| : new InputStreamReader(is, encoding); |
| |
| this.reader = new BufferedReader(isr); |
| this.lineNo= 0; |
| } |
| catch (UnsupportedEncodingException e) { |
| } |
| } |
| |
| /** |
| * Construct a reader object for the input stream associated with |
| * the given resource name. |
| * @param is the input stream of the resource |
| * @param resourceName the name of the resource |
| */ |
| public ResourceReader(InputStream is, String resourceName) { |
| this(is, resourceName, null); |
| } |
| |
| /** |
| * Construct a reader object for the text file of the given name |
| * in the given class's package, using the default encoding. |
| * @param resourceName the name of the text file located in the |
| * given class's package. |
| */ |
| public ResourceReader(Class<?> rootClass, String resourceName) { |
| this.root = rootClass; |
| this.resourceName = resourceName; |
| this.encoding = null; |
| lineNo = -1; |
| try { |
| _reset(); |
| } catch (UnsupportedEncodingException e) {} |
| } |
| |
| /** |
| * Read and return the next line of the file or <code>null</code> |
| * if the end of the file has been reached. |
| */ |
| public String readLine() throws IOException { |
| if (lineNo == 0) { |
| // Remove BOMs |
| ++lineNo; |
| String line = reader.readLine(); |
| if (line != null && (line.charAt(0) == '\uFFEF' || |
| line.charAt(0) == '\uFEFF')) { |
| line = line.substring(1); |
| } |
| return line; |
| } |
| ++lineNo; |
| return reader.readLine(); |
| } |
| |
| /** |
| * Read a line, ignoring blank lines and lines that start with |
| * '#'. |
| * @param trim if true then trim leading Pattern_White_Space. |
| */ |
| public String readLineSkippingComments(boolean trim) throws IOException { |
| for (;;) { |
| String line = readLine(); |
| if (line == null) { |
| return line; |
| } |
| // Skip over white space |
| int pos = PatternProps.skipWhiteSpace(line, 0); |
| // Ignore blank lines and comment lines |
| if (pos == line.length() || line.charAt(pos) == '#') { |
| continue; |
| } |
| // Process line |
| if (trim) line = line.substring(pos); |
| return line; |
| } |
| } |
| |
| |
| /** |
| * Read a line, ignoring blank lines and lines that start with |
| * '#'. Do not trim leading Pattern_White_Space. |
| */ |
| public String readLineSkippingComments() throws IOException { |
| return readLineSkippingComments(false); |
| } |
| |
| /** |
| * Return the one-based line number of the last line returned by |
| * readLine() or readLineSkippingComments(). Should only be called |
| * after a call to one of these methods; otherwise the return |
| * value is undefined. |
| */ |
| public int getLineNumber() { |
| return lineNo; |
| } |
| |
| /** |
| * Return a string description of the position of the last line |
| * returned by readLine() or readLineSkippingComments(). |
| */ |
| public String describePosition() { |
| return resourceName + ':' + lineNo; |
| } |
| |
| /** |
| * Reset this reader so that the next call to |
| * <code>readLine()</code> returns the first line of the file |
| * again. This is a somewhat expensive call, however, calling |
| * <code>reset()</code> after calling it the first time does |
| * nothing if <code>readLine()</code> has not been called in |
| * between. |
| */ |
| public void reset() { |
| try { |
| _reset(); |
| } catch (UnsupportedEncodingException e) {} |
| // We swallow this exception, if there is one. If the encoding is |
| // invalid, the constructor will have thrown this exception already and |
| // the caller shouldn't use the object afterwards. |
| } |
| |
| /** |
| * Reset to the start by reconstructing the stream and readers. |
| * We could also use mark() and reset() on the stream or reader, |
| * but that would cause them to keep the stream data around in |
| * memory. We don't want that because some of the resource files |
| * are large, e.g., 400k. |
| */ |
| private void _reset() throws UnsupportedEncodingException { |
| try { |
| close(); |
| } catch (IOException e) {} |
| if (lineNo == 0) { |
| return; |
| } |
| InputStream is = ICUData.getStream(root, resourceName); |
| if (is == null) { |
| throw new IllegalArgumentException("Can't open " + resourceName); |
| } |
| |
| InputStreamReader isr = |
| (encoding == null) ? new InputStreamReader(is) : |
| new InputStreamReader(is, encoding); |
| reader = new BufferedReader(isr); |
| lineNo = 0; |
| } |
| |
| /** |
| * Closes the underlying reader and releases any system resources |
| * associated with it. If the stream is already closed then invoking |
| * this method has no effect. |
| */ |
| @Override |
| public void close() throws IOException { |
| if (reader != null) { |
| reader.close(); |
| reader = null; |
| } |
| } |
| } |