Bug 426492 - Missing dependency message from Equinox got worse
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/compatibility/state/FilterParser.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/compatibility/state/FilterParser.java
deleted file mode 100644
index 8a9251d..0000000
--- a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/compatibility/state/FilterParser.java
+++ /dev/null
@@ -1,416 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2012 IBM Corporation and others. All rights reserved.
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- * 
- * Contributors: IBM - Initial API and implementation
- ******************************************************************************/
-package org.eclipse.osgi.compatibility.state;
-
-import java.util.*;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.Version;
-
-public class FilterParser {
-	static class Range {
-		private char leftRule = 0;
-		private Version leftVersion;
-		private Version rightVersion;
-		private char rightRule = 0;
-		private Collection<Version> excludes = new ArrayList<Version>(0);
-
-		public String toString() {
-			if (rightVersion == null) {
-				return leftVersion.toString();
-			}
-			return leftRule + leftVersion.toString() + ',' + rightVersion.toString() + rightRule;
-		}
-
-		void addExclude(Version exclude) {
-			this.excludes.add(exclude);
-			setLeft(leftRule, leftVersion);
-			setRight(rightRule, rightVersion);
-		}
-
-		boolean setLeft(char leftRule, Version leftVersion) {
-			if (this.leftVersion != null && this.leftVersion != leftVersion)
-				return false;
-			this.leftRule = excludes.contains(leftVersion) ? '(' : leftRule;
-			this.leftVersion = leftVersion;
-			return true;
-		}
-
-		boolean setRight(char rightRule, Version rightVersion) {
-			if (this.rightVersion != null && this.rightVersion != rightVersion)
-				return false;
-			this.rightRule = excludes.contains(rightVersion) ? ')' : rightRule;
-			this.rightVersion = rightVersion;
-			return true;
-		}
-	}
-
-	public static class FilterComponent {
-		/* filter operators */
-		public static final int EQUAL = 1;
-		public static final int APPROX = 2;
-		public static final int GREATER = 3;
-		public static final int LESS = 4;
-		public static final int AND = 7;
-		public static final int OR = 8;
-		public static final int NOT = 9;
-
-		private final int op;
-		private final String attr;
-		private final String value;
-		private final List<FilterComponent> nested;
-
-		public FilterComponent(int op, List<FilterComponent> nested) {
-			this.op = op;
-			this.attr = null;
-			this.value = null;
-			this.nested = nested;
-		}
-
-		public FilterComponent(int op, String attr, String value) {
-			this.op = op;
-			this.attr = attr;
-			this.value = value;
-			this.nested = Collections.emptyList();
-		}
-
-		public int getOp() {
-			return op;
-		}
-
-		public String getAttr() {
-			return attr;
-		}
-
-		public String getValue() {
-			return value;
-		}
-
-		public List<FilterComponent> getNested() {
-			return nested;
-		}
-
-		public Map<String, String> getStandardOSGiAttributes(String... versions) {
-			if (op != AND && op != EQUAL)
-				throw new IllegalStateException("Invalid filter for Starndard OSGi Attributes: " + op); //$NON-NLS-1$
-			Map<String, String> result = new HashMap<String, String>();
-			Map<String, Range> versionAttrs = new HashMap<String, Range>();
-			if (versions != null) {
-				for (String versionAttr : versions) {
-					versionAttrs.put(versionAttr, null);
-				}
-			}
-			addAttributes(result, versionAttrs, false);
-			for (Map.Entry<String, Range> entry : versionAttrs.entrySet()) {
-				Range range = entry.getValue();
-				if (range != null) {
-					result.put(entry.getKey(), range.toString());
-				}
-			}
-
-			return result;
-		}
-
-		private void addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not) {
-			if (op == EQUAL) {
-				if (!versionAttrs.containsKey(attr)) {
-					attributes.put(attr, value);
-				} else {
-					// this is an exact range e.g. [value,value]
-					Range currentRange = versionAttrs.get(attr);
-					if (currentRange != null) {
-						if (not) {
-							// this is an expanded for of the filter, e.g.:
-							// [1.0,2.0) -> (&(version>=1.0)(version<=2.0)(!(version=2.0)))
-							currentRange.addExclude(new Version(value));
-						} else {
-							throw new IllegalStateException("Invalid range for: " + attr); //$NON-NLS-1$
-						}
-					}
-					currentRange = new Range();
-					Version version = new Version(value);
-					currentRange.setLeft('[', version);
-					currentRange.setRight(']', version);
-					versionAttrs.put(attr, currentRange);
-				}
-			} else if (op == LESS) {
-				if (!versionAttrs.containsKey(attr))
-					throw new IllegalStateException("Invalid attribute: " + attr); //$NON-NLS-1$
-				Range currentRange = versionAttrs.get(attr);
-				if (currentRange == null) {
-					currentRange = new Range();
-					versionAttrs.put(attr, currentRange);
-				}
-				if (not) {
-					// this must be a range start "(value"
-					if (!currentRange.setLeft('(', new Version(value)))
-						throw new IllegalStateException("range start is already processed for attribute: " + attr); //$NON-NLS-1$
-				} else {
-					// this must be a range end "value]"
-					if (!currentRange.setRight(']', new Version(value)))
-						throw new IllegalStateException("range end is already processed for attribute: " + attr); //$NON-NLS-1$
-				}
-			} else if (op == GREATER) {
-				if (!versionAttrs.containsKey(attr))
-					throw new IllegalStateException("Invalid attribute: " + attr); //$NON-NLS-1$
-				Range currentRange = versionAttrs.get(attr);
-				if (currentRange == null) {
-					currentRange = new Range();
-					versionAttrs.put(attr, currentRange);
-				}
-				if (not) {
-					// this must be a range end "value)"
-					if (!currentRange.setRight(')', new Version(value)))
-						throw new IllegalStateException("range end is already processed for attribute: " + attr); //$NON-NLS-1$
-				} else {
-					// this must be a range start "[value"
-					if (!currentRange.setLeft('[', new Version(value)))
-						throw new IllegalStateException("range start is already processed for attribute: " + attr); //$NON-NLS-1$
-				}
-			} else if (op == AND) {
-				for (FilterComponent component : nested) {
-					component.addAttributes(attributes, versionAttrs, false);
-				}
-			} else if (op == NOT) {
-				nested.get(0).addAttributes(attributes, versionAttrs, true);
-			} else {
-				throw new IllegalStateException("Invalid filter for standard OSGi requirements: " + op); //$NON-NLS-1$
-			}
-		}
-	}
-
-	private final String filterstring;
-	private final char[] filterChars;
-	private int pos;
-
-	public FilterParser(String filterstring) {
-		this.filterstring = filterstring;
-		filterChars = filterstring.toCharArray();
-		pos = 0;
-	}
-
-	public FilterComponent parse() throws InvalidSyntaxException {
-		FilterComponent filter;
-		try {
-			filter = parse_filter();
-		} catch (ArrayIndexOutOfBoundsException e) {
-			throw new InvalidSyntaxException("Filter ended abruptly", filterstring, e); //$NON-NLS-1$
-		}
-
-		if (pos != filterChars.length) {
-			throw new InvalidSyntaxException("Extraneous trailing characters: " + filterstring.substring(pos), filterstring); //$NON-NLS-1$
-		}
-		return filter;
-	}
-
-	private FilterComponent parse_filter() throws InvalidSyntaxException {
-		FilterComponent filter;
-		skipWhiteSpace();
-
-		if (filterChars[pos] != '(') {
-			throw new InvalidSyntaxException("Missing '(': " + filterstring.substring(pos), filterstring); //$NON-NLS-1$
-		}
-
-		pos++;
-
-		filter = parse_filtercomp();
-
-		skipWhiteSpace();
-
-		if (filterChars[pos] != ')') {
-			throw new InvalidSyntaxException("Missing ')': " + filterstring.substring(pos), filterstring); //$NON-NLS-1$
-		}
-
-		pos++;
-
-		skipWhiteSpace();
-
-		return filter;
-	}
-
-	private FilterComponent 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 FilterComponent parse_and() throws InvalidSyntaxException {
-		int lookahead = pos;
-		skipWhiteSpace();
-
-		if (filterChars[pos] != '(') {
-			pos = lookahead - 1;
-			return parse_item();
-		}
-
-		List<FilterComponent> operands = new ArrayList<FilterComponent>(10);
-
-		while (filterChars[pos] == '(') {
-			FilterComponent child = parse_filter();
-			operands.add(child);
-		}
-
-		return new FilterComponent(FilterComponent.AND, operands);
-	}
-
-	private FilterComponent parse_or() throws InvalidSyntaxException {
-		int lookahead = pos;
-		skipWhiteSpace();
-
-		if (filterChars[pos] != '(') {
-			pos = lookahead - 1;
-			return parse_item();
-		}
-
-		List<FilterComponent> operands = new ArrayList<FilterComponent>(10);
-
-		while (filterChars[pos] == '(') {
-			FilterComponent child = parse_filter();
-			operands.add(child);
-		}
-
-		return new FilterComponent(FilterComponent.OR, operands);
-	}
-
-	private FilterComponent parse_not() throws InvalidSyntaxException {
-		int lookahead = pos;
-		skipWhiteSpace();
-
-		if (filterChars[pos] != '(') {
-			pos = lookahead - 1;
-			return parse_item();
-		}
-
-		List<FilterComponent> operands = new ArrayList<FilterComponent>(1);
-		FilterComponent child = parse_filter();
-		operands.add(child);
-
-		return new FilterComponent(FilterComponent.NOT, operands);
-	}
-
-	private FilterComponent parse_item() throws InvalidSyntaxException {
-		String attr = parse_attr();
-
-		skipWhiteSpace();
-
-		switch (filterChars[pos]) {
-			case '~' : {
-				if (filterChars[pos + 1] == '=') {
-					pos += 2;
-					return new FilterComponent(FilterComponent.APPROX, attr, parse_value());
-				}
-				break;
-			}
-			case '>' : {
-				if (filterChars[pos + 1] == '=') {
-					pos += 2;
-					return new FilterComponent(FilterComponent.GREATER, attr, parse_value());
-				}
-				break;
-			}
-			case '<' : {
-				if (filterChars[pos + 1] == '=') {
-					pos += 2;
-					return new FilterComponent(FilterComponent.LESS, attr, parse_value());
-				}
-				break;
-			}
-			case '=' : {
-				pos++;
-				return new FilterComponent(FilterComponent.EQUAL, attr, parse_value());
-			}
-		}
-
-		throw new InvalidSyntaxException("Invalid operator: " + filterstring.substring(pos), filterstring); //$NON-NLS-1$
-	}
-
-	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); //$NON-NLS-1$
-		}
-
-		return new String(filterChars, begin, length);
-	}
-
-	private String parse_value() throws InvalidSyntaxException {
-		StringBuffer sb = new StringBuffer(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); //$NON-NLS-1$
-				}
-
-				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); //$NON-NLS-1$
-		}
-
-		return sb.toString();
-	}
-
-	private void skipWhiteSpace() {
-		for (int length = filterChars.length; (pos < length) && Character.isWhitespace(filterChars[pos]);) {
-			pos++;
-		}
-	}
-}
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/compatibility/state/StateConverter.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/compatibility/state/StateConverter.java
index dbf45ab..426607b 100644
--- a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/compatibility/state/StateConverter.java
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/compatibility/state/StateConverter.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013 IBM Corporation and others. All rights reserved.
+ * Copyright (c) 2013, 2014 IBM Corporation and others. All rights reserved.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
@@ -11,7 +11,7 @@
 
 import java.util.*;
 import java.util.Map.Entry;
