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://&lt;module-id&gt;/&lt;resource-path&gt;
+ * </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