patch for FELIX-1583 - refactored LDAP and VersionRange classes into own bundle
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@814023 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/sigil/common/osgi/.classpath b/sigil/common/osgi/.classpath
new file mode 100644
index 0000000..c9ffd60
--- /dev/null
+++ b/sigil/common/osgi/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.apache.felix.sigil.classpathContainer"/>
+ <classpathentry kind="output" path="build/classes"/>
+</classpath>
diff --git a/sigil/common/osgi/.project b/sigil/common/osgi/.project
new file mode 100644
index 0000000..9702092
--- /dev/null
+++ b/sigil/common/osgi/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.felix.sigil.common.osgi</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.apache.felix.sigil.eclipse.core.sigilBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.apache.felix.sigil.sigilnature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/common/osgi/build.xml b/sigil/common/osgi/build.xml
new file mode 100644
index 0000000..8f55817
--- /dev/null
+++ b/sigil/common/osgi/build.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="common.osgi" default="build">
+ <import file="../build.xml"/>
+</project>
diff --git a/sigil/common/osgi/ivy.xml b/sigil/common/osgi/ivy.xml
new file mode 100644
index 0000000..a28cff4
--- /dev/null
+++ b/sigil/common/osgi/ivy.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache"
+ module="felix.sigil.common.osgi"
+ status="integration"/>
+ <publications>
+ <artifact name="org.apache.felix.sigil.common.osgi" />
+ </publications>
+</ivy-module>
diff --git a/sigil/common/osgi/sigil.properties b/sigil/common/osgi/sigil.properties
new file mode 100644
index 0000000..2422c42
--- /dev/null
+++ b/sigil/common/osgi/sigil.properties
@@ -0,0 +1,17 @@
+
+# sigil project file, saved by plugin.
+
+-bundles: \
+ org.apache.felix.sigil.common.osgi, \
+
+-sourcedirs: \
+ src, \
+
+-exports: \
+ org.apache.felix.sigil.common.osgi, \
+
+-imports: \
+ org.apache.felix.sigil.common.osgi, \
+ org.osgi.framework, \
+
+# end
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/And.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/And.java
new file mode 100644
index 0000000..026dd8e
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/And.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+import java.util.Map;
+
+
+public class And implements LDAPExpr
+{
+
+ /**
+ */
+ private static final long serialVersionUID = 1L;
+ private LDAPExpr[] children;
+
+
+ public static LDAPExpr apply( LDAPExpr... terms )
+ {
+ if ( terms == null )
+ {
+ throw new NullPointerException( "terms cannot be null" );
+ }
+ else if ( terms.length == 0 )
+ {
+ return Expressions.T;
+ }
+ else if ( terms.length == 1 )
+ {
+ return terms[0];
+ }
+ LDAPExpr[] filtered = new LDAPExpr[terms.length];
+ int ctr = 0;
+ for ( int i = 0; i < terms.length; i++ )
+ {
+ if ( terms[i].equals( Expressions.F ) )
+ return Expressions.F;
+ if ( terms[i].equals( Expressions.T ) )
+ continue;
+ filtered[ctr] = terms[i];
+ ctr++;
+ }
+ if ( ctr == 0 )
+ {
+ return Expressions.T;
+ }
+ else if ( ctr == 1 )
+ {
+ return filtered[0];
+ }
+ LDAPExpr[] andTerms = new LDAPExpr[ctr];
+ System.arraycopy( filtered, 0, andTerms, 0, ctr );
+
+ return new And( andTerms );
+ }
+
+
+ private And( LDAPExpr... children )
+ {
+ this.children = children;
+ }
+
+
+ public boolean eval( Map<String, ?> map )
+ {
+ for ( int i = 0; i < children.length; i++ )
+ {
+ if ( !children[i].eval( map ) )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ public void visit( ExprVisitor v )
+ {
+ v.visitAnd( this );
+ }
+
+
+ public LDAPExpr[] getChildren()
+ {
+ return children;
+ }
+
+
+ public void setChildren( LDAPExpr[] children )
+ {
+ this.children = children;
+ }
+
+
+ @Override
+ public boolean equals( Object other )
+ {
+ if ( other instanceof And )
+ {
+ And that = ( And ) other;
+ if ( children.length != that.children.length )
+ {
+ return false;
+ }
+ for ( int i = 0; i < children.length; i++ )
+ {
+ if ( !children[i].equals( that.children[i] ) )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer( 256 );
+ buf.append( "(&" );
+ for ( int i = 0; i < children.length; i++ )
+ {
+ buf.append( " " ).append( children[i] ).append( " " );
+ }
+ buf.append( ")" );
+ return buf.toString();
+ }
+
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Cardinality.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Cardinality.java
new file mode 100644
index 0000000..b0ad6cb
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Cardinality.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+import java.io.Serializable;
+
+
+/**
+ * Immutable class representing cardinality constraints between two entities.
+ *
+ */
+public class Cardinality implements Serializable
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ public static final Cardinality ZERO_TO_MANY = new Cardinality( 0, -1 );
+ public static final Cardinality ONE_TO_MANY = new Cardinality( 1, -1 );
+ public static final Cardinality ZERO_TO_ONE = new Cardinality( 0, 1 );
+ public static final Cardinality ONE_TO_ONE = new Cardinality( 1, 1 );
+
+ private int min;
+ private int max;
+
+
+ /**
+ * @param min
+ * >=0 (usually 0 or 1)
+ * @param max
+ * >=min or -1 to indicate an unbounded maximum
+ */
+ public Cardinality( int min, int max )
+ {
+ if ( min < 0 )
+ {
+ throw new IllegalArgumentException( "Min cannot be less than 0" );
+ }
+
+ if ( ( max < min ) && ( max != -1 ) )
+ {
+ throw new IllegalArgumentException( "Max cannot be less than min" );
+ }
+
+ this.min = min;
+ this.max = max;
+ }
+
+
+ public int getMin()
+ {
+ return min;
+ }
+
+
+ public int getMax()
+ {
+ return max;
+ }
+
+
+ public String toString()
+ {
+ return min + ".." + ( ( max == -1 ) ? ( "n" ) : ( Integer.toString( max ) ) );
+ }
+
+
+ public boolean isDefined( Cardinality cardinality )
+ {
+ return ( min <= cardinality.min ) && ( ( max == -1 ) || ( max >= cardinality.max ) );
+ }
+
+
+ public boolean isSingleton()
+ {
+ return ( min == 1 ) && ( max == 1 );
+ }
+
+
+ public static Cardinality parse( String stringRep ) throws IllegalArgumentException
+ {
+ stringRep = stringRep.trim();
+
+ int dotdot = stringRep.indexOf( ".." );
+
+ if ( dotdot == -1 )
+ {
+ throw new IllegalArgumentException( "Invalid cardinality string representation, expected .." );
+ }
+
+ String minStr = stringRep.substring( 0, dotdot );
+ String maxStr = stringRep.substring( dotdot + 2 );
+
+ int min = Integer.parseInt( minStr );
+ int max = min;
+
+ if ( "n".equals( maxStr ) )
+ {
+ max = -1;
+ }
+ else
+ {
+ max = Integer.parseInt( maxStr );
+ }
+
+ return cardinality( min, max );
+ }
+
+
+ public static Cardinality cardinality( int min, int max )
+ {
+ Cardinality c = null;
+
+ if ( min == 0 )
+ {
+ if ( max == 1 )
+ {
+ c = ZERO_TO_ONE;
+ }
+ else if ( max == -1 )
+ {
+ c = ZERO_TO_MANY;
+ }
+ }
+ else if ( min == 1 )
+ {
+ if ( max == 1 )
+ {
+ c = ONE_TO_ONE;
+ }
+ else if ( max == -1 )
+ {
+ c = ONE_TO_MANY;
+ }
+ }
+
+ if ( c == null )
+ c = new Cardinality( min, max );
+
+ return c;
+ }
+
+
+ public int hashCode()
+ {
+ return max ^ min;
+ }
+
+
+ public boolean equals( Object o )
+ {
+ if ( o == this )
+ {
+ return true;
+ }
+
+ if ( o == null )
+ {
+ return false;
+ }
+
+ try
+ {
+ Cardinality c = ( Cardinality ) o;
+
+ return ( min == c.min ) && ( max == c.max );
+ }
+ catch ( ClassCastException cce )
+ {
+ return false;
+ }
+ }
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/ExprVisitor.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/ExprVisitor.java
new file mode 100644
index 0000000..c208f87
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/ExprVisitor.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+public interface ExprVisitor
+{
+
+ void visitAnd( And a );
+
+
+ void visitOr( Or o );
+
+
+ void visitNot( Not n );
+
+
+ void visitSimple( SimpleTerm st );
+
+
+ // if none of the above matches use this
+ void visitAny( LDAPExpr ex );
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Expressions.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Expressions.java
new file mode 100644
index 0000000..6dab3ae
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Expressions.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+import java.util.Map;
+
+
+public class Expressions
+{
+
+ public static LDAPExpr and( LDAPExpr... terms )
+ {
+ return And.apply( terms );
+ }
+
+
+ public static LDAPExpr or( LDAPExpr... terms )
+ {
+ return Or.apply( terms );
+ }
+
+
+ public static LDAPExpr not( LDAPExpr e )
+ {
+ return Not.apply( e );
+ }
+
+ public static LDAPExpr T = Bool.TRUE;
+ public static LDAPExpr F = Bool.FALSE;
+
+
+ // supports direct use of wildcards for ease of testing, but not literal *s
+ public static SimpleTerm ex( String name, Ops op, String rhs )
+ {
+
+ rhs = rhs.replace( '*', SimpleTerm.WILDCARD );
+ return new SimpleTerm( name, op, rhs );
+ }
+
+}
+
+class Bool implements LDAPExpr
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ public static final Bool TRUE = new Bool( true );
+ public static final Bool FALSE = new Bool( false );
+
+ private boolean bool;
+
+
+ public Bool( boolean bool )
+ {
+ this.bool = bool;
+ }
+
+
+ public boolean eval( Map<String, ?> map )
+ {
+ return bool;
+ }
+
+
+ public void visit( ExprVisitor v )
+ {
+ }
+
+
+ public LDAPExpr[] getChildren()
+ {
+ return CHILDLESS;
+ }
+
+
+ public String toString()
+ {
+ return "(" + bool + ")";
+ }
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/FilterValidator.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/FilterValidator.java
new file mode 100644
index 0000000..7b21e3d
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/FilterValidator.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+public interface FilterValidator
+{
+
+ public static FilterValidator ACCEPT_ALL = new AcceptEverythingValidator();
+
+
+ boolean validate( LDAPExpr filter );
+
+ static class AcceptEverythingValidator implements FilterValidator
+ {
+
+ public boolean validate( LDAPExpr filter )
+ {
+ return true;
+ }
+
+ }
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/LDAPExpr.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/LDAPExpr.java
new file mode 100644
index 0000000..7d911b6
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/LDAPExpr.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+import java.io.Serializable;
+import java.util.Map;
+
+
+public interface LDAPExpr extends Serializable
+{
+
+ public static final LDAPExpr[] CHILDLESS = new LDAPExpr[]
+ {};
+ public static LDAPExpr ACCEPT_ALL = Expressions.T;
+
+
+ LDAPExpr[] getChildren();
+
+
+ void visit( ExprVisitor v );
+
+
+ boolean equals( Object other );
+
+
+ int hashCode();
+
+
+ boolean eval( Map<String, ?> map );
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/LDAPParseException.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/LDAPParseException.java
new file mode 100644
index 0000000..109a791
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/LDAPParseException.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+public class LDAPParseException extends Exception
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ private ParseState ps;
+ private static final String LINE_SEPARATOR = System.getProperty( "line.separator", "\\r\\n" );
+
+
+ public LDAPParseException( String message, ParseState ps )
+ {
+ super( message );
+ this.ps = ps;
+ }
+
+
+ public LDAPParseException( String message )
+ {
+ super( message );
+ }
+
+
+ @Override
+ public String getMessage()
+ {
+ if ( ps == null )
+ {
+ return super.getMessage();
+ }
+
+ String basicMessage = super.getMessage();
+ StringBuffer buf = new StringBuffer( basicMessage.length() + ps.str.length() * 2 );
+ buf.append( basicMessage ).append( LINE_SEPARATOR );
+ buf.append( ps.str ).append( LINE_SEPARATOR );
+ for ( int i = 0; i < ps.pos; i++ )
+ {
+ buf.append( " " );
+ }
+ buf.append( "^" );
+ return buf.toString();
+ }
+
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/LDAPParser.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/LDAPParser.java
new file mode 100644
index 0000000..11086dd
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/LDAPParser.java
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+import static org.apache.felix.sigil.common.osgi.Expressions.and;
+import static org.apache.felix.sigil.common.osgi.Expressions.not;
+import static org.apache.felix.sigil.common.osgi.Expressions.or;
+import static org.apache.felix.sigil.common.osgi.Ops.APPROX;
+import static org.apache.felix.sigil.common.osgi.Ops.EQ;
+import static org.apache.felix.sigil.common.osgi.Ops.GE;
+import static org.apache.felix.sigil.common.osgi.Ops.GT;
+import static org.apache.felix.sigil.common.osgi.Ops.LE;
+import static org.apache.felix.sigil.common.osgi.Ops.LT;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class LDAPParser
+{
+
+ private static final LDAPParser parser = new LDAPParser();
+
+
+ public static LDAPExpr parseExpression( String strExpr ) throws LDAPParseException
+ {
+ return parser.parse( strExpr );
+ }
+
+
+ public static void main( String[] args )
+ {
+ for ( String arg : args )
+ {
+ try
+ {
+ System.out.println( parseExpression( arg ) );
+ }
+ catch ( LDAPParseException e )
+ {
+ System.out.println( "Failed to parse " + arg );
+ e.printStackTrace();
+ }
+ }
+ }
+
+
+ public LDAPExpr parse( String strExpr ) throws LDAPParseException
+ {
+
+ if ( strExpr == null || strExpr.trim().length() == 0 )
+ {
+ return LDAPExpr.ACCEPT_ALL;
+ }
+
+ ParseState ps = new ParseState( strExpr );
+ LDAPExpr expr = parseExpr( ps );
+ ps.skipWhitespace();
+ if ( !ps.isEndOfString() )
+ {
+ error( "expected end of expression ", ps );
+ }
+ return expr;
+ }
+
+
+ public LDAPExpr parseExpr( ParseState ps ) throws LDAPParseException
+ {
+ ps.skipWhitespace();
+ if ( !( ps.peek() == '(' ) )
+ {
+ error( "expected (", ps );
+ }
+ ps.read();
+ LDAPExpr expr = null;
+ ps.skipWhitespace();
+ char ch = ps.peek();
+ switch ( ch )
+ {
+ case '&':
+ ps.readAndSkipWhiteSpace();
+ List<LDAPExpr> andList = new ArrayList<LDAPExpr>();
+ while ( ps.peek() == '(' )
+ {
+ andList.add( parseExpr( ps ) );
+ ps.skipWhitespace();
+ }
+ LDAPExpr[] andArr = andList.toArray( new LDAPExpr[andList.size()] );
+ expr = and( andArr );
+ break;
+ case '|':
+ ps.readAndSkipWhiteSpace();
+ List<LDAPExpr> orList = new ArrayList<LDAPExpr>();
+ while ( ps.peek() == '(' )
+ {
+ orList.add( parseExpr( ps ) );
+ ps.skipWhitespace();
+ }
+ LDAPExpr[] orArray = orList.toArray( new LDAPExpr[orList.size()] );
+ expr = or( orArray );
+ break;
+ case '!':
+ ps.readAndSkipWhiteSpace();
+ expr = not( parseExpr( ps ) );
+ break;
+ default:
+ if ( isNameChar( ch ) )
+ {
+ expr = parseSimple( ps );
+ }
+ else
+ {
+ error( "unexpected character: '" + ch + "'", ps );
+ }
+ }
+ ps.skipWhitespace();
+ if ( ps.peek() != ')' )
+ {
+ error( "expected )", ps );
+ }
+ ps.read();
+ return expr;
+
+ }
+
+
+ void error( String message, ParseState ps ) throws LDAPParseException
+ {
+ throw new LDAPParseException( message, ps );
+ }
+
+
+ private SimpleTerm parseSimple( ParseState ps ) throws LDAPParseException
+ {
+ // read name
+ StringBuffer name = new StringBuffer( 16 );
+ for ( char c = ps.peek(); !ps.isEndOfString() && isNameChar( c ); c = ps.peek() )
+ {
+ ps.read();
+ name.append( c );
+ }
+ ps.skipWhitespace();
+ Ops op = null;
+ // read op
+ if ( ps.lookingAt( "=" ) )
+ {
+ op = EQ;
+ ps.skip( 1 );
+ }
+ else if ( ps.lookingAt( ">=" ) )
+ {
+ op = GE;
+ ps.skip( 2 );
+ }
+ else if ( ps.lookingAt( "<=" ) )
+ {
+ op = LE;
+ ps.skip( 2 );
+ }
+ else if ( ps.lookingAt( ">" ) )
+ {
+ op = GT;
+ ps.skip( 1 );
+ }
+ else if ( ps.lookingAt( "<" ) )
+ {
+ op = LT;
+ ps.skip( 1 );
+ }
+ else if ( ps.lookingAt( "-=" ) )
+ {
+ op = APPROX;
+ ps.skip( 2 );
+ }
+ else if ( ps.isEndOfString() )
+ {
+ error( "unexpected end of expression", ps );
+ }
+ else
+ {
+ error( "unexpected character: '" + ps.peek() + "'", ps );
+ }
+ ps.skipWhitespace();
+
+ boolean escaped = false;
+ StringBuffer value = new StringBuffer( 16 );
+
+ while ( !ps.isEndOfString() && !Character.isWhitespace( ps.peek() ) && !( ps.peek() == ')' && !escaped ) )
+ {
+
+ char ch = ps.peek();
+
+ if ( ch == '\\' )
+ {
+ escaped = true;
+ ps.read();
+ }
+ else if ( ch == '*' )
+ {
+ if ( escaped )
+ {
+ value.append( ch );
+ escaped = false;
+ }
+ else
+ {
+ value.append( SimpleTerm.WILDCARD );
+ }
+ ps.read();
+ }
+ else if ( isLiteralValue( ch ) )
+ {
+ if ( escaped )
+ {
+ error( "incorrectly applied escape of '" + ch + "'", ps );
+ }
+ value.append( ps.read() );
+ }
+ else if ( isEscapedValue( ch ) )
+ {
+ if ( !escaped )
+ {
+ error( "missing escape for '" + ch + "'", ps );
+ }
+ value.append( ps.read() );
+ escaped = false;
+ }
+ else
+ {
+ error( "unexpected character: '" + ps.peek() + "'", ps );
+ }
+ }
+ ps.skipWhitespace();
+
+ SimpleTerm expr = new SimpleTerm( name.toString(), op, value.toString() );
+
+ return expr;
+ }
+
+
+ private boolean isNameChar( int ch )
+ {
+ return !( Character.isWhitespace( ch ) || ( ch == '(' ) || ( ch == ')' ) || ( ch == '<' ) || ( ch == '>' )
+ || ( ch == '=' ) || ( ch == '~' ) || ( ch == '*' ) || ( ch == '\\' ) );
+ }
+
+
+ private boolean isLiteralValue( int ch )
+ {
+ return !( Character.isWhitespace( ch ) || ( ch == '(' ) || ( ch == ')' ) || ( ch == '*' ) );
+ }
+
+
+ private boolean isEscapedValue( int ch )
+ {
+ return ( ch == '(' ) || ( ch == ')' ) || ( ch == '*' );
+ }
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Not.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Not.java
new file mode 100644
index 0000000..661bd43
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Not.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+import java.util.Map;
+
+
+public class Not implements LDAPExpr
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ private LDAPExpr[] children;
+
+
+ public static LDAPExpr apply( LDAPExpr e )
+ {
+ if ( e == null )
+ {
+ throw new NullPointerException( "cannot apply Not to a null expression" );
+ }
+ if ( e.equals( Expressions.T ) )
+ {
+ return Expressions.F;
+ }
+ if ( e.equals( Expressions.F ) )
+ {
+ return Expressions.T;
+ }
+ return new Not( e );
+ }
+
+
+ private Not( LDAPExpr child )
+ {
+ this.children = new LDAPExpr[]
+ { child };
+ }
+
+
+ public boolean eval( Map<String, ?> map )
+ {
+ return !children[0].eval( map );
+ }
+
+
+ public LDAPExpr getEx()
+ {
+ return children[0];
+ }
+
+
+ public void visit( ExprVisitor v )
+ {
+ v.visitNot( this );
+ }
+
+
+ public LDAPExpr[] getChildren()
+ {
+ return children;
+ }
+
+
+ public void setChild( LDAPExpr child )
+ {
+ this.children = new LDAPExpr[]
+ { child };
+ }
+
+
+ @Override
+ public boolean equals( Object other )
+ {
+ if ( other instanceof Not )
+ {
+ Not that = ( Not ) other;
+ return children[0].equals( that.children[0] );
+ }
+ return false;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer( 256 );
+ buf.append( "(!" );
+ buf.append( " " ).append( children[0] ).append( " " );
+ buf.append( ")" );
+ return buf.toString();
+ }
+
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Ops.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Ops.java
new file mode 100644
index 0000000..200e497
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Ops.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+public enum Ops
+{
+ EQ, GE, LE, GT, LT, APPROX;
+
+ @Override
+ public String toString()
+ {
+ switch ( this )
+ {
+ case EQ:
+ return "=";
+ case GE:
+ return ">=";
+ case LE:
+ return "<=";
+ case GT:
+ return ">";
+ case LT:
+ return "<";
+ case APPROX:
+ return "~=";
+ default:
+ return super.toString();
+ }
+ }
+
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Or.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Or.java
new file mode 100644
index 0000000..0d2cc03
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Or.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+import java.util.Map;
+
+
+public class Or implements LDAPExpr
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ private LDAPExpr[] children;
+
+
+ public static LDAPExpr apply( LDAPExpr... terms )
+ {
+ if ( terms == null )
+ {
+ throw new NullPointerException( "terms cannot be null" );
+ }
+ else if ( terms.length == 0 )
+ {
+ return Expressions.T;
+ }
+ else if ( terms.length == 1 )
+ {
+ return terms[0];
+ }
+ LDAPExpr[] filtered = new LDAPExpr[terms.length];
+ int ctr = 0;
+ for ( int i = 0; i < terms.length; i++ )
+ {
+ if ( terms[i].equals( Expressions.T ) )
+ return Expressions.T;
+ if ( terms[i].equals( Expressions.F ) )
+ continue;
+ filtered[ctr] = terms[i];
+ ctr++;
+ }
+ if ( ctr == 0 )
+ {
+ return Expressions.F;
+ }
+ else if ( ctr == 1 )
+ {
+ return filtered[0];
+ }
+ LDAPExpr[] orTerms = new LDAPExpr[ctr];
+ System.arraycopy( filtered, 0, orTerms, 0, ctr );
+
+ return new Or( orTerms );
+ }
+
+
+ private Or( LDAPExpr... children )
+ {
+ this.children = children;
+ }
+
+
+ public boolean eval( Map<String, ?> map )
+ {
+ for ( int i = 0; i < children.length; i++ )
+ {
+ if ( children[i].eval( map ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public void visit( ExprVisitor v )
+ {
+ v.visitOr( this );
+ }
+
+
+ public LDAPExpr[] getChildren()
+ {
+ return children;
+ }
+
+
+ public void setChildren( LDAPExpr[] children )
+ {
+ this.children = children;
+ }
+
+
+ @Override
+ public boolean equals( Object other )
+ {
+ if ( other instanceof Or )
+ {
+ Or that = ( Or ) other;
+ if ( children.length != that.children.length )
+ {
+ return false;
+ }
+ for ( int i = 0; i < children.length; i++ )
+ {
+ if ( children[i].equals( that.children[i] ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ return false;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer( 256 );
+ buf.append( "(|" );
+ for ( int i = 0; i < children.length; i++ )
+ {
+ buf.append( " " ).append( children[i] ).append( " " );
+ }
+ buf.append( ")" );
+ return buf.toString();
+ }
+
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/ParseState.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/ParseState.java
new file mode 100644
index 0000000..46e98fd
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/ParseState.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+import java.io.Serializable;
+
+
+/**
+ * @author dave
+ *
+ */
+class ParseState implements Serializable
+{
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ int pos;
+
+ String str;
+
+
+ ParseState( String str )
+ {
+ this.str = str;
+ }
+
+
+ public boolean lookingAt( String start )
+ {
+ return str.substring( pos ).startsWith( start );
+ }
+
+
+ public CharSequence skip( int n )
+ {
+ int end = pos + n < str.length() ? pos + n : str.length();
+ int start = pos;
+ pos = end;
+ return str.subSequence( start, end );
+ }
+
+
+ public char read()
+ {
+ char ch = str.charAt( pos );
+ if ( pos < str.length() )
+ {
+ pos++;
+ }
+ return ch;
+ }
+
+
+ public char readAndSkipWhiteSpace()
+ {
+ char ch = read();
+ skipWhitespace();
+ return ch;
+ }
+
+
+ char peek()
+ {
+ if ( isEndOfString() )
+ {
+ return ( char ) -1;
+ }
+ return str.charAt( pos );
+ }
+
+
+ boolean isEndOfString()
+ {
+ return pos == str.length();
+ }
+
+
+ void skipWhitespace()
+ {
+ while ( pos < str.length() && Character.isWhitespace( str.charAt( pos ) ) )
+ {
+ pos++;
+ }
+ }
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/SimpleTerm.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/SimpleTerm.java
new file mode 100644
index 0000000..0ebd791
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/SimpleTerm.java
@@ -0,0 +1,353 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+import java.lang.reflect.Constructor;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+
+
+public class SimpleTerm implements LDAPExpr
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ public static final char WILDCARD = 2 ^ 16 - 1;
+ private static final String WILDCARD_STRING = new String( new char[]
+ { SimpleTerm.WILDCARD } );
+
+ private Ops op;
+ private String name;
+ private String rval;
+
+
+ public SimpleTerm( String name, Ops op, String value )
+ {
+ this.op = op;
+ this.name = name.intern();
+ this.rval = value.intern();
+ }
+
+
+ public String getName()
+ {
+ return name;
+ }
+
+
+ public Ops getOp()
+ {
+ return op;
+ }
+
+
+ public String getRval()
+ {
+ return rval;
+ }
+
+
+ public boolean eval( Map<String, ?> map )
+ {
+
+ Object lval = map.get( name );
+ if ( lval == null )
+ {
+ return false;
+ }
+ else if ( Ops.EQ == op && WILDCARD_STRING.equals( lval ) )
+ {
+ return true;
+ }
+ // any match in the vector will do
+ else if ( lval instanceof Vector<?> )
+ {
+ Vector<?> vec = ( Vector<?> ) lval;
+ for ( Iterator<?> i = vec.iterator(); i.hasNext(); )
+ {
+ if ( check( i.next() ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ // any match in the array will do
+ else if ( lval instanceof Object[] )
+ {
+ Object[] arr = ( Object[] ) lval;
+ for ( int i = 0; i < arr.length; i++ )
+ {
+ if ( check( arr[i] ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ return check( lval );
+ }
+
+
+ @SuppressWarnings("unchecked")
+ private boolean check( Object lval )
+ {
+ if ( lval == null )
+ {
+ return false;
+ }
+ else if ( Ops.EQ == op && WILDCARD_STRING.equals( lval ) )
+ {
+ return true;
+ }
+
+ Object rhs = null;
+
+ if ( lval instanceof String )
+ {
+
+ if ( Ops.APPROX == op )
+ {
+ rhs = collapseWhiteSpace( rval );
+ lval = collapseWhiteSpace( ( String ) lval );
+ }
+
+ if ( Ops.EQ == op || Ops.APPROX == op )
+ {
+ return stringCheck( ( String ) lval );
+ }
+ // rhs already a string
+
+ }
+ else if ( lval.getClass() == Byte.class )
+ {
+ rhs = Byte.valueOf( rval );
+ }
+ else if ( lval.getClass() == Short.class )
+ {
+ rhs = Short.valueOf( rval );
+ }
+ else if ( lval.getClass() == Integer.class )
+ {
+ rhs = Integer.valueOf( rval );
+ }
+ else if ( lval.getClass() == Long.class )
+ {
+ rhs = Long.valueOf( rval );
+ }
+ else if ( lval.getClass() == Float.class )
+ {
+ rhs = Float.valueOf( rval );
+ }
+ else if ( lval.getClass() == Double.class )
+ {
+ rhs = Double.valueOf( rval );
+ }
+ else
+ {
+ try
+ {
+ Constructor<?> stringCtor = lval.getClass().getConstructor( new Class[]
+ { String.class } );
+ rhs = stringCtor.newInstance( rval );
+ }
+ catch ( Exception e )
+ {
+ // log it
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ if ( !( lval instanceof Comparable ) )
+ {
+ return Ops.EQ == op && lval.equals( rval );
+ }
+ else
+ {
+
+ Comparable<? super Object> lhs = ( Comparable<? super Object> ) lval;
+
+ int compare = lhs.compareTo( rhs );
+
+ switch ( op )
+ {
+ case EQ:
+ return compare == 0;
+ case APPROX:
+ return compare == 0;
+ case GE:
+ return compare >= 0;
+ case LE:
+ return compare <= 0;
+ case GT:
+ return compare > 0;
+ case LT:
+ return compare < 0;
+ }
+ }
+
+ return false;
+ }
+
+
+ private boolean stringCheck( String lhs )
+ {
+
+ String rhs;
+ switch ( op )
+ {
+ case EQ:
+ case APPROX:
+ rhs = rval;
+ break;
+ default:
+ return false;
+ }
+
+ int valLength = lhs.length();
+ int patLength = rval.length();
+
+ if ( valLength == 0 && patLength == 0 )
+ {
+ return true;
+ }
+
+ boolean wc = false;
+ int j = 0;
+ for ( int i = 0; i < patLength; i++ )
+ {
+ // trailing wildcards
+ char pc = rhs.charAt( i );
+ if ( j == valLength )
+ {
+ if ( pc != SimpleTerm.WILDCARD )
+ {
+ return false;
+ }
+ continue;
+ }
+ if ( pc == SimpleTerm.WILDCARD )
+ {
+ wc = true;
+ continue;
+ }
+ while ( wc && j < valLength - 1 && lhs.charAt( j ) != pc )
+ {
+ j++;
+ }
+ if ( lhs.charAt( j ) != pc )
+ {
+ return false;
+ }
+ else
+ {
+ wc = false;
+ j++;
+ }
+ }
+ return ( wc || j == valLength );
+
+ }
+
+
+ private String collapseWhiteSpace( String in )
+ {
+ StringBuffer out = new StringBuffer( in.trim().length() );
+ boolean white = false;
+ for ( int i = 0; i < in.length(); i++ )
+ {
+ char ch = in.charAt( i );
+ if ( Character.isWhitespace( ch ) )
+ {
+ white = true;
+ }
+ else
+ {
+ if ( white )
+ {
+ out.append( " " );
+ white = false;
+ }
+ out.append( ch );
+ }
+ }
+ return out.toString();
+ }
+
+
+ public void visit( ExprVisitor v )
+ {
+ v.visitSimple( this );
+ }
+
+
+ public LDAPExpr[] getChildren()
+ {
+ return CHILDLESS;
+ }
+
+
+ @Override
+ public boolean equals( Object other )
+ {
+ if ( other instanceof SimpleTerm )
+ {
+ SimpleTerm that = ( SimpleTerm ) other;
+ return name.equals( that.name ) && op.equals( that.op ) && rval.equals( that.rval );
+ }
+ return false;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ return "(" + name + " " + op.toString() + " " + escape( rval ) + ")";
+ }
+
+
+ private String escape( String raw )
+ {
+ StringBuffer buf = new StringBuffer( raw.length() + 10 );
+ for ( int i = 0; i < raw.length(); i++ )
+ {
+ char ch = raw.charAt( i );
+ switch ( ch )
+ {
+ case SimpleTerm.WILDCARD:
+ buf.append( "*" );
+ break;
+ case '(':
+ case ')':
+ case '*':
+ buf.append( "\\" ).append( ch );
+ break;
+ default:
+ buf.append( ch );
+ }
+ }
+ return buf.toString();
+ }
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Utils.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Utils.java
new file mode 100644
index 0000000..9720702
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/Utils.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class Utils
+{
+ public static MapBuilder map( String name, Object value )
+ {
+ return new MapBuilder().put( name, value );
+ }
+
+
+ public static String toString( Map<String, Object> attrs )
+ {
+ if ( attrs == null )
+ {
+ return "NULL";
+ }
+
+ StringBuffer buf = new StringBuffer( 128 );
+ List<String> keys = new ArrayList<String>( attrs.keySet() );
+ Collections.sort( keys );
+ buf.append( "{" );
+
+ for ( int i = 0; i < keys.size(); i++ )
+ {
+ Object name = keys.get( i );
+ Object value = attrs.get( name );
+ buf.append( name ).append( "=" ).append( value ).append( "," );
+ }
+
+ if ( buf.length() > 1 )
+ {
+ buf.delete( buf.length() - 1, buf.length() );
+ }
+
+ buf.append( "}" );
+
+ return buf.toString();
+ }
+
+ public static class MapBuilder
+ {
+ private Map<String, Object> map = new HashMap<String, Object>();
+
+
+ public MapBuilder put( String name, Object value )
+ {
+ map.put( name, value );
+
+ return this;
+ }
+
+
+ public Map<String, Object> toMap()
+ {
+ return map;
+ }
+ }
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/VersionRange.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/VersionRange.java
new file mode 100644
index 0000000..a4a1558
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/VersionRange.java
@@ -0,0 +1,303 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+import java.io.Serializable;
+
+import org.osgi.framework.Version;
+
+
+public class VersionRange implements Serializable
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ public static final Version INFINITE_VERSION = new Version( Integer.MAX_VALUE, Integer.MAX_VALUE,
+ Integer.MAX_VALUE, "" );
+ public static final VersionRange ANY_VERSION = new VersionRange( false, Version.emptyVersion, INFINITE_VERSION,
+ true );
+
+ private boolean openFloor;
+ private Version floor;
+ private Version ceiling;
+ private boolean openCeiling;
+
+
+ /**
+ * Interval constructor
+ *
+ * @param openFloor Whether the lower bound of the range is inclusive (false) or exclusive (true).
+ * @param floor The lower bound version of the range.
+ * @param ceiling The upper bound version of the range.
+ * @param openCeiling Whether the upper bound of the range is inclusive (false) or exclusive (true).
+ */
+ public VersionRange( boolean openFloor, Version floor, Version ceiling, boolean openCeiling )
+ {
+ this.openFloor = openFloor;
+ this.floor = floor;
+ this.ceiling = ceiling;
+ this.openCeiling = openCeiling;
+ }
+
+
+ /**
+ * atLeast constructor
+ *
+ * @param openFloor
+ * @param floor
+ */
+ public VersionRange( Version atLeast )
+ {
+ this.openFloor = false;
+ this.floor = atLeast;
+ this.ceiling = INFINITE_VERSION;
+ this.openCeiling = true;
+ }
+
+
+ public static VersionRange parseVersionRange( String val ) throws IllegalArgumentException, NumberFormatException
+ {
+ if ( val == null || val.trim().length() == 0 )
+ {
+ return ANY_VERSION;
+ }
+
+ boolean openFloor;
+ boolean openCeiling;
+ val = val.replaceAll( "\\s", "" );
+ val = val.replaceAll( "\"", "" );
+ int fst = val.charAt( 0 );
+ if ( fst == '[' )
+ {
+ openFloor = false;
+ }
+ else if ( fst == '(' )
+ {
+ openFloor = true;
+ }
+ else
+ {
+ Version atLeast = Version.parseVersion( val );
+ return new VersionRange( atLeast );
+ }
+
+ int lst = val.charAt( val.length() - 1 );
+ if ( lst == ']' )
+ {
+ openCeiling = false;
+ }
+ else if ( lst == ')' )
+ {
+ openCeiling = true;
+ }
+ else
+ {
+ throw new IllegalArgumentException( "illegal version range syntax " + val
+ + ": range must end in ')' or ']'" );
+ }
+
+ String inner = val.substring( 1, val.length() - 1 );
+ String[] floorCeiling = inner.split( "," );
+ if ( floorCeiling.length != 2 )
+ {
+ throw new IllegalArgumentException( "illegal version range syntax " + "too many commas" );
+ }
+ Version floor = Version.parseVersion( floorCeiling[0] );
+ Version ceiling = "*".equals( floorCeiling[1] ) ? INFINITE_VERSION : Version.parseVersion( floorCeiling[1] );
+ return new VersionRange( openFloor, floor, ceiling, openCeiling );
+ }
+
+
+ public Version getCeiling()
+ {
+ return ceiling;
+ }
+
+
+ public Version getFloor()
+ {
+ return floor;
+ }
+
+
+ public boolean isOpenCeiling()
+ {
+ return openCeiling;
+ }
+
+
+ public boolean isOpenFloor()
+ {
+ return openFloor;
+ }
+
+
+ public boolean isPointVersion()
+ {
+ return !openFloor && !openCeiling && floor.equals( ceiling );
+ }
+
+
+ /**
+ * test a version to see if it falls in the range
+ *
+ * @param version
+ * @return
+ */
+ public boolean contains( Version version )
+ {
+ if ( version.equals( INFINITE_VERSION ) )
+ {
+ return ceiling.equals( INFINITE_VERSION );
+ }
+ else
+ {
+ return ( version.compareTo( floor ) > 0 && version.compareTo( ceiling ) < 0 )
+ || ( !openFloor && version.equals( floor ) ) || ( !openCeiling && version.equals( ceiling ) );
+ }
+ }
+
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ( ( ceiling == null ) ? 0 : ceiling.hashCode() );
+ result = prime * result + ( ( floor == null ) ? 0 : floor.hashCode() );
+ result = prime * result + ( openCeiling ? 1231 : 1237 );
+ result = prime * result + ( openFloor ? 1231 : 1237 );
+ return result;
+ }
+
+
+ @Override
+ public boolean equals( Object obj )
+ {
+ if ( this == obj )
+ return true;
+ if ( obj == null )
+ return false;
+ if ( getClass() != obj.getClass() )
+ return false;
+ final VersionRange other = ( VersionRange ) obj;
+ if ( ceiling == null )
+ {
+ if ( other.ceiling != null )
+ return false;
+ }
+ else if ( !ceiling.equals( other.ceiling ) )
+ return false;
+ if ( floor == null )
+ {
+ if ( other.floor != null )
+ return false;
+ }
+ else if ( !floor.equals( other.floor ) )
+ return false;
+ if ( openCeiling != other.openCeiling )
+ return false;
+ if ( openFloor != other.openFloor )
+ return false;
+ return true;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ if ( ANY_VERSION.equals( this ) )
+ {
+ return makeString( openFloor, Version.emptyVersion, INFINITE_VERSION, openCeiling );
+ }
+ return makeString( openFloor, floor, ceiling, openCeiling );
+ }
+
+
+ private String makeString( boolean openFloor, Version floor, Version ceiling, boolean openCeiling )
+ {
+ StringBuffer vr = new StringBuffer( 32 );
+ if ( INFINITE_VERSION.equals( ceiling ) )
+ {
+ vr.append( Version.emptyVersion.equals( floor ) ? "0" : floor.toString() );
+ }
+ else
+ {
+ vr.append( openFloor ? "(" : "[" );
+ String floorStr = Version.emptyVersion.equals( floor ) ? "0" : floor.toString();
+ String ceilingStr = ceiling.toString();
+ vr.append( floorStr ).append( "," ).append( ceilingStr );
+ vr.append( openCeiling ? ")" : "]" );
+ }
+ return vr.toString();
+ }
+
+
+ public static VersionRange newInstance( Version pointVersion, VersionRangeBoundingRule lowerBoundRule,
+ VersionRangeBoundingRule upperBoundRule )
+ {
+ Version floor = null;
+ switch ( lowerBoundRule )
+ {
+ case Any:
+ floor = new Version( 0, 0, 0 );
+ break;
+ case Major:
+ floor = new Version( pointVersion.getMajor(), 0, 0 );
+ break;
+ case Minor:
+ floor = new Version( pointVersion.getMajor(), pointVersion.getMinor(), 0 );
+ break;
+ case Micro:
+ floor = new Version( pointVersion.getMajor(), pointVersion.getMinor(), pointVersion.getMicro() );
+ break;
+ case Exact:
+ floor = pointVersion;
+ break;
+ }
+
+ Version ceiling = null;
+ boolean openCeiling = true;
+ switch ( upperBoundRule )
+ {
+ case Any:
+ ceiling = INFINITE_VERSION;
+ break;
+ case Major:
+ ceiling = new Version( pointVersion.getMajor() + 1, 0, 0 );
+ break;
+ case Minor:
+ ceiling = new Version( pointVersion.getMajor(), pointVersion.getMinor() + 1, 0 );
+ break;
+ case Micro:
+ ceiling = new Version( pointVersion.getMajor(), pointVersion.getMinor(), pointVersion.getMicro() + 1 );
+ break;
+ case Exact:
+ ceiling = pointVersion;
+ openCeiling = false;
+ break;
+ }
+
+ return new VersionRange( false, floor, ceiling, openCeiling );
+ }
+}
diff --git a/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/VersionRangeBoundingRule.java b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/VersionRangeBoundingRule.java
new file mode 100644
index 0000000..5ee45da
--- /dev/null
+++ b/sigil/common/osgi/src/org/apache/felix/sigil/common/osgi/VersionRangeBoundingRule.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.osgi;
+
+
+public enum VersionRangeBoundingRule
+{
+ Exact, Micro, Minor, Major, Any
+}
\ No newline at end of file