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