-import org.eclipse.osgi.compatibility.state.FilterParser.FilterComponent;
+import org.eclipse.osgi.internal.framework.FilterImpl;
 import org.eclipse.osgi.service.resolver.*;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.Version;
@@ -156,14 +156,13 @@
 		String filter = directives.remove(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
 		if (filter == null)
 			throw new IllegalArgumentException("No filter directive found:" + requirement); //$NON-NLS-1$
-		FilterParser parser = new FilterParser(filter);
-		FilterComponent component = null;
+		FilterImpl parser;
 		try {
-			component = parser.parse();
+			parser = FilterImpl.newInstance(filter);
 		} catch (InvalidSyntaxException e) {
 			throw new IllegalArgumentException("Invalid filter directive", e); //$NON-NLS-1$
 		}
-		Map<String, String> matchingAttributes = component.getStandardOSGiAttributes(versions);
+		Map<String, String> matchingAttributes = parser.getStandardOSGiAttributes(versions);
 		String name = matchingAttributes.remove(namespace);
 		if (name == null)
 			throw new IllegalArgumentException("Invalid requirement: " + requirement); //$NON-NLS-1$
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/Module.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/Module.java
index bc5f295..bc21bbd 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/Module.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/Module.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 IBM Corporation and others.
+ * Copyright (c) 2012, 2014 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -421,7 +421,7 @@
 					return;
 				if (getState().equals(State.INSTALLED)) {
 					String reportMessage = report.getResolutionReportMessage(getCurrentRevision());
-					throw new BundleException(Msg.Module_ResolveError + reportMessage, BundleException.RESOLVE_ERROR, e);
+					throw new BundleException(Msg.Module_ResolveError + reportMessage, BundleException.RESOLVE_ERROR);
 				}
 			}
 
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolutionReport.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolutionReport.java
index 3e66c4f..359d9db 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolutionReport.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolutionReport.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013 IBM Corporation and others.
+ * Copyright (c) 2013, 2014 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -11,8 +11,12 @@
 package org.eclipse.osgi.container;
 
 import java.util.*;
