Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2017-11-03 15:41:37 +0000
committerThomas Watson2017-11-03 15:44:09 +0000
commitaf9796c48cdef3e4f83fbd382fbb69b4441267c4 (patch)
tree9cee46f4f8bc9e12af7b48d244cecd85f921f4e8
parent31812841909c750095865d82b0b87c55b4797c6f (diff)
downloadrt.equinox.framework-af9796c48cdef3e4f83fbd382fbb69b4441267c4.tar.gz
rt.equinox.framework-af9796c48cdef3e4f83fbd382fbb69b4441267c4.tar.xz
rt.equinox.framework-af9796c48cdef3e4f83fbd382fbb69b4441267c4.zip
Change-Id: Ibec9d2adcd3cb52dafa8d25d3b8466af448f0270 Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
-rw-r--r--bundles/org.eclipse.osgi/.settings/.api_filters61
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ExtendedLogServiceImpl.java26
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LoggerImpl.java38
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdaptPermission.java4
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdminPermission.java4
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/CapabilityPermission.java4
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Constants.java34
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkUtil.java1496
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/PackagePermission.java4
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServicePermission.java4
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/log/Logger.java45
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/log/LoggerConsumer.java42
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/resolver/ResolveContext.java2
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/resolver/Resolver.java2
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/util/tracker/BundleTracker.java2
15 files changed, 1752 insertions, 16 deletions
diff --git a/bundles/org.eclipse.osgi/.settings/.api_filters b/bundles/org.eclipse.osgi/.settings/.api_filters
index b7fb09582..41baf7f67 100644
--- a/bundles/org.eclipse.osgi/.settings/.api_filters
+++ b/bundles/org.eclipse.osgi/.settings/.api_filters
@@ -16,6 +16,30 @@
<filter id="403767336">
<message_arguments>
<message_argument value="org.osgi.framework.Constants"/>
+ <message_argument value="INTENT_ASYNC"/>
+ </message_arguments>
+ </filter>
+ <filter id="403767336">
+ <message_arguments>
+ <message_argument value="org.osgi.framework.Constants"/>
+ <message_argument value="INTENT_BASIC"/>
+ </message_arguments>
+ </filter>
+ <filter id="403767336">
+ <message_arguments>
+ <message_argument value="org.osgi.framework.Constants"/>
+ <message_argument value="INTENT_CONFIDENTIAL"/>
+ </message_arguments>
+ </filter>
+ <filter id="403767336">
+ <message_arguments>
+ <message_argument value="org.osgi.framework.Constants"/>
+ <message_argument value="INTENT_PRIVATE"/>
+ </message_arguments>
+ </filter>
+ <filter id="403767336">
+ <message_arguments>
+ <message_argument value="org.osgi.framework.Constants"/>
<message_argument value="SERVICE_CHANGECOUNT"/>
</message_arguments>
</filter>
@@ -37,6 +61,34 @@
<message_arguments>
<message_argument value="1.9"/>
<message_argument value="3.13"/>
+ <message_argument value="INTENT_ASYNC"/>
+ </message_arguments>
+ </filter>
+ <filter id="1209008130">
+ <message_arguments>
+ <message_argument value="1.9"/>
+ <message_argument value="3.13"/>
+ <message_argument value="INTENT_BASIC"/>
+ </message_arguments>
+ </filter>
+ <filter id="1209008130">
+ <message_arguments>
+ <message_argument value="1.9"/>
+ <message_argument value="3.13"/>
+ <message_argument value="INTENT_CONFIDENTIAL"/>
+ </message_arguments>
+ </filter>
+ <filter id="1209008130">
+ <message_arguments>
+ <message_argument value="1.9"/>
+ <message_argument value="3.13"/>
+ <message_argument value="INTENT_PRIVATE"/>
+ </message_arguments>
+ </filter>
+ <filter id="1209008130">
+ <message_arguments>
+ <message_argument value="1.9"/>
+ <message_argument value="3.13"/>
<message_argument value="SERVICE_CHANGECOUNT"/>
</message_arguments>
</filter>
@@ -144,6 +196,15 @@
</message_arguments>
</filter>
</resource>
+ <resource path="osgi/src/org/osgi/service/log/LoggerConsumer.java" type="org.osgi.service.log.LoggerConsumer">
+ <filter id="1108344834">
+ <message_arguments>
+ <message_argument value="1.4"/>
+ <message_argument value="3.13"/>
+ <message_argument value="org.osgi.service.log.LoggerConsumer"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="osgi/src/org/osgi/service/log/LoggerFactory.java" type="org.osgi.service.log.LoggerFactory">
<filter id="1108344834">
<message_arguments>
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ExtendedLogServiceImpl.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ExtendedLogServiceImpl.java
index cc4d186cd..43c1a1efe 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ExtendedLogServiceImpl.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ExtendedLogServiceImpl.java
@@ -15,6 +15,7 @@ import org.eclipse.osgi.internal.log.ExtendedLogServiceFactory.EquinoxLoggerCont
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.FormatterLogger;
+import org.osgi.service.log.LoggerConsumer;
import org.osgi.service.log.admin.LoggerContext;
public class ExtendedLogServiceImpl implements ExtendedLogService {
@@ -308,6 +309,31 @@ public class ExtendedLogServiceImpl implements ExtendedLogService {
getLogger((String) null).audit(format, arguments);
}
+ @Override
+ public <E extends Exception> void trace(LoggerConsumer<E> consumer) throws E {
+ getLogger((String) null).trace(consumer);
+ }
+
+ @Override
+ public <E extends Exception> void debug(LoggerConsumer<E> consumer) throws E {
+ getLogger((String) null).debug(consumer);
+ }
+
+ @Override
+ public <E extends Exception> void info(LoggerConsumer<E> consumer) throws E {
+ getLogger((String) null).info(consumer);
+ }
+
+ @Override
+ public <E extends Exception> void warn(LoggerConsumer<E> consumer) throws E {
+ getLogger((String) null).warn(consumer);
+ }
+
+ @Override
+ public <E extends Exception> void error(LoggerConsumer<E> consumer) throws E {
+ getLogger((String) null).error(consumer);
+ }
+
void applyLogLevels(EquinoxLoggerContext effectiveLoggerContext) {
for (Map<String, LoggerImpl> loggers : loggerCache.values()) {
for (LoggerImpl logger : loggers.values()) {
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LoggerImpl.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LoggerImpl.java
index 1876e10ba..90a2ee3bf 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LoggerImpl.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LoggerImpl.java
@@ -12,8 +12,7 @@ import java.util.regex.Pattern;
import org.eclipse.equinox.log.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceReference;
-import org.osgi.service.log.LogLevel;
-import org.osgi.service.log.LogService;
+import org.osgi.service.log.*;
import org.osgi.service.log.admin.LoggerContext;
public class LoggerImpl implements Logger {
@@ -238,6 +237,41 @@ public class LoggerImpl implements Logger {
log(LogLevel.AUDIT, format, arguments);
}
+ @Override
+ public <E extends Exception> void trace(LoggerConsumer<E> consumer) throws E {
+ if (isTraceEnabled()) {
+ consumer.accept(this);
+ }
+ }
+
+ @Override
+ public <E extends Exception> void debug(LoggerConsumer<E> consumer) throws E {
+ if (isDebugEnabled()) {
+ consumer.accept(this);
+ }
+ }
+
+ @Override
+ public <E extends Exception> void info(LoggerConsumer<E> consumer) throws E {
+ if (isInfoEnabled()) {
+ consumer.accept(this);
+ }
+ }
+
+ @Override
+ public <E extends Exception> void warn(LoggerConsumer<E> consumer) throws E {
+ if (isWarnEnabled()) {
+ consumer.accept(this);
+ }
+ }
+
+ @Override
+ public <E extends Exception> void error(LoggerConsumer<E> consumer) throws E {
+ if (isErrorEnabled()) {
+ consumer.accept(this);
+ }
+ }
+
private static final Pattern pattern = Pattern.compile("(\\\\?)(\\\\?)(\\{\\})"); //$NON-NLS-1$
private void log(LogLevel level, String format, Object... arguments) {
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdaptPermission.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdaptPermission.java
index 0ac35f219..a2bc7aac5 100644
--- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdaptPermission.java
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdaptPermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2010, 2016). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2010, 2017). All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -443,7 +443,7 @@ public final class AdaptPermission extends BasicPermission {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
- map.put("id", new Long(bundle.getBundleId()));
+ map.put("id", Long.valueOf(bundle.getBundleId()));
map.put("location", bundle.getLocation());
String name = bundle.getSymbolicName();
if (name != null) {
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdminPermission.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdminPermission.java
index 35d7725e3..7906054ba 100644
--- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdminPermission.java
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/AdminPermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2000, 2016). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2000, 2017). All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -843,7 +843,7 @@ public final class AdminPermission extends BasicPermission {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
- map.put("id", new Long(bundle.getBundleId()));
+ map.put("id", Long.valueOf(bundle.getBundleId()));
map.put("location", bundle.getLocation());
String name = bundle.getSymbolicName();
if (name != null) {
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/CapabilityPermission.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/CapabilityPermission.java
index 90e1d0c01..8a38df04e 100644
--- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/CapabilityPermission.java
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/CapabilityPermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2000, 2016). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2000, 2017). All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -507,7 +507,7 @@ public final class CapabilityPermission extends BasicPermission {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
- props.put("id", new Long(bundle.getBundleId()));
+ props.put("id", Long.valueOf(bundle.getBundleId()));
props.put("location", bundle.getLocation());
String name = bundle.getSymbolicName();
if (name != null) {
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Constants.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Constants.java
index 2747cd815..b53b1a9b4 100644
--- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Constants.java
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Constants.java
@@ -1846,4 +1846,38 @@ public interface Constants {
* @since 1.9
*/
String SERVICE_CHANGECOUNT = "service.changecount";
+
+ /**
+ * Intent supported by Remote Services implementations that support Basic
+ * Remote Services as defined for the {@code osgi.basic} intent.
+ *
+ * @since 1.9
+ */
+ String INTENT_BASIC = "osgi.basic";
+
+ /**
+ * Intent supported by Remote Service implementations that support
+ * Asynchronous Remote Services as defined for the {@code osgi.async}
+ * intent.
+ *
+ * @since 1.9
+ */
+ String INTENT_ASYNC = "osgi.async";
+
+ /**
+ * Intent supported by Remote Service implementation that provide
+ * confidential communications as defined for the {@code osgi.confidential}
+ * intent.
+ *
+ * @since 1.9
+ */
+ String INTENT_CONFIDENTIAL = "osgi.confidential";
+
+ /**
+ * Intent supported by Remote Service implementations that provide private
+ * communications as defined for the {@code osgi.private} intent.
+ *
+ * @since 1.9
+ */
+ String INTENT_PRIVATE = "osgi.private";
}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkUtil.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkUtil.java
index cb98cc18d..90d50e4d3 100644
--- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkUtil.java
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/FrameworkUtil.java
@@ -32,8 +32,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+
import javax.security.auth.x500.X500Principal;
-import org.eclipse.osgi.internal.framework.FilterImpl;
/**
* Framework Utility class.
@@ -220,6 +220,1500 @@ public class FrameworkUtil {
}
/**
+ * RFC 1960-based Filter. Filter objects can be created by calling the
+ * constructor with the desired filter string. A Filter object can be called
+ * numerous times to determine if the match argument matches the filter
+ * string that was used to create the Filter object.
+ *
+ * <p>
+ * The syntax of a filter string is the string representation of LDAP search
+ * filters as defined in RFC 1960: <i>A String Representation of LDAP Search
+ * Filters</i> (available at http://www.ietf.org/rfc/rfc1960.txt). It should
+ * be noted that RFC 2254: <i>A String Representation of LDAP Search
+ * Filters</i> (available at http://www.ietf.org/rfc/rfc2254.txt) supersedes
+ * RFC 1960 but only adds extensible matching and is not applicable for this
+ * API.
+ *
+ * <p>
+ * The string representation of an LDAP search filter is defined by the
+ * following grammar. It uses a prefix format.
+ *
+ * <pre>
+ * &lt;filter&gt; ::= '(' &lt;filtercomp&gt; ')'
+ * &lt;filtercomp&gt; ::= &lt;and&gt; | &lt;or&gt; | &lt;not&gt; | &lt;item&gt;
+ * &lt;and&gt; ::= '&amp;' &lt;filterlist&gt;
+ * &lt;or&gt; ::= '|' &lt;filterlist&gt;
+ * &lt;not&gt; ::= '!' &lt;filter&gt;
+ * &lt;filterlist&gt; ::= &lt;filter&gt; | &lt;filter&gt; &lt;filterlist&gt;
+ * &lt;item&gt; ::= &lt;simple&gt; | &lt;present&gt; | &lt;substring&gt;
+ * &lt;simple&gt; ::= &lt;attr&gt; &lt;filtertype&gt; &lt;value&gt;
+ * &lt;filtertype&gt; ::= &lt;equal&gt; | &lt;approx&gt; | &lt;greater&gt; | &lt;less&gt;
+ * &lt;equal&gt; ::= '='
+ * &lt;approx&gt; ::= '&tilde;='
+ * &lt;greater&gt; ::= '&gt;='
+ * &lt;less&gt; ::= '&lt;='
+ * &lt;present&gt; ::= &lt;attr&gt; '=*'
+ * &lt;substring&gt; ::= &lt;attr&gt; '=' &lt;initial&gt; &lt;any&gt; &lt;final&gt;
+ * &lt;initial&gt; ::= NULL | &lt;value&gt;
+ * &lt;any&gt; ::= '*' &lt;starval&gt;
+ * &lt;starval&gt; ::= NULL | &lt;value&gt; '*' &lt;starval&gt;
+ * &lt;final&gt; ::= NULL | &lt;value&gt;
+ * </pre>
+ *
+ * {@code &lt;attr&gt;} is a string representing an attribute, 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.
+ * {@code &lt;value&gt;} is a string representing the value, or part of one,
+ * of a key in the properties objects of the registered services. If a
+ * {@code &lt;value&gt;} must contain one of the characters ' {@code *}' or
+ * '{@code (}' or '{@code )}', these characters should be escaped by
+ * preceding them with the backslash '{@code \}' character. Note that
+ * although both the {@code &lt;substring&gt;} and {@code &lt;present&gt;}
+ * productions can produce the {@code 'attr=*'} construct, this construct is
+ * used only to denote a presence filter.
+ *
+ * <p>
+ * Examples of LDAP filters are:
+ *
+ * <pre>
+ * &quot;(cn=Babs Jensen)&quot;
+ * &quot;(!(cn=Tim Howes))&quot;
+ * &quot;(&amp;(&quot; + Constants.OBJECTCLASS + &quot;=Person)(|(sn=Jensen)(cn=Babs J*)))&quot;
+ * &quot;(o=univ*of*mich*)&quot;
+ * </pre>
+ *
+ * <p>
+ * The approximate match ({@code ~=}) is implementation specific but should
+ * at least ignore case and white space differences. Optional are codes like
+ * soundex or other smart "closeness" comparisons.
+ *
+ * <p>
+ * Comparison of values is not straightforward. Strings are compared
+ * differently than numbers and it is possible for a key to have multiple
+ * values. Note that that keys in the match argument must always be strings.
+ * The comparison is defined by the object type of the key's value. The
+ * following rules apply for comparison:
+ *
+ * <blockquote>
+ * <TABLE BORDER=0>
+ * <TR>
+ * <TD><b>Property Value Type </b></TD>
+ * <TD><b>Comparison Type</b></TD>
+ * </TR>
+ * <TR>
+ * <TD>String</TD>
+ * <TD>String comparison</TD>
+ * </TR>
+ * <TR valign=top>
+ * <TD>Integer, Long, Float, Double, Byte, Short, BigInteger, BigDecimal</TD>
+ * <TD>numerical comparison</TD>
+ * </TR>
+ * <TR>
+ * <TD>Character</TD>
+ * <TD>character comparison</TD>
+ * </TR>
+ * <TR>
+ * <TD>Boolean</TD>
+ * <TD>equality comparisons only</TD>
+ * </TR>
+ * <TR>
+ * <TD>[] (array)</TD>
+ * <TD>recursively applied to values</TD>
+ * </TR>
+ * <TR>
+ * <TD>Collection</TD>
+ * <TD>recursively applied to values</TD>
+ * </TR>
+ * </TABLE>
+ * Note: arrays of primitives are also supported. </blockquote>
+ *
+ * A filter matches a key that has multiple values if it matches at least
+ * one of those values. For example,
+ *
+ * <pre>
+ * Dictionary d = new Hashtable();
+ * d.put(&quot;cn&quot;, new String[] {&quot;a&quot;, &quot;b&quot;, &quot;c&quot;});
+ * </pre>
+ *
+ * d will match {@code (cn=a)} and also {@code (cn=b)}
+ *
+ * <p>
+ * A filter component that references a key having an unrecognizable data
+ * type will evaluate to {@code false} .
+ */
+ static private final class FilterImpl implements Filter {
+ /* filter operators */
+ private static final int EQUAL = 1;
+ private static final int APPROX = 2;
+ private static final int GREATER = 3;
+ private static final int LESS = 4;
+ private static final int PRESENT = 5;
+ private static final int SUBSTRING = 6;
+ private static final int AND = 7;
+ private static final int OR = 8;
+ private static final int NOT = 9;
+
+ /** filter operation */
+ private final int op;
+ /** filter attribute or null if operation AND, OR or NOT */
+ private final String attr;
+ /** filter operands */
+ private final Object value;
+
+ /* normalized filter string for Filter object */
+ private transient String filterString;
+
+ /**
+ * Constructs a {@link FilterImpl} object. This filter object may be
+ * used to match a {@link ServiceReference} or a Dictionary.
+ *
+ * <p>
+ * If the filter cannot be parsed, an {@link InvalidSyntaxException}
+ * will be thrown with a human readable message where the filter became
+ * unparsable.
+ *
+ * @param filterString the filter string.
+ * @throws InvalidSyntaxException If the filter parameter contains an
+ * invalid filter string that cannot be parsed.
+ */
+ static FilterImpl newInstance(String filterString) throws InvalidSyntaxException {
+ return new Parser(filterString).parse();
+ }
+
+ FilterImpl(int operation, String attr, Object value) {
+ this.op = operation;
+ this.attr = attr;
+ this.value = value;
+ filterString = null;
+ }
+
+ /**
+ * Filter using a service's properties.
+ * <p>
+ * This {@code Filter} is executed using the keys and values of the
+ * referenced service's properties. The keys are looked up in a case
+ * insensitive manner.
+ *
+ * @param reference The reference to the service whose properties are
+ * used in the match.
+ * @return {@code true} if the service's properties match this
+ * {@code Filter}; {@code false} otherwise.
+ */
+ @Override
+ public boolean match(ServiceReference<?> reference) {
+ return matches(new ServiceReferenceMap(reference));
+ }
+
+ /**
+ * Filter using a {@code Dictionary} with case insensitive key lookup.
+ * This {@code Filter} is executed using the specified
+ * {@code Dictionary}'s keys and values. The keys are looked up in a
+ * case insensitive manner.
+ *
+ * @param dictionary The {@code Dictionary} whose key/value pairs are
+ * used in the match.
+ * @return {@code true} if the {@code Dictionary}'s values match this
+ * filter; {@code false} otherwise.
+ * @throws IllegalArgumentException If {@code dictionary} contains case
+ * variants of the same key name.
+ */
+ @Override
+ public boolean match(Dictionary<String, ?> dictionary) {
+ return matches(new CaseInsensitiveMap(dictionary));
+ }
+
+ /**
+ * Filter using a {@code Dictionary}. This {@code Filter} is executed
+ * using the specified {@code Dictionary}'s keys and values. The keys
+ * are looked up in a normal manner respecting case.
+ *
+ * @param dictionary The {@code Dictionary} whose key/value pairs are
+ * used in the match.
+ * @return {@code true} if the {@code Dictionary}'s values match this
+ * filter; {@code false} otherwise.
+ * @since 1.3
+ */
+ @Override
+ public boolean matchCase(Dictionary<String, ?> dictionary) {
+ switch (op) {
+ case AND : {
+ FilterImpl[] filters = (FilterImpl[]) value;
+ for (FilterImpl f : filters) {
+ if (!f.matchCase(dictionary)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case OR : {
+ FilterImpl[] filters = (FilterImpl[]) value;
+ for (FilterImpl f : filters) {
+ if (f.matchCase(dictionary)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ case NOT : {
+ FilterImpl filter = (FilterImpl) value;
+ return !filter.matchCase(dictionary);
+ }
+
+ case SUBSTRING :
+ case EQUAL :
+ case GREATER :
+ case LESS :
+ case APPROX : {
+ Object prop = (dictionary == null) ? null : dictionary.get(attr);
+ return compare(op, prop, value);
+ }
+
+ case PRESENT : {
+ Object prop = (dictionary == null) ? null : dictionary.get(attr);
+ return prop != null;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Filter using a {@code Map}. This {@code Filter} is executed using the
+ * specified {@code Map}'s keys and values. The keys are looked up in a
+ * normal manner respecting case.
+ *
+ * @param map The {@code Map} whose key/value pairs are used in the
+ * match. Maps with {@code null} key or values are not supported.
+ * A {@code null} value is considered not present to the filter.
+ * @return {@code true} if the {@code Map}'s values match this filter;
+ * {@code false} otherwise.
+ * @since 1.6
+ */
+ @Override
+ public boolean matches(Map<String, ?> map) {
+ switch (op) {
+ case AND : {
+ FilterImpl[] filters = (FilterImpl[]) value;
+ for (FilterImpl f : filters) {
+ if (!f.matches(map)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case OR : {
+ FilterImpl[] filters = (FilterImpl[]) value;
+ for (FilterImpl f : filters) {
+ if (f.matches(map)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ case NOT : {
+ FilterImpl filter = (FilterImpl) value;
+ return !filter.matches(map);
+ }
+
+ case SUBSTRING :
+ case EQUAL :
+ case GREATER :
+ case LESS :
+ case APPROX : {
+ Object prop = (map == null) ? null : map.get(attr);
+ return compare(op, prop, value);
+ }
+
+ case PRESENT : {
+ Object prop = (map == null) ? null : map.get(attr);
+ return prop != null;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns this {@code Filter}'s filter string.
+ * <p>
+ * The filter string is normalized by removing whitespace which does not
+ * affect the meaning of the filter.
+ *
+ * @return This {@code Filter}'s filter string.
+ */
+ @Override
+ public String toString() {
+ String result = filterString;
+ if (result == null) {
+ filterString = result = normalize().toString();
+ }
+ return result;
+ }
+
+ /**
+ * Returns this {@code Filter}'s normalized filter string.
+ * <p>
+ * The filter string is normalized by removing whitespace which does not
+ * affect the meaning of the filter.
+ *
+ * @return This {@code Filter}'s filter string.
+ */
+ private StringBuilder normalize() {
+ StringBuilder sb = new StringBuilder();
+ sb.append('(');
+
+ switch (op) {
+ case AND : {
+ sb.append('&');
+
+ FilterImpl[] filters = (FilterImpl[]) value;
+ for (FilterImpl f : filters) {
+ sb.append(f.normalize());
+ }
+
+ break;
+ }
+
+ case OR : {
+ sb.append('|');
+
+ FilterImpl[] filters = (FilterImpl[]) value;
+ for (FilterImpl f : filters) {
+ sb.append(f.normalize());
+ }
+
+ break;
+ }
+
+ case NOT : {
+ sb.append('!');
+ FilterImpl filter = (FilterImpl) value;
+ sb.append(filter.normalize());
+
+ break;
+ }
+
+ case SUBSTRING : {
+ sb.append(attr);
+ sb.append('=');
+
+ String[] substrings = (String[]) value;
+
+ for (String substr : substrings) {
+ if (substr == null) /* * */{
+ sb.append('*');
+ } else /* xxx */{
+ sb.append(encodeValue(substr));
+ }
+ }
+
+ break;
+ }
+ case EQUAL : {
+ sb.append(attr);
+ sb.append('=');
+ sb.append(encodeValue((String) value));
+
+ break;
+ }
+ case GREATER : {
+ sb.append(attr);
+ sb.append(">=");
+ sb.append(encodeValue((String) value));
+
+ break;
+ }
+ case LESS : {
+ sb.append(attr);
+ sb.append("<=");
+ sb.append(encodeValue((String) value));
+
+ break;
+ }
+ case APPROX : {
+ sb.append(attr);
+ sb.append("~=");
+ sb.append(encodeValue(approxString((String) value)));
+
+ break;
+ }
+
+ case PRESENT : {
+ sb.append(attr);
+ sb.append("=*");
+
+ break;
+ }
+ }
+
+ sb.append(')');
+
+ return sb;
+ }
+
+ /**
+ * Compares this {@code Filter} to another {@code Filter}.
+ *
+ * <p>
+ * This implementation returns the result of calling
+ * {@code this.toString().equals(obj.toString()}.
+ *
+ * @param obj The object to compare against this {@code Filter}.
+ * @return If the other object is a {@code Filter} object, then returns
+ * the result of calling
+ * {@code this.toString().equals(obj.toString()}; {@code false}
+ * otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (!(obj instanceof Filter)) {
+ return false;
+ }
+
+ return this.toString().equals(obj.toString());
+ }
+
+ /**
+ * Returns the hashCode for this {@code Filter}.
+ *
+ * <p>
+ * This implementation returns the result of calling
+ * {@code this.toString().hashCode()}.
+ *
+ * @return The hashCode of this {@code Filter}.
+ */
+ @Override
+ public int hashCode() {
+ return this.toString().hashCode();
+ }
+
+ /**
+ * Encode the value string such that '(', '*', ')' and '\' are escaped.
+ *
+ * @param value unencoded value string.
+ * @return encoded value string.
+ */
+ private static String encodeValue(String value) {
+ boolean encoded = false;
+ int inlen = value.length();
+ int outlen = inlen << 1; /* inlen 2 */
+
+ char[] output = new char[outlen];
+ value.getChars(0, inlen, output, inlen);
+
+ int cursor = 0;
+ for (int i = inlen; i < outlen; i++) {
+ char c = output[i];
+
+ switch (c) {
+ case '(' :
+ case '*' :
+ case ')' :
+ case '\\' : {
+ output[cursor] = '\\';
+ cursor++;
+ encoded = true;
+
+ break;
+ }
+ }
+
+ output[cursor] = c;
+ cursor++;
+ }
+
+ return encoded ? new String(output, 0, cursor) : value;
+ }
+
+ private boolean compare(int operation, Object value1, Object value2) {
+ if (value1 == null) {
+ return false;
+ }
+ if (value1 instanceof String) {
+ return compare_String(operation, (String) value1, value2);
+ }
+ if (value1 instanceof Version) {
+ return compare_Version(operation, (Version) value1, value2);
+ }
+
+ Class<?> clazz = value1.getClass();
+ if (clazz.isArray()) {
+ Class<?> type = clazz.getComponentType();
+ if (type.isPrimitive()) {
+ return compare_PrimitiveArray(operation, type, value1, value2);
+ }
+ return compare_ObjectArray(operation, (Object[]) value1, value2);
+ }
+ if (value1 instanceof Collection<?>) {
+ return compare_Collection(operation, (Collection<?>) value1, value2);
+ }
+ if (value1 instanceof Integer) {
+ return compare_Integer(operation, ((Integer) value1).intValue(), value2);
+ }
+ if (value1 instanceof Long) {
+ return compare_Long(operation, ((Long) value1).longValue(), value2);
+ }
+ if (value1 instanceof Byte) {
+ return compare_Byte(operation, ((Byte) value1).byteValue(), value2);
+ }
+ if (value1 instanceof Short) {
+ return compare_Short(operation, ((Short) value1).shortValue(), value2);
+ }
+ if (value1 instanceof Character) {
+ return compare_Character(operation, ((Character) value1).charValue(), value2);
+ }
+ if (value1 instanceof Float) {
+ return compare_Float(operation, ((Float) value1).floatValue(), value2);
+ }
+ if (value1 instanceof Double) {
+ return compare_Double(operation, ((Double) value1).doubleValue(), value2);
+ }
+ if (value1 instanceof Boolean) {
+ return compare_Boolean(operation, ((Boolean) value1).booleanValue(), value2);
+ }
+ if (value1 instanceof Comparable<?>) {
+ @SuppressWarnings("unchecked")
+ Comparable<Object> comparable = (Comparable<Object>) value1;
+ return compare_Comparable(operation, comparable, value2);
+ }
+ return compare_Unknown(operation, value1, value2);
+ }
+
+ private boolean compare_Collection(int operation, Collection<?> collection, Object value2) {
+ for (Object value1 : collection) {
+ if (compare(operation, value1, value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_ObjectArray(int operation, Object[] array, Object value2) {
+ for (Object value1 : array) {
+ if (compare(operation, value1, value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_PrimitiveArray(int operation, Class<?> type, Object primarray, Object value2) {
+ if (Integer.TYPE.isAssignableFrom(type)) {
+ int[] array = (int[]) primarray;
+ for (int value1 : array) {
+ if (compare_Integer(operation, value1, value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Long.TYPE.isAssignableFrom(type)) {
+ long[] array = (long[]) primarray;
+ for (long value1 : array) {
+ if (compare_Long(operation, value1, value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Byte.TYPE.isAssignableFrom(type)) {
+ byte[] array = (byte[]) primarray;
+ for (byte value1 : array) {
+ if (compare_Byte(operation, value1, value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Short.TYPE.isAssignableFrom(type)) {
+ short[] array = (short[]) primarray;
+ for (short value1 : array) {
+ if (compare_Short(operation, value1, value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Character.TYPE.isAssignableFrom(type)) {
+ char[] array = (char[]) primarray;
+ for (char value1 : array) {
+ if (compare_Character(operation, value1, value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Float.TYPE.isAssignableFrom(type)) {
+ float[] array = (float[]) primarray;
+ for (float value1 : array) {
+ if (compare_Float(operation, value1, value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Double.TYPE.isAssignableFrom(type)) {
+ double[] array = (double[]) primarray;
+ for (double value1 : array) {
+ if (compare_Double(operation, value1, value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Boolean.TYPE.isAssignableFrom(type)) {
+ boolean[] array = (boolean[]) primarray;
+ for (boolean value1 : array) {
+ if (compare_Boolean(operation, value1, value2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return false;
+ }
+
+ private boolean compare_String(int operation, String string, Object value2) {
+ switch (operation) {
+ case SUBSTRING : {
+ String[] substrings = (String[]) value2;
+ int pos = 0;
+ for (int i = 0, size = substrings.length; i < size; i++) {
+ String substr = substrings[i];
+
+ if (i + 1 < size) /* if this is not that last substr */{
+ if (substr == null) /* * */{
+ String substr2 = substrings[i + 1];
+
+ if (substr2 == null) /* ** */
+ continue; /* ignore first star */
+ /* xxx */
+ int index = string.indexOf(substr2, pos);
+ if (index == -1) {
+ return false;
+ }
+
+ pos = index + substr2.length();
+ if (i + 2 < size) // if there are more
+ // substrings, increment
+ // over the string we just
+ // matched; otherwise need
+ // to do the last substr
+ // check
+ i++;
+ } else /* xxx */{
+ int len = substr.length();
+ if (string.regionMatches(pos, substr, 0, len)) {
+ pos += len;
+ } else {
+ return false;
+ }
+ }
+ } else /* last substr */{
+ if (substr == null) /* * */{
+ return true;
+ }
+ /* xxx */
+ return string.endsWith(substr);
+ }
+ }
+
+ return true;
+ }
+ case EQUAL : {
+ return string.equals(value2);
+ }
+ case APPROX : {
+ string = approxString(string);
+ String string2 = approxString((String) value2);
+
+ return string.equalsIgnoreCase(string2);
+ }
+ case GREATER : {
+ return string.compareTo((String) value2) >= 0;
+ }
+ case LESS : {
+ return string.compareTo((String) value2) <= 0;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Integer(int operation, int intval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ int intval2;
+ try {
+ intval2 = Integer.parseInt(((String) value2).trim());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ switch (operation) {
+ case APPROX :
+ case EQUAL : {
+ return intval == intval2;
+ }
+ case GREATER : {
+ return intval >= intval2;
+ }
+ case LESS : {
+ return intval <= intval2;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Long(int operation, long longval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ long longval2;
+ try {
+ longval2 = Long.parseLong(((String) value2).trim());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+
+ switch (operation) {
+ case APPROX :
+ case EQUAL : {
+ return longval == longval2;
+ }
+ case GREATER : {
+ return longval >= longval2;
+ }
+ case LESS : {
+ return longval <= longval2;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Byte(int operation, byte byteval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ byte byteval2;
+ try {
+ byteval2 = Byte.parseByte(((String) value2).trim());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+
+ switch (operation) {
+ case APPROX :
+ case EQUAL : {
+ return byteval == byteval2;
+ }
+ case GREATER : {
+ return byteval >= byteval2;
+ }
+ case LESS : {
+ return byteval <= byteval2;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Short(int operation, short shortval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ short shortval2;
+ try {
+ shortval2 = Short.parseShort(((String) value2).trim());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+
+ switch (operation) {
+ case APPROX :
+ case EQUAL : {
+ return shortval == shortval2;
+ }
+ case GREATER : {
+ return shortval >= shortval2;
+ }
+ case LESS : {
+ return shortval <= shortval2;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Character(int operation, char charval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ char charval2;
+ try {
+ charval2 = ((String) value2).charAt(0);
+ } catch (IndexOutOfBoundsException e) {
+ return false;
+ }
+
+ switch (operation) {
+ case EQUAL : {
+ return charval == charval2;
+ }
+ case APPROX : {
+ return (charval == charval2) || (Character.toUpperCase(charval) == Character.toUpperCase(charval2)) || (Character.toLowerCase(charval) == Character.toLowerCase(charval2));
+ }
+ case GREATER : {
+ return charval >= charval2;
+ }
+ case LESS : {
+ return charval <= charval2;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Boolean(int operation, boolean boolval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ boolean boolval2 = Boolean.valueOf(((String) value2).trim()).booleanValue();
+ switch (operation) {
+ case APPROX :
+ case EQUAL :
+ case GREATER :
+ case LESS : {
+ return boolval == boolval2;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Float(int operation, float floatval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ float floatval2;
+ try {
+ floatval2 = Float.parseFloat(((String) value2).trim());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+
+ switch (operation) {
+ case APPROX :
+ case EQUAL : {
+ return Float.compare(floatval, floatval2) == 0;
+ }
+ case GREATER : {
+ return Float.compare(floatval, floatval2) >= 0;
+ }
+ case LESS : {
+ return Float.compare(floatval, floatval2) <= 0;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_Double(int operation, double doubleval, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ double doubleval2;
+ try {
+ doubleval2 = Double.parseDouble(((String) value2).trim());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+
+ switch (operation) {
+ case APPROX :
+ case EQUAL : {
+ return Double.compare(doubleval, doubleval2) == 0;
+ }
+ case GREATER : {
+ return Double.compare(doubleval, doubleval2) >= 0;
+ }
+ case LESS : {
+ return Double.compare(doubleval, doubleval2) <= 0;
+ }
+ }
+ return false;
+ }
+
+ private static Object valueOf(Class<?> target, String value2) {
+ do {
+ Method method;
+ try {
+ method = target.getMethod("valueOf", String.class);
+ } catch (NoSuchMethodException e) {
+ break;
+ }
+ if (Modifier.isStatic(method.getModifiers()) && target.isAssignableFrom(method.getReturnType())) {
+ setAccessible(method);
+ try {
+ return method.invoke(null, value2.trim());
+ } catch (IllegalAccessException e) {
+ return null;
+ } catch (InvocationTargetException e) {
+ return null;
+ }
+ }
+ } while (false);
+
+ do {
+ Constructor<?> constructor;
+ try {
+ constructor = target.getConstructor(String.class);
+ } catch (NoSuchMethodException e) {
+ break;
+ }
+ setAccessible(constructor);
+ try {
+ return constructor.newInstance(value2.trim());
+ } catch (IllegalAccessException e) {
+ return null;
+ } catch (InvocationTargetException e) {
+ return null;
+ } catch (InstantiationException e) {
+ return null;
+ }
+ } while (false);
+
+ return null;
+ }
+
+ private static void setAccessible(AccessibleObject accessible) {
+ if (!accessible.isAccessible()) {
+ AccessController.doPrivileged(new SetAccessibleAction(accessible));
+ }
+ }
+
+ private boolean compare_Comparable(int operation, Comparable<Object> value1, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ value2 = valueOf(value1.getClass(), (String) value2);
+ if (value2 == null) {
+ return false;
+ }
+ try {
+ switch (operation) {
+ case APPROX :
+ case EQUAL : {
+ return value1.compareTo(value2) == 0;
+ }
+ case GREATER : {
+ return value1.compareTo(value2) >= 0;
+ }
+ case LESS : {
+ return value1.compareTo(value2) <= 0;
+ }
+ }
+ } catch (Exception e) {
+ // if the compareTo method throws an exception; return false
+ return false;
+ }
+ return false;
+ }
+
+ private boolean compare_Version(int operation, Version value1, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ try {
+ Version version2 = Version.valueOf((String) value2);
+ switch (operation) {
+ case APPROX :
+ case EQUAL : {
+ return value1.compareTo(version2) == 0;
+ }
+ case GREATER : {
+ return value1.compareTo(version2) >= 0;
+ }
+ case LESS : {
+ return value1.compareTo(version2) <= 0;
+ }
+ }
+ } catch (Exception e) {
+ // if the valueOf or compareTo method throws an exception
+ return false;
+ }
+ return false;
+ }
+
+ private boolean compare_Unknown(int operation, Object value1, Object value2) {
+ if (operation == SUBSTRING) {
+ return false;
+ }
+ value2 = valueOf(value1.getClass(), (String) value2);
+ if (value2 == null) {
+ return false;
+ }
+ try {
+ switch (operation) {
+ case APPROX :
+ case EQUAL :
+ case GREATER :
+ case LESS : {
+ return value1.equals(value2);
+ }
+ }
+ } catch (Exception e) {
+ // if the equals method throws an exception; return false
+ return false;
+ }
+ return false;
+ }
+
+ /**
+ * Map a string for an APPROX (~=) comparison.
+ *
+ * This implementation removes white spaces. This is the minimum
+ * implementation allowed by the OSGi spec.
+ *
+ * @param input Input string.
+ * @return String ready for APPROX comparison.
+ */
+ private static String approxString(String input) {
+ boolean changed = false;
+ char[] output = input.toCharArray();
+ int cursor = 0;
+ for (char c : output) {
+ if (Character.isWhitespace(c)) {
+ changed = true;
+ continue;
+ }
+
+ output[cursor] = c;
+ cursor++;
+ }
+
+ return changed ? new String(output, 0, cursor) : input;
+ }
+
+ /**
+ * Parser class for OSGi filter strings. This class parses the complete
+ * filter string and builds a tree of Filter objects rooted at the
+ * parent.
+ */
+ static private final class Parser {
+ private final String filterstring;
+ private final char[] filterChars;
+ private int pos;
+
+ Parser(String filterstring) {
+ this.filterstring = filterstring;
+ filterChars = filterstring.toCharArray();
+ pos = 0;
+ }
+
+ FilterImpl parse() throws InvalidSyntaxException {
+ FilterImpl filter;
+ try {
+ filter = parse_filter();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new InvalidSyntaxException("Filter ended abruptly", filterstring, e);
+ }
+
+ if (pos != filterChars.length) {
+ throw new InvalidSyntaxException("Extraneous trailing characters: " + filterstring.substring(pos), filterstring);
+ }
+ return filter;
+ }
+
+ private FilterImpl parse_filter() throws InvalidSyntaxException {
+ FilterImpl filter;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ throw new InvalidSyntaxException("Missing '(': " + filterstring.substring(pos), filterstring);
+ }
+
+ pos++;
+
+ filter = parse_filtercomp();
+
+ skipWhiteSpace();
+
+ if (filterChars[pos] != ')') {
+ throw new InvalidSyntaxException("Missing ')': " + filterstring.substring(pos), filterstring);
+ }
+
+ pos++;
+
+ skipWhiteSpace();
+
+ return filter;
+ }
+
+ private FilterImpl parse_filtercomp() throws InvalidSyntaxException {
+ skipWhiteSpace();
+
+ char c = filterChars[pos];
+
+ switch (c) {
+ case '&' : {
+ pos++;
+ return parse_and();
+ }
+ case '|' : {
+ pos++;
+ return parse_or();
+ }
+ case '!' : {
+ pos++;
+ return parse_not();
+ }
+ }
+ return parse_item();
+ }
+
+ private FilterImpl parse_and() throws InvalidSyntaxException {
+ int lookahead = pos;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ pos = lookahead - 1;
+ return parse_item();
+ }
+
+ List<FilterImpl> operands = new ArrayList<FilterImpl>(10);
+
+ while (filterChars[pos] == '(') {
+ FilterImpl child = parse_filter();
+ operands.add(child);
+ }
+
+ return new FilterImpl(FilterImpl.AND, null,
+ operands.toArray(new FilterImpl[0]));
+ }
+
+ private FilterImpl parse_or() throws InvalidSyntaxException {
+ int lookahead = pos;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ pos = lookahead - 1;
+ return parse_item();
+ }
+
+ List<FilterImpl> operands = new ArrayList<FilterImpl>(10);
+
+ while (filterChars[pos] == '(') {
+ FilterImpl child = parse_filter();
+ operands.add(child);
+ }
+
+ return new FilterImpl(FilterImpl.OR, null,
+ operands.toArray(new FilterImpl[0]));
+ }
+
+ private FilterImpl parse_not() throws InvalidSyntaxException {
+ int lookahead = pos;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ pos = lookahead - 1;
+ return parse_item();
+ }
+
+ FilterImpl child = parse_filter();
+
+ return new FilterImpl(FilterImpl.NOT, null, child);
+ }
+
+ private FilterImpl parse_item() throws InvalidSyntaxException {
+ String attr = parse_attr();
+
+ skipWhiteSpace();
+
+ switch (filterChars[pos]) {
+ case '~' : {
+ if (filterChars[pos + 1] == '=') {
+ pos += 2;
+ return new FilterImpl(FilterImpl.APPROX, attr, parse_value());
+ }
+ break;
+ }
+ case '>' : {
+ if (filterChars[pos + 1] == '=') {
+ pos += 2;
+ return new FilterImpl(FilterImpl.GREATER, attr, parse_value());
+ }
+ break;
+ }
+ case '<' : {
+ if (filterChars[pos + 1] == '=') {
+ pos += 2;
+ return new FilterImpl(FilterImpl.LESS, attr, parse_value());
+ }
+ break;
+ }
+ case '=' : {
+ if (filterChars[pos + 1] == '*') {
+ int oldpos = pos;
+ pos += 2;
+ skipWhiteSpace();
+ if (filterChars[pos] == ')') {
+ return new FilterImpl(FilterImpl.PRESENT, attr, null);
+ }
+ pos = oldpos;
+ }
+
+ pos++;
+ Object string = parse_substring();
+
+ if (string instanceof String) {
+ return new FilterImpl(FilterImpl.EQUAL, attr, string);
+ }
+ return new FilterImpl(FilterImpl.SUBSTRING, attr, string);
+ }
+ }
+
+ throw new InvalidSyntaxException("Invalid operator: " + filterstring.substring(pos), filterstring);
+ }
+
+ private String parse_attr() throws InvalidSyntaxException {
+ skipWhiteSpace();
+
+ int begin = pos;
+ int end = pos;
+
+ char c = filterChars[pos];
+
+ while (c != '~' && c != '<' && c != '>' && c != '=' && c != '(' && c != ')') {
+ pos++;
+
+ if (!Character.isWhitespace(c)) {
+ end = pos;
+ }
+
+ c = filterChars[pos];
+ }
+
+ int length = end - begin;
+
+ if (length == 0) {
+ throw new InvalidSyntaxException("Missing attr: " + filterstring.substring(pos), filterstring);
+ }
+
+ return new String(filterChars, begin, length);
+ }
+
+ private String parse_value() throws InvalidSyntaxException {
+ StringBuilder sb = new StringBuilder(filterChars.length - pos);
+
+ parseloop: while (true) {
+ char c = filterChars[pos];
+
+ switch (c) {
+ case ')' : {
+ break parseloop;
+ }
+
+ case '(' : {
+ throw new InvalidSyntaxException("Invalid value: " + filterstring.substring(pos), filterstring);
+ }
+
+ case '\\' : {
+ pos++;
+ c = filterChars[pos];
+ /* fall through into default */
+ }
+
+ default : {
+ sb.append(c);
+ pos++;
+ break;
+ }
+ }
+ }
+
+ if (sb.length() == 0) {
+ throw new InvalidSyntaxException("Missing value: " + filterstring.substring(pos), filterstring);
+ }
+
+ return sb.toString();
+ }
+
+ private Object parse_substring() throws InvalidSyntaxException {
+ StringBuilder sb = new StringBuilder(filterChars.length - pos);
+
+ List<String> operands = new ArrayList<String>(10);
+
+ parseloop: while (true) {
+ char c = filterChars[pos];
+
+ switch (c) {
+ case ')' : {
+ if (sb.length() > 0) {
+ operands.add(sb.toString());
+ }
+
+ break parseloop;
+ }
+
+ case '(' : {
+ throw new InvalidSyntaxException("Invalid value: " + filterstring.substring(pos), filterstring);
+ }
+
+ case '*' : {
+ if (sb.length() > 0) {
+ operands.add(sb.toString());
+ }
+
+ sb.setLength(0);
+
+ operands.add(null);
+ pos++;
+
+ break;
+ }
+
+ case '\\' : {
+ pos++;
+ c = filterChars[pos];
+ /* fall through into default */
+ }
+
+ default : {
+ sb.append(c);
+ pos++;
+ break;
+ }
+ }
+ }
+
+ int size = operands.size();
+
+ if (size == 0) {
+ return "";
+ }
+
+ if (size == 1) {
+ Object single = operands.get(0);
+
+ if (single != null) {
+ return single;
+ }
+ }
+
+ return operands.toArray(new String[0]);
+ }
+
+ private void skipWhiteSpace() {
+ for (int length = filterChars.length; (pos < length) && Character.isWhitespace(filterChars[pos]);) {
+ pos++;
+ }
+ }
+ }
+ }
+
+ /**
+ * This Map is used for case-insensitive key lookup during filter
+ * evaluation. This Map implementation only supports the get operation using
+ * a String key as no other operations are used by the Filter
+ * implementation.
+ */
+ static private final class CaseInsensitiveMap extends AbstractMap<String, Object> implements Map<String, Object> {
+ private final Dictionary<String, ?> dictionary;
+ private final String[] keys;
+
+ /**
+ * Create a case insensitive map from the specified dictionary.
+ *
+ * @param dictionary
+ * @throws IllegalArgumentException If {@code dictionary} contains case
+ * variants of the same key name.
+ */
+ CaseInsensitiveMap(Dictionary<String, ?> dictionary) {
+ if (dictionary == null) {
+ this.dictionary = null;
+ this.keys = new String[0];
+ return;
+ }
+ this.dictionary = dictionary;
+ List<String> keyList = new ArrayList<String>(dictionary.size());
+ for (Enumeration<?> e = dictionary.keys(); e.hasMoreElements();) {
+ Object k = e.nextElement();
+ if (k instanceof String) {
+ String key = (String) k;
+ for (String i : keyList) {
+ if (key.equalsIgnoreCase(i)) {
+ throw new IllegalArgumentException();
+ }
+ }
+ keyList.add(key);
+ }
+ }
+ this.keys = keyList.toArray(new String[0]);
+ }
+
+ @Override
+ public Object get(Object o) {
+ String k = (String) o;
+ for (String key : keys) {
+ if (key.equalsIgnoreCase(k)) {
+ return dictionary.get(key);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Set<java.util.Map.Entry<String, Object>> entrySet() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * This Map is used for key lookup from a ServiceReference during filter
+ * evaluation. This Map implementation only supports the get operation using
+ * a String key as no other operations are used by the Filter
+ * implementation.
+ */
+ static private final class ServiceReferenceMap extends AbstractMap<String, Object> implements Map<String, Object> {
+ private final ServiceReference<?> reference;
+
+ ServiceReferenceMap(ServiceReference<?> reference) {
+ this.reference = reference;
+ }
+
+ @Override
+ public Object get(Object key) {
+ if (reference == null) {
+ return null;
+ }
+ return reference.getProperty((String) key);
+ }
+
+ @Override
+ public Set<java.util.Map.Entry<String, Object>> entrySet() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ static private final class SetAccessibleAction implements PrivilegedAction<Void> {
+ private final AccessibleObject accessible;
+
+ SetAccessibleAction(AccessibleObject accessible) {
+ this.accessible = accessible;
+ }
+
+ @Override
+ public Void run() {
+ accessible.setAccessible(true);
+ return null;
+ }
+ }
+
+ /**
* This class contains a method to match a distinguished name (DN) chain
* against and DN chain pattern.
* <p>
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/PackagePermission.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/PackagePermission.java
index 79379880a..264ccd683 100644
--- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/PackagePermission.java
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/PackagePermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2000, 2016). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2000, 2017). All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -544,7 +544,7 @@ public final class PackagePermission extends BasicPermission {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
- map.put("id", new Long(bundle.getBundleId()));
+ map.put("id", Long.valueOf(bundle.getBundleId()));
map.put("location", bundle.getLocation());
String name = bundle.getSymbolicName();
if (name != null) {
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServicePermission.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServicePermission.java
index 868d24c0c..8db61d0b1 100644
--- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServicePermission.java
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/ServicePermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2000, 2016). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2000, 2017). All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -562,7 +562,7 @@ public final class ServicePermission extends BasicPermission {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
- props.put("id", new Long(bundle.getBundleId()));
+ props.put("id", Long.valueOf(bundle.getBundleId()));
props.put("location", bundle.getLocation());
String name = bundle.getSymbolicName();
if (name != null) {
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/log/Logger.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/log/Logger.java
index a9cc36a67..863d1404f 100644
--- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/log/Logger.java
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/log/Logger.java
@@ -106,6 +106,15 @@ public interface Logger {
void trace(String format, Object... arguments);
/**
+ * Call the specified function if logging enabled for the
+ * {@link LogLevel#TRACE} level.
+ *
+ * @param consumer The function to call passing this Logger.
+ * @throws E An exception thrown by the function.
+ */
+ <E extends Exception> void trace(LoggerConsumer<E> consumer) throws E;
+
+ /**
* Is logging enabled for the {@link LogLevel#DEBUG} level?
*
* @return {@code true} if logging is enabled for the {@link LogLevel#DEBUG
@@ -146,6 +155,15 @@ public interface Logger {
void debug(String format, Object... arguments);
/**
+ * Call the specified function if logging enabled for the
+ * {@link LogLevel#DEBUG} level.
+ *
+ * @param consumer The function to call passing this Logger.
+ * @throws E An exception thrown by the function.
+ */
+ <E extends Exception> void debug(LoggerConsumer<E> consumer) throws E;
+
+ /**
* Is logging enabled for the {@link LogLevel#INFO} level?
*
* @return {@code true} if logging is enabled for the {@link LogLevel#INFO
@@ -186,6 +204,15 @@ public interface Logger {
void info(String format, Object... arguments);
/**
+ * Call the specified function if logging enabled for the
+ * {@link LogLevel#INFO} level.
+ *
+ * @param consumer The function to call passing this Logger.
+ * @throws E An exception thrown by the function.
+ */
+ <E extends Exception> void info(LoggerConsumer<E> consumer) throws E;
+
+ /**
* Is logging enabled for the {@link LogLevel#WARN} level?
*
* @return {@code true} if logging is enabled for the {@link LogLevel#WARN
@@ -226,6 +253,15 @@ public interface Logger {
void warn(String format, Object... arguments);
/**
+ * Call the specified function if logging enabled for the
+ * {@link LogLevel#WARN} level.
+ *
+ * @param consumer The function to call passing this Logger.
+ * @throws E An exception thrown by the function.
+ */
+ <E extends Exception> void warn(LoggerConsumer<E> consumer) throws E;
+
+ /**
* Is logging enabled for the {@link LogLevel#ERROR} level?
*
* @return {@code true} if logging is enabled for the {@link LogLevel#ERROR
@@ -266,6 +302,15 @@ public interface Logger {
void error(String format, Object... arguments);
/**
+ * Call the specified function if logging enabled for the
+ * {@link LogLevel#ERROR} level.
+ *
+ * @param consumer The function to call passing this Logger.
+ * @throws E An exception thrown by the function.
+ */
+ <E extends Exception> void error(LoggerConsumer<E> consumer) throws E;
+
+ /**
* Log a message at the {@link LogLevel#AUDIT} level.
*
* @param message The message to log.
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/log/LoggerConsumer.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/log/LoggerConsumer.java
new file mode 100644
index 000000000..82201dc52
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/log/LoggerConsumer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) OSGi Alliance (2017). All Rights Reserved.
+ *
+ * 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.osgi.service.log;
+
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * A function that accepts a {@link Logger} argument and produces no result.
+ * <p>
+ * This is a functional interface and can be used as the assignment target for a
+ * lambda expression or method reference.
+ *
+ * @param <E> The type of the exception that may be thrown.
+ * @ThreadSafe
+ * @since 1.4
+ * @author $Id$
+ */
+@ConsumerType
+@FunctionalInterface
+public interface LoggerConsumer<E extends Exception> {
+ /**
+ * Applies this function to the specified {@link Logger}.
+ *
+ * @param l The {@link Logger} input to this function.
+ * @throws E An exception thrown by the method.
+ */
+ void accept(Logger l) throws E;
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/resolver/ResolveContext.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/resolver/ResolveContext.java
index 887cb785c..5a3d32eb9 100644
--- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/resolver/ResolveContext.java
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/resolver/ResolveContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2011, 2016). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2011, 2017). All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/resolver/Resolver.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/resolver/Resolver.java
index b51c25d7a..86bff0073 100644
--- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/resolver/Resolver.java
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/resolver/Resolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2006, 2016). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2006, 2017). All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/util/tracker/BundleTracker.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/util/tracker/BundleTracker.java
index e00c3bf5c..ac16c218a 100644
--- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/util/tracker/BundleTracker.java
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/util/tracker/BundleTracker.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2007, 2016). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2007, 2017). All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

Back to the top