Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*******************************************************************************
* Copyright (c) 2003, 2021 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.internal.framework;
import static java.util.Objects.requireNonNull;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.eclipse.osgi.framework.util.CaseInsensitiveDictionaryMap;
import org.eclipse.osgi.internal.debug.Debug;
import org.eclipse.osgi.internal.messages.Msg;
import org.eclipse.osgi.internal.serviceregistry.ServiceReferenceImpl;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
/**
* 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.
*
* The syntax of a filter string is the string representation of LDAP search
* filters as defined in RFC 1960: A String Representation of LDAP Search
* Filters (available at http://www.ietf.org/rfc/rfc1960.txt). It should be
* noted that RFC 2254: A String Representation of LDAP Search Filters
* (available at http://www.ietf.org/rfc/rfc2254.txt) supersedes RFC 1960 but
* only adds extensible matching and is not applicable for this API.
*
* The string representation of an LDAP search filter is defined by the
* following grammar. It uses a prefix format.
*
*
*
* {@code <attr>} 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 <value>} is a string representing the value, or part of one, of
* a key in the properties objects of the registered services. If a
* {@code <value>} 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 <substring>} and {@code <present>} productions can produce
* the {@code 'attr=*'} construct, this construct is used only to denote a
* presence filter.
*
* 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.
*
* 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:
A filter matches
* a key that has multiple values if it matches at least one of those values.
* For example,
*
*
* Dictionary d = new Hashtable();
* d.put("cn", new String[] {
* "a", "b", "c"
* });
*
*
* d will match {@code (cn=a)} and also {@code (cn=b)}
*
* A filter component that references a key having an unrecognizable data type
* will evaluate to {@code false} .
*/
public abstract class FilterImpl implements Filter {
/* normalized filter string for Filter object */
private transient String filterString;
/**
* Creates a {@link FilterImpl} object. This filter object may be used to
* match a {@link ServiceReference} or a Dictionary.
*
* 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.
*/
public static FilterImpl newInstance(String filterString) throws InvalidSyntaxException {
return newInstance(filterString, false);
}
public static FilterImpl newInstance(String filterString, boolean debug) throws InvalidSyntaxException {
return new Parser(filterString, debug).parse();
}
FilterImpl() {
// empty constructor for subclasses
}
/**
* Filter using a service's properties.
*
* 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 matches0((reference != null) ? ServiceReferenceMap.asMap(reference) : Collections.emptyMap());
}
/**
* 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 dictionary) {
return matches0((dictionary != null) ? new CaseInsensitiveDictionaryMap<>(dictionary) : Collections.emptyMap());
}
/**
* 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 dictionary) {
return matches0((dictionary != null) ? DictionaryMap.asMap(dictionary) : Collections.emptyMap());
}
/**
* 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 map) {
return matches0((map != null) ? map : Collections.emptyMap());
}
abstract boolean matches0(Map map);
/**
* Returns this {@code Filter}'s filter string.
*
* 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(new StringBuilder()).toString();
}
return result;
}
/**
* Returns this {@code Filter}'s normalized filter string.
*
* The filter string is normalized by removing whitespace which does not
* affect the meaning of the filter.
*
* @return This {@code Filter}'s filter string.
*/
abstract StringBuilder normalize(StringBuilder sb);
/**
* Compares this {@code Filter} to another {@code Filter}.
*
* 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}.
*
* 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();
}
static final class And extends FilterImpl {
private final FilterImpl[] operands;
And(FilterImpl[] operands) {
this.operands = operands;
}
@Override
boolean matches0(Map map) {
for (FilterImpl operand : operands) {
if (!operand.matches0(map)) {
return false;
}
}
return true;
}
@Override
StringBuilder normalize(StringBuilder sb) {
sb.append('(').append('&');
for (FilterImpl operand : operands) {
operand.normalize(sb);
}
return sb.append(')');
}
@Override
public String getPrimaryKeyValue(String primaryKey) {
// just checking for simple filters here where primaryKey is the only attr or it is one attr of a base '&' clause
// (primaryKey=org.acme.BrickService) OK
// (&(primaryKey=org.acme.BrickService)(|(vendor=IBM)(vendor=SUN))) OK
// (primaryKey=org.acme.*) NOT OK
// (|(primaryKey=org.acme.BrickService)(primaryKey=org.acme.CementService)) NOT OK
// (&(primaryKey=org.acme.BrickService)(primaryKey=org.acme.CementService)) OK but only the first primaryKey is returned
for (FilterImpl operand : operands) {
if (operand instanceof Equal) {
String result = operand.getPrimaryKeyValue(primaryKey);
if (result != null) {
return result;
}
}
}
return null;
}
@Override
public List getChildren() {
return new ArrayList<>(Arrays.asList(operands));
}
@Override
void getAttributesInternal(List results) {
for (FilterImpl operand : operands) {
operand.getAttributesInternal(results);
}
}
@Override
void addAttributes(Map attributes, Map versionAttrs, boolean not) {
for (FilterImpl operand : operands) {
operand.addAttributes(attributes, versionAttrs, false);
}
}
}
static final class Or extends FilterImpl {
private final FilterImpl[] operands;
Or(FilterImpl[] operands) {
this.operands = operands;
}
@Override
boolean matches0(Map map) {
for (FilterImpl operand : operands) {
if (operand.matches0(map)) {
return true;
}
}
return false;
}
@Override
StringBuilder normalize(StringBuilder sb) {
sb.append('(').append('|');
for (FilterImpl operand : operands) {
operand.normalize(sb);
}
return sb.append(')');
}
@Override
public List getChildren() {
return new ArrayList<>(Arrays.asList(operands));
}
@Override
void getAttributesInternal(List results) {
for (FilterImpl operand : operands) {
operand.getAttributesInternal(results);
}
}
@Override
public Map getStandardOSGiAttributes(String... versions) {
throw new IllegalArgumentException("Invalid filter for standard OSGi Attributes: OR"); //$NON-NLS-1$
}
@Override
void addAttributes(Map attributes, Map versionAttrs, boolean not) {
throw new IllegalStateException("Invalid filter for standard OSGi requirements: OR"); //$NON-NLS-1$
}
}
static final class Not extends FilterImpl {
private final FilterImpl operand;
Not(FilterImpl operand) {
this.operand = operand;
}
@Override
boolean matches0(Map map) {
return !operand.matches0(map);
}
@Override
StringBuilder normalize(StringBuilder sb) {
sb.append('(').append('!');
operand.normalize(sb);
return sb.append(')');
}
@Override
void getAttributesInternal(List results) {
operand.getAttributesInternal(results);
}
@Override
public Map getStandardOSGiAttributes(String... versions) {
throw new IllegalArgumentException("Invalid filter for standard OSGi Attributes: NOT"); //$NON-NLS-1$
}
@Override
void addAttributes(Map attributes, Map versionAttrs, boolean not) {
operand.addAttributes(attributes, versionAttrs, true);
}
}
static abstract class Item extends FilterImpl {
/** debug mode */
final boolean debug;
final String attr;
Item(String attr, boolean debug) {
this.attr = attr;
this.debug = debug;
}
@Override
boolean matches0(Map map) {
return compare(map.get(attr));
}
abstract String operation();
abstract String value();
private boolean compare(Object value1) {
if (debug) {
if (value1 == null) {
Debug.println("compare(" + value1 + "," + value() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else if (!(value1.getClass().isArray() || (value1 instanceof Collection))) {
Debug.println(operation() + "(" + value1 + "," + value() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
if (value1 == null) {
return false;
}
if (value1 instanceof String) {
return compare_String((String) value1);
}
if (value1 instanceof Version) {
return compare_Version((Version) value1);
}
Class clazz = value1.getClass();
if (clazz.isArray()) {
Class type = clazz.getComponentType();
if (type.isPrimitive()) {
return compare_PrimitiveArray(type, value1);
}
return compare_ObjectArray((Object[]) value1);
}
if (value1 instanceof Collection) {
return compare_Collection((Collection) value1);
}
if (value1 instanceof Integer || value1 instanceof Long || value1 instanceof Byte
|| value1 instanceof Short) {
return compare_Long(((Number) value1).longValue());
}
if (value1 instanceof Character) {
return compare_Character(((Character) value1).charValue());
}
if (value1 instanceof Float) {
return compare_Float(((Float) value1).floatValue());
}
if (value1 instanceof Double) {
return compare_Double(((Double) value1).doubleValue());
}
if (value1 instanceof Boolean) {
return compare_Boolean(((Boolean) value1).booleanValue());
}
if (value1 instanceof Comparable) {
@SuppressWarnings("unchecked")
Comparable