+import org.eclipse.osgi.internal.framework.FilterImpl;
 import org.eclipse.osgi.internal.messages.Msg;
 import org.eclipse.osgi.report.resolution.ResolutionReport;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.namespace.*;
 import org.osgi.framework.wiring.BundleRevision;
 import org.osgi.resource.*;
 import org.osgi.service.resolver.ResolutionException;
@@ -112,7 +116,7 @@
 	private static void printResolutionEntry(StringBuilder result, String prepend, ResolutionReport.Entry entry, Map<Resource, List<ResolutionReport.Entry>> reportEntries, Set<BundleRevision> visited) {
 		switch (entry.getType()) {
 			case MISSING_CAPABILITY :
-				result.append(prepend).append(Msg.ModuleResolutionReport_UnresolvedReq).append(entry.getData()).append('\n');
+				result.append(prepend).append(Msg.ModuleResolutionReport_UnresolvedReq).append(printRequirement(entry.getData())).append('\n');
 				break;
 			case SINGLETON_SELECTION :
 				result.append(prepend).append(Msg.ModuleResolutionReport_AnotherSingleton).append(entry.getData()).append('\n');
@@ -127,8 +131,8 @@
 						Capability unresolvedCapability = unresolvedCapabilities.iterator().next();
 						// make sure this is not a case of importing and exporting the same package
 						if (!unresolvedRequirement.getKey().getResource().equals(unresolvedCapability.getResource())) {
-							result.append(prepend).append(Msg.ModuleResolutionReport_UnresolvedReq).append(unresolvedRequirement.getKey()).append('\n');
-							result.append(prepend).append("  -> ").append(unresolvedCapability).append('\n'); //$NON-NLS-1$
+							result.append(prepend).append(Msg.ModuleResolutionReport_UnresolvedReq).append(printRequirement(unresolvedRequirement.getKey())).append('\n');
+							result.append(prepend).append("  -> ").append(printCapability(unresolvedCapability)).append('\n'); //$NON-NLS-1$
 							result.append(getResolutionReport0(prepend + "     ", (ModuleRevision) unresolvedCapability.getResource(), reportEntries, visited)); //$NON-NLS-1$
 						}
 					}
@@ -147,6 +151,57 @@
 		}
 	}
 
