00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 package org.knopflerfish.framework;
00036
00037 import org.osgi.framework.InvalidSyntaxException;
00038 import java.util.Dictionary;
00039 import java.util.Vector;
00040 import java.util.ArrayList;
00041 import java.util.HashMap;
00042 import java.util.List;
00043 import java.util.Enumeration;
00044 import java.lang.reflect.Array;
00045 import java.lang.reflect.Constructor;
00046 import java.lang.reflect.Method;
00047
00048
00049 public class LDAPExpr {
00050 public static final int AND = 0;
00051 public static final int OR = 1;
00052 public static final int NOT = 2;
00053 public static final int EQ = 4;
00054 public static final int LE = 8;
00055 public static final int GE = 16;
00056 public static final int APPROX = 32;
00057 public static final int COMPLEX = AND | OR | NOT;
00058 public static final int SIMPLE = EQ | LE | GE | APPROX;
00059
00060 private static final char WILDCARD = 65535;
00061 private static final String WILDCARD_STRING =
00062 new String(new char [] { WILDCARD });
00063
00064 private static final String NULL = "Null query";
00065 private static final String GARBAGE = "Trailing garbage";
00066 private static final String EOS = "Unexpected end of query";
00067 private static final String MALFORMED = "Malformed query";
00068 private static final String EMPTY = "Empty list";
00069 private static final String SUBEXPR = "No subexpression";
00070 private static final String OPERATOR = "Undefined operator";
00071
00072 private static Class classBigDecimal;
00073 private static Constructor consBigDecimal;
00074 private static Method compBigDecimal;
00075
00076 private static Class classBigInteger;
00077 private static Constructor consBigInteger;
00078 private static Method compBigInteger;
00079
00080
00081 public int operator;
00082 public LDAPExpr[] args;
00083 public String attrName;
00084 public String attrValue;
00085
00086
00087 public LDAPExpr(String filter) throws InvalidSyntaxException {
00088
00089 ParseState ps = new ParseState(filter);
00090 LDAPExpr expr = null;
00091 try {
00092 expr = parseExpr(ps);
00093 } catch (StringIndexOutOfBoundsException e) {
00094 ps.error(EOS);
00095 }
00096 if (ps.rest().trim().length() != 0)
00097 ps.error(GARBAGE + " '" + ps.rest() + "'");
00098 operator = expr.operator;
00099 args = expr.args;
00100 attrName = expr.attrName;
00101 attrValue = expr.attrValue;
00102 }
00103
00104 private LDAPExpr(int operator, LDAPExpr[] args) {
00105 this.operator = operator;
00106 this.args = args;
00107 this.attrName = null;
00108 this.attrValue = null;
00109 }
00110
00111 private LDAPExpr(int operator, String attrName, String attrValue) {
00112 this.operator = operator;
00113 this.args = null;
00114 this.attrName = attrName;
00115 this.attrValue = attrValue;
00116 }
00117
00140 public boolean isSimple(List keywords, List[] cache) {
00141 if (operator == EQ) {
00142 int index;
00143 if ((index = keywords.indexOf(attrName)) >= 0 &&
00144 attrValue.indexOf(WILDCARD) < 0) {
00145 if (cache[index] == null) {
00146 cache[index] = new ArrayList();
00147 }
00148 cache[index].add(attrValue);
00149 return true;
00150 }
00151 } else if (operator == OR) {
00152 for (int i = 0; i < args.length; i++) {
00153 if (!args[i].isSimple(keywords, cache))
00154 return false;
00155 }
00156 return true;
00157 }
00158 return false;
00159 }
00160
00161
00162 public static boolean query(String filter, Dictionary pd)
00163 throws InvalidSyntaxException {
00164 return new LDAPExpr(filter).evaluate(pd, true);
00165 }
00166
00170 public boolean evaluate(Dictionary p, boolean matchCase) {
00171 if ((operator & SIMPLE) != 0) {
00172 return compare(p.get(attrName), operator, attrValue, matchCase);
00173 } else {
00174 switch (operator) {
00175 case AND:
00176 for (int i = 0; i < args.length; i++) {
00177 if (!args[i].evaluate(p, matchCase))
00178 return false;
00179 }
00180 return true;
00181 case OR:
00182 for (int i = 0; i < args.length; i++) {
00183 if (args[i].evaluate(p, matchCase))
00184 return true;
00185 }
00186 return false;
00187 case NOT:
00188 return !args[0].evaluate(p, matchCase);
00189 default:
00190 return false;
00191 }
00192 }
00193 }
00194
00195
00196
00197
00198
00199 protected boolean compare(Object obj, int op, String s, boolean matchCase) {
00200 if (obj == null)
00201 return false;
00202 if (op == EQ && s.equals(WILDCARD_STRING))
00203 return true;
00204 try {
00205 if (obj instanceof String) {
00206 return compareString((String)obj, op, s, matchCase);
00207 } else if (obj instanceof Character) {
00208 return compareString(obj.toString(), op, s, matchCase);
00209 } else if (obj instanceof Boolean) {
00210 if (op==LE || op==GE)
00211 return false;
00212 return ((Boolean)obj).equals(new Boolean(s));
00213 } else if (obj instanceof Number) {
00214 if (obj instanceof Byte) {
00215 switch(op) {
00216 case LE:
00217 return ((Byte)obj).byteValue() <= Byte.parseByte(s);
00218 case GE:
00219 return ((Byte)obj).byteValue() >= Byte.parseByte(s);
00220 default:
00221 return (new Byte(s)).equals(obj);
00222 }
00223 } else if (obj instanceof Integer) {
00224 switch(op) {
00225 case LE:
00226 return ((Integer)obj).intValue() <= Integer.parseInt(s);
00227 case GE:
00228 return ((Integer)obj).intValue() >= Integer.parseInt(s);
00229 default:
00230 return (new Integer(s)).equals(obj);
00231 }
00232 } else if (obj instanceof Short) {
00233 switch(op) {
00234 case LE:
00235 return ((Short)obj).shortValue() <= Short.parseShort(s);
00236 case GE:
00237 return ((Short)obj).shortValue() >= Short.parseShort(s);
00238 default:
00239 return (new Short(s)).equals(obj);
00240 }
00241 } else if (obj instanceof Long) {
00242 switch(op) {
00243 case LE:
00244 return ((Long)obj).longValue() <= Long.parseLong(s);
00245 case GE:
00246 return ((Long)obj).longValue() >= Long.parseLong(s);
00247 default:
00248 return (new Long(s)).equals(obj);
00249 }
00250 } else if (obj instanceof Float) {
00251 switch(op) {
00252 case LE:
00253 return ((Float)obj).floatValue() <= (new Float(s)).floatValue();
00254 case GE:
00255 return ((Float)obj).floatValue() >= (new Float(s)).floatValue();
00256 default:
00257 return (new Float(s)).equals(obj);
00258 }
00259 } else if (obj instanceof Double) {
00260 switch(op) {
00261 case LE:
00262 return ((Double)obj).doubleValue() <= (new Double(s)).doubleValue();
00263 case GE:
00264 return ((Double)obj).doubleValue() >= (new Double(s)).doubleValue();
00265 default:
00266 return (new Double(s)).equals(obj);
00267 }
00268 } else if (classBigInteger != null && classBigInteger.isInstance(obj)) {
00269 Object n = consBigInteger.newInstance(new Object [] { s });
00270 int c = ((Integer)compBigInteger.invoke(obj, new Object [] { n })).intValue();
00271
00272 switch(op) {
00273 case LE:
00274 return c <= 0;
00275 case GE:
00276 return c >= 0;
00277 default:
00278 return c == 0;
00279 }
00280 } else if (classBigDecimal != null && classBigDecimal.isInstance(obj)) {
00281 Object n = consBigDecimal.newInstance(new Object [] { s });
00282 int c = ((Integer)compBigDecimal.invoke(obj, new Object [] { n })).intValue();
00283
00284 switch(op) {
00285 case LE:
00286 return c <= 0;
00287 case GE:
00288 return c >= 0;
00289 default:
00290 return c == 0;
00291 }
00292 }
00293 } else if (obj instanceof Vector) {
00294 for (Enumeration e=((Vector)obj).elements(); e.hasMoreElements();)
00295 if (compare(e.nextElement(), op, s, matchCase))
00296 return true;
00297 } else if (obj.getClass().isArray()) {
00298 int len = Array.getLength(obj);
00299 for(int i=0; i<len; i++)
00300 if (compare(Array.get(obj, i), op, s, matchCase))
00301 return true;
00302 } else {
00303
00304
00305
00306
00307 Class clazz = obj.getClass();
00308 Constructor cons = getConstructor(clazz);
00309
00310 if(cons != null) {
00311 Object other = cons.newInstance(new Object [] { s } );
00312 if(obj instanceof Comparable) {
00313 int c = ((Comparable)obj).compareTo(other);
00314 switch(op) {
00315 case LE:
00316 return c <= 0;
00317 case GE:
00318 return c >= 0;
00319 default:
00320 return c == 0;
00321 }
00322 } else {
00323 boolean b = false;
00324 if(op == LE || op == GE ||op == EQ ||op == APPROX){
00325 b = obj.equals(other);
00326 }
00327 return b;
00328 }
00329 }
00330 }
00331 } catch (Exception e) {
00332 e.printStackTrace();
00333 }
00334 return false;
00335 }
00336
00337
00338 private static HashMap constructorMap = new HashMap();
00339
00343 private static Constructor getConstructor(Class clazz) {
00344 synchronized(constructorMap) {
00345
00346
00347 Constructor cons = (Constructor)constructorMap.get(clazz);
00348
00349
00350
00351 if(!constructorMap.containsKey(clazz)) {
00352 try {
00353 cons = clazz.getConstructor(new Class [] { String.class });
00354 } catch (Exception e) {
00355
00356 }
00357 constructorMap.put(clazz, cons);
00358 }
00359 return cons;
00360 }
00361 }
00362
00363 static {
00364 try {
00365 classBigDecimal = Class.forName("java.math.BigDecimal");
00366 consBigDecimal = getConstructor(classBigDecimal);
00367 compBigDecimal = classBigDecimal.getMethod("compareTo", new Class [] { classBigDecimal });
00368 } catch (Exception ignore) {
00369 classBigDecimal = null;
00370 }
00371 try {
00372 classBigInteger = Class.forName("java.math.BigInteger");
00373 consBigInteger = getConstructor(classBigInteger);
00374 compBigInteger = classBigInteger.getMethod("compareTo", new Class [] { classBigInteger });
00375 } catch (Exception ignore) {
00376 classBigInteger = null;
00377 }
00378 }
00379
00380
00381 private static boolean compareString(String s1, int op, String s2, boolean matchCase) {
00382 switch(op) {
00383 case LE:
00384 return s1.compareTo(s2) <= 0;
00385 case GE:
00386 return s1.compareTo(s2) >= 0;
00387 case EQ:
00388 return patSubstr(s1,s2, matchCase);
00389 case APPROX:
00390 return fixupString(s2).equals(fixupString(s1));
00391 default:
00392 return false;
00393 }
00394 }
00395
00396 private static String fixupString(String s) {
00397 StringBuffer sb = new StringBuffer();
00398 int len = s.length();
00399 boolean isStart = true;
00400 boolean isWhite = false;
00401 for(int i=0; i<len; i++) {
00402 char c = s.charAt(i);
00403 if (Character.isWhitespace(c)) {
00404 isWhite = true;
00405 } else {
00406 if (!isStart && isWhite)
00407 sb.append(' ');
00408 if (Character.isUpperCase(c))
00409 c = Character.toLowerCase(c);
00410 sb.append(c);
00411 isStart = false;
00412 isWhite = false;
00413 }
00414 }
00415 return sb.toString();
00416 }
00417
00418 private static boolean patSubstr(String s, String pat, boolean matchCase) {
00419 return s==null ? false : patSubstr(s.toCharArray(),0,pat.toCharArray(),0, matchCase);
00420 }
00421
00422 private static boolean patSubstr(char[] s, int si, char[] pat, int pi, boolean matchCase) {
00423 if (pat.length-pi == 0)
00424 return s.length-si == 0;
00425 if (pat[pi] == WILDCARD) {
00426 pi++;
00427 for (;;) {
00428 if (patSubstr( s, si, pat, pi, matchCase))
00429 return true;
00430 if (s.length-si == 0)
00431 return false;
00432 si++;
00433 }
00434 } else {
00435 if (s.length-si==0){
00436 return false;
00437 }
00438 if(matchCase){
00439 if(s[si]!=pat[pi]){
00440 return false;
00441 }
00442 }
00443 else{
00444 if(Character.toLowerCase(s[si]) != pat[pi] &&
00445 Character.toUpperCase(s[si]) != pat[pi]){
00446 return false;
00447 }
00448 }
00449 return patSubstr( s, ++si, pat, ++pi, matchCase);
00450 }
00451 }
00452
00453 private static LDAPExpr parseExpr(ParseState ps)
00454 throws InvalidSyntaxException {
00455 ps.skipWhite();
00456 if (!ps.prefix("("))
00457 ps.error(MALFORMED);
00458
00459 int operator;
00460 ps.skipWhite();
00461 switch(ps.peek()) {
00462 case '&': operator = AND; break;
00463 case '|': operator = OR; break;
00464 case '!': operator = NOT; break;
00465 default: return parseSimple(ps);
00466 }
00467 ps.skip(1);
00468 List v = new ArrayList();
00469 do {
00470 v.add(parseExpr(ps));
00471 ps.skipWhite();
00472 } while (ps.peek() == '(');
00473 int n = v.size();
00474 if (!ps.prefix(")") || n == 0 || (operator == NOT && n > 1))
00475 ps.error(MALFORMED);
00476 LDAPExpr[] args = new LDAPExpr[n];
00477 v.toArray(args);
00478 return new LDAPExpr(operator, args);
00479 }
00480
00481 private static LDAPExpr parseSimple(ParseState ps)
00482 throws InvalidSyntaxException {
00483 String attrName = ps.getAttributeName();
00484 int operator = 0;
00485 if (ps.prefix("="))
00486 operator = EQ;
00487 else if (ps.prefix("<="))
00488 operator = LE;
00489 else if(ps.prefix(">="))
00490 operator = GE;
00491 else if(ps.prefix("~="))
00492 operator = APPROX;
00493 else {
00494
00495 ps.error(OPERATOR);
00496 }
00497 String attrValue = ps.getAttributeValue();
00498 if (!ps.prefix(")"))
00499 ps.error(MALFORMED);
00500 return new LDAPExpr(operator, attrName, attrValue);
00501 }
00502
00503 public String toString() {
00504 StringBuffer res = new StringBuffer();
00505 res.append("(");
00506 if ((operator & SIMPLE) != 0) {
00507 res.append(attrName);
00508 switch (operator) {
00509 case EQ:
00510 res.append("=");
00511 break;
00512 case LE:
00513 res.append("<=");
00514 break;
00515 case GE:
00516 res.append(">=");
00517 break;
00518 case APPROX:
00519 res.append("~=");
00520 break;
00521 }
00522 for (int i = 0; i < attrValue.length(); i++) {
00523 char c = attrValue.charAt(i);
00524 if (c == '(' || c == ')' || c == '*' || c == '\\') {
00525 res.append('\\');
00526 } else if (c == WILDCARD) {
00527 c = '*';
00528 }
00529 res.append(c);
00530 }
00531 } else {
00532 switch (operator) {
00533 case AND:
00534 res.append("&");
00535 break;
00536 case OR:
00537 res.append("|");
00538 break;
00539 case NOT:
00540 res.append("!");
00541 break;
00542 }
00543 for (int i = 0; i < args.length; i++) {
00544 res.append(args[i].toString());
00545 }
00546 }
00547 res.append(")");
00548 return res.toString();
00549 }
00550
00554 private static class ParseState {
00555 int pos;
00556 String str;
00557
00558 public ParseState(String str) throws InvalidSyntaxException {
00559 this.str = str;
00560 if (str.length() == 0)
00561 error(NULL);
00562 pos = 0;
00563 }
00564
00565 public int getPos() {
00566 return pos;
00567 }
00568
00569 public boolean prefix(String pre) {
00570 if (!str.startsWith(pre, pos))
00571 return false;
00572 pos += pre.length();
00573 return true;
00574 }
00575
00576 public char peek() {
00577 return str.charAt(pos);
00578 }
00579
00580 public void skip(int n) {
00581 pos += n;
00582 }
00583
00584 public String rest() {
00585 return str.substring(pos);
00586 }
00587
00588 public void skipWhite() {
00589 while (Character.isWhitespace(str.charAt(pos))) {
00590 pos++;
00591 }
00592 }
00593
00594 public String getAttributeName() {
00595 int start = pos;
00596 for(;; pos++) {
00597 char c = str.charAt(pos);
00598 if (Character.isWhitespace(c) ||
00599 c == '(' || c == ')' ||
00600 c == '<' || c == '>' ||
00601 c == '=' || c == '~' ||
00602 c == '*' || c == '\\') {
00603 break;
00604 }
00605 }
00606 String res = str.substring(start, pos).toLowerCase();
00607 skipWhite();
00608 return res;
00609 }
00610
00611 public String getAttributeValue() {
00612 StringBuffer sb = new StringBuffer();
00613 label:
00614 for(;; pos++) {
00615 char c = str.charAt(pos);
00616 switch(c) {
00617 case '(':
00618 case ')':
00619 break label;
00620 case '*':
00621 sb.append(WILDCARD);
00622 break;
00623 case '\\':
00624 sb.append(str.charAt(++pos));
00625 break;
00626 default:
00627 sb.append(c);
00628 break;
00629 }
00630 }
00631 return sb.toString();
00632 }
00633
00634 public void error(String m) throws InvalidSyntaxException {
00635 throw new InvalidSyntaxException(m, (str == null) ? "" : str.substring(pos));
00636 }
00637 }
00638 }