User: dsundstrom
Date: 01/08/06 17:18:31
Added: src/main/org/jboss/ejb/plugins/cmp/ejbql
ApproximateNumericLiteral.java
EJBQLNumberState.java EJBQLQuoteState.java
EJBQLWordState.java ExactNumericLiteral.java
StringLiteral.java SymbolState.java
SymbolToken.java Token.java Tokenizer.java
TokenizerState.java WhitespaceState.java
WordToken.java
Log:
Initial revision of a tokenizer designed for EJB-QL.
Revision Changes Path
1.1
jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/ApproximateNumericLiteral.java
Index: ApproximateNumericLiteral.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
public class ApproximateNumericLiteral implements Token {
private final double value;
public ApproximateNumericLiteral(double value) {
this.value = value;
}
public double getValue() {
return value;
}
public int hashCode() {
long l = Double.doubleToLongBits(value);
return 37 * 17 + (int)(l ^ (l >>> 32));
}
public boolean equals(Object o) {
if(o instanceof ApproximateNumericLiteral) {
return ((ApproximateNumericLiteral)o).value == value;
}
return false;
}
public String toString() {
return "" + value;
}
}
1.1
jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/EJBQLNumberState.java
Index: EJBQLNumberState.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
import java.io.PushbackReader;
import java.io.IOException;
import java.io.CharArrayWriter;
public class EJBQLNumberState implements TokenizerState {
private TokenizerState symbolState = new SymbolState();
public EJBQLNumberState() {
}
public Token nextToken(PushbackReader in, char c, Tokenizer tokenizer)
throws IOException {
if(!Character.isDigit(c) && c != '.') {
throw new IllegalArgumentException("EJBQLWordState must begin
with a digit or a '.': c="+c);
}
// do we just have a peroid
if(c == '.') {
int peek = peekChar(in);
if(!Character.isDigit((char)peek)) {
// deligate to the symbolState
return symbolState.nextToken(in, c, tokenizer);
}
}
//
// Now we are definately working on a number
//
// put the first characte back on, makes the code easier to write
in.unread(c);
// output buffer
CharArrayWriter out = new CharArrayWriter(16);
// whole number part
readWholeNumberPart(in, out);
// fractional part
readFractionalPart(in, out);
// exponent part
readExponentPart(in, out);
// exponent part
readSuffix(in, out);
String number = out.toString().toLowerCase();
System.out.println("number is ["+number+"]");
if(isExactNumeric(number)) {
return createExactNumericLiteral(number);
} else {
return createApproximateNumericLiteral(number);
}
}
private void readWholeNumberPart(PushbackReader in, CharArrayWriter out)
throws IOException {
int first = peekChar(in);
if(Character.isDigit((char)first)) {
// read the first digit off the stream and write it out
out.write(in.read());
// is it a hex number
int second = peekChar(in);
System.out.println("HEX? " + (char)first + (char)second);
if(first == '0' && (second == 'x' || second == 'X')) {
// read the x off the stream and write it out
out.write(in.read());
readNumber(in, out, 16);
} else {
// can't check for octal yet, because we don't
// know if this is a float number yet, but dec will
work for now
readNumber(in, out, 10);
}
}
}
private void readFractionalPart(PushbackReader in, CharArrayWriter out) throws
IOException {
int peek = peekChar(in);
if(peek == '.') {
// read the peroid off the stream and write it out
out.write(in.read());
// get all the decimal digits
readNumber(in, out, 10);
System.out.println("Read fractional part numebr is
["+out.toString()+"]");
}
}
private void readExponentPart(PushbackReader in, CharArrayWriter out) throws
IOException {
int peek = peekChar(in);
if(peek == 'e' || peek == 'E') {
// read the e off the stream and write it out
out.write(in.read());
// check for a sign in the exponent
peek = peekChar(in);
if(peek == '+' || peek == '-') {
// read the sign off the stream and write it out
out.write(in.read());
}
// read the integer
readNumber(in, out, 10);
}
}
private void readSuffix(PushbackReader in, CharArrayWriter out) throws
IOException {
int peek = peekChar(in);
if(peek == 'l' || peek == 'L' ||
peek == 'f' || peek == 'F' ||
peek == 'd' || peek == 'D') {
// read the suffix off the stream and write it out
out.write(in.read());
}
}
private void readNumber(PushbackReader in, CharArrayWriter out, int radix)
throws IOException {
// which the read character is a digit in the specified radix
int c = in.read();
while(Character.digit((char)c, radix) != -1) {
out.write(c);
c = in.read();
}
// unread that last character because it is not a digit
if(c != -1) {
in.unread(c);
}
}
private boolean isExactNumeric(String number) {
// is this a hexadecimal number
if(number.startsWith("0x") || number.startsWith("0X")){
return true;
}
// does it contain a peroid
if(number.indexOf('.')>=0) {
return false;
}
// does it contain an exponent
if(number.indexOf('e')>=0 || number.indexOf('E')>=0) {
return false;
}
// does it end with an f
if(number.endsWith("f") || number.endsWith("F")) {
return false;
}
// does it end with a d
if(number.endsWith("d") || number.endsWith("D")) {
return false;
}
return true;
}
/**
* This function is broken. It does not support bit field style
* integers and longs 0xffffffff
*/
private ExactNumericLiteral createExactNumericLiteral(String number) throws
IOException {
// long suffix
if(number.endsWith("l") || number.endsWith("L")) {
// chop off the suffix
number = number.substring(0, number.length()-1);
System.out.println("decode long number ["+number+"]");
return new
ExactNumericLiteral(Long.decode(number).longValue());
} else {
return new
ExactNumericLiteral(Integer.decode(number.toUpperCase()).intValue());
}
}
private ApproximateNumericLiteral createApproximateNumericLiteral(String
number) throws IOException {
// float suffix
if(number.endsWith("f") || number.endsWith("F")) {
// chop off the suffix
number = number.substring(0, number.length()-1);
return new ApproximateNumericLiteral(Float.parseFloat(number));
}
// ends with a d suffix, chop it off
if(number.endsWith("d") || number.endsWith("D")) {
number = number.substring(0, number.length()-1);
}
// regular double
return new ApproximateNumericLiteral(Double.parseDouble(number));
}
private int peekChar(PushbackReader in) throws IOException {
int nextChar = in.read();
if(nextChar != -1) {
in.unread(nextChar);
}
return nextChar;
}
}
1.1
jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/EJBQLQuoteState.java
Index: EJBQLQuoteState.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
import java.io.PushbackReader;
import java.io.IOException;
import java.io.CharArrayWriter;
public class EJBQLQuoteState implements TokenizerState {
private static final char SINGLE_QUOTE = '\'';
public EJBQLQuoteState() {
}
public Token nextToken(PushbackReader in, char character, Tokenizer tokenizer)
throws IOException {
if(character != SINGLE_QUOTE) {
throw new IllegalArgumentException("EJBQLQuoteState can only
tokenize single quoted strings");
}
if(in == null) {
throw new IllegalArgumentException("in is null");
}
CharArrayWriter out = new CharArrayWriter(16);
// the returned token is quoted
out.write(SINGLE_QUOTE);
for(int c = in.read(); c != -1; c = in.read()) {
// check for bad characters
if(c == '\n' || c == '\r') {
throw new IOException("Error: Line terminator in
string literal");
}
//
// Should we check for unicode encoded characters here?
//
out.write(c);
// check for end of string
if(c == SINGLE_QUOTE) {
// we have a possible end, check if the next character
is
// a single quote. If it is, we just have an escaped
single quote.
int nextChar = in.read();
if(nextChar != SINGLE_QUOTE) {
// unread next char if it is not an end of
stream
if(nextChar != -1) {
in.unread(nextChar);
}
return new StringLiteral(out.toString());
}
}
}
throw new IOException("Error: Unterminated string literal");
}
}
1.1
jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/EJBQLWordState.java
Index: EJBQLWordState.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
import java.io.PushbackReader;
import java.io.IOException;
import java.io.CharArrayWriter;
public class EJBQLWordState implements TokenizerState {
public EJBQLWordState() {
}
public Token nextToken(PushbackReader in, char character, Tokenizer tokenizer)
throws IOException {
if(!isEJBQLIdentifierStart(character)) {
throw new IllegalArgumentException("EJBQLWordState must begin
with a valid identified start: c="+character);
}
if(in == null) {
throw new IllegalArgumentException("in is null");
}
CharArrayWriter out = new CharArrayWriter(16);
out.write(character);
// read all chars in identifier
int c = in.read();
while(isEJBQLIdentifierPart((char)c)) {
out.write(c);
c = in.read();
}
// unread last char as it is not part of the identifier
if(c != -1) {
in.unread(c);
}
return new WordToken(out.toString());
}
private boolean isEJBQLIdentifierStart(char c) {
return Character.isJavaIdentifierStart(c) || c=='?';
}
private boolean isEJBQLIdentifierPart(char c) {
return Character.isJavaIdentifierPart(c) && c!='?';
}
}
1.1
jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/ExactNumericLiteral.java
Index: ExactNumericLiteral.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
public class ExactNumericLiteral implements Token {
private final long value;
public ExactNumericLiteral(long value) {
this.value = value;
}
public long getValue() {
return value;
}
public int hashCode() {
return 37 * 17 + (int)(value ^ (value >>> 32));
}
public boolean equals(Object o) {
if(o instanceof ExactNumericLiteral) {
return ((ExactNumericLiteral)o).value == value;
}
return false;
}
public String toString() {
return "" + value;
}
}
1.1
jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/StringLiteral.java
Index: StringLiteral.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
public final class StringLiteral implements Token {
private final String value;
public StringLiteral(String value) {
this.value = value;
}
public int hashCode() {
return 37 * 17 + value.hashCode();
}
public boolean equals(Object o) {
if(o instanceof StringLiteral) {
return ((StringLiteral)o).value.equals(value);
}
return false;
}
public String toString() {
return value;
}
}
1.1 jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/SymbolState.java
Index: SymbolState.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
import java.io.PushbackReader;
import java.io.IOException;
/**
* Note: this is not a robust implementation of a SymbolState and only works because
* the ejb-ql symbols are simple
*
*/
public class SymbolState implements TokenizerState {
public SymbolState() {
}
public Token nextToken(PushbackReader in, char character, Tokenizer tokenizer)
throws IOException {
if(character == '<') {
// check for <> or <=
int nextChar = in.read();
if(nextChar == '=') {
return new SymbolToken("<=");
} else if(nextChar == '>') {
return new SymbolToken("<>");
} else {
// nope, unread nextChar
if(nextChar != -1) {
in.unread(nextChar);
}
}
} else if(character == '>') {
// check for >=
int nextChar = in.read();
if(nextChar == '=') {
return new SymbolToken(">=");
} else {
// nope, unread nextChar
if(nextChar != -1) {
in.unread(nextChar);
}
}
}
return new SymbolToken(""+character);
}
}
1.1 jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/SymbolToken.java
Index: SymbolToken.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
public final class SymbolToken implements Token {
private final String value;
public SymbolToken(String value) {
this.value = value;
}
public int hashCode() {
return 37 * 17 + value.hashCode();
}
public boolean equals(Object o) {
if(o instanceof SymbolToken) {
return ((SymbolToken)o).value.equals(value);
}
return false;
}
public String toString() {
return value;
}
}
1.1 jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/Token.java
Index: Token.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
public interface Token {
}
1.1 jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/Tokenizer.java
Index: Tokenizer.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
import java.io.Reader;
import java.io.PushbackReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
public class Tokenizer implements Iterator {
private static final TokenizerState NUMBER_STATE = new EJBQLNumberState();
private static final TokenizerState QUOTE_STATE = new EJBQLQuoteState();
private static final TokenizerState SYMBOL_STATE = new SymbolState();
private static final TokenizerState WHITESPACE_STATE = new WhitespaceState();
private static final TokenizerState WORD_STATE = new EJBQLWordState();
private Map tokenizerStates = new HashMap(256);
private TokenizerState defaultState;
private PushbackReader reader;
private Token peekedToken;
public Tokenizer() {
initDefaultStates();
}
public TokenizerState getCharacterState(char character) {
TokenizerState s = (TokenizerState)tokenizerStates.get(new
Character(character));
if(s != null) {
return s;
}
return SYMBOL_STATE;
}
public void setCharacterState(char character, TokenizerState state) {
tokenizerStates.put(new Character(character), state);
}
public Reader getReader() {
return reader;
}
public void setReader(Reader reader){
this.reader = new PushbackReader(reader, 32);
}
public boolean hasNext() {
return peekToken() != null;
}
public Object next() {
return nextToken();
}
public Token nextToken() {
Token n = peekToken();
if(n == null) {
throw new NoSuchElementException();
}
peekedToken = null;
return n;
}
private Token peekToken() {
if(peekedToken == null) {
try{
int c = reader.read();
if(c == -1) {
return null;
}
TokenizerState state = getCharacterState((char)c);
peekedToken = state.nextToken(reader, (char)c, this);
} catch(IOException e) {
throw new RuntimeException(e.getMessage());
}
}
return peekedToken;
}
public void remove() {
throw new UnsupportedOperationException("Tokenier does not support
remove");
}
protected void initDefaultStates() {
for(int i=0; i<(int)Character.MAX_VALUE; i++) {
if(Character.isJavaIdentifierStart((char)i)) {
tokenizerStates.put(new Character((char)i),
WORD_STATE);
}
if(Character.isWhitespace((char)i)) {
tokenizerStates.put(new Character((char)i),
WHITESPACE_STATE);
}
if(Character.isDigit((char)i)) {
tokenizerStates.put(new Character((char)i),
NUMBER_STATE);
}
}
tokenizerStates.put(new Character('\''), QUOTE_STATE);
tokenizerStates.put(new Character('?'), WORD_STATE);
tokenizerStates.put(new Character('.'), NUMBER_STATE);
}
}
1.1
jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/TokenizerState.java
Index: TokenizerState.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
import java.io.PushbackReader;
import java.io.IOException;
public interface TokenizerState {
public Token nextToken(PushbackReader reader, char character, Tokenizer
tokenizer)
throws IOException;
}
1.1
jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/WhitespaceState.java
Index: WhitespaceState.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
import java.io.PushbackReader;
import java.io.IOException;
public class WhitespaceState implements TokenizerState {
public WhitespaceState() {
}
public Token nextToken(PushbackReader in, char character, Tokenizer tokenizer)
throws IOException {
for(int c = in.read(); c != -1; c = in.read()) {
// get the state for the next character
TokenizerState state = tokenizer.getCharacterState((char)c);
// if we are NOT the state for the next character, call the
// next state, else loop
if(state != this) {
return state.nextToken(in, (char)c, tokenizer);
}
}
// white space to the end of the stream
return null;
}
}
1.1 jboss/src/main/org/jboss/ejb/plugins/cmp/ejbql/WordToken.java
Index: WordToken.java
===================================================================
package org.jboss.ejb.plugins.cmp.ejbql;
public final class WordToken implements Token {
private final String value;
public WordToken(String value) {
this.value = value;
}
public int hashCode() {
return 37 * 17 + value.hashCode();
}
public boolean equals(Object o) {
if(o instanceof WordToken) {
return ((WordToken)o).value.equals(value);
}
return false;
}
public String toString() {
return value;
}
}
_______________________________________________
Jboss-development mailing list
[EMAIL PROTECTED]
http://lists.sourceforge.net/lists/listinfo/jboss-development