001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.util;
012:
013: import java.util.Map;
014:
015: /**
016: * This class is a simple lexical analyzer for a list of Strings, ints and
017: * doubles separated by a delim character. It is much faster than
018: * StringTokenizer for lists of numbers etc as it does not create a String
019: * for each token.
020: */
021: public final class StringListParser {
022:
023: public static final char DEFAULT_DELIM = ',';
024:
025: private String s;
026: private int slen;
027: private char delim;
028: private int pos;
029:
030: public StringListParser(char delim) {
031: this .delim = delim;
032: }
033:
034: public StringListParser(String s, char delim) {
035: this .delim = delim;
036: setString(s);
037: }
038:
039: public StringListParser(String s) {
040: this (s, DEFAULT_DELIM);
041: }
042:
043: public StringListParser() {
044: this (DEFAULT_DELIM);
045: }
046:
047: /**
048: * Set the String we are parsing.
049: */
050: public void setString(String s) {
051: this .s = s;
052: pos = 0;
053: slen = s.length();
054: }
055:
056: /**
057: * Get the String we are parsing.
058: */
059: public String getString() {
060: return s;
061: }
062:
063: /**
064: * Get the next String from the string.
065: *
066: * @throws IllegalStateException if there are no more or invalid format
067: */
068: public String nextString() throws IllegalStateException {
069: if (!hasNext()) {
070: throw new IllegalStateException("Expected String at end: "
071: + s);
072: }
073: int start = pos;
074: for (; pos < slen;) {
075: char c = s.charAt(pos++);
076: if (c == delim)
077: return s.substring(start, pos - 1);
078: }
079: return s.substring(start);
080: }
081:
082: /**
083: * Get the next double quoted String from the string.
084: *
085: * @throws IllegalStateException if there are no more or invalid format
086: */
087: public String nextQuotedString() throws IllegalStateException {
088: if (!hasNext()) {
089: throw new IllegalStateException(
090: "Expected quoted String at end: " + s);
091: }
092: if (s.charAt(pos) == '-') {
093: if (++pos < slen)
094: ++pos; // skip the delim
095: return null;
096: }
097: StringBuffer a = new StringBuffer();
098: ++pos; // skip the opening double quote
099: for (; pos < slen;) {
100: char c = s.charAt(pos++);
101: if (c == '"') {
102: if (pos >= slen)
103: break;
104: c = s.charAt(pos++);
105: if (c != '"')
106: break;
107: }
108: a.append(c);
109: }
110: return a.toString();
111: }
112:
113: /**
114: * Get the next Class from the string.
115: *
116: * @throws IllegalStateException if there are no more or invalid format
117: */
118: public Class nextClass(ClassLoader cl)
119: throws IllegalStateException, ClassNotFoundException {
120: if (!hasNext()) {
121: throw new IllegalStateException(
122: "Expected class name at end: " + s);
123: }
124: if (s.charAt(pos) == '-') {
125: if (++pos < slen)
126: ++pos; // skip the delim
127: return null;
128: }
129: int start = pos;
130: for (; pos < slen;) {
131: char c = s.charAt(pos++);
132: if (c == delim)
133: break;
134: }
135: String name = s.substring(start, pos - 1);
136: return BeanUtils.loadClass(name, true, cl);
137: }
138:
139: /**
140: * Get the next int from the string.
141: *
142: * @throws IllegalStateException if there are no more or invalid format
143: */
144: public int nextInt() throws IllegalStateException {
145: if (!hasNext()) {
146: throw new IllegalStateException("Expected int at end: " + s);
147: }
148: int ibuf = 0;
149: boolean neg = false;
150: if (pos < slen && s.charAt(pos) == '-') {
151: neg = true;
152: pos++;
153: }
154: for (; pos < slen;) {
155: char c = s.charAt(pos++);
156: if (c >= '0' && c <= '9') {
157: ibuf = ibuf * 10 + (c - '0');
158: //cat.debug("c = '" + c + "' ibuf " + ibuf);
159: } else if (c == delim) {
160: break;
161: } else {
162: throw new IllegalStateException("Expected int at pos "
163: + (pos - 1) + ": " + s);
164: }
165: }
166: return neg ? -ibuf : ibuf;
167: }
168:
169: /**
170: * Get the next boolean from the string.
171: *
172: * @throws IllegalStateException if there are no more or invalid format
173: */
174: public boolean nextBoolean() {
175: if (!hasNext()) {
176: throw new IllegalStateException("Expected boolean at end: "
177: + s);
178: }
179: char c = s.charAt(pos++);
180: if (pos < slen)
181: pos++; // skip the delim
182: if (c == 'Y') {
183: return true;
184: } else if (c == 'N')
185: return false;
186: throw new IllegalStateException("Invalid boolean character '"
187: + c + "' in " + s);
188: }
189:
190: /**
191: * Get the next double from the string.
192: *
193: * @throws IllegalStateException if there are no more or invalid format
194: */
195: public double nextDouble() throws IllegalStateException {
196: if (!hasNext()) {
197: throw new IllegalStateException("Expected double at end: "
198: + s);
199: }
200: double dbuf = 0.0;
201: boolean neg = false;
202: if (pos < slen && s.charAt(pos) == '-') {
203: neg = true;
204: pos++;
205: }
206: for (; pos < slen;) {
207: char c = s.charAt(pos++);
208: if (c >= '0' && c <= '9') {
209: dbuf = dbuf * 10.0 + (c - '0');
210: } else if (c == delim) {
211: return neg ? -dbuf : dbuf;
212: } else if (c == '.') {
213: break;
214: } else {
215: throw new IllegalStateException(
216: "Expected double at pos " + (pos - 1) + ": "
217: + s);
218: }
219: }
220: double m = 10.0;
221: double f = 0.0;
222: for (; pos < slen;) {
223: char c = s.charAt(pos++);
224: if (c >= '0' && c <= '9') {
225: f = f * 10.0 + (c - '0');
226: m = m * 10.0;
227: } else if (c == delim) {
228: break;
229: } else {
230: throw new IllegalStateException(
231: "Expected double at pos " + (pos - 1) + ": "
232: + s);
233: }
234: }
235: dbuf = dbuf + f / m;
236: return neg ? -dbuf : dbuf;
237: }
238:
239: /**
240: * Read the rest of the String as key,value pairs.
241: */
242: public void nextProperties(Map map) {
243: for (; hasNext();) {
244: String key = nextString();
245: String value = nextQuotedString();
246: map.put(key, value);
247: }
248: }
249:
250: /**
251: * Are there more Strings, ints or doubles?
252: */
253: public final boolean hasNext() {
254: return pos < slen;
255: }
256:
257: public void nextProperties(IntObjectHashMap map) {
258: for (; hasNext();) {
259: int key = nextInt();
260: String value = nextQuotedString();
261: map.put(key, value);
262: }
263: }
264: }
|