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