Quadcap Embeddable Server

com/quadcap/util/text/Scanner.java

Go to the documentation of this file.
00001 package com.quadcap.util.text; 00002 00003 /* Copyright 1997 - 2003 Quadcap Software. All rights reserved. 00004 * 00005 * This software is distributed under the Quadcap Free Software License. 00006 * This software may be used or modified for any purpose, personal or 00007 * commercial. Open Source redistributions are permitted. Commercial 00008 * redistribution of larger works derived from, or works which bundle 00009 * this software requires a "Commercial Redistribution License"; see 00010 * http://www.quadcap.com/purchase. 00011 * 00012 * Redistributions qualify as "Open Source" under one of the following terms: 00013 * 00014 * Redistributions are made at no charge beyond the reasonable cost of 00015 * materials and delivery. 00016 * 00017 * Redistributions are accompanied by a copy of the Source Code or by an 00018 * irrevocable offer to provide a copy of the Source Code for up to three 00019 * years at the cost of materials and delivery. Such redistributions 00020 * must allow further use, modification, and redistribution of the Source 00021 * Code under substantially the same terms as this license. 00022 * 00023 * Redistributions of source code must retain the copyright notices as they 00024 * appear in each source code file, these license terms, and the 00025 * disclaimer/limitation of liability set forth as paragraph 6 below. 00026 * 00027 * Redistributions in binary form must reproduce this Copyright Notice, 00028 * these license terms, and the disclaimer/limitation of liability set 00029 * forth as paragraph 6 below, in the documentation and/or other materials 00030 * provided with the distribution. 00031 * 00032 * The Software is provided on an "AS IS" basis. No warranty is 00033 * provided that the Software is free of defects, or fit for a 00034 * particular purpose. 00035 * 00036 * Limitation of Liability. Quadcap Software shall not be liable 00037 * for any damages suffered by the Licensee or any third party resulting 00038 * from use of the Software. 00039 */ 00040 00041 import java.io.BufferedInputStream; 00042 import java.io.IOException; 00043 import java.io.InputStream; 00044 import java.io.OutputStream; 00045 00046 import com.quadcap.util.Debug; 00047 00048 //#ifdef DEBUG 00049 import java.io.ByteArrayOutputStream; 00050 import com.quadcap.io.LogInputStream; 00051 //#endif 00052 00053 /** 00054 * The class implements a series of low-level stream scanning operations, 00055 * using OctetMaps to implement 'while' and 'until'. 00056 * @author Stan Bailes 00057 */ 00058 public class Scanner { 00059 InputStream is; 00060 StringBuffer sb = new StringBuffer(); 00061 int pushback = -1; 00062 00063 //#ifdef DEBUG 00064 LogInputStream log = null; 00065 ByteArrayOutputStream bos = null; 00066 00067 public Scanner(InputStream is, boolean saveit) { 00068 if (saveit) { 00069 bos = new ByteArrayOutputStream(); 00070 this.is = new LogInputStream(is, bos, ""); 00071 } else { 00072 this.is = is; 00073 } 00074 } 00075 00076 public String getLog() { return bos.toString(); } 00077 //#endif 00078 00079 /** 00080 * Construct a new scanner, attached to the specified input stream. 00081 * 00082 * @param is the stream to scan. 00083 */ 00084 public Scanner(InputStream is) { 00085 this.is = is; 00086 } 00087 00088 public void reset(InputStream is) { 00089 this.is = is; 00090 //#ifdef DEBUG 00091 if (bos != null) { 00092 bos.reset(); 00093 this.is = new LogInputStream(is, bos, ""); 00094 } 00095 //#endif 00096 pushback = -1; 00097 } 00098 00099 final int read() throws IOException { 00100 int c = pushback; 00101 if (c >= 0) { 00102 pushback = -1; 00103 } else { 00104 c = is.read(); 00105 } 00106 return c; 00107 } 00108 00109 final void unread(int c) { 00110 pushback = c; 00111 } 00112 00113 /** 00114 * Read and discard characters until a character is found which 00115 * <b>is not</b> in the map. Then push back the terminating character. 00116 * 00117 * @param map the set of characters to skip. 00118 * @exception IOException if an I/O exception is thrown 00119 */ 00120 final public void skipWhile(OctetMap map) throws IOException { 00121 int c; 00122 while (map.has(c = read()) && c >= 0) continue; 00123 if (c >= 0) unread(c); 00124 } 00125 00126 /** 00127 * Read and discard characters until a character is found which 00128 * <b>is</b> in the map. Then push back the terminating character. 00129 * 00130 * @param map the set of characters which skip. 00131 * @exception IOException if an I/O exception is thrown 00132 */ 00133 final public void skipUntil(OctetMap map) throws IOException { 00134 int c; 00135 while (!map.has(c = read()) && c >= 0) continue; 00136 if (c >= 0) unread(c); 00137 } 00138 00139 /** 00140 * Return the next portion of the stream which consists of characters 00141 * <b>IN</b> the specified set. 00142 * 00143 * @param map the set of characters to parse. 00144 * @exception IOException if an I/O exception is thrown 00145 */ 00146 final public String parseWhile(OctetMap map) throws IOException { 00147 sb.setLength(0); 00148 int c; 00149 while (map.has(c = read()) && c >= 0) { 00150 sb.append((char)c); 00151 } 00152 if (c >= 0) unread(c); 00153 return sb.toString(); 00154 } 00155 00156 /** 00157 * Return the next portion of the stream which consists of characters 00158 * <b>NOT IN</b> the specified set. 00159 * 00160 * @param map the set of characters to parse. 00161 * @exception IOException if an I/O exception is thrown 00162 */ 00163 final public String parseUntil(OctetMap map) throws IOException { 00164 sb.setLength(0); 00165 int c; 00166 while (!map.has(c = read()) && c >= 0) { 00167 sb.append((char)c); 00168 } 00169 if (c >= 0) unread(c); 00170 return sb.toString(); 00171 } 00172 00173 00174 /** 00175 * Read the next character, and verify that it is equal to the expected 00176 * value. 00177 * 00178 * @param expected the expected character 00179 * @exception IOException if the character read from the stream is not 00180 * equal to the specified character. 00181 */ 00182 final public void matchChar(int expected) throws IOException { 00183 int c = read(); 00184 if (c != expected) { 00185 if (c < 0x1f || expected < 0x1f) { 00186 throw new IOException("Expected: " + expected + ", got " + c); 00187 } else { 00188 throw new IOException("Expected: " + expected + "(" + 00189 (char)expected + "), got " + c + "(" + 00190 (char)c + ")"); 00191 } 00192 } 00193 } 00194 00195 /** 00196 * Read the next string using the specified map, and verify that it 00197 * <b>IS</b> equal to the expected value. 00198 * 00199 * @param expected the expected string 00200 * @exception IOException if the character read from the stream is not 00201 * equal to the specified character. 00202 */ 00203 final public void matchString(OctetMap map, String expected) 00204 throws IOException 00205 { 00206 String actual = parseWhile(map); 00207 if (!actual.equals(expected)) { 00208 throw new IOException("Expected: " + expected + 00209 ", got: " + actual); 00210 } 00211 } 00212 00213 /** 00214 * Read the next string using the specified map, and verify that it 00215 * <b>IS</b> equal to the expected value, if all characters in both 00216 * strings are converted to monocase. 00217 * 00218 * @param expected the expected string 00219 * @exception IOException if the character read from the stream is not 00220 * equal to the specified character. 00221 */ 00222 final public void matchStringIgnoreCase(OctetMap map, 00223 String expected) 00224 throws IOException 00225 { 00226 String actual = parseWhile(map); 00227 if (!actual.equalsIgnoreCase(expected)) { 00228 throw new IOException("Expected: " + expected + 00229 ", got: '" + actual + "', next char = " + 00230 ((char)peek()) + " (" + peek() + ")"); 00231 } 00232 } 00233 00234 /** 00235 * Peek ahead one character in the stream by reading, then pushing back 00236 * the character. 00237 * 00238 * @return the next character from the stream. 00239 * @exception IOException if an exception is thrown when the character 00240 * is read. 00241 */ 00242 final public int peek() throws IOException { 00243 if (pushback > 0) { 00244 return pushback; 00245 } else { 00246 int c = read(); 00247 unread(c); 00248 return c; 00249 } 00250 } 00251 00252 /** 00253 * Copy bytes from an input stream to an output stream until a byte 00254 * not in the specified set is found. That byte is returned, or -1 00255 * the end of file is reached. 00256 * 00257 * @param is the input stream from which bytes are is.read 00258 * @param os the output stream to which bytes are written 00259 * @param map the set of valid byte to copy 00260 * @return the first non matching byte 00261 * @exception IOException may be thrown 00262 */ 00263 public static int copyWhile(InputStream is, OutputStream os, OctetMap map) 00264 throws IOException 00265 { 00266 int c = is.read(); 00267 while (c >= 0 && map.has(c)) { 00268 os.write(c); 00269 c = is.read(); 00270 } 00271 return c; 00272 } 00273 00274 /** 00275 * Copy bytes from an input stream to an output stream until a byte 00276 * in the specified set is found. That byte is returned, or -1 00277 * the end of file is reached. 00278 * 00279 * @param is the input stream from which bytes are is.read 00280 * @param os the output stream to which bytes are written 00281 * @param map the set of valid delimiter bytes 00282 * @return the first matching byte 00283 * @exception IOException may be thrown 00284 */ 00285 public static int copyUntil(InputStream is, OutputStream os, OctetMap map) 00286 throws IOException 00287 { 00288 int c = is.read(); 00289 while (c >= 0 && !map.has(c)) { 00290 os.write(c); 00291 c = is.read(); 00292 } 00293 return c; 00294 } 00295 00296 public static int copyUntil(InputStream is, OutputStream os, int dc) 00297 throws IOException 00298 { 00299 int c = is.read(); 00300 while (c >= 0 && c != dc) { 00301 os.write(c); 00302 c = is.read(); 00303 } 00304 return c; 00305 } 00306 00307 /** 00308 * Copy bytes from <code>in</code> to <code>out</code> until the 00309 * specified string is is.read from <code>in</code. The string bytes 00310 * are not written to <code>out</code> 00311 * 00312 * @param is the input stream from which bytes are is.read 00313 * @param os the output stream to which bytes are written 00314 * @param s the delimiter string 00315 * @return < 0 if end of file is reached before the string is found, 00316 * >= 0 otherwise 00317 * @exception IOException may be thrown 00318 */ 00319 public static int copyUntil(BufferedInputStream is, OutputStream os, 00320 String s) 00321 throws IOException 00322 { 00323 if (s.length() == 0) throw new IOException("empty target"); 00324 00325 int ret = -1; 00326 int dc = s.charAt(0) & 0xff; 00327 for (boolean found = false; !found; ) { 00328 if (copyUntil(is, os, dc) < 0) return -1; 00329 00330 is.mark(s.length()); 00331 found = true; 00332 for (int i = 1; found && i < s.length(); i++) { 00333 int c = 0; 00334 if ((c = is.read()) != s.charAt(i)) { 00335 for (int j = 0; j < i; j++) os.write(s.charAt(j)); 00336 00337 if (c < 0) return -1; 00338 00339 is.reset(); 00340 found = false; 00341 } 00342 } 00343 } 00344 return 1; 00345 } 00346 00347 /** 00348 * Read and discard characters until a character is found which 00349 * <b>is not</b> equal to the specified character. Return the 00350 * terminating character. 00351 * 00352 * @param dc the character to discard 00353 * @exception IOException if an I/O exception is thrown 00354 */ 00355 public static int skipWhile(InputStream is, int dc) throws IOException { 00356 int c; 00357 while (dc == (c = is.read())) continue; 00358 return c; 00359 } 00360 }