You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2008/09/01 17:08:10 UTC
svn commit: r690991 [14/20] - in /cxf/sandbox/dosgi: ./ discovery/
discovery/local/ discovery/local/src/ discovery/local/src/main/
discovery/local/src/main/java/ discovery/local/src/main/java/org/
discovery/local/src/main/java/org/apache/ discovery/loc...
Added: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Operator.java
URL: http://svn.apache.org/viewvc/cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Operator.java?rev=690991&view=auto
==============================================================================
--- cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Operator.java (added)
+++ cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Operator.java Mon Sep 1 08:08:01 2008
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util.ldap;
+
+import java.util.Stack;
+
+public abstract class Operator
+{
+ public abstract void execute(Stack operands, Mapper mapper)
+ throws EvaluationException;
+
+ public abstract String toString();
+
+ public abstract void buildTree(Stack operands); // re-build the parsetree
+ public abstract void toStringInfix(StringBuffer b); // convert to canonical string
+
+ // Place to store the reconstructed parsetree
+ // Vector -> ArrayList is using jdk1.2 or later
+ public Operator[] children = null;
+}
\ No newline at end of file
Propchange: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Operator.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Operator.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/ParseException.java
URL: http://svn.apache.org/viewvc/cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/ParseException.java?rev=690991&view=auto
==============================================================================
--- cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/ParseException.java (added)
+++ cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/ParseException.java Mon Sep 1 08:08:01 2008
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util.ldap;
+
+public class ParseException extends Exception {
+ public ParseException() {super();}
+ public ParseException(String msg) {super(msg);}
+}
Propchange: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/ParseException.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/ParseException.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Parser.java
URL: http://svn.apache.org/viewvc/cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Parser.java?rev=690991&view=auto
==============================================================================
--- cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Parser.java (added)
+++ cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Parser.java Mon Sep 1 08:08:01 2008
@@ -0,0 +1,1790 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util.ldap;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+public class Parser
+{
+ //
+ // Parser contants.
+ //
+
+ // End of file.
+ public static final int EOF = -1;
+
+ // Special characters in parse
+ public static final char LPAREN = '(';
+ public static final char RPAREN = ')';
+ public static final char STAR = '*';
+
+ // Define an enum for substring procedure
+ public static final int SIMPLE = 0;
+ public static final int PRESENT = 1;
+ public static final int SUBSTRING = 2;
+
+ // different from =|>|<|~
+ public static final int NOOP = 0;
+
+ // Comparison operators.
+ public static final int EQUAL = 0;
+ public static final int GREATER_EQUAL = 1;
+ public static final int LESS_EQUAL = 2;
+ public static final int APPROX = 3;
+
+ // Criteria in % to accept something as approximate.
+ public static final int APPROX_CRITERIA = 10;
+
+ // Flag indicating presense of BigDecimal.
+ private static boolean m_hasBigDecimal = false;
+
+ private static final Class[] STRING_CLASS = new Class[] { String.class };
+
+ static
+ {
+ try
+ {
+ Class.forName("java.math.BigDecimal");
+ m_hasBigDecimal = true;
+ }
+ catch (Exception ex)
+ {
+ // Ignore.
+ }
+ }
+ //
+ // Instance variables.
+ //
+
+ private LdapLexer lexer = null;
+ private List program;
+
+ public Parser()
+ {
+ reset();
+ }
+
+ public Parser(LdapLexer l)
+ {
+ reset(l);
+ }
+
+ public void reset()
+ {
+ lexer = null;
+ if (program == null)
+ {
+ program = new ArrayList();
+ }
+ program.clear();
+ }
+
+ public void reset(LdapLexer l)
+ {
+ reset();
+ lexer = l;
+ }
+
+ public Object[] getProgram()
+ {
+ return program.toArray(new Object[program.size()]);
+ }
+
+ // Define the recursive descent procedures
+
+ /*
+ <start>::= <filter> <EOF>
+ */
+ public boolean start() throws ParseException, IOException
+ {
+ boolean ok = filter();
+ if (!ok)
+ {
+ return ok;
+ }
+ int ch = lexer.get();
+ if (ch != EOF)
+ {
+ throw new ParseException(
+ "expected <EOF>; found '" + ((char) ch) + "'");
+ }
+ return ok;
+ }
+
+ /*
+ <filter> ::= '(' <filtercomp> ')'
+ */
+ boolean filter() throws ParseException, IOException
+ {
+ debug("filter");
+ if (lexer.peeknw() != LPAREN)
+ {
+ return false;
+ }
+ lexer.get();
+ if (!filtercomp())
+ {
+ throw new ParseException("expected filtercomp");
+ }
+ if (lexer.getnw() != RPAREN)
+ {
+ throw new ParseException("expected )");
+ }
+ return true;
+ }
+
+ /*
+ <filtercomp> ::= <and> | <or> | <not> | <item>
+ <and> ::= '&' <filterlist>
+ <or> ::= '|' <filterlist>
+ <not> ::= '!' <filter>
+ */
+ boolean filtercomp() throws ParseException, IOException
+ {
+ debug("filtercomp");
+ int c = lexer.peeknw();
+ switch (c)
+ {
+ case '&' :
+ case '|' :
+ lexer.get();
+ int cnt = filterlist();
+ if (cnt == 0)
+ {
+ return item((c == '&') ? "&" : "|");
+ }
+ // Code: [And|Or](cnt)
+ program.add(
+ c == '&'
+ ? (Operator) new AndOperator(cnt)
+ : (Operator) new OrOperator(cnt));
+ return true;
+ case '!' :
+ lexer.get();
+ if (!filter())
+ {
+ return item("!");
+ }
+ // Code: Not()
+ program.add(new NotOperator());
+ return true;
+ case '=':
+ case '>':
+ case '<':
+ case '~':
+ case '(':
+ case ')':
+ case EOF :
+ return false;
+ default :
+ return item("");
+ }
+ }
+
+ /*
+ <filterlist> ::= <filter> | <filter> <filterlist>
+ */
+ int filterlist() throws ParseException, IOException
+ {
+ debug("filterlist");
+ int cnt = 0;
+ if (filter())
+ {
+ do
+ {
+ cnt++;
+ }
+ while (filter());
+ }
+ return (cnt);
+ }
+
+ /*
+ <item> ::= <simple> | <present> | <substring>
+ <simple> ::= <attr> <filtertype> <value>
+ <filtertype> ::= <equal> | <approx> | <greater> | <less>
+ <present> ::= <attr> '=*'
+ <substring> ::= <attr> '=' <initial> <any> <final>
+ */
+ boolean item(String start) throws ParseException, IOException
+ {
+ debug("item");
+
+ StringBuffer attr = new StringBuffer(start);
+ if (!attribute(attr))
+ {
+ return false;
+ }
+ lexer.skipwhitespace(); // assume allowable before equal operator
+ // note: I treat the =* case as = followed by a special substring
+ int op = equalop();
+ if (op == NOOP)
+ {
+ String oplist = "=|~=|>=|<=";
+ throw new ParseException("expected " + oplist);
+ }
+ ArrayList pieces = new ArrayList();
+ int kind = substring(pieces);
+ // Get some of the illegal cases out of the way
+ if (op != '=' && kind != SIMPLE)
+ {
+ // We assume that only the = operator can work
+ // with right sides containing stars. If not correct
+ // then this code must change.
+ throw new ParseException("expected value|substring|*");
+ }
+
+ switch (kind)
+ {
+ case SIMPLE :
+ if ((op == '=') && "objectClass".equalsIgnoreCase(attr.toString()))
+ {
+ program.add(new ObjectClassOperator((String) pieces.get(0)));
+ return true;
+ }
+ // Code: Push(attr); Constant(pieces.get(0)); <operator>();
+ program.add(new PushOperator(attr.toString()));
+ program.add(new ConstOperator(pieces.get(0)));
+ switch (op)
+ {
+ case '<' :
+ program.add(new LessEqualOperator());
+ break;
+ case '>' :
+ program.add(new GreaterEqualOperator());
+ break;
+ case '~' :
+ program.add(new ApproxOperator());
+ break;
+ case '=' :
+ default :
+ program.add(new EqualOperator());
+ }
+ break;
+ case PRESENT :
+ // Code: Present(attr);
+ program.add(new PresentOperator(attr.toString()));
+ break;
+ case SUBSTRING :
+ generateSubStringCode(attr.toString(), pieces);
+ break;
+ default :
+ throw new ParseException("expected value|substring|*");
+ }
+ return true;
+ }
+
+ // Generating code for substring right side is mildly
+ // complicated.
+
+ void generateSubStringCode(String attr, ArrayList pieces)
+ {
+ // Code: Push(attr)
+ program.add(new PushOperator(attr.toString()));
+
+ // Convert the pieces arraylist to a String[]
+ String[] list =
+ (String[]) pieces.toArray(new String[pieces.size()]);
+
+ // Code: SubString(list)
+ program.add(new SubStringOperator(list));
+ }
+
+ /*
+ <attr> is a string representing an attributte,
+ or key, in the properties
+ objects of the registered services. Attribute names are not case
+ sensitive; that is cn and CN both refer to the same attribute.
+ Attribute names may have embedded spaces, but not leading or
+ trailing spaces.
+ */
+ boolean attribute(StringBuffer buf) throws ParseException, IOException
+ {
+ debug("attribute");
+ lexer.skipwhitespace();
+ int c = lexer.peek();
+ // need to make sure there
+ // is at least one KEYCHAR
+ switch (c)
+ {
+ case '=':
+ case '>':
+ case '<':
+ case '~':
+ case '(':
+ case ')':
+ case EOF:
+ return false;
+ default:
+ break;
+ }
+
+ boolean parsing = true;
+ while (parsing)
+ {
+ buf.append((char) lexer.get());
+ c = lexer.peek();
+ switch (c)
+ {
+ case '=':
+ case '>':
+ case '<':
+ case '~':
+ case '(':
+ case ')':
+ case EOF:
+ parsing = false;
+ default:
+ break;
+ }
+ }
+
+ // The above may have accumulated trailing blanks that must be removed
+ int i = buf.length() - 1;
+ while (i > 0 && Character.isWhitespace(buf.charAt(i)))
+ {
+ i--;
+ }
+ buf.setLength(i + 1);
+ return true;
+ }
+
+ /*
+ <equal> ::= '='
+ <approx> ::= '~='
+ <greater> ::= '>='
+ <less> ::= '<='
+ <present> ::= <attr> '=*'
+ */
+ int equalop() throws ParseException, IOException
+ {
+ debug("equalop");
+ lexer.skipwhitespace();
+ int op = lexer.peek();
+ switch (op)
+ {
+ case '=' :
+ lexer.get();
+ break;
+ case '~' :
+ case '<' :
+ case '>' :
+ // skip main operator char
+ int c = lexer.get();
+ // make sure that the next char is '='
+ c = lexer.get();
+ if (c != '=')
+ {
+ throw new ParseException("expected ~=|>=|<=");
+ }
+ break;
+ default :
+ op = NOOP;
+ }
+ return op;
+ }
+
+ /*
+ <substring> ::= <attr> '=' <initial> <any> <final>
+ <initial> ::= NULL | <value>
+ <any> ::= '*' <starval>
+ <starval> ::= NULL | <value> '*' <starval>
+ <final> ::= NULL | <value>
+ <value> ::= ...
+ */
+ /*
+ This procedure handles all cases on right side of an item
+ */
+ int substring(ArrayList pieces) throws ParseException, IOException
+ {
+ debug("substring");
+
+ pieces.clear();
+ StringBuffer ss = new StringBuffer();
+ // int kind = SIMPLE; // assume until proven otherwise
+ boolean wasStar = false; // indicates last piece was a star
+ boolean leftstar = false; // track if the initial piece is a star
+ boolean rightstar = false; // track if the final piece is a star
+
+ // We assume (sub)strings can contain leading and trailing blanks
+loop: for (;;)
+ {
+ int c = lexer.peek();
+ switch (c)
+ {
+ case RPAREN :
+ if (wasStar)
+ {
+ // insert last piece as "" to handle trailing star
+ rightstar = true;
+ }
+ else
+ {
+ pieces.add(ss.toString());
+ // accumulate the last piece
+ // note that in the case of
+ // (cn=); this might be
+ // the string "" (!=null)
+ }
+ ss.setLength(0);
+ break loop;
+ case '\\' :
+ wasStar = false;
+ lexer.get();
+ c = lexer.get();
+ if (c == EOF)
+ {
+ throw new ParseException("unexpected EOF");
+ }
+ ss.append((char) c);
+ break;
+ case LPAREN:
+ case EOF :
+ if (pieces.size() > 0)
+ {
+ throw new ParseException("expected ')'");
+ }
+ else
+ {
+ throw new ParseException("expected value|substring");
+ }
+ case '*' :
+ if (wasStar)
+ {
+ // encountered two successive stars;
+ // I assume this is illegal
+ throw new ParseException("unexpected '**'");
+ }
+ lexer.get();
+ if (ss.length() > 0)
+ {
+ pieces.add(ss.toString()); // accumulate the pieces
+ // between '*' occurrences
+ }
+ ss.setLength(0);
+ // if this is a leading star, then track it
+ if (pieces.size() == 0)
+ {
+ leftstar = true;
+ }
+ ss.setLength(0);
+ wasStar = true;
+ break;
+ default :
+ wasStar = false;
+ ss.append((char) lexer.get());
+ }
+ }
+ if (pieces.size() == 0)
+ {
+ return PRESENT;
+ }
+ if (leftstar || rightstar || pieces.size() > 1)
+ {
+ // insert leading and/or trailing "" to anchor ends
+ if (rightstar)
+ {
+ pieces.add("");
+ }
+ if (leftstar)
+ {
+ pieces.add(0, "");
+ }
+ return SUBSTRING;
+ }
+ // assert !leftstar && !rightstar && pieces.size == 1
+ return SIMPLE;
+ }
+
+ // Debug stuff
+
+ static boolean debug = false;
+
+ PrintStream dbgout = null;
+
+ public void setDebug(PrintStream out)
+ {
+ debug = true;
+ dbgout = out;
+ }
+
+ void debug(String proc)
+ {
+ if (!debug || dbgout == null)
+ {
+ return;
+ }
+ dbgout.println("parsing " + proc + ":" + lexer.charno());
+ dbgout.flush();
+ }
+
+ // Exclusive inner classes
+ private static final class AndOperator extends Operator
+ {
+ private int operandCount;
+
+ public AndOperator(int opcnt)
+ {
+ operandCount = opcnt;
+ }
+
+ public void execute(Stack operands, Mapper mapper)
+ throws EvaluationException
+ {
+ // Determine result using short-circuit evaluation.
+ boolean result = true;
+ for (int i = 0; i < operandCount; i++)
+ {
+ if (operands.empty())
+ {
+ fewOperands("AND");
+ }
+
+ // For short-circuited evaluation, once the AND
+ // becomes false, we can ignore the remaining
+ // expressions, but we must still pop them off.
+ if (!result)
+ {
+ operands.pop();
+ }
+ else
+ {
+ result = ((Boolean) operands.pop()).booleanValue();
+ }
+ }
+ operands.push((result ? Boolean.TRUE : Boolean.FALSE));
+ }
+
+ public String toString()
+ {
+ return "&(" + operandCount + ")";
+ }
+
+ public void buildTree(Stack operands)
+ {
+ children = new Operator[operandCount];
+ // need to preserve stack order
+ for (int i = 0; i < operandCount; i++)
+ {
+ children[(operandCount - 1) - i] =
+ (Operator) operands.pop();
+ }
+ operands.push(this);
+ }
+
+ public void toStringInfix(StringBuffer b)
+ {
+ b.append("(&");
+ for (int i = 0; i < children.length; i++)
+ {
+ Operator o = (Operator) children[i];
+ o.toStringInfix(b);
+ }
+ b.append(")");
+ }
+ }
+
+ private static final class OrOperator extends Operator
+ {
+ private int operandCount;
+
+ public OrOperator(int opcnt)
+ {
+ operandCount = opcnt;
+ }
+
+ public void execute(Stack operands, Mapper mapper)
+ throws EvaluationException
+ {
+ // Determine result using short-circuit evaluation.
+ boolean result = false;
+ for (int i = 0; i < operandCount; i++)
+ {
+ if (operands.empty())
+ {
+ fewOperands("OR");
+ }
+
+ // For short-circuited evaluation, once the OR
+ // becomes true, we can ignore the remaining
+ // expressions, but we must still pop them off.
+ if (result)
+ {
+ operands.pop();
+ }
+ else
+ {
+ result = ((Boolean) operands.pop()).booleanValue();
+ }
+ }
+ operands.push((result ? Boolean.TRUE : Boolean.FALSE));
+ }
+
+ public String toString()
+ {
+ return "|(" + operandCount + ")";
+ }
+
+ public void buildTree(Stack operands)
+ {
+ children = new Operator[operandCount];
+ // need to preserve stack order
+ for (int i = 0; i < operandCount; i++)
+ {
+ children[(operandCount - 1) - i] =
+ (Operator) operands.pop();
+ }
+ operands.push(this);
+ }
+
+ public void toStringInfix(StringBuffer b)
+ {
+ b.append("(|");
+ for (int i = 0; i < children.length; i++)
+ {
+ Operator o = (Operator) children[i];
+ o.toStringInfix(b);
+ }
+ b.append(")");
+ }
+ }
+
+ private static final class NotOperator extends Operator
+ {
+ public NotOperator()
+ {
+ }
+
+ public void execute(Stack operands, Mapper mapper)
+ throws EvaluationException
+ {
+ if (operands.empty())
+ {
+ fewOperands("NOT");
+ }
+ boolean result = !((Boolean) operands.pop()).booleanValue();
+ operands.push((result ? Boolean.TRUE : Boolean.FALSE));
+ }
+
+ public String toString()
+ {
+ return "!()";
+ }
+
+ public void buildTree(Stack operands)
+ {
+ children = new Operator[1];
+ children[0] = (Operator) operands.pop();
+ operands.push(this);
+ }
+
+ public void toStringInfix(StringBuffer b)
+ {
+ b.append("(!");
+ for (int i = 0; i < children.length; i++)
+ {
+ Operator o = (Operator) children[i];
+ o.toStringInfix(b);
+ }
+ b.append(")");
+ }
+ }
+
+ private static final class ObjectClassOperator extends Operator
+ {
+ public final String m_target;
+
+ public ObjectClassOperator(String target)
+ {
+ m_target = target;
+ }
+
+ public void buildTree(Stack operands)
+ {
+ operands.push(this);
+ }
+
+ public void execute(Stack operands, Mapper mapper)
+ throws EvaluationException
+ {
+ String[] objectClass = (String[]) mapper.lookup("objectClass");
+ if (objectClass != null)
+ {
+ for (int i = 0; i < objectClass.length; i++)
+ {
+ if (m_target.equals(objectClass[i]))
+ {
+ operands.push(Boolean.TRUE);
+ return;
+ }
+ }
+ }
+ operands.push(Boolean.FALSE);
+ }
+
+ public String toString()
+ {
+ return "=()";
+ }
+
+ public void toStringInfix(StringBuffer b)
+ {
+ b.append('(').append("objectClass=").append(m_target).append(')');
+ }
+ }
+
+ private static final class EqualOperator extends Operator
+ {
+ public EqualOperator()
+ {
+ }
+
+ public void execute(Stack operands, Mapper mapper)
+ throws EvaluationException
+ {
+ if (operands.empty())
+ {
+ fewOperands("=");
+ }
+
+ // We cheat and use the knowledge that top (right) operand
+ // will always be a string because of the way code was generated
+ String rhs = (String) operands.pop();
+ if (operands.empty())
+ {
+ fewOperands("=");
+ }
+
+ Object lhs = operands.pop();
+
+ operands.push((compare(lhs, rhs, EQUAL) ? Boolean.TRUE : Boolean.FALSE));
+ }
+
+ public String toString()
+ {
+ return "=()";
+ }
+
+ public void buildTree(Stack operands)
+ {
+ children = new Operator[2];
+ // need to preserve stack order
+ for (int i = 0; i < 2; i++)
+ {
+ Operator o = (Operator) operands.pop();
+ children[1 - i] = o;
+ }
+ operands.push(this);
+ }
+
+ public void toStringInfix(StringBuffer b)
+ {
+ b.append("(");
+ for (int i = 0; i < children.length; i++)
+ {
+ Operator o = (Operator) children[i];
+ if (i > 0)
+ {
+ b.append("=");
+ }
+ o.toStringInfix(b);
+ }
+ b.append(")");
+ }
+ }
+
+ private static final class GreaterEqualOperator extends Operator
+ {
+ public GreaterEqualOperator()
+ {
+ }
+
+ public void execute(Stack operands, Mapper mapper)
+ throws EvaluationException
+ {
+ if (operands.empty())
+ {
+ fewOperands(">=");
+ }
+ // We cheat and use the knowledge that top (right) operand
+ // will always be a string because of the way code was generated
+ String rhs = (String) operands.pop();
+ if (operands.empty())
+ {
+ fewOperands(">=");
+ }
+ Object lhs = operands.pop();
+
+ operands.push((compare(lhs, rhs, GREATER_EQUAL) ? Boolean.TRUE : Boolean.FALSE));
+ }
+
+ public String toString()
+ {
+ return ">=()";
+ }
+
+ public void buildTree(Stack operands)
+ {
+ children = new Operator[2];
+ // need to preserve stack order
+ for (int i = 0; i < 2; i++)
+ {
+ children[1 - i] = (Operator) operands.pop();
+ }
+ operands.push(this);
+ }
+
+ public void toStringInfix(StringBuffer b)
+ {
+ b.append("(");
+ for (int i = 0; i < children.length; i++)
+ {
+ Operator o = (Operator) children[i];
+ if (i > 0)
+ {
+ b.append(">=");
+ }
+ o.toStringInfix(b);
+ }
+ b.append(")");
+ }
+ }
+
+ private static final class LessEqualOperator extends Operator
+ {
+ public LessEqualOperator()
+ {
+ }
+
+ public void execute(Stack operands, Mapper mapper)
+ throws EvaluationException
+ {
+ if (operands.empty())
+ {
+ fewOperands("<=");
+ }
+ // We cheat and use the knowledge that top (right) operand
+ // will always be a string because of the way code was generated
+ String rhs = (String) operands.pop();
+ if (operands.empty())
+ {
+ fewOperands("<=");
+ }
+ Object lhs = (Object) operands.pop();
+ operands.push((compare(lhs, rhs, LESS_EQUAL) ? Boolean.TRUE : Boolean.FALSE));
+ }
+
+ public String toString()
+ {
+ return "<=()";
+ }
+
+ public void buildTree(Stack operands)
+ {
+ children = new Operator[2];
+ // need to preserve stack order
+ for (int i = 0; i < 2; i++)
+ {
+ children[1 - i] = (Operator) operands.pop();
+ }
+ operands.push(this);
+ }
+
+ public void toStringInfix(StringBuffer b)
+ {
+ b.append("(");
+ for (int i = 0; i < children.length; i++)
+ {
+ Operator o = (Operator) children[i];
+ if (i > 0)
+ {
+ b.append("<=");
+ }
+ o.toStringInfix(b);
+ }
+ b.append(")");
+ }
+ }
+
+ private static final class ApproxOperator extends Operator
+ {
+ public ApproxOperator()
+ {
+ }
+
+ public void execute(Stack operands, Mapper mapper)
+ throws EvaluationException
+ {
+ if (operands.empty())
+ {
+ fewOperands("~=");
+ }
+ // We cheat and use the knowledge that top (right) operand
+ // will always be a string because of the way code was generated
+ String rhs = (String) operands.pop();
+ if (operands.empty())
+ {
+ fewOperands("~=");
+ }
+ Object lhs = operands.pop();
+ operands.push((compare(lhs, rhs, APPROX) ? Boolean.TRUE : Boolean.FALSE));
+ }
+
+ public String toString()
+ {
+ return "~=()";
+ }
+
+ public void buildTree(Stack operands)
+ {
+ children = new Operator[2];
+ // need to preserve stack order
+ for (int i = 0; i < 2; i++)
+ {
+ children[1 - i] = (Operator) operands.pop();
+ }
+ operands.push(this);
+ }
+
+ public void toStringInfix(StringBuffer b)
+ {
+ b.append("(");
+ for (int i = 0; i < children.length; i++)
+ {
+ Operator o = (Operator) children[i];
+ if (i > 0)
+ {
+ b.append("~=");
+ }
+ o.toStringInfix(b);
+ }
+ b.append(")");
+ }
+ }
+
+ private static final class PresentOperator extends Operator
+ {
+ String attribute;
+
+ public PresentOperator(String attribute)
+ {
+ this.attribute = attribute;
+ }
+
+ public void execute(Stack operands, Mapper mapper)
+ throws EvaluationException
+ {
+ Object value = mapper.lookup(attribute);
+ operands.push((value != null) ? Boolean.TRUE : Boolean.FALSE);
+ }
+
+ public String toString()
+ {
+ return attribute + "=*";
+ }
+
+ public void buildTree(Stack operands)
+ {
+ operands.push(this);
+ }
+
+ public void toStringInfix(StringBuffer b)
+ {
+ b.append("(");
+ b.append(attribute + "=*");
+ b.append(")");
+ }
+ }
+
+ private static final class PushOperator extends Operator
+ {
+ String attribute;
+
+ public PushOperator(String attribute)
+ {
+ this.attribute = attribute;
+ }
+
+ public void execute(Stack operands, Mapper mapper)
+ throws EvaluationException
+ {
+ // find and push the value of a given attribute
+ Object value = mapper.lookup(attribute);
+ if (value == null)
+ {
+ throw new AttributeNotFoundException(
+ "attribute " + attribute + " not found");
+ }
+ operands.push(value);
+ }
+
+ public String toString()
+ {
+ return "push(" + attribute + ")";
+ }
+
+ public String toStringInfix()
+ {
+ return attribute;
+ }
+
+ public void buildTree(Stack operands)
+ {
+ operands.push(this);
+ }
+
+ public void toStringInfix(StringBuffer b)
+ {
+ b.append(attribute);
+ }
+ }
+
+ private static final class ConstOperator extends Operator
+ {
+ Object val;
+
+ public ConstOperator(Object val)
+ {
+ this.val = val;
+ }
+
+ public void execute(Stack operands, Mapper mapper)
+ throws EvaluationException
+ {
+ operands.push(val);
+ }
+
+ public String toString()
+ {
+ return "const(" + val + ")";
+ }
+
+ public String toStringInfix()
+ {
+ return val.toString();
+ }
+
+ public void buildTree(Stack operands)
+ {
+ operands.push(this);
+ }
+
+ public void toStringInfix(StringBuffer b)
+ {
+ appendEscaped(b, val.toString());
+ }
+ }
+
+ private static final class SubStringOperator extends Operator
+ implements OperatorConstants
+ {
+ String[] pieces;
+
+ public SubStringOperator(String[] pieces)
+ {
+ this.pieces = pieces;
+ }
+
+ public void execute(Stack operands, Mapper mapper)
+ throws EvaluationException
+ {
+ if (operands.empty())
+ {
+ fewOperands("SUBSTRING");
+ }
+
+ Object op = operands.pop();
+
+ // The operand can either be a string or an array of strings.
+ if (op instanceof String)
+ {
+ operands.push(check((String) op));
+ }
+ else if (op instanceof String[])
+ {
+ // If one element of the array matches, then push true.
+ String[] ops = (String[]) op;
+ boolean result = false;
+ for (int i = 0; !result && (i < ops.length); i++)
+ {
+ if (check(ops[i]) == Boolean.TRUE)
+ {
+ result = true;
+ }
+ }
+
+ operands.push(result ? Boolean.TRUE : Boolean.FALSE);
+ }
+ else
+ {
+ unsupportedType("SUBSTRING", op.getClass());
+ }
+ }
+
+ private Boolean check(String s)
+ {
+ // Walk the pieces to match the string
+ // There are implicit stars between each piece,
+ // and the first and last pieces might be "" to anchor the match.
+ // assert (pieces.length > 1)
+ // minimal case is <string>*<string>
+
+ Boolean result = Boolean.FALSE;
+ int len = pieces.length;
+
+ int index = 0;
+ for (int i = 0; i < len; i++)
+ {
+ String piece = (String) pieces[i];
+
+ if (i == len - 1)
+ {
+ // this is the last piece
+ if (s.endsWith(piece))
+ {
+ result = Boolean.TRUE;
+ }
+ else
+ {
+ result = Boolean.FALSE;
+ }
+ break;
+ }
+ // initial non-star; assert index == 0
+ else if (i == 0)
+ {
+ if (!s.startsWith(piece))
+ {
+ result = Boolean.FALSE;
+ break;
+ }
+ }
+ // assert i > 0 && i < len-1
+ else
+ {
+ // Sure wish stringbuffer supported e.g. indexOf
+ index = s.indexOf(piece, index);
+ if (index < 0)
+ {
+ result = Boolean.FALSE;
+ break;
+ }
+ }
+ // start beyond the matching piece
+ index += piece.length();
+ }
+
+ return result;
+ }
+
+ public String toString()
+ {
+ StringBuffer b = new StringBuffer();
+ b.append("substring(");
+ for (int i = 0; i < pieces.length; i++)
+ {
+ String piece = pieces[i];
+ if (i > 0)
+ {
+ b.append("*");
+ }
+ b.append(escape(piece));
+ }
+ b.append(")");
+ return b.toString();
+ }
+
+ public String escape(String s)
+ {
+ int len = s.length();
+ StringBuffer buf = new StringBuffer(len);
+ for (int i = 0; i < len; i++)
+ {
+ char c = s.charAt(i);
+ if (c == ')' || c == '*')
+ buf.append('\\');
+ buf.append(c);
+ }
+ return buf.toString();
+ }
+
+ public void buildTree(Stack operands)
+ {
+ children = new Operator[1];
+ children[0] = (Operator) operands.pop();
+ operands.push(this);
+ }
+
+ public void toStringInfix(StringBuffer b)
+ {
+ b.append("(");
+ children[0].toStringInfix(b); // dump attribute
+ b.append("=");
+ for (int i = 0; i < pieces.length; i++)
+ {
+ String piece = pieces[i];
+ if (i > 0)
+ {
+ b.append("*");
+ }
+ appendEscaped(b, piece);
+ }
+ b.append(")");
+ }
+ }
+
+ // Utility classes and Interfaces
+
+ private interface OperatorConstants
+ {
+ static final int SSINIT = 0;
+ static final int SSFINAL = 1;
+ static final int SSMIDDLE = 2;
+ static final int SSANY = 3;
+ }
+
+ /**
+ * Compare two operands in an expression with respect
+ * to the following operators =, <=, >= and ~=
+ *
+ * Example: value=100
+ *
+ * @param lhs an object that implements comparable or an array of
+ * objects that implement comparable.
+ * @param rhs a string representing the right operand.
+ * @param operator an integer that represents the operator.
+ * @return <tt>true</tt> or <tt>false</tt> according to the evaluation.
+ * @throws EvaluationException if it is not possible to do the comparison.
+ **/
+ public static boolean compare(Object lhs, String rhs, int operator)
+ throws EvaluationException
+ {
+ // Try to optimize the common case of strings by just
+ // checking for them directly.
+ if (lhs instanceof String)
+ {
+ switch (operator)
+ {
+ case EQUAL :
+ return (((String) lhs).compareTo(rhs) == 0);
+ case GREATER_EQUAL :
+ return (((String) lhs).compareTo(rhs) >= 0);
+ case LESS_EQUAL :
+ return (((String) lhs).compareTo(rhs) <= 0);
+ case APPROX:
+ return compareToApprox(((String) lhs), rhs);
+ default:
+ throw new EvaluationException("Unknown comparison operator..."
+ + operator);
+ }
+ }
+ else if (lhs instanceof Comparable)
+ {
+ // Here we know that the LHS is a comparable object, so
+ // try to create an object for the RHS by using a constructor
+ // that will take the RHS string as a parameter.
+ Comparable rhsComparable = null;
+ try
+ {
+ // We are expecting to be able to construct a comparable
+ // instance from the RHS string by passing it into the
+ // constructor of the corresponing comparable class. The
+ // Character class is a special case, since its constructor
+ // does not take a string, so handle it separately.
+ if (lhs instanceof Character)
+ {
+ rhsComparable = new Character(rhs.charAt(0));
+ }
+ else
+ {
+ rhsComparable = (Comparable) lhs.getClass()
+ .getConstructor(STRING_CLASS)
+ .newInstance(new Object[] { rhs });
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new EvaluationException(
+ "Could not instantiate class "
+ + lhs.getClass().getName()
+ + " with constructor String parameter "
+ + rhs + " " + ex);
+ }
+
+ Comparable lhsComparable = (Comparable) lhs;
+
+ switch (operator)
+ {
+ case EQUAL :
+ return (lhsComparable.compareTo(rhsComparable) == 0);
+ case GREATER_EQUAL :
+ return (lhsComparable.compareTo(rhsComparable) >= 0);
+ case LESS_EQUAL :
+ return (lhsComparable.compareTo(rhsComparable) <= 0);
+ case APPROX:
+ return compareToApprox(lhsComparable, rhsComparable);
+ default:
+ throw new EvaluationException("Unknown comparison operator..."
+ + operator);
+ }
+ }
+
+ // Determine class of LHS.
+ Class lhsClass = lhs.getClass();
+
+ // If LHS is an array, then call compare() on each element
+ // of the array until a match is found.
+ if (lhsClass.isArray())
+ {
+ // First, if this is an array of primitives, then convert
+ // the entire array to an array of the associated
+ // primitive wrapper class instances.
+ if (lhsClass.getComponentType().isPrimitive())
+ {
+ lhs = convertPrimitiveArray(lhs);
+ }
+
+ // Now call compare on each element of array.
+ Object[] array = (Object[]) lhs;
+ for (int i = 0; i < array.length; i++)
+ {
+ if (compare(array[i], rhs, operator))
+ {
+ return true;
+ }
+ }
+ }
+ // If LHS is a vector, then call compare() on each element
+ // of the vector until a match is found.
+ else if (lhs instanceof Collection)
+ {
+ for (Iterator iter = ((Collection) lhs).iterator(); iter.hasNext();)
+ {
+ if (compare(iter.next(), rhs, operator))
+ {
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // At this point we are expecting the LHS to be a comparable,
+ // but Boolean is a special case since it is the only primitive
+ // wrapper class that does not implement comparable; deal with
+ // Boolean separately.
+ if (lhsClass == Boolean.class)
+ {
+ return compareBoolean(lhs, rhs, operator);
+ }
+
+ // If the LHS is not a comparable, then try to use simple
+ // equals() comparison. If that fails, return false.
+ if (!(Comparable.class.isAssignableFrom(lhsClass)))
+ {
+ try
+ {
+ Object rhsObject = lhsClass
+ .getConstructor(STRING_CLASS)
+ .newInstance(new Object[] { rhs });
+ return lhs.equals(rhsObject);
+ }
+ catch (Exception ex)
+ {
+ // Always return false.
+ }
+
+ return false;
+ }
+
+ }
+
+ return false;
+ }
+
+ private static void appendEscaped(StringBuffer buf, String value)
+ {
+ for (int i = 0; i < value.length(); i++)
+ {
+ char c = value.charAt(i);
+ if (c == '(' || c == ')' || c == '*' || c == '\\')
+ {
+ buf.append('\\');
+ }
+ buf.append(c);
+ }
+ }
+
+ /**
+ * This is an ugly utility method to convert an array of primitives
+ * to an array of primitive wrapper objects. This method simplifies
+ * processing LDAP filters since the special case of primitive arrays
+ * can be ignored.
+ * @param array
+ * @return
+ **/
+ private static Object[] convertPrimitiveArray(Object array)
+ {
+ Class clazz = array.getClass().getComponentType();
+
+ if (clazz == Boolean.TYPE)
+ {
+ boolean[] src = (boolean[]) array;
+ array = new Boolean[src.length];
+ for (int i = 0; i < src.length; i++)
+ {
+ ((Object[]) array)[i] = (src[i] ? Boolean.TRUE : Boolean.FALSE);
+ }
+ }
+ else if (clazz == Character.TYPE)
+ {
+ char[] src = (char[]) array;
+ array = new Character[src.length];
+ for (int i = 0; i < src.length; i++)
+ {
+ ((Object[]) array)[i] = new Character(src[i]);
+ }
+ }
+ else if (clazz == Byte.TYPE)
+ {
+ byte[] src = (byte[]) array;
+ array = new Byte[src.length];
+ for (int i = 0; i < src.length; i++)
+ {
+ ((Object[]) array)[i] = new Byte(src[i]);
+ }
+ }
+ else if (clazz == Short.TYPE)
+ {
+ byte[] src = (byte[]) array;
+ array = new Byte[src.length];
+ for (int i = 0; i < src.length; i++)
+ {
+ ((Object[]) array)[i] = new Byte(src[i]);
+ }
+ }
+ else if (clazz == Integer.TYPE)
+ {
+ int[] src = (int[]) array;
+ array = new Integer[src.length];
+ for (int i = 0; i < src.length; i++)
+ {
+ ((Object[]) array)[i] = new Integer(src[i]);
+ }
+ }
+ else if (clazz == Long.TYPE)
+ {
+ long[] src = (long[]) array;
+ array = new Long[src.length];
+ for (int i = 0; i < src.length; i++)
+ {
+ ((Object[]) array)[i] = new Long(src[i]);
+ }
+ }
+ else if (clazz == Float.TYPE)
+ {
+ float[] src = (float[]) array;
+ array = new Float[src.length];
+ for (int i = 0; i < src.length; i++)
+ {
+ ((Object[]) array)[i] = new Float(src[i]);
+ }
+ }
+ else if (clazz == Double.TYPE)
+ {
+ double[] src = (double[]) array;
+ array = new Double[src.length];
+ for (int i = 0; i < src.length; i++)
+ {
+ ((Object[]) array)[i] = new Double(src[i]);
+ }
+ }
+
+ return (Object[]) array;
+ }
+
+ private static boolean compareBoolean(Object lhs, String rhs, int operator)
+ throws EvaluationException
+ {
+ Boolean rhsBoolean = new Boolean(rhs);
+ if (lhs.getClass().isArray())
+ {
+ Object[] objs = (Object[]) lhs;
+ for (int i = 0; i < objs.length; i++)
+ {
+ switch (operator)
+ {
+ case EQUAL :
+ case GREATER_EQUAL :
+ case LESS_EQUAL :
+ case APPROX:
+ if (objs[i].equals(rhsBoolean))
+ {
+ return true;
+ }
+ break;
+ default:
+ throw new EvaluationException(
+ "Unknown comparison operator: " + operator);
+ }
+ }
+ return false;
+ }
+ else
+ {
+ switch (operator)
+ {
+ case EQUAL :
+ case GREATER_EQUAL :
+ case LESS_EQUAL :
+ case APPROX:
+ return (lhs.equals(rhsBoolean));
+ default:
+ throw new EvaluationException("Unknown comparison operator..."
+ + operator);
+ }
+ }
+ }
+
+ /**
+ * Test if two objects are approximate. The two objects that are passed must
+ * have the same type.
+ *
+ * Approximate for numerical values involves a difference of less than APPROX_CRITERIA
+ * Approximate for string values is calculated by using the Levenshtein distance
+ * between strings and is case insensitive. Less than APPROX_CRITERIA of
+ * difference is considered as approximate.
+ *
+ * Supported types only include the following subclasses of Number:
+ * - Byte
+ * - Double
+ * - Float
+ * - Int
+ * - Long
+ * - Short
+ * - BigInteger
+ * - BigDecimal
+ * As subclasses of Number must provide methods to convert the represented numeric value
+ * to byte, double, float, int, long, and short. (see API)
+ *
+ * @param obj1
+ * @param obj2
+ * @return true if they are approximate
+ * @throws EvaluationException if it the two objects cannot be approximated
+ **/
+ private static boolean compareToApprox(Object obj1, Object obj2) throws EvaluationException
+ {
+ if (obj1 instanceof Byte)
+ {
+ byte value1 = ((Byte)obj1).byteValue();
+ byte value2 = ((Byte)obj2).byteValue();
+ return (value2 >= (value1-((Math.abs(value1)*(byte)APPROX_CRITERIA)/(byte)100))
+ && value2 <= (value1+((Math.abs(value1)*(byte)APPROX_CRITERIA)/(byte)100)));
+ }
+ else if (obj1 instanceof Character)
+ {
+ char value1 = ((Character)obj1).charValue();
+ char value2 = ((Character)obj2).charValue();
+ return (value2 >= (value1-((Math.abs(value1)*(char)APPROX_CRITERIA)/(char)100))
+ && value2 <= (value1+((Math.abs(value1)*(char)APPROX_CRITERIA)/(char)100)));
+ }
+ else if (obj1 instanceof Double)
+ {
+ double value1 = ((Double)obj1).doubleValue();
+ double value2 = ((Double)obj2).doubleValue();
+ return (value2 >= (value1-((Math.abs(value1)*(double)APPROX_CRITERIA)/(double)100))
+ && value2 <= (value1+((Math.abs(value1)*(double)APPROX_CRITERIA)/(double)100)));
+ }
+ else if (obj1 instanceof Float)
+ {
+ float value1 = ((Float)obj1).floatValue();
+ float value2 = ((Float)obj2).floatValue();
+ return (value2 >= (value1-((Math.abs(value1)*(float)APPROX_CRITERIA)/(float)100))
+ && value2 <= (value1+((Math.abs(value1)*(float)APPROX_CRITERIA)/(float)100)));
+ }
+ else if (obj1 instanceof Integer)
+ {
+ int value1 = ((Integer)obj1).intValue();
+ int value2 = ((Integer)obj2).intValue();
+ return (value2 >= (value1-((Math.abs(value1)*(int)APPROX_CRITERIA)/(int)100))
+ && value2 <= (value1+((Math.abs(value1)*(int)APPROX_CRITERIA)/(int)100)));
+ }
+ else if (obj1 instanceof Long)
+ {
+ long value1 = ((Long)obj1).longValue();
+ long value2 = ((Long)obj2).longValue();
+ return (value2 >= (value1-((Math.abs(value1)*(long)APPROX_CRITERIA)/(long)100))
+ && value2 <= (value1+((Math.abs(value1)*(long)APPROX_CRITERIA)/(long)100)));
+ }
+ else if (obj1 instanceof Short)
+ {
+ short value1 = ((Short)obj1).shortValue();
+ short value2 = ((Short)obj2).shortValue();
+ return (value2 >= (value1-((Math.abs(value1)*(short)APPROX_CRITERIA)/(short)100))
+ && value2 <= (value1+((Math.abs(value1)*(short)APPROX_CRITERIA)/(short)100)));
+ }
+ else if (obj1 instanceof String)
+ {
+ int distance = getDistance(
+ obj1.toString().toLowerCase(), obj2.toString().toLowerCase());
+ int size = ((String)obj1).length();
+ return (distance <= ((size*APPROX_CRITERIA)/100));
+ }
+ else if (obj1 instanceof BigInteger)
+ {
+ BigInteger value1 = (BigInteger)obj1;
+ BigInteger value2 = (BigInteger)obj2;
+ BigInteger delta = value1.abs().multiply(
+ BigInteger.valueOf(APPROX_CRITERIA)
+ .divide(BigInteger.valueOf(100)));
+ BigInteger low = value1.subtract(delta);
+ BigInteger high = value1.add(delta);
+ return (value2.compareTo(low) >= 0) && (value2.compareTo(high) <= 0);
+ }
+ else if (m_hasBigDecimal && (obj1 instanceof BigDecimal))
+ {
+ BigDecimal value1 = (BigDecimal)obj1;
+ BigDecimal value2 = (BigDecimal)obj2;
+ BigDecimal delta = value1.abs().multiply(
+ BigDecimal.valueOf(APPROX_CRITERIA)
+ .divide(BigDecimal.valueOf(100), BigDecimal.ROUND_HALF_DOWN));
+ BigDecimal low = value1.subtract(delta);
+ BigDecimal high = value1.add(delta);
+ return (value2.compareTo(low) >= 0) && (value2.compareTo(high) <= 0);
+ }
+ throw new EvaluationException(
+ "Approximate operator not supported for type "
+ + obj1.getClass().getName());
+ }
+
+ /**
+ * Calculate the Levenshtein distance (LD) between two strings.
+ * The Levenshteing distance is a measure of the similarity between
+ * two strings, which we will refer to as the source string (s) and
+ * the target string (t). The distance is the number of deletions,
+ * insertions, or substitutions required to transform s into t.
+ *
+ * Algorithm from: http://www.merriampark.com/ld.htm
+ *
+ * @param s the first string
+ * @param t the second string
+ * @return
+ */
+ private static int getDistance(String s, String t)
+ {
+ int d[][]; // matrix
+ int n; // length of s
+ int m; // length of t
+ int i; // iterates through s
+ int j; // iterates through t
+ char s_i; // ith character of s
+ char t_j; // jth character of t
+ int cost; // cost
+
+ // Step 1
+ n = s.length();
+ m = t.length();
+ if (n == 0)
+ {
+ return m;
+ }
+ if (m == 0)
+ {
+ return n;
+ }
+ d = new int[n + 1][m + 1];
+
+ // Step 2
+ for (i = 0; i <= n; i++)
+ {
+ d[i][0] = i;
+ }
+
+ for (j = 0; j <= m; j++)
+ {
+ d[0][j] = j;
+ }
+
+ // Step 3
+ for (i = 1; i <= n; i++)
+ {
+ s_i = s.charAt(i - 1);
+ // Step 4
+ for (j = 1; j <= m; j++)
+ {
+ t_j = t.charAt(j - 1);
+ // Step 5
+ if (s_i == t_j)
+ {
+ cost = 0;
+ }
+ else
+ {
+ cost = 1;
+ }
+ // Step 6
+ d[i][j] =
+ Minimum(
+ d[i - 1][j] + 1,
+ d[i][j - 1] + 1,
+ d[i - 1][j - 1] + cost);
+ }
+ }
+ // Step 7
+ return d[n][m];
+ }
+
+ /**
+ * Calculate the minimum between three values
+ *
+ * @param a
+ * @param b
+ * @param c
+ * @return
+ */
+ private static int Minimum(int a, int b, int c)
+ {
+ int mi;
+ mi = a;
+ if (b < mi)
+ {
+ mi = b;
+ }
+ if (c < mi)
+ {
+ mi = c;
+ }
+ return mi;
+ }
+
+ private static void fewOperands(String op) throws EvaluationException
+ {
+ throw new EvaluationException(op + ": too few operands");
+ }
+
+ private static void unsupportedType(String opStr, Class clazz)
+ throws EvaluationException
+ {
+ throw new EvaluationException(
+ opStr + ": unsupported type " + clazz.getName(), clazz);
+ }
+}
Propchange: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Parser.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Parser.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Unknown.java
URL: http://svn.apache.org/viewvc/cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Unknown.java?rev=690991&view=auto
==============================================================================
--- cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Unknown.java (added)
+++ cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Unknown.java Mon Sep 1 08:08:01 2008
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util.ldap;
+
+/**
+ * This class is used to create simple marker instances that are inserted
+ * into the evaluation stack of a LDAP filter expression when an attribute
+ * is referenced that has no defined value. These invalid marker instances
+ * force the operators to throw an "unsupported type" exception, which the
+ * evaluator catches and then converts the entire subexpression containing
+ * the non-existent attribute to <tt>false</tt>.
+**/
+class Unknown
+{
+}
Propchange: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Unknown.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/ldap/Unknown.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Capability.java
URL: http://svn.apache.org/viewvc/cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Capability.java?rev=690991&view=auto
==============================================================================
--- cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Capability.java (added)
+++ cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Capability.java Mon Sep 1 08:08:01 2008
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util.manifestparser;
+
+import java.util.*;
+
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.moduleloader.ICapability;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+public class Capability implements ICapability
+{
+ private String m_namespace = null;
+ private R4Directive[] m_directives = null;
+ private R4Attribute[] m_attributes = null;
+ private Map m_attrMap = null;
+ private String[] m_uses = new String[0];
+ private String[][] m_includeFilter = null;
+ private String[][] m_excludeFilter = null;
+
+ // Cached properties for performance reasons.
+ private String m_pkgName = null;
+ private Version m_pkgVersion = Version.emptyVersion;
+
+ public Capability(String namespace, R4Directive[] dirs, R4Attribute[] attrs)
+ {
+ m_namespace = namespace;
+ m_directives = dirs;
+ m_attributes = attrs;
+
+ // Find all export directives: uses, mandatory, include, and exclude.
+ String mandatory = "";
+ for (int dirIdx = 0; (m_directives != null) && (dirIdx < m_directives.length); dirIdx++)
+ {
+ if (m_directives[dirIdx].getName().equals(Constants.USES_DIRECTIVE))
+ {
+ // Parse these uses directive.
+ StringTokenizer tok = new StringTokenizer(m_directives[dirIdx].getValue(), ",");
+ m_uses = new String[tok.countTokens()];
+ for (int i = 0; i < m_uses.length; i++)
+ {
+ m_uses[i] = tok.nextToken().trim();
+ }
+ }
+ else if (m_directives[dirIdx].getName().equals(Constants.MANDATORY_DIRECTIVE))
+ {
+ mandatory = m_directives[dirIdx].getValue();
+ }
+ else if (m_directives[dirIdx].getName().equals(Constants.INCLUDE_DIRECTIVE))
+ {
+ String[] ss = ManifestParser.parseDelimitedString(m_directives[dirIdx].getValue(), ",");
+ m_includeFilter = new String[ss.length][];
+ for (int filterIdx = 0; filterIdx < ss.length; filterIdx++)
+ {
+ m_includeFilter[filterIdx] = parseSubstring(ss[filterIdx]);
+ }
+ }
+ else if (m_directives[dirIdx].getName().equals(Constants.EXCLUDE_DIRECTIVE))
+ {
+ String[] ss = ManifestParser.parseDelimitedString(m_directives[dirIdx].getValue(), ",");
+ m_excludeFilter = new String[ss.length][];
+ for (int filterIdx = 0; filterIdx < ss.length; filterIdx++)
+ {
+ m_excludeFilter[filterIdx] = parseSubstring(ss[filterIdx]);
+ }
+ }
+ }
+
+ // Parse mandatory directive and mark specified
+ // attributes as mandatory.
+ StringTokenizer tok = new StringTokenizer(mandatory, ", ");
+ while (tok.hasMoreTokens())
+ {
+ // Get attribute name.
+ String attrName = tok.nextToken().trim();
+ // Find attribute and mark it as mandatory.
+ boolean found = false;
+ for (int i = 0; (!found) && (i < m_attributes.length); i++)
+ {
+ if (m_attributes[i].getName().equals(attrName))
+ {
+ m_attributes[i] = new R4Attribute(
+ m_attributes[i].getName(),
+ m_attributes[i].getValue(), true);
+ found = true;
+ }
+ }
+ // If a specified mandatory attribute was not found,
+ // then error.
+ if (!found)
+ {
+ throw new IllegalArgumentException(
+ "Mandatory attribute '" + attrName + "' does not exist.");
+ }
+ }
+
+ // For performance reasons, find the package name and version properties.
+ for (int i = 0; i < m_attributes.length; i++)
+ {
+ if (m_attributes[i].getName().equals(ICapability.PACKAGE_PROPERTY))
+ {
+ m_pkgName = (String) m_attributes[i].getValue();
+ }
+ else if (m_attributes[i].getName().equals(ICapability.VERSION_PROPERTY))
+ {
+ m_pkgVersion = (Version) m_attributes[i].getValue();
+ }
+ }
+ }
+
+ public String getNamespace()
+ {
+ return m_namespace;
+ }
+
+// TODO: RB - Determine how to eliminate these non-generic methods;
+// at least make sure they are not used in the generic resolver.
+ public String getPackageName()
+ {
+ return m_pkgName;
+ }
+
+ public Version getPackageVersion()
+ {
+ return m_pkgVersion;
+ }
+
+ public R4Directive[] getDirectives()
+ {
+ // TODO: RB - We should return copies of the arrays probably.
+ return m_directives;
+ }
+
+ public R4Attribute[] getAttributes()
+ {
+ // TODO: RB - We should return copies of the arrays probably.
+ return m_attributes;
+ }
+
+ public String[] getUses()
+ {
+ // TODO: RB - We should return copies of the arrays probably.
+ return m_uses;
+ }
+
+ public boolean isIncluded(String name)
+ {
+ if ((m_includeFilter == null) && (m_excludeFilter == null))
+ {
+ return true;
+ }
+
+ // Get the class name portion of the target class.
+ String className = Util.getClassName(name);
+
+ // If there are no include filters then all classes are included
+ // by default, otherwise try to find one match.
+ boolean included = (m_includeFilter == null);
+ for (int i = 0;
+ (!included) && (m_includeFilter != null) && (i < m_includeFilter.length);
+ i++)
+ {
+ included = checkSubstring(m_includeFilter[i], className);
+ }
+
+ // If there are no exclude filters then no classes are excluded
+ // by default, otherwise try to find one match.
+ boolean excluded = false;
+ for (int i = 0;
+ (!excluded) && (m_excludeFilter != null) && (i < m_excludeFilter.length);
+ i++)
+ {
+ excluded = checkSubstring(m_excludeFilter[i], className);
+ }
+ return included && !excluded;
+ }
+
+// TODO: RB - Terminology mismatch property vs. attribute.
+ public Map getProperties()
+ {
+ if (m_attrMap == null)
+ {
+ m_attrMap = new Map() {
+
+ public int size()
+ {
+ // A name and version attribute is always present, since it has a
+ // default value.
+ return m_attributes.length + 2;
+ }
+
+ public boolean isEmpty()
+ {
+ // A version attribute is always present, since it has a
+ // default value.
+ return false;
+ }
+
+ public boolean containsKey(Object key)
+ {
+ return (get(key) != null);
+ }
+
+ public boolean containsValue(Object value)
+ {
+ // Check the package name.
+ if (m_pkgName.equals(value))
+ {
+ return true;
+ }
+
+ // Check the package version.
+ if (m_pkgVersion.equals(value))
+ {
+ return true;
+ }
+
+ // Check all attributes.
+ for (int i = 0; i < m_attributes.length; i++)
+ {
+ if (m_attributes[i].getValue().equals(value))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public Object get(Object key)
+ {
+ if (ICapability.PACKAGE_PROPERTY.equals(key))
+ {
+ return m_pkgName;
+ }
+ else if (ICapability.VERSION_PROPERTY.equals(key))
+ {
+ return m_pkgVersion;
+ }
+
+ for (int i = 0; i < m_attributes.length; i++)
+ {
+ if (m_attributes[i].getName().equals(key))
+ {
+ return m_attributes[i].getValue();
+ }
+ }
+
+ return null;
+ }
+
+ public Object put(Object key, Object value)
+ {
+ throw new UnsupportedOperationException("Map.put() not implemented.");
+ }
+
+ public Object remove(Object key)
+ {
+ throw new UnsupportedOperationException("Map.remove() not implemented.");
+ }
+
+ public void putAll(Map t)
+ {
+ throw new UnsupportedOperationException("Map.putAll() not implemented.");
+ }
+
+ public void clear()
+ {
+ throw new UnsupportedOperationException("Map.clear() not implemented.");
+ }
+
+ public Set keySet()
+ {
+ Set set = new HashSet();
+ set.add(ICapability.PACKAGE_PROPERTY);
+ set.add(ICapability.VERSION_PROPERTY);
+ for (int i = 0; i < m_attributes.length; i++)
+ {
+ set.add(m_attributes[i].getName());
+ }
+ return set;
+ }
+
+ public Collection values()
+ {
+ throw new UnsupportedOperationException("Map.values() not implemented.");
+ }
+
+ public Set entrySet()
+ {
+ throw new UnsupportedOperationException("Map.entrySet() not implemented.");
+ }
+ };
+ }
+ return m_attrMap;
+ }
+
+// TODO: RB - Remove or simplify toString() for final version.
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getNamespace());
+ for (int i = 0; (m_directives != null) && (i < m_directives.length); i++)
+ {
+ sb.append(";");
+ sb.append(m_directives[i].getName());
+ sb.append(":=\"");
+ sb.append(m_directives[i].getValue());
+ sb.append("\"");
+ }
+ for (int i = 0; (m_attributes != null) && (i < m_attributes.length); i++)
+ {
+ sb.append(";");
+ sb.append(m_attributes[i].getName());
+ sb.append("=\"");
+ sb.append(m_attributes[i].getValue());
+ sb.append("\"");
+ }
+ return sb.toString();
+ }
+
+ //
+ // The following substring-related code was lifted and modified
+ // from the LDAP parser code.
+ //
+
+ private static String[] parseSubstring(String target)
+ {
+ List pieces = new ArrayList();
+ StringBuffer ss = new StringBuffer();
+ // int kind = SIMPLE; // assume until proven otherwise
+ boolean wasStar = false; // indicates last piece was a star
+ boolean leftstar = false; // track if the initial piece is a star
+ boolean rightstar = false; // track if the final piece is a star
+
+ int idx = 0;
+
+ // We assume (sub)strings can contain leading and trailing blanks
+loop: for (;;)
+ {
+ if (idx >= target.length())
+ {
+ if (wasStar)
+ {
+ // insert last piece as "" to handle trailing star
+ rightstar = true;
+ }
+ else
+ {
+ pieces.add(ss.toString());
+ // accumulate the last piece
+ // note that in the case of
+ // (cn=); this might be
+ // the string "" (!=null)
+ }
+ ss.setLength(0);
+ break loop;
+ }
+
+ char c = target.charAt(idx++);
+ if (c == '*')
+ {
+ if (wasStar)
+ {
+ // encountered two successive stars;
+ // I assume this is illegal
+ throw new IllegalArgumentException("Invalid filter string: " + target);
+ }
+ if (ss.length() > 0)
+ {
+ pieces.add(ss.toString()); // accumulate the pieces
+ // between '*' occurrences
+ }
+ ss.setLength(0);
+ // if this is a leading star, then track it
+ if (pieces.size() == 0)
+ {
+ leftstar = true;
+ }
+ ss.setLength(0);
+ wasStar = true;
+ }
+ else
+ {
+ wasStar = false;
+ ss.append(c);
+ }
+ }
+ if (leftstar || rightstar || pieces.size() > 1)
+ {
+ // insert leading and/or trailing "" to anchor ends
+ if (rightstar)
+ {
+ pieces.add("");
+ }
+ if (leftstar)
+ {
+ pieces.add(0, "");
+ }
+ }
+ return (String[]) pieces.toArray(new String[pieces.size()]);
+ }
+
+ private static boolean checkSubstring(String[] pieces, String s)
+ {
+ // Walk the pieces to match the string
+ // There are implicit stars between each piece,
+ // and the first and last pieces might be "" to anchor the match.
+ // assert (pieces.length > 1)
+ // minimal case is <string>*<string>
+
+ boolean result = false;
+ int len = pieces.length;
+
+loop: for (int i = 0; i < len; i++)
+ {
+ String piece = (String) pieces[i];
+ int index = 0;
+ if (i == len - 1)
+ {
+ // this is the last piece
+ if (s.endsWith(piece))
+ {
+ result = true;
+ }
+ else
+ {
+ result = false;
+ }
+ break loop;
+ }
+ // initial non-star; assert index == 0
+ else if (i == 0)
+ {
+ if (!s.startsWith(piece))
+ {
+ result = false;
+ break loop;
+ }
+ }
+ // assert i > 0 && i < len-1
+ else
+ {
+ // Sure wish stringbuffer supported e.g. indexOf
+ index = s.indexOf(piece, index);
+ if (index < 0)
+ {
+ result = false;
+ break loop;
+ }
+ }
+ // start beyond the matching piece
+ index += piece.length();
+ }
+
+ return result;
+ }
+}
\ No newline at end of file
Propchange: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Capability.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cxf/sandbox/dosgi/felix/framework/src/main/java/org/apache/felix/framework/util/manifestparser/Capability.java
------------------------------------------------------------------------------
svn:keywords = Rev Date