+	private static Object printCapability(Capability cap) {
+		if (PackageNamespace.PACKAGE_NAMESPACE.equals(cap.getNamespace())) {
+			return Constants.EXPORT_PACKAGE + ": " + createOSGiCapability(cap); //$NON-NLS-1$
+		} else if (BundleNamespace.BUNDLE_NAMESPACE.equals(cap.getNamespace())) {
+			return Constants.BUNDLE_SYMBOLICNAME + ": " + createOSGiCapability(cap); //$NON-NLS-1$
+		} else if (HostNamespace.HOST_NAMESPACE.equals(cap.getNamespace())) {
+			return Constants.BUNDLE_SYMBOLICNAME + ": " + createOSGiCapability(cap); //$NON-NLS-1$
+		}
+		return Constants.PROVIDE_CAPABILITY + ": " + cap.toString(); //$NON-NLS-1$
+	}
+
+	private static String createOSGiCapability(Capability cap) {
+		Map<String, Object> attributes = new HashMap<String, Object>(cap.getAttributes());
+		Map<String, String> directives = cap.getDirectives();
+		String name = (String) attributes.remove(cap.getNamespace());
+		return name + ModuleRevision.toString(attributes, false, true) + ModuleRevision.toString(directives, true, true);
+	}
+
+	private static String printRequirement(Object data) {
+		if (!(data instanceof Requirement)) {
+			return String.valueOf(data);
+		}
+		Requirement req = (Requirement) data;
+		if (PackageNamespace.PACKAGE_NAMESPACE.equals(req.getNamespace())) {
+			return Constants.IMPORT_PACKAGE + ": " + createOSGiRequirement(req, PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE, PackageNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE); //$NON-NLS-1$
+		} else if (BundleNamespace.BUNDLE_NAMESPACE.equals(req.getNamespace())) {
+			return Constants.REQUIRE_BUNDLE + ": " + createOSGiRequirement(req, BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE); //$NON-NLS-1$
+		} else if (HostNamespace.HOST_NAMESPACE.equals(req.getNamespace())) {
+			return Constants.FRAGMENT_HOST + ": " + createOSGiRequirement(req, HostNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE); //$NON-NLS-1$
+		}
+		return Constants.REQUIRE_CAPABILITY + ": " + req.toString(); //$NON-NLS-1$
+	}
+
+	private static String createOSGiRequirement(Requirement requirement, String... versions) {
+		Map<String, String> directives = new HashMap<String, String>(requirement.getDirectives());
+		String filter = directives.remove(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
+		if (filter == null)
+			throw new IllegalArgumentException("No filter directive found:" + requirement); //$NON-NLS-1$
+		FilterImpl filterImpl;
+		try {
+			filterImpl = FilterImpl.newInstance(filter);
+		} catch (InvalidSyntaxException e) {
+			throw new IllegalArgumentException("Invalid filter directive", e); //$NON-NLS-1$
+		}
+		Map<String, String> matchingAttributes = filterImpl.getStandardOSGiAttributes(versions);
+		String name = matchingAttributes.remove(requirement.getNamespace());
+		if (name == null)
+			throw new IllegalArgumentException("Invalid requirement: " + requirement); //$NON-NLS-1$
+		return name + ModuleRevision.toString(matchingAttributes, false, true) + ModuleRevision.toString(directives, true, true);
+	}
+
 	@Override
 	public String getResolutionReportMessage(Resource resource) {
 		return getResolutionReport0(null, (ModuleRevision) resource, getEntries(), null);
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRevision.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRevision.java
index 5951e5e..b8e452f 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRevision.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleRevision.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 IBM Corporation and others.
+ * Copyright (c) 2012, 2014 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -197,6 +197,10 @@
 	}
 
 	static <V> String toString(Map<String, V> map, boolean directives) {
+		return toString(map, directives, false);
+	}
+
+	static <V> String toString(Map<String, V> map, boolean directives, boolean stringsOnly) {
 		if (map.size() == 0)
 			return ""; //$NON-NLS-1$
 		String assignment = directives ? ":=" : "="; //$NON-NLS-1$ //$NON-NLS-2$
@@ -221,7 +225,7 @@
 				sb.append('"');
 			} else {
 				String type = ""; //$NON-NLS-1$
-				if (!(value instanceof String)) {
+				if (!(value instanceof String) && !stringsOnly) {
 					String className = value.getClass().getName();
 					type = ":" + className.substring(className.lastIndexOf('.') + 1); //$NON-NLS-1$
 				}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/FilterImpl.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/FilterImpl.java
index 1977c34..4e012d8 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/FilterImpl.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/FilterImpl.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2003, 2013 IBM Corporation and others.
+ * Copyright (c) 2003, 2014 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -1765,4 +1765,145 @@
 			return null;
 		}
 	}
+
+	static class Range {
+		private char leftRule = 0;
+		private Version leftVersion;
+		private Version rightVersion;
+		private char rightRule = 0;
+		private Collection<Version> excludes = new ArrayList<Version>(0);
+
+		public String toString() {
+			if (rightVersion == null) {
+				return leftVersion.toString();
+			}
+			return leftRule + leftVersion.toString() + ',' + rightVersion.toString() + rightRule;
+		}
+
+		void addExclude(Version exclude) {
+			this.excludes.add(exclude);
+			setLeft(leftRule, leftVersion);
+			setRight(rightRule, rightVersion);
+		}
+
+		boolean setLeft(char leftRule, Version leftVersion) {
+			if (this.leftVersion != null && this.leftVersion != leftVersion)
+				return false;
+			this.leftRule = excludes.contains(leftVersion) ? '(' : leftRule;
+			this.leftVersion = leftVersion;
+			return true;
+		}
+
+		boolean setRight(char rightRule, Version rightVersion) {
+			if (this.rightVersion != null && this.rightVersion != rightVersion)
+				return false;
+			this.rightRule = excludes.contains(rightVersion) ? ')' : rightRule;
+			this.rightVersion = rightVersion;
+			return true;
+		}
+	}
+
+	public Map<String, String> getStandardOSGiAttributes(String... versions) {
+		if (op != AND && op != EQUAL && op != SUBSTRING && op != PRESENT)
+			throw new IllegalArgumentException("Invalid filter for Starndard OSGi Attributes: " + op); //$NON-NLS-1$
+		Map<String, String> result = new HashMap<String, String>();
+		Map<String, Range> versionAttrs = new HashMap<String, Range>();
+		if (versions != null) {
+			for (String versionAttr : versions) {
+				versionAttrs.put(versionAttr, null);
+			}
+		}
+		addAttributes(result, versionAttrs, false);
+		for (Map.Entry<String, Range> entry : versionAttrs.entrySet()) {
+			Range range = entry.getValue();
+			if (range != null) {
+				result.put(entry.getKey(), range.toString());
+			}
+		}
+
+		return result;
+	}
+
+	private void addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not) {
+		if (op == EQUAL) {
+			if (!versionAttrs.containsKey(attr)) {
+				attributes.put(attr, (String) value);
+			} else {
+				// this is an exact range e.g. [value,value]
+				Range currentRange = versionAttrs.get(attr);
+				if (currentRange != null) {
+					if (not) {
+						// this is an expanded form of the filter, e.g.:
+						// [1.0,2.0) -> (&(version>=1.0)(version<=2.0)(!(version=2.0)))
+						currentRange.addExclude(new Version((String) value));
+					} else {
+						throw new IllegalStateException("Invalid range for: " + attr); //$NON-NLS-1$
+					}
+				} else {
+					currentRange = new Range();
+					Version version = new Version((String) value);
+					currentRange.setLeft('[', version);
+					currentRange.setRight(']', version);
+					versionAttrs.put(attr, currentRange);
+				}
+			}
+		} else if (op == SUBSTRING || op == PRESENT) {
+			if (value == null) {
+				attributes.put(attr, "*"); //$NON-NLS-1$
+			} else {
+				StringBuilder builder = new StringBuilder();
+				for (String component : (String[]) value) {
+					if (component == null) {
+						builder.append('*');
+					} else {
+						builder.append(component);
+					}
+				}
+				attributes.put(attr, builder.toString());
+			}
+
+		} else if (op == LESS) {
+			if (!versionAttrs.containsKey(attr))
+				throw new IllegalStateException("Invalid attribute: " + attr); //$NON-NLS-1$
+			Range currentRange = versionAttrs.get(attr);
+			if (currentRange == null) {
+				currentRange = new Range();
+				versionAttrs.put(attr, currentRange);
+			}
+			if (not) {
+				// this must be a range start "(value"
+				if (!currentRange.setLeft('(', new Version((String) value)))
+					throw new IllegalStateException("range start is already processed for attribute: " + attr); //$NON-NLS-1$
+			} else {
+				// this must be a range end "value]"
+				if (!currentRange.setRight(']', new Version((String) value)))
+					throw new IllegalStateException("range end is already processed for attribute: " + attr); //$NON-NLS-1$
+			}
+		} else if (op == GREATER) {
+			if (!versionAttrs.containsKey(attr))
+				throw new IllegalStateException("Invalid attribute: " + attr); //$NON-NLS-1$
+			Range currentRange = versionAttrs.get(attr);
+			if (currentRange == null) {
+				currentRange = new Range();
+				versionAttrs.put(attr, currentRange);
+			}
+			if (not) {
+				// this must be a range end "value)"
+				if (!currentRange.setRight(')', new Version((String) value)))
+					throw new IllegalStateException("range end is already processed for attribute: " + attr); //$NON-NLS-1$
+			} else {
+				// this must be a range start "[value"
+				if (!currentRange.setLeft('[', new Version((String) value)))
+					throw new IllegalStateException("range start is already processed for attribute: " + attr); //$NON-NLS-1$
+			}
+		} else if (op == AND) {
+			for (FilterImpl component : (FilterImpl[]) value) {
+				component.addAttributes(attributes, versionAttrs, false);
+			}
+		} else if (op == NOT) {
+			((FilterImpl) value).addAttributes(attributes, versionAttrs, true);
+		} else {
+			throw new IllegalStateException("Invalid filter for standard OSGi requirements: " + op); //$NON-NLS-1$
+		}
+	}
 }