package com.sun.slp;
import java.util.*;
import java.io.*;
abstract class Parser extends Object {
final private static char NONASCII_LOWER = '\u0080';
final private static char NONASCII_UPPER = '\uffff';
final static char EQUAL = '=';
final static char LESS = '<';
final static char GREATER = '>';
private final static char STAR = '*';
final static char PRESENT = STAR;
private final static char OPAREN = '(';
private final static char CPAREN = ')';
private final static char APPROX = '~';
private final static char NOT = '!';
private final static char AND = '&';
private final static char OR = '|';
private final static char SPACE = ' ';
static final class ParserRecord extends Object {
Hashtable services = new Hashtable();
Hashtable signatures = new Hashtable();
}
interface QueryEvaluator {
boolean evaluate(AttributeString tag,
char op,
Object pattern,
boolean invert,
ParserRecord returns)
throws ServiceLocationException;
}
static void
parseAndEvaluateQuery(String query,
Parser.QueryEvaluator ev,
Locale locale,
ParserRecord ret)
throws ServiceLocationException {
StreamTokenizer tk = new StreamTokenizer(new StringReader(query));
tk.resetSyntax();
tk.wordChars('\177','\177');
tk.wordChars('\000', SPACE);
tk.ordinaryChar(NOT);
tk.wordChars('"', '%');
tk.ordinaryChar(AND);
tk.wordChars('\'', '\'');
tk.ordinaryChar(OPAREN);
tk.ordinaryChar(CPAREN);
tk.ordinaryChar(STAR);
tk.wordChars('+', '{');
tk.ordinaryChar(OR);
tk.wordChars('}', '~');
tk.ordinaryChar(EQUAL);
tk.ordinaryChar(LESS);
tk.ordinaryChar(GREATER);
tk.ordinaryChar(APPROX);
try {
ParserRecord rec = parseFilter(tk, ev, locale, false, true);
if (tk.nextToken() != StreamTokenizer.TT_EOF) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"par_char_closing",
new Object[] {query});
}
mergeQueryReturns(ret, rec, OR);
} catch (IOException ex) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"par_syn_err",
new Object[] {query});
}
}
private static boolean
mergeQueryReturns(ParserRecord target,
ParserRecord source,
char op) {
Hashtable targetServices = target.services;
Hashtable sourceServices = source.services;
boolean eval;
if (op == AND) {
eval = mergeTablesWithAnd(targetServices, sourceServices);
} else {
eval = mergeTablesWithOr(targetServices, sourceServices);
}
Hashtable targetSigs = target.signatures;
Hashtable sourceSigs = source.signatures;
if (op == AND) {
mergeTablesWithAnd(targetSigs, sourceSigs);
} else {
mergeTablesWithOr(targetSigs, sourceSigs);
}
return eval;
}
private static boolean mergeTablesWithAnd(Hashtable target,
Hashtable source) {
Enumeration en = target.keys();
while (en.hasMoreElements()) {
Object tkey = en.nextElement();
if (source.get(tkey) == null) {
target.remove(tkey);
}
}
if (target.size() <= 0) {
return false;
}
return true;
}
private static boolean mergeTablesWithOr(Hashtable target,
Hashtable source) {
Enumeration en = source.keys();
while (en.hasMoreElements()) {
Object skey = en.nextElement();
target.put(skey, source.get(skey));
}
return true;
}
private static ParserRecord
parseFilter(StreamTokenizer tk,
Parser.QueryEvaluator ev,
Locale locale,
boolean invert,
boolean eval)
throws ServiceLocationException, IOException {
ParserRecord ret = null;
int tok = tk.nextToken();
if (tok != OPAREN) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"par_init_par",
new Object[0]);
}
tok = tk.nextToken();
if (tok == AND || tok == OR) {
ret = parseFilterlist(tk, ev, locale, (char)tok, invert, eval);
} else if (tok == NOT) {
ret = parseFilter(tk, ev, locale, !invert, eval);
} else if (tok == StreamTokenizer.TT_WORD) {
tk.pushBack();
ret = parseItem(tk, ev, locale, invert, eval);
} else {
tk.pushBack();
ret = parseItem(tk, ev, locale, invert, eval);
}
tok = tk.nextToken();
if (tok != CPAREN) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"par_final_par",
new Object[0]);
}
return ret;
}
private static ParserRecord
parseFilterlist(StreamTokenizer tk,
Parser.QueryEvaluator ev,
Locale locale,
char op,
boolean invert,
boolean eval)
throws ServiceLocationException, IOException {
boolean match;
ParserRecord mrex = null;
do {
ParserRecord prex = null;
if (op == AND) {
prex = parseFilter(tk, ev, locale, invert, eval);
} else {
prex = parseFilter(tk, ev, locale, invert, eval);
}
if (mrex == null) {
mrex = prex;
} else {
eval = mergeQueryReturns(mrex, prex, op);
}
int tok = tk.nextToken();
tk.pushBack();
if (tok == CPAREN) {
return mrex;
}
} while (true);
}
private static ParserRecord
parseItem(StreamTokenizer tk,
Parser.QueryEvaluator ev,
Locale locale,
boolean invert,
boolean eval)
throws ServiceLocationException, IOException {
ParserRecord prex = new ParserRecord();
AttributeString attr = parseAttr(tk, locale);
char op = parseOp(tk);
Object value = null;
if (op == PRESENT) {
int tok = tk.nextToken();
tk.pushBack();
if ((char)tok != CPAREN) {
op = EQUAL;
value = parseValue(tk, locale);
value =
new AttributePattern(PRESENT + value.toString(), locale);
}
} else {
value = parseValue(tk, locale);
}
if (value instanceof AttributePattern &&
((AttributePattern)value).isWildcarded() &&
op != EQUAL) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"par_wild_op",
new Object[] {Character.valueOf(op)});
}
if ((value instanceof Boolean ||
value instanceof Opaque) &&
(op == GREATER || op == LESS)) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"par_bool_op",
new Object[] {Character.valueOf(op)});
}
if ((value == null || value.toString().length() <= 0) &&
op != PRESENT) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"par_key_op",
new Object[] {Character.valueOf(op)});
}
if (eval) {
if (!ev.evaluate(attr, op, value, invert, prex) &&
!(value instanceof AttributeString)) {
ev.evaluate(attr,
op,
new AttributeString(
value.toString().trim(),
locale),
invert,
prex);
}
}
return prex;
}
private static AttributeString parseAttr(StreamTokenizer tk, Locale locale)
throws ServiceLocationException, IOException {
String str = parsePotentialNonASCII(tk);
str =
ServiceLocationAttribute.unescapeAttributeString(str, true);
return new AttributeString(str, locale);
}
private static char parseOp(StreamTokenizer tk)
throws ServiceLocationException, IOException {
int tok = tk.nextToken();
switch (tok) {
case EQUAL:
tok = tk.nextToken();
if (tok == STAR) {
return PRESENT;
} else {
tk.pushBack();
return EQUAL;
}
case APPROX: case GREATER: case LESS:
if (tk.nextToken() != EQUAL) {
break;
}
if (tok == APPROX) {
tok = EQUAL;
}
return (char)tok;
default:
break;
}
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"par_comp_op",
new Object[0]);
}
private static Object parseValue(StreamTokenizer tk, Locale locale)
throws ServiceLocationException, IOException {
StringBuffer buf = new StringBuffer();
do {
int tok = tk.nextToken();
if (tok == CPAREN) {
tk.pushBack();
Object o =
ServiceLocationAttribute.evaluate(buf.toString().trim());
if (o instanceof String) {
o = new AttributePattern((String)o, locale);
} else if (o instanceof byte[]) {
o = new Opaque((byte[])o);
}
return o;
} else if (tok != StreamTokenizer.TT_EOF) {
if (tok == StreamTokenizer.TT_WORD) {
buf.append(tk.sval);
} else if (tok == StreamTokenizer.TT_NUMBER) {
Assert.slpassert(false,
"par_ntok",
new Object[0]);
} else {
buf.append((char)tok);
}
} else {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"par_qend",
new Object[0]);
}
} while (true);
}
private static String parsePotentialNonASCII(StreamTokenizer tk)
throws IOException {
StringBuffer buf = new StringBuffer();
do {
int tok = tk.nextToken();
if (tok == StreamTokenizer.TT_WORD) {
buf.append(tk.sval);
} else if (((char)tok >= NONASCII_LOWER) &&
((char)tok <= NONASCII_UPPER)) {
buf.append((char)tok);
} else {
tk.pushBack();
break;
}
} while (true);
return buf.toString();
}
}