Bug 323427 - [RFC 154] Support for generic capabilities and requirements
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java
index 5dd77bf..1062e8e 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java
@@ -35,7 +35,7 @@
private static final String ATTR_TYPE_LONG = "long"; //$NON-NLS-1$
private static final String ATTR_TYPE_DOUBLE = "double"; //$NON-NLS-1$
private static final String ATTR_TYPE_SET = "set"; //$NON-NLS-1$
- private static final String ATTR_TYPE_LIST = "List<"; //$NON-NLS-1$
+ private static final String ATTR_TYPE_LIST = "List"; //$NON-NLS-1$
private static final String OPTIONAL_ATTR = "optional"; //$NON-NLS-1$
private static final String MULTIPLE_ATTR = "multiple"; //$NON-NLS-1$
private static final String TRUE = "true"; //$NON-NLS-1$
@@ -367,8 +367,8 @@
int colonIndex = key.indexOf(':');
String type = ATTR_TYPE_STRING;
if (colonIndex > 0) {
- type = key.substring(colonIndex + 1);
- key = key.substring(0, colonIndex);
+ type = key.substring(colonIndex + 1).trim();
+ key = key.substring(0, colonIndex).trim();
}
if (!definedAttr) {
if (arbitraryAttrs == null)
@@ -380,40 +380,51 @@
}
private static Object convertValue(String type, String value) {
- Object convertValue = value;
+
if (ATTR_TYPE_STRING.equalsIgnoreCase(type))
- convertValue = value;
- else if (ATTR_TYPE_DOUBLE.equalsIgnoreCase(type))
- convertValue = new Double(value);
+ return value;
+
+ value = value.trim();
+ if (ATTR_TYPE_DOUBLE.equalsIgnoreCase(type))
+ return new Double(value);
else if (ATTR_TYPE_LONG.equalsIgnoreCase(type))
- convertValue = new Long(value);
+ return new Long(value);
else if (ATTR_TYPE_URI.equalsIgnoreCase(type))
try {
Class uriClazz = Class.forName("java.net.URI"); //$NON-NLS-1$
Constructor constructor = uriClazz.getConstructor(new Class[] {String.class});
- convertValue = constructor.newInstance(new Object[] {value});
+ return constructor.newInstance(new Object[] {value});
} catch (ClassNotFoundException e) {
// oh well cannot support; just use string
- convertValue = value;
+ return value;
} catch (RuntimeException e) { // got some reflection exception
throw e;
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
else if (ATTR_TYPE_VERSION.equalsIgnoreCase(type))
- convertValue = new Version(value);
+ return new Version(value);
else if (ATTR_TYPE_SET.equalsIgnoreCase(type))
- convertValue = ManifestElement.getArrayFromList(value, ","); //$NON-NLS-1$
- else if (type.startsWith(ATTR_TYPE_LIST) && type.endsWith(">")) { //$NON-NLS-1$
- String componentType = type.substring(ATTR_TYPE_LIST.length(), type.length() - 1);
- String[] list = ManifestElement.getArrayFromList(value, ","); //$NON-NLS-1$
- List<Object> components = new ArrayList<Object>();
- for (String component : list) {
- components.add(convertValue(componentType, component));
- }
- convertValue = components;
+ return ManifestElement.getArrayFromList(value, ","); //$NON-NLS-1$
+
+ // assume list type, anything else will throw an exception
+ Tokenizer listTokenizer = new Tokenizer(type);
+ String listType = listTokenizer.getToken("<"); //$NON-NLS-1$
+ if (!ATTR_TYPE_LIST.equalsIgnoreCase(listType))
+ throw new RuntimeException("Unsupported type: " + type); //$NON-NLS-1$
+ char c = listTokenizer.getChar();
+ String componentType = ATTR_TYPE_STRING;
+ if (c == '<') {
+ componentType = listTokenizer.getToken(">"); //$NON-NLS-1$
+ if (listTokenizer.getChar() != '>')
+ throw new RuntimeException("Invalid type, missing ending '>' : " + type); //$NON-NLS-1$
}
- return convertValue;
+ List<String> tokens = new Tokenizer(value).getEscapedTokens(","); //$NON-NLS-1$
+ List<Object> components = new ArrayList<Object>();
+ for (String component : tokens) {
+ components.add(convertValue(componentType, component));
+ }
+ return components;
}
private static HostSpecification createHostSpecification(ManifestElement spec, StateImpl state) {
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/core/Tokenizer.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/core/Tokenizer.java
index ff4d101..2cbf602 100644
--- a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/core/Tokenizer.java
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/framework/internal/core/Tokenizer.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2003, 2006 IBM Corporation and others.
+ * Copyright (c) 2003, 2010 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,6 +11,9 @@
package org.eclipse.osgi.framework.internal.core;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Simple tokenizer class. Used to parse data.
*/
@@ -62,7 +65,41 @@
return (null);
}
- public String getString(String terminals) {
+ public String getEscapedToken(String terminals) {
+ char[] val = value;
+ int cur = cursor;
+ if (cur >= max)
+ return null;
+ StringBuffer sb = new StringBuffer();
+ char c;
+ for (; cur < max; cur++) {
+ c = val[cur];
+ // this is an escaped char
+ if (c == '\\') {
+ cur++; // skip the escape char
+ if (cur == max)
+ break;
+ c = val[cur]; // include the escaped char
+ } else if (terminals.indexOf(c) != -1) {
+ break;
+ }
+ sb.append(c);
+ }
+
+ cursor = cur;
+ return sb.toString();
+ }
+
+ public List<String> getEscapedTokens(String terminals) {
+ List<String> result = new ArrayList<String>();
+ for (String token = getEscapedToken(terminals); token != null; token = getEscapedToken(terminals)) {
+ result.add(token);
+ getChar(); // consume terminal
+ }
+ return result;
+ }
+
+ public String getString(String terminals, String preserveEscapes) {
skipWhiteSpace();
char[] val = value;
int cur = cursor;
@@ -82,6 +119,8 @@
if (cur == max)
break;
c = val[cur]; // include the escaped char
+ if (preserveEscapes != null && preserveEscapes.indexOf(c) != -1)
+ sb.append('\\'); // must preserve escapes for c
} else if (c == '\"') {
break;
}
@@ -104,6 +143,10 @@
return (null);
}
+ public String getString(String terminals) {
+ return getString(terminals, null);
+ }
+
public char getChar() {
int cur = cursor;
if (cur < max) {
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/util/ManifestElement.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/util/ManifestElement.java
index b5cbbea..db3185a 100644
--- a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/util/ManifestElement.java
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/util/ManifestElement.java
@@ -396,7 +396,17 @@
} else
directive = true;
}
- String val = tokenizer.getString(";,"); //$NON-NLS-1$
+ // determine if the attribute is the form attr:List<type>
+ String preserveEscapes = null;
+ if (!directive && next.indexOf("List") > 0) { //$NON-NLS-1$
+ Tokenizer listTokenizer = new Tokenizer(next);
+ String attrKey = listTokenizer.getToken(":"); //$NON-NLS-1$
+ if (attrKey != null && listTokenizer.getChar() == ':' && "List".equals(listTokenizer.getToken("<"))) { //$NON-NLS-1$//$NON-NLS-2$
+ // we assume we must preserve escapes for , and "
+ preserveEscapes = "\\,"; //$NON-NLS-1$
+ }
+ }
+ String val = tokenizer.getString(";,", preserveEscapes); //$NON-NLS-1$
if (val == null)
throw new BundleException(NLS.bind(Msg.MANIFEST_INVALID_HEADER_EXCEPTION, header, value), BundleException.MANIFEST_ERROR);