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 */ ResourceReader(String resourceName, String encoding)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 */ ResourceReader(String resourceName)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 */ ResourceReader(Class<?> rootClass, String resourceName, String encoding)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 */ ResourceReader(InputStream is, String resourceName, String encoding)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 */ ResourceReader(InputStream is, String resourceName)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 */ ResourceReader(Class<?> rootClass, String resourceName)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 */ readLine()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 */ readLineSkippingComments(boolean trim)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 */ readLineSkippingComments()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 */ getLineNumber()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 */ describePosition()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 */ reset()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 */ _reset()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 */ close()256 public void close() throws IOException { 257 if (reader != null) { 258 reader.close(); 259 reader = null; 260 } 261 } 262 } 263