You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2005/08/16 20:34:41 UTC
svn commit: r233031 [13/21] - in /incubator/oscar/trunk: ./ etc/ lib/ src/
src/org/ src/org/apache/ src/org/apache/osgi/ src/org/apache/osgi/bundle/
src/org/apache/osgi/bundle/bundlerepository/
src/org/apache/osgi/bundle/bundlerepository/kxmlsax/ src/o...
Added: incubator/oscar/trunk/src/org/apache/osgi/framework/util/ldap/Parser.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/framework/util/ldap/Parser.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/framework/util/ldap/Parser.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/framework/util/ldap/Parser.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,1696 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.osgi.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 the list of legal leading and trailing
+ // characters in an attribute name.
+ public static final String ATTRIBUTECHARS0 =
+ ".abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
+ // Define the list of legal internal characters in an attribute name.
+ public static final String ATTRIBUTECHARS1 =
+ ".abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_ ";
+
+ // 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 BigInteger/Decimal.
+ private static boolean m_hasBigNumbers = false;
+
+ static
+ {
+ try
+ {
+ Class.forName("java.math.BigDecimal");
+ m_hasBigNumbers = 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 false;
+ }
+ // Code: [And|Or](cnt)
+ program.add(
+ c == '&'
+ ? (Operator) new AndOperator(cnt)
+ : (Operator) new OrOperator(cnt));
+ return true;
+ case '!' :
+ lexer.get();
+ if (!filter())
+ {
+ return false;
+ }
+ // Code: Not()
+ program.add(new NotOperator());
+ return true;
+ case EOF :
+ return false;
+ default :
+ // check for key
+ if (ATTRIBUTECHARS0.indexOf(c) <= 0)
+ {
+ return false;
+ }
+ boolean b = item();
+ return b;
+ }
+ }
+
+ /*
+ <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() throws ParseException, IOException
+ {
+ debug("item");
+
+ StringBuffer attr = new StringBuffer();
+ 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 :
+ // 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();
+ buf.setLength(0);
+ int c = lexer.peek(); // need to make sure there
+ // is at least one KEYCHAR
+ if (c == EOF)
+ {
+ return false;
+ }
+ if (ATTRIBUTECHARS0.indexOf(c) < 0)
+ {
+ return false;
+ }
+
+ do
+ {
+ buf.append((char) lexer.get());
+ }
+ while (ATTRIBUTECHARS1.indexOf(lexer.peek()) >= 0);
+
+ // The above may have accumulated trailing blanks that must be removed
+ int i = buf.length() - 1;
+ while (i > 0 && 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 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 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(new Boolean(result));
+ }
+
+ 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 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(new Boolean(result));
+ }
+
+ 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 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(new Boolean(result));
+ }
+
+ 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 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(new Boolean(compare(lhs, rhs, EQUAL)));
+ }
+
+ 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 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(new Boolean(compare(lhs, rhs, GREATER_EQUAL)));
+ }
+
+ 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 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(new Boolean(compare(lhs, rhs, LESS_EQUAL)));
+ }
+
+ 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 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(new Boolean(compare(lhs, rhs, APPROX)));
+ }
+
+ 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 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(new Boolean(value != null));
+ }
+
+ 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 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 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)
+ {
+ b.append(val.toString());
+ }
+ }
+
+ private static 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;
+
+ 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 = Boolean.TRUE;
+ }
+ else
+ {
+ result = Boolean.FALSE;
+ }
+ break loop;
+ }
+ // initial non-star; assert index == 0
+ else if (i == 0)
+ {
+ if (!s.startsWith(piece))
+ {
+ result = Boolean.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 = Boolean.FALSE;
+ break loop;
+ }
+ }
+ // 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 = (String) pieces[i];
+ if (i > 0)
+ {
+ b.append("*");
+ }
+ b.append(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
+ {
+ // Determine class of LHS.
+ Class lhsClass = null;
+
+ // If LHS is an array, then call compare() on each element
+ // of the array until a match is found.
+ if (lhs.getClass().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 (lhs.getClass().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 Vector)
+ {
+ for (Enumeration e = ((Vector) lhs).elements(); e.hasMoreElements();)
+ {
+ if (compare(e.nextElement(), rhs, operator))
+ {
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // Get the class of LHS.
+ lhsClass = lhs.getClass();
+
+ // 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 LHS is not a Boolean, then verify it is a comparable
+ // and perform comparison.
+ if (!(Comparable.class.isAssignableFrom(lhsClass)))
+ {
+ String opName = null;
+ switch (operator)
+ {
+ case EQUAL :
+ opName = "=";
+ case GREATER_EQUAL :
+ opName = ">=";
+ case LESS_EQUAL :
+ opName = "<=";
+ case APPROX:
+ opName = "~=";
+ default:
+ opName = "UNKNOWN OP";
+ }
+
+ unsupportedType(opName, lhsClass);
+ }
+
+ // We will try to create a comparable object from the
+ // RHS string.
+ 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 (lhsClass == Character.class)
+ {
+ rhsComparable = new Character(rhs.charAt(0));
+ }
+ else
+ {
+ rhsComparable = (Comparable) lhsClass
+ .getConstructor(new Class[] { String.class })
+ .newInstance(new Object[] { rhs });
+ }
+ }
+ catch (Exception ex)
+ {
+ String msg = (ex.getCause() == null)
+ ? ex.toString() : ex.getCause().toString();
+ throw new EvaluationException(
+ "Could not instantiate class "
+ + lhsClass.getName()
+ + " with constructor String parameter "
+ + rhs + " " + msg);
+ }
+
+ 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);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 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] = new Boolean(src[i]);
+ }
+ }
+ 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. 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(),obj2.toString());
+ int size = ((String)obj1).length();
+ return (distance <= ((size*APPROX_CRITERIA)/100));
+ }
+ else if (m_hasBigNumbers && (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_hasBigNumbers && (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);
+ }
+}
\ No newline at end of file
Added: incubator/oscar/trunk/src/org/apache/osgi/framework/util/ldap/Unknown.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/framework/util/ldap/Unknown.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/framework/util/ldap/Unknown.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/framework/util/ldap/Unknown.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.osgi.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
+{
+}
Added: incubator/oscar/trunk/src/org/apache/osgi/moduleloader/DefaultURLPolicy.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/moduleloader/DefaultURLPolicy.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/moduleloader/DefaultURLPolicy.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/moduleloader/DefaultURLPolicy.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.osgi.moduleloader;
+
+import java.net.URL;
+
+/**
+ * <p>
+ * This class implements a simple <tt>URLPolicy</tt> that the <tt>ModuleManager</tt>
+ * uses if the application does not specify one. This implementation always returns
+ * <tt>null</tt> for <tt>CodeSource</tt> <tt>URL</tt>s, which means that security
+ * is simply ignored. For resource <tt>URL</tt>s, it returns an <tt>URL</tt> in the
+ * form of:
+ * </p>
+ * <pre>
+ * module://<module-id>/<resource-path>
+ * </pre>
+ * <p>
+ * In order to properly handle the "<tt>module:</tt>" protocol, this policy
+ * also defines a custom <tt>java.net.URLStreamHandler</tt> that it assigns
+ * to each <tt>URL</tt> as it is created. This custom handler is used to
+ * return a custom <tt>java.net.URLConnection</tt> that will correctly parse
+ * the above <tt>URL</tt> and retrieve the associated resource bytes using
+ * methods from <tt>ModuleManager</tt> and <tt>Module</tt>.
+ * </p>
+ * @see org.apache.osgi.moduleloader.ModuleManager
+ * @see org.apache.osgi.moduleloader.Module
+ * @see org.apache.osgi.moduleloader.URLPolicy
+**/
+public class DefaultURLPolicy implements URLPolicy
+{
+ private ModuleURLStreamHandler m_handler = null;
+
+ /**
+ * <p>
+ * This method is a stub and always returns <tt>null</tt>.
+ * </p>
+ * @param mgr the <tt>ModuleManager</tt> of the module.
+ * @param module the module for which the <tt>URL</tt> is to be created.
+ * @return <tt>null</tt>.
+ **/
+ public URL createCodeSourceURL(ModuleManager mgr, Module module)
+ {
+ return null;
+ }
+
+ /**
+ * <p>
+ * This method returns a <tt>URL</tt> that is suitable
+ * for accessing the bytes of the specified resource.
+ * </p>
+ * @param mgr the <tt>ModuleManager</tt> of the module.
+ * @param module the module for which the resource is being loaded.
+ * @param rsIdx the index of the <tt>ResourceSource</tt> containing the resource.
+ * @param name the name of the resource being loaded.
+ * @return an <tt>URL</tt> for retrieving the resource.
+ **/
+ public URL createResourceURL(ModuleManager mgr, Module module, int rsIdx, String name)
+ {
+ if (m_handler == null)
+ {
+ m_handler = new ModuleURLStreamHandler(mgr);
+ }
+
+ // Add a slash if there is one already, otherwise
+ // the is no slash separating the host from the file
+ // in the resulting URL.
+ if (!name.startsWith("/"))
+ {
+ name = "/" + name;
+ }
+
+ try
+ {
+ return new URL("module", module.getId(), -1, "/" + rsIdx + name, m_handler);
+ }
+ catch (Exception ex)
+ {
+ System.err.println("DefaultResourceURLPolicy: " + ex);
+ return null;
+ }
+ }
+}
\ No newline at end of file
Added: incubator/oscar/trunk/src/org/apache/osgi/moduleloader/JarResourceSource.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/moduleloader/JarResourceSource.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/moduleloader/JarResourceSource.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/moduleloader/JarResourceSource.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.osgi.moduleloader;
+
+import java.io.*;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+/**
+ * <p>
+ * This class implements a <tt>ResourceSource</tt> for retrieving resources
+ * from a JAR file. The approach used by this implementation is to defer
+ * opening the JAR file until a request for a resource is made.
+ * </p>
+ * @see org.apache.osgi.moduleloader.ResourceSource
+**/
+public class JarResourceSource implements ResourceSource
+{
+ private static final int BUFSIZE = 4096;
+
+ private File m_file = null;
+ private JarFile m_jarFile = null;
+ private boolean m_opened = false;
+
+ /**
+ * <p>
+ * Constructs an instance using the specified file name as the source
+ * of the JAR file.
+ * </p>
+ * @param fileName the name of the JAR file to be used as the source.
+ **/
+ public JarResourceSource(String fileName)
+ {
+ m_file = new File(fileName);
+ }
+
+ /**
+ * <p>
+ * Constructs an instance using the specified file as the source
+ * of the JAR file.
+ * </p>
+ * @param file the JAR file to be used as the source.
+ **/
+ public JarResourceSource(File file)
+ {
+ m_file = file;
+ }
+
+ /**
+ * <p>
+ * Closes the JAR file if it has not already been closed.
+ * <p>
+ **/
+ protected void finalize()
+ {
+ if (m_jarFile != null)
+ {
+ try {
+ m_jarFile.close();
+ } catch (IOException ex) {
+ // Not much we can do, so ignore it.
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * This method initializes the resource source. Since opening
+ * the JAR file is deferred until a request for a resource is
+ * actually made, this method really only sets a flag indicating
+ * that the resource source has been initialized.
+ * <p>
+ **/
+ public void open()
+ {
+ m_opened = true;
+ }
+
+ /**
+ * <p>
+ * This method deinitializes the resource source by closing
+ * the associated JAR file if it is open.
+ * <p>
+ **/
+ public synchronized void close()
+ {
+ try {
+ if (m_jarFile != null)
+ {
+ m_jarFile.close();
+ }
+ } catch (Exception ex) {
+ System.err.println("JarResourceSource: " + ex);
+ }
+
+ m_jarFile = null;
+ m_opened = false;
+ }
+
+ // JavaDoc comments are copied from ResourceSource.
+ public synchronized boolean hasResource(String name) throws IllegalStateException
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("JarResourceSource is not open");
+ }
+
+ // Open JAR file if not already opened.
+ if (m_jarFile == null)
+ {
+ try {
+ openJarFile();
+ } catch (IOException ex) {
+ System.err.println("JarResourceSource: " + ex);
+ return false;
+ }
+ }
+
+ try {
+ ZipEntry ze = m_jarFile.getEntry(name);
+ return ze != null;
+ } catch (Exception ex) {
+ return false;
+ } finally {
+ }
+ }
+
+ // JavaDoc comments are copied from ResourceSource.
+ public synchronized byte[] getBytes(String name) throws IllegalStateException
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("JarResourceSource is not open");
+ }
+
+ // Open JAR file if not already opened.
+ if (m_jarFile == null)
+ {
+ try {
+ openJarFile();
+ } catch (IOException ex) {
+ System.err.println("JarResourceSource: " + ex);
+ return null;
+ }
+ }
+
+ // Get the embedded resource.
+ InputStream is = null;
+ ByteArrayOutputStream baos = null;
+
+ try {
+ ZipEntry ze = m_jarFile.getEntry(name);
+ if (ze == null)
+ {
+ return null;
+ }
+ is = m_jarFile.getInputStream(ze);
+ if (is == null)
+ {
+ return null;
+ }
+ baos = new ByteArrayOutputStream(BUFSIZE);
+ byte[] buf = new byte[BUFSIZE];
+ int n = 0;
+ while ((n = is.read(buf, 0, buf.length)) >= 0)
+ {
+ baos.write(buf, 0, n);
+ }
+ return baos.toByteArray();
+
+ } catch (Exception ex) {
+ return null;
+ } finally {
+ try {
+ if (baos != null) baos.close();
+ } catch (Exception ex) {
+ }
+ try {
+ if (is != null) is.close();
+ } catch (Exception ex) {
+ }
+ }
+ }
+
+ private void openJarFile() throws IOException
+ {
+ if (m_jarFile == null)
+ {
+ m_jarFile = new JarFile(m_file);
+ }
+ }
+
+ public String toString()
+ {
+ return "JAR " + m_file.getPath();
+ }
+}
\ No newline at end of file
Added: incubator/oscar/trunk/src/org/apache/osgi/moduleloader/LibrarySource.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/moduleloader/LibrarySource.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/moduleloader/LibrarySource.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/moduleloader/LibrarySource.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.osgi.moduleloader;
+
+/**
+ * <p>
+ * This interface represents a source for obtaining native libraries for a
+ * given module via the module's class loader. The main goal of a library
+ * source is to map a library name to a path in the file system.
+ * </p>
+ * <p>
+ * All library sources are initialized before first usage via a call
+ * to the <a href="#open()"><tt>LibrarySource.open()</tt></a> method and
+ * are also deinitialized via a call to
+ * <a href="#open()"><tt>LibrarySource.close()</tt></a>. Library sources
+ * should be implemented such that they can be opened, closed, and then
+ * re-opened.
+ * </p>
+ * @see org.apache.osgi.moduleloader.Module
+ * @see org.apache.osgi.moduleloader.ModuleClassLoader
+**/
+public interface LibrarySource
+{
+ /**
+ * <p>
+ * This method initializes the library source. It is called when
+ * the associated module is added to the <tt>ModuleManager</tt>. It
+ * is acceptable for implementations to ignore duplicate calls to this
+ * method if the library source is already opened.
+ * </p>
+ **/
+ public void open();
+
+ /**
+ * <p>
+ * This method de-initializes the library source. It is called when
+ * the associated module is removed from the <tt>ModuleManager</tt> or
+ * when the module is reset by the <tt>ModuleManager</tt>.
+ * </p>
+ **/
+ public void close();
+
+ /**
+ * <p>
+ * Returns a file system path to the specified library.
+ * </p>
+ * @param name the name of the library that is being requested.
+ * @return a file system path to the specified library.
+ * @throws java.lang.IllegalStateException if the resource source has not
+ * been opened.
+ **/
+ public String getPath(String name) throws IllegalStateException;
+}
\ No newline at end of file
Added: incubator/oscar/trunk/src/org/apache/osgi/moduleloader/Module.java
URL: http://svn.apache.org/viewcvs/incubator/oscar/trunk/src/org/apache/osgi/moduleloader/Module.java?rev=233031&view=auto
==============================================================================
--- incubator/oscar/trunk/src/org/apache/osgi/moduleloader/Module.java (added)
+++ incubator/oscar/trunk/src/org/apache/osgi/moduleloader/Module.java Tue Aug 16 11:33:34 2005
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.osgi.moduleloader;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+
+/**
+ * <p>
+ * The <tt>Module</tt> class is a grouping mechanism for application classes
+ * and resources. Conceptually, most applications are grouped into
+ * entities such as JAR files (containing classes and resources) and native
+ * libraries. In some cases, these entities are core application classes and
+ * resources, while in other cases, these entities are ancillary, such as
+ * dynamically loaded plug-ins. Applications place some level of semantics
+ * onto these types of entities or <i>modules</i>, but for the <tt>ModuleLoader</tt>,
+ * no particular semantics are attached to modules (other than they are a grouping
+ * mechanism for classes and resources). This means that the application
+ * is free to map itself into modules any way that is appropriate.
+ * </p>
+ * <p>
+ * A module has the following features:
+ * </p>
+ * <ul>
+ * <li>A unique identifier within the scope of its <tt>ModuleManager</tt>.
+ * </li>
+ * <li>A set of key-value attribute pairs.
+ * </li>
+ * <li>A set of resource sources from which it is possible to
+ * retrieve classes and resources.
+ * </li>
+ * <li>A set of native library sources from which it is possible
+ * to retrieve native libraries.
+ * </li>
+ * </ul>
+ * <p>
+ * A module's identifier must be unique within the scope of its
+ * <tt>ModuleManager</tt>, but there is no meaning associated with it. The
+ * set of attribute-value pairs attached to the module have no meaning to
+ * the <tt>ModuleManager</tt>, nor does it consult them at all. The point
+ * of these attributes is to attach meta-data for use by
+ * <a href="SearchPolicy.html"><tt>SearchPolicy</tt></a> implementations.
+ * Attributes are represented as an array of <tt>Object</tt>
+ * arrays, i.e., <tt>Object[][]</tt>. Each element in the attribute array is
+ * a two-element <tt>Object</tt> array, where <tt>Module.KEY_IDX</tt> is the attribute's
+ * key and <tt>Module.VALUE_IDX</tt> is the attribute's value.
+ * </p>
+ * <p>
+ * The actual contents of a module is contained in two sets of sources
+ * for its resources and native libraries,
+ * <a href="ResourceSource.html"><tt>ResourceSource</tt></a>s
+ * and <a href="LibrarySource.html"><tt>LibrarySource</tt></a>s, respectively.
+ * Each module also has a <a href="ModuleClassLoader.html"><tt>ModuleClassLoader</tt></a>
+ * associated with it. The <tt>ModuleClassLoader</tt> consults these two types
+ * of sources to find classes, resources, and native libraries.
+ * </p>
+ * @see org.apache.osgi.moduleloader.ModuleManager
+ * @see org.apache.osgi.moduleloader.ModuleClassLoader
+ * @see org.apache.osgi.moduleloader.ResourceSource
+ * @see org.apache.osgi.moduleloader.LibrarySource
+**/
+public class Module
+{
+ /**
+ * This is the index used to retrieve the key of an attribute;
+ * an attribute is represented as an <tt>Object[]</tt> instance.
+ **/
+ public static final int KEY_IDX = 0;
+ /**
+ * This is the index used to retrieve the value of an attribute;
+ * an attribute is represented as an <tt>Object[]</tt> instance.
+ **/
+ public static final int VALUE_IDX = 1;
+
+ private ModuleManager m_mgr = null;
+ private String m_id = null;
+ private boolean m_useParentSource = false;
+ private Map m_attributeMap = new HashMap();
+ private ResourceSource[] m_resSources = null;
+ private LibrarySource[] m_libSources = null;
+ private ModuleClassLoader m_loader = null;
+
+ /**
+ * <p>
+ * Constructs a <tt>Module</tt> instance that will be associated with
+ * the specified <tt>ModuleManager</tt> and will have the specified
+ * identifier, attributes, resource sources, and library sources. In general,
+ * modules should not be created directly, but should be created by making
+ * a call to <tt>ModuleManager.addModule()</tt>.
+ * </p>
+ * @param mgr the <tt>ModuleManager</tt> that will be associated to
+ * the instance.
+ * @param id the identifier of the instance.
+ * @param attributes the set of attributes associated with the instance.
+ * @param resSources the set of <tt>ResourceSource</tt>s associated with
+ * the instance.
+ * @param libSources the set of <tt>LibrarySource</tt>s associated with
+ * the instance.
+ * @param useParentSource a flag indicating whether or not the parent
+ * class loader should be used as a resource source; this is an
+ * ugly hack to allow a module to masquerade as the system
+ * class loader.
+ * @see org.apache.osgi.moduleloader.ModuleManager
+ * @see org.apache.osgi.moduleloader.ResourceSource
+ * @see org.apache.osgi.moduleloader.LibrarySource
+ **/
+ public Module(
+ ModuleManager mgr, String id, Object[][] attributes,
+ ResourceSource[] resSources, LibrarySource[] libSources,
+ boolean useParentSource)
+ {
+ m_mgr = mgr;
+ m_id = id;
+ m_useParentSource = useParentSource;
+ initialize(attributes, resSources, libSources);
+ }
+
+ /**
+ * <p>
+ * Returns the identifier of the module.
+ * </p>
+ * @return the identifier of the module.
+ **/
+ public String getId()
+ {
+ return m_id;
+ }
+
+ /**
+ * <p>
+ * Returns the attribute set associated with this module. Attributes
+ * are represented as an array of <tt>Object</tt> arrays, i.e.,
+ * <tt>Object[][]</tt>. Each element in the attribute array is
+ * two-element <tt>Object</tt> array, where <tt>Module.KEY_IDX</tt>
+ * is the index to the attribute key and <tt>Module.VALUE_IDX</tt>
+ * is the index to the attribute value. The returned array is a
+ * copy and may be freely modified.
+ * </p>
+ * @return the attribute set associated with this module.
+ **/
+ public synchronized Object[][] getAttributes()
+ {
+ Set s = m_attributeMap.entrySet();
+ Object[][] attributes = new Object[s.size()][];
+ Iterator iter = s.iterator();
+ for (int i = 0; iter.hasNext(); i++)
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ attributes[i] = new Object[] { entry.getKey(), entry.getValue() };
+ }
+ return attributes;
+ }
+
+ /**
+ * <p>
+ * Returns the attribute value associated with the specified key.
+ * </p>
+ * @param key the key of the attribute whose value is to be retrieved.
+ * @return the attribute's value or <tt>null</tt>.
+ **/
+ public synchronized Object getAttribute(String key)
+ {
+ return m_attributeMap.get(key);
+ }
+
+ /**
+ * <p>
+ * Sets the attribute value associated with the specified key. The
+ * attribute will be added if it does not currently exist.
+ * </p>
+ * @param key the key of the attribute whose value is to be set.
+ * @param value the new value to be associated with the attribute key.
+ **/
+ public synchronized void setAttribute(String key, Object value)
+ {
+ m_attributeMap.put(key, value);
+ }
+
+ /**
+ * <p>
+ * Returns the array of <tt>ResourceSource</tt>s associated with
+ * the module. The returned array is not a copy and therefore should
+ * not be modified.
+ * </p>
+ * @return the array of <tt>ResourceSource</tt>s associated with
+ * the module.
+ * @see org.apache.osgi.moduleloader.ResourceSource
+ **/
+ public ResourceSource[] getResourceSources()
+ {
+ return m_resSources;
+ }
+
+ /**
+ * <p>
+ * Returns the array of <tt>LibrarySource</tt>s associated with
+ * the module. The returned array is not a copy and therefore should
+ * not be modified.
+ * </p>
+ * @return the array of <tt>LibrarySource</tt>s associated with
+ * the module.
+ * @see org.apache.osgi.moduleloader.LibrarySource
+ **/
+ public LibrarySource[] getLibrarySources()
+ {
+ return m_libSources;
+ }
+
+ /**
+ * <p>
+ * Returns the <tt>ModuleClassLoader</tt> associated with this module.
+ * If a security manager is installed, then this method uses a privileged
+ * action to avoid a security exception being thrown to the caller.
+ * </p>
+ * @return the <tt>ModuleClassLoader</tt> associated with this module.
+ * @see org.apache.osgi.moduleloader.ModuleClassLoader
+ **/
+ public synchronized ModuleClassLoader getClassLoader()
+ {
+ if (m_loader == null)
+ {
+ if (System.getSecurityManager() != null)
+ {
+ m_loader = (ModuleClassLoader) AccessController.doPrivileged(
+ new GetClassLoaderPrivileged(m_mgr, this, m_useParentSource));
+ }
+ else
+ {
+ m_loader = new ModuleClassLoader(m_mgr, this, m_useParentSource);
+ }
+ }
+
+ return m_loader;
+ }
+
+ /**
+ * <p>
+ * Returns the module's identifier.
+ * </p>
+ * @return the module's identifier.
+ **/
+ public String toString()
+ {
+ return m_id;
+ }
+
+ /**
+ * <p>
+ * Resets the module by throwing away its associated class loader and
+ * re-initializing its attributes, resource sources, and library sources
+ * with the specified values.
+ * </p>
+ * @param attributes the new attributes to be associated with the module.
+ * @param resSources the new resource sources to be associated with the module.
+ * @param libSources the new library sources to be associated with the module.
+ * @see org.apache.osgi.moduleloader.ResourceSource
+ * @see org.apache.osgi.moduleloader.LibrarySource
+ **/
+ protected synchronized void reset(
+ Object[][] attributes, ResourceSource[] resSources,
+ LibrarySource[] libSources)
+ {
+ // Throw away class loader.
+ m_loader = null;
+ // Clear attribute map.
+ m_attributeMap.clear();
+ // Close all sources.
+ dispose();
+ // Re-initialize.
+ initialize(attributes, resSources, libSources);
+ }
+
+ /**
+ * <p>
+ * Disposes the module by closing all resource and library sources.
+ * </p>
+ **/
+ protected synchronized void dispose()
+ {
+ // Close sources.
+ for (int i = 0; (m_resSources != null) && (i < m_resSources.length); i++)
+ {
+ m_resSources[i].close();
+ }
+ for (int i = 0; (m_libSources != null) && (i < m_libSources.length); i++)
+ {
+ m_libSources[i].close();
+ }
+ }
+
+ /**
+ * <p>
+ * Initializes the module by copying the specified attribute array into
+ * a map and opening all resource and library sources.
+ * </p>
+ * @param attributes the attributes to be put into a map.
+ * @param resSources the resource sources to be opened.
+ * @param libSources the library sources to be opened.
+ * @see org.apache.osgi.moduleloader.ResourceSource
+ * @see org.apache.osgi.moduleloader.LibrarySource
+ **/
+ private void initialize(
+ Object[][] attributes, ResourceSource[] resSources, LibrarySource[] libSources)
+ {
+ for (int i = 0; (attributes != null) && (i < attributes.length); i++)
+ {
+ m_attributeMap.put(attributes[i][KEY_IDX], attributes[i][VALUE_IDX]);
+ }
+
+ m_resSources = resSources;
+ m_libSources = libSources;
+
+ // Open sources.
+ for (int i = 0; (m_resSources != null) && (i < m_resSources.length); i++)
+ {
+ m_resSources[i].open();
+ }
+ for (int i = 0; (m_libSources != null) && (i < m_libSources.length); i++)
+ {
+ m_libSources[i].open();
+ }
+ }
+
+ private static class GetClassLoaderPrivileged implements PrivilegedAction
+ {
+ private ModuleManager m_mgr = null;
+ private Module m_module = null;
+ private boolean m_useParentSource = false;
+
+ public GetClassLoaderPrivileged(ModuleManager mgr, Module module, boolean useParentSource)
+ {
+ m_mgr = mgr;
+ m_module = module;
+ m_useParentSource = useParentSource;
+ }
+
+ public Object run()
+ {
+ return new ModuleClassLoader(m_mgr, m_module, m_useParentSource);
+ }
+ }
+}
\ No newline at end of file