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.jdo;
012:
013: import com.versant.core.util.CharBuf;
014:
015: import com.versant.core.common.BindingSupportImpl;
016:
017: /**
018: * This parses a comma separated list of query parameters into types and names.
019: * TODO Make this parse array types e.g. String[]
020: */
021: public class ParamDeclarationParser {
022:
023: /**
024: * Callback interface for parsing results.
025: */
026: public static interface Handler {
027:
028: /**
029: * The parser has sucessfully parsed a parameter type and name at
030: * position index in the parameter list (first param is 0).
031: */
032: public void parameterParsed(int index, String type, String name);
033: }
034:
035: private static final int STATE_START = 0;
036: private static final int STATE_LOOK_FOR_TYPE = 1;
037: private static final int STATE_IN_TYPE = 2;
038: private static final int STATE_LOOK_FOR_NAME = 3;
039: private static final int STATE_IN_NAME = 4;
040: private static final int STATE_LOOK_FOR_COMMA = 5;
041:
042: /**
043: * Parse params into types and names and return the number of parameters
044: * parsed.
045: */
046: public static int parse(String params, Handler handler) {
047: if (params == null)
048: return 0;
049: int len = params.length();
050: if (len == 0)
051: return 0;
052: int state = STATE_START;
053: String type = null;
054: int count = 0;
055: CharBuf buf = new CharBuf();
056: for (int pos = 0;; pos++) {
057: if (pos < len) {
058: char c = params.charAt(pos);
059: switch (state) {
060: case STATE_START:
061: case STATE_LOOK_FOR_TYPE:
062: if (isIdStart(c)) {
063: buf.append(c);
064: state = STATE_IN_TYPE;
065: } else if (!isWs(c)) {
066: error(pos, "parameter type", c);
067: }
068: break;
069: case STATE_IN_TYPE:
070: if (isId(c) || c == '.') {
071: buf.append(c);
072: } else if (isWs(c)) {
073: type = buf.toString();
074: buf.clear();
075: state = STATE_LOOK_FOR_NAME;
076: } else {
077: error(pos, "parameter type", c);
078: }
079: break;
080: case STATE_LOOK_FOR_NAME:
081: if (isIdStart(c)) {
082: buf.append(c);
083: state = STATE_IN_NAME;
084: } else if (!isWs(c)) {
085: error(pos, "parameter name", c);
086: }
087: break;
088: case STATE_IN_NAME:
089: if (isId(c)) {
090: buf.append(c);
091: } else if (c == ',' || isWs(c)) {
092: handler.parameterParsed(count++, type, buf
093: .toString());
094: buf.clear();
095: type = null;
096: if (c == ',')
097: state = STATE_LOOK_FOR_TYPE;
098: else
099: state = STATE_LOOK_FOR_COMMA;
100: } else {
101: error(pos, "parameter name", c);
102: }
103: break;
104: case STATE_LOOK_FOR_COMMA:
105: if (c == ',') {
106: state = STATE_LOOK_FOR_TYPE;
107: } else if (!isWs(c)) {
108: error(pos, "comma", c);
109: }
110: break;
111: }
112: } else {
113: switch (state) {
114: case STATE_LOOK_FOR_TYPE:
115: error(pos, "parameter type", (char) -1);
116: case STATE_IN_TYPE:
117: case STATE_LOOK_FOR_NAME:
118: error(pos, "parameter name", (char) -1);
119: case STATE_IN_NAME:
120: handler.parameterParsed(count++, type, buf
121: .toString());
122: }
123: break;
124: }
125: }
126: return count;
127: }
128:
129: private static boolean isWs(char c) {
130: return c == ' ' || c == '\n' || c == '\t';
131: }
132:
133: private static boolean isIdStart(char c) {
134: return c == '_' || (c >= 'A' && c <= 'Z')
135: || (c >= 'a' && c <= 'z');
136: }
137:
138: private static boolean isId(char c) {
139: return c == '_' || (c >= 'A' && c <= 'Z')
140: || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
141: }
142:
143: private static void error(int pos, String msg, char c) {
144: throw BindingSupportImpl.getInstance().runtime(
145: "Invalid parameter declaration: Expected " + msg
146: + ", got '" + c + "' at position " + pos);
147: }
148:
149: }
|