You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by jw...@apache.org on 2015/09/29 04:06:33 UTC

svn commit: r1705795 - in /aries/trunk/subsystem: subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/

Author: jwross
Date: Tue Sep 29 02:06:33 2015
New Revision: 1705795

URL: http://svn.apache.org/viewvc?rev=1705795&view=rev
Log:
ARIES-1419 Provide-Capability header parser does not support typed attributes.

Add support for the cardinality directive in Require-Capability.

Add support for the cardinality directive to Subsystem-ImportService. Although the specification only references the filter directive
in conjunction with this header, the same directives used with Require-Capability in the osgi.service namespace (i.e. resolution,
effective, and cardinality) are necessary because Subsystem-ImportService serves a similar role.

Fix Require-Capability grammar, which should allow typed attributes.

Add test.

Move the typed parameter parsing to the abstract clause class to be used by both Require-Capability and Provide-Capability.

Require-Capability must use a typed parameter rather than the attribute factory when converting a requirement into a clause.

Support attributes in the require capability requirement.

Support lists of scalars in typed attributes. Support requirement to header clause conversion. Override toString in order to include the type information.

Added:
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/CardinalityDirective.java
    aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1419Test.java
Modified:
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractClause.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DirectiveFactory.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Grammar.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Patterns.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ProvideCapabilityHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/RequireCapabilityHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/RequireCapabilityRequirement.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemImportServiceHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/TypedAttribute.java

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractClause.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractClause.java?rev=1705795&r1=1705794&r2=1705795&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractClause.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractClause.java Tue Sep 29 02:06:33 2015
@@ -47,6 +47,34 @@ public abstract class AbstractClause imp
         }
 		return parameters;
     }
+	
+	protected static Map<String, Parameter> parseTypedParameters(String clause) {
+    	Map<String, Parameter> parameters = new HashMap<String, Parameter>();
+		Matcher matcher = Patterns.TYPED_PARAMETER.matcher(clause);
+		while (matcher.find()) {
+        	if (":=".equals(matcher.group(2))) {
+        		// This is a directive.
+        		parameters.put(matcher.group(1), DirectiveFactory.createDirective(matcher.group(1), removeQuotes(matcher.group(3))));
+        	}
+        	else if (":".equals(matcher.group(5))) {
+        		// This is a typed attribute with a declared version.
+        		parameters.put(matcher.group(4), new TypedAttribute(matcher.group(4), removeQuotes(matcher.group(7)), matcher.group(6)));
+        	}
+        	else {
+        		// This is a typed attribute without a declared version.
+        		parameters.put(matcher.group(4), new TypedAttribute(matcher.group(4), removeQuotes(matcher.group(7)), "String"));
+        	}
+        }
+		return parameters;
+    }
+	
+	protected static String removeQuotes(String value) {
+		if (value == null)
+			return null;
+		if (value.startsWith("\"") && value.endsWith("\""))
+			return value.substring(1, value.length() - 1);
+		return value;
+	}
     
     protected static String parsePath(String clause, Pattern pattern, boolean replaceAllWhitespace) {
     	Matcher matcher = pattern.matcher(clause);

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/CardinalityDirective.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/CardinalityDirective.java?rev=1705795&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/CardinalityDirective.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/CardinalityDirective.java Tue Sep 29 02:06:33 2015
@@ -0,0 +1,51 @@
+/*
+ * 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.apache.aries.subsystem.core.archive;
+
+import org.osgi.resource.Namespace;
+
+public class CardinalityDirective extends AbstractDirective {
+	public static final String NAME = Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE;
+	public static final String VALUE_MULTIPLE = Namespace.CARDINALITY_MULTIPLE;
+	public static final String VALUE_SINGLE = Namespace.CARDINALITY_SINGLE;
+	
+	public static final CardinalityDirective MULTIPLE = new CardinalityDirective(VALUE_MULTIPLE);
+	public static final CardinalityDirective SINGLE = new CardinalityDirective(VALUE_SINGLE);
+	
+	public static final CardinalityDirective DEFAULT = SINGLE;
+	
+	public static CardinalityDirective getInstance(String value) {
+		if (VALUE_SINGLE.equals(value))
+			return SINGLE;
+		if (VALUE_MULTIPLE.equals(value))
+			return MULTIPLE;
+		return new CardinalityDirective(value);
+	}
+	
+	public CardinalityDirective() {
+		this(VALUE_SINGLE);
+	}
+	
+	public CardinalityDirective(String value) {
+		super(NAME, value);
+	}
+	
+	public boolean isMultiple() {
+		return MULTIPLE == this || VALUE_MULTIPLE.equals(getValue());
+	}
+	
+	public boolean isSingle() {
+		return SINGLE == this || VALUE_SINGLE.equals(getValue());
+	}
+}

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DirectiveFactory.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DirectiveFactory.java?rev=1705795&r1=1705794&r2=1705795&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DirectiveFactory.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DirectiveFactory.java Tue Sep 29 02:06:33 2015
@@ -29,6 +29,8 @@ public class DirectiveFactory {
 			return ProvisionPolicyDirective.getInstance(value);
 		if (ReferenceDirective.NAME.equals(name))
 			return ReferenceDirective.getInstance(value);
+		if (CardinalityDirective.NAME.equals(name))
+			return CardinalityDirective.getInstance(value);
 		return new GenericDirective(name, value);
 	}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Grammar.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Grammar.java?rev=1705795&r1=1705794&r2=1705795&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Grammar.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Grammar.java Tue Sep 29 02:06:33 2015
@@ -116,8 +116,6 @@ public interface Grammar {
 	public static final String IMPORTPACKAGE = IMPORT + "(?:\\,\\s*" + IMPORT + ")*";
 
 	public static final String NAMESPACE = SYMBOLICNAME;
-	public static final String REQUIREMENT = NAMESPACE + "(?:;\\s*(?:" + PARAMETER + "))*";
-	public static final String REQUIRE_CAPABILITY = REQUIREMENT + "(?:,\\s*(?:" + REQUIREMENT + "))*";
 
 	public static final String BUNDLE_DESCRIPTION = SYMBOLICNAME + "(?:;\\s*(?:" + PARAMETER + "))*";
 	public static final String REQUIRE_BUNDLE = BUNDLE_DESCRIPTION + "(?:,\\s*(?:" + BUNDLE_DESCRIPTION + "))*";
@@ -129,6 +127,8 @@ public interface Grammar {
 	public static final String LIST = "List<(?:" + SCALAR + ")>";
 	public static final String TYPE = "(?:" + SCALAR + ")|" + LIST;
 	public static final String TYPED_ATTR = EXTENDED + "(?:\\:" + TYPE + ")?=(?:" + ARGUMENT + ')';
+	public static final String REQUIREMENT = NAMESPACE + "(?:;\\s*(?:(?:" + DIRECTIVE + ")|(?:" + TYPED_ATTR + ")))*";
+	public static final String REQUIRE_CAPABILITY = REQUIREMENT + "(?:,\\s*(?:" + REQUIREMENT + "))*";
 	public static final String CAPABILITY = NAMESPACE + "(?:;\\s*(?:(?:" + DIRECTIVE + ")|(?:" + TYPED_ATTR + ")))*";
 	public static final String PROVIDE_CAPABILITY = CAPABILITY + "(?:,\\s*(?:" + CAPABILITY + "))*";
 

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Patterns.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Patterns.java?rev=1705795&r1=1705794&r2=1705795&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Patterns.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Patterns.java Tue Sep 29 02:06:33 2015
@@ -18,4 +18,6 @@ public class Patterns {
 	private static final String DIRECTIVE = '(' + Grammar.EXTENDED + ")(:=)(" + Grammar.ARGUMENT + ')';
 	private static final String TYPED_ATTR = '(' + Grammar.EXTENDED + ")(?:(\\:)(" + Grammar.TYPE + "))?=(" + Grammar.ARGUMENT + ')';
 	public static final Pattern TYPED_PARAMETER = Pattern.compile("(?:(?:" + DIRECTIVE + ")|(?:" + TYPED_ATTR + "))(?=;|\\z)");
+	
+	public static final Pattern SCALAR_LIST = Pattern.compile("List(?:<(String|Long|Double|Version)>)?");
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ProvideCapabilityHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ProvideCapabilityHeader.java?rev=1705795&r1=1705794&r2=1705795&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ProvideCapabilityHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ProvideCapabilityHeader.java Tue Sep 29 02:06:33 2015
@@ -14,47 +14,25 @@
 package org.apache.aries.subsystem.core.archive;
 
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collection;
 import java.util.List;
-import java.util.regex.Matcher;
 
 import org.osgi.framework.Constants;
 import org.osgi.resource.Resource;
 
 public class ProvideCapabilityHeader extends AbstractClauseBasedHeader<ProvideCapabilityHeader.Clause> implements CapabilityHeader<ProvideCapabilityHeader.Clause> {	
     public static class Clause extends AbstractClause {
-		public static final String DIRECTIVE_EFFECTIVE = Constants.EFFECTIVE_DIRECTIVE;
+		public static final String DIRECTIVE_EFFECTIVE = EffectiveDirective.NAME;
 		public static final String DIRECTIVE_USES = Constants.USES_DIRECTIVE;
 		
-		private static String removeQuotes(String value) {
-			if (value == null)
-				return null;
-			if (value.startsWith("\"") && value.endsWith("\""))
-				return value.substring(1, value.length() - 1);
-			return value;
-		}
+		private static final Collection<Parameter> defaultParameters = generateDefaultParameters(
+				EffectiveDirective.DEFAULT);
 		
 		public Clause(String clause) {
             super(
             		parsePath(clause, Patterns.NAMESPACE, false),
-            		new HashMap<String, Parameter>(),
-            		generateDefaultParameters(
-            				EffectiveDirective.DEFAULT));
-            Matcher matcher = Patterns.TYPED_PARAMETER.matcher(clause);
-            while (matcher.find()) {
-            	if (":=".equals(matcher.group(2))) {
-            		// This is a directive.
-            		parameters.put(matcher.group(1), DirectiveFactory.createDirective(matcher.group(1), removeQuotes(matcher.group(3))));
-            	}
-            	else if (":".equals(matcher.group(5))) {
-            		// This is a typed attribute with a declared version.
-            		parameters.put(matcher.group(4), new TypedAttribute(matcher.group(4), removeQuotes(matcher.group(7)), matcher.group(6)));
-            	}
-            	else {
-            		// This is a typed attribute without a declared version.
-            		parameters.put(matcher.group(4), new TypedAttribute(matcher.group(4), removeQuotes(matcher.group(7)), TypedAttribute.Type.String));
-            	}
-            }
+            		parseTypedParameters(clause),
+            		defaultParameters);
 		}
 
         public String getNamespace() {

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/RequireCapabilityHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/RequireCapabilityHeader.java?rev=1705795&r1=1705794&r2=1705795&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/RequireCapabilityHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/RequireCapabilityHeader.java Tue Sep 29 02:06:33 2015
@@ -25,19 +25,22 @@ import org.osgi.resource.Resource;
 
 public class RequireCapabilityHeader extends AbstractClauseBasedHeader<RequireCapabilityHeader.Clause> implements RequirementHeader<RequireCapabilityHeader.Clause> {
     public static class Clause extends AbstractClause {
-		public static final String DIRECTIVE_EFFECTIVE = Constants.EFFECTIVE_DIRECTIVE;
-		public static final String DIRECTIVE_FILTER = Constants.FILTER_DIRECTIVE;
-		public static final String DIRECTIVE_RESOLUTION = Constants.RESOLUTION_DIRECTIVE;
+    	public static final String DIRECTIVE_CARDINALITY = CardinalityDirective.NAME;
+		public static final String DIRECTIVE_EFFECTIVE = EffectiveDirective.NAME;
+		public static final String DIRECTIVE_FILTER = FilterDirective.NAME;
+		public static final String DIRECTIVE_RESOLUTION = ResolutionDirective.NAME;
 		
 		private static final Collection<Parameter> defaultParameters = generateDefaultParameters(
-				EffectiveDirective.RESOLVE,
-				ResolutionDirective.MANDATORY);
+				EffectiveDirective.DEFAULT,
+				ResolutionDirective.MANDATORY,
+				CardinalityDirective.DEFAULT);
 		
 		public Clause(String clause) {
 			super(
             		parsePath(clause, Patterns.NAMESPACE, false), 
-            		parseParameters(clause, false), 
+            		parseTypedParameters(clause), 
             		defaultParameters);
+			
 		}
 		
 		public Clause(String path, Map<String, Parameter> parameters, Collection<Parameter> defaultParameters) {
@@ -54,7 +57,7 @@ public class RequireCapabilityHeader ext
 			Map<String, Parameter> parameters = new HashMap<String, Parameter>(attributes.size() + directives.size());
 			for (Map.Entry<String, Object> entry : attributes.entrySet()) {
 				String key = entry.getKey();
-				parameters.put(key, AttributeFactory.createAttribute(key, String.valueOf(entry.getValue())));
+				parameters.put(key, new TypedAttribute(key, entry.getValue()));
 			}
 			for (Map.Entry<String, String> entry : directives.entrySet()) {
 				String key = entry.getKey();

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/RequireCapabilityRequirement.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/RequireCapabilityRequirement.java?rev=1705795&r1=1705794&r2=1705795&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/RequireCapabilityRequirement.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/RequireCapabilityRequirement.java Tue Sep 29 02:06:33 2015
@@ -24,21 +24,27 @@ import org.osgi.resource.Resource;
 public class RequireCapabilityRequirement extends AbstractRequirement {
 	public static final String DIRECTIVE_FILTER = Namespace.REQUIREMENT_FILTER_DIRECTIVE;
 	
+	private final Map<String, Object> attributes;
 	private final Map<String, String> directives;
 	private final String namespace;
 	private final Resource resource;
 	
 	public RequireCapabilityRequirement(RequireCapabilityHeader.Clause clause, Resource resource) {
 		namespace = clause.getNamespace();
+		attributes = new HashMap<String, Object>(clause.getAttributes().size());
+		for (Attribute attribute : clause.getAttributes()) {
+			attributes.put(attribute.getName(), attribute.getValue());
+		}
 		directives = new HashMap<String, String>(clause.getDirectives().size());
-		for (Directive directive : clause.getDirectives())
+		for (Directive directive : clause.getDirectives()) {
 			directives.put(directive.getName(), directive.getValue());
+		}
 		this.resource = resource;
 	}
 
 	@Override
 	public Map<String, Object> getAttributes() {
-		return Collections.emptyMap();
+		return Collections.unmodifiableMap(attributes);
 	}
 
 	@Override

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemImportServiceHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemImportServiceHeader.java?rev=1705795&r1=1705794&r2=1705795&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemImportServiceHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemImportServiceHeader.java Tue Sep 29 02:06:33 2015
@@ -27,13 +27,15 @@ import org.osgi.service.subsystem.Subsys
 
 public class SubsystemImportServiceHeader extends AbstractClauseBasedHeader<SubsystemImportServiceHeader.Clause> implements RequirementHeader<SubsystemImportServiceHeader.Clause> {
     public static class Clause extends AbstractClause {
-		public static final String DIRECTIVE_EFFECTIVE = Constants.EFFECTIVE_DIRECTIVE;
-		public static final String DIRECTIVE_FILTER = Constants.FILTER_DIRECTIVE;
-		public static final String DIRECTIVE_RESOLUTION = Constants.RESOLUTION_DIRECTIVE;
+    	public static final String DIRECTIVE_CARDINALITY = CardinalityDirective.NAME;
+		public static final String DIRECTIVE_EFFECTIVE = EffectiveDirective.NAME;
+		public static final String DIRECTIVE_FILTER = FilterDirective.NAME;
+		public static final String DIRECTIVE_RESOLUTION = ResolutionDirective.NAME;
 
 		private static final Collection<Parameter> defaultParameters = generateDefaultParameters(
 				EffectiveDirective.ACTIVE,
-				ResolutionDirective.MANDATORY);
+				ResolutionDirective.MANDATORY,
+				CardinalityDirective.SINGLE);
 
 		public Clause(String clause) {
 			super(

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/TypedAttribute.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/TypedAttribute.java?rev=1705795&r1=1705794&r2=1705795&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/TypedAttribute.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/TypedAttribute.java Tue Sep 29 02:06:33 2015
@@ -13,41 +13,144 @@
  */
 package org.apache.aries.subsystem.core.archive;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+
 import org.osgi.framework.Version;
 
 public class TypedAttribute extends AbstractAttribute {
-	public static enum Type {
-		Double,
-		Long,
-		String,
-		Version
-	}
-	
-	private static Object parseValue(String value, Type type) {
-		switch (type) {
-			case Double:
-				return Double.valueOf(value);
-			case Long:
-				return Long.valueOf(value);
-			case Version:
-				return Version.parseVersion(value);
-			default:
-				return value;
+	private static final String DOUBLE = "Double";
+	private static final String LIST = "List";
+	private static final String LIST_DOUBLE = "List<Double>";
+	private static final String LIST_LONG = "List<Long>";
+	private static final String LIST_STRING = "List<String>";
+	private static final String LIST_VERSION = "List<Version>";
+	private static final String LONG = "Long";
+	private static final String STRING = "String";
+	private static final String VERSION = "Version";
+	
+	private static Object parseScalar(String value, String type) {
+		if (STRING.equals(type)) {
+			return value;
+		}
+		if (VERSION.equals(type)) {
+			return Version.parseVersion(value);
+		}
+		if (LONG.equals(type)) {
+			return Long.valueOf(value);
 		}
+		if (DOUBLE.equals(type)) {
+			return Double.valueOf(value);
+		}
+		return null;
 	}
 	
-	private final Type type;
+	private static Object parseList(String value, String type) {
+		if (!type.startsWith(LIST)) {
+			return null;
+		}
+		String scalar;
+		if (type.length() == LIST.length()) {
+			scalar = STRING;
+		}
+		else {
+			Matcher matcher = Patterns.SCALAR_LIST.matcher(type);
+			if (!matcher.matches()) {
+				return null;
+			}
+			scalar = matcher.group(1);
+		}
+		String[] values = value.split(",");
+		List<Object> result = new ArrayList<Object>(values.length);
+		for (String s : values) {
+			result.add(parseScalar(s, scalar));
+		}
+		return result;
+	}
 	
-	public TypedAttribute(String name, String value, String type) {
-		this(name, value, Type.valueOf(type));
+	private static Object parseValue(String value, String type) {
+		if (type == null) {
+			return value;
+		}
+		Object result = parseScalar(value, type);
+		if (result == null) {
+			result = parseList(value, type);
+		}
+		return result;
 	}
 	
-	public TypedAttribute(String name, String value, Type type) {
+	private final String type;
+	
+	public TypedAttribute(String name, String value, String type) {
 		super(name, parseValue(value, type));
 		this.type = type;
 	}
 	
-	public Type getType() {
-		return type;
+	public TypedAttribute(String name, Object value) {
+		super(name, value);
+		if (value instanceof String) {
+			type = STRING;
+		}
+		else if (value instanceof List) {
+			@SuppressWarnings("rawtypes")
+			List list = (List)value;
+			if (list.isEmpty()) {
+				type = LIST;
+			}
+			else {
+				Object o = list.get(0);
+				if (o instanceof String) {
+					type = LIST_STRING;
+				}
+				else if (o instanceof Version) {
+					type = LIST_VERSION;
+				}
+				else if (o instanceof Long) {
+					type = LIST_LONG;
+				}
+				else if (o instanceof Double) {
+					type = LIST_DOUBLE;
+				}
+				else {
+					throw new IllegalArgumentException(name + '=' + value);
+				}
+			}
+		}
+		else if (value instanceof Version) {
+			type = VERSION;
+		}
+		else if (value instanceof Long) {
+			type = LONG;
+		}
+		else if (value instanceof Double) {
+			type = DOUBLE;
+		}
+		else {
+			throw new IllegalArgumentException(name + '=' + value);
+		}
+	}
+	
+	@Override
+    public String toString() {
+		StringBuilder builder = new StringBuilder()
+				.append(getName())
+				.append(':')
+				.append(type)
+				.append("=\"");
+		if (type.startsWith(LIST)) {
+			@SuppressWarnings("rawtypes")
+			List list = (List)getValue();
+			if (!list.isEmpty()) {
+				builder.append(list.get(0));
+			}
+			for (int i = 1; i < list.size(); i++) {
+				builder.append(',').append(list.get(i));
+			}
+		}
+		else {
+			builder.append(getValue());
+		}
+		return builder.append('"').toString();
 	}
 }

Added: aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1419Test.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1419Test.java?rev=1705795&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1419Test.java (added)
+++ aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1419Test.java Tue Sep 29 02:06:33 2015
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.subsystem.itests.defect;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.aries.subsystem.core.archive.RequireCapabilityHeader;
+import org.apache.aries.subsystem.core.internal.BasicSubsystem;
+import org.apache.aries.subsystem.itests.SubsystemTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.service.subsystem.Subsystem;
+import org.osgi.service.subsystem.SubsystemConstants;
+import org.osgi.service.subsystem.SubsystemException;
+
+import aQute.bnd.osgi.Constants;
+
+/*
+ * https://issues.apache.org/jira/browse/ARIES-1419
+ * 
+ * Provide-Capability header parser does not support typed attributes.
+ */
+public class Aries1419Test extends SubsystemTest {
+    /*
+     * Subsystem-SymbolicName: application.a.esa
+     * 
+	 * Included In Archive
+	 * 		bundle.a.jar
+     */
+    private static final String APPLICATION_A = "application.a.esa";
+    /*
+     * Subsystem-SymbolicName: application.b.esa
+     * 
+	 * Included In Archive
+	 * 		bundle.c.jar
+     */
+    private static final String APPLICATION_B = "application.b.esa";
+	/*
+	 * Bundle-SymbolicName: bundle.a.jar
+	 * Require-Capability: "a;resolution:=optional;filter:=\"(b=c)\";d=e;
+	 *  f:String=g;h:Long=21474836470;i:Double=3.4028234663852886E39;
+	 *  j:Version=2.1;k:List=\"foo,bar,acme\";l:List<Version>=\"1.1,2.2,3.3\""
+	 */
+	private static final String BUNDLE_A = "bundle.a.jar";
+	/*
+	 * Bundle-SymbolicName: bundle.b.jar
+	 * Provide-Capability: "a;d=e;f:String=g;h:Long=21474836470;
+	 *  i:Double=3.4028234663852886E39;j:Version=2.1;k:List=\"foo,bar,acme\";
+	 *  l:List<Version>=\"1.1,2.2,3.3\""
+	 */
+	private static final String BUNDLE_B = "bundle.b.jar";
+	/*
+	 * Bundle-SymbolicName: bundle.c.jar
+	 * Require-Capability: "a;filter:="(d=e)"
+	 */
+	private static final String BUNDLE_C = "bundle.c.jar";
+	
+	private static boolean createdTestFiles;
+	
+	@Before
+	public void createTestFiles() throws Exception {
+		if (createdTestFiles)
+			return;
+		createBundleA();
+		createBundleB();
+		createBundleC();
+		createApplicationA();
+		createApplicationB();
+		createdTestFiles = true;
+	}
+	
+	private void createBundleA() throws IOException {
+		createBundle(
+				name(BUNDLE_A), 
+				requireCapability("a;resolution:=optional;filter:=\"(b=c)\"" 
+						+ ";d=e;f:String=g;h:Long=21474836470;i:Double=3.4028234663852886E39"
+						+ ";j:Version=2.1;k:List=\"foo,bar,acme\";l:List<Version>=\"1.1,2.2,3.3\""));
+	}
+	
+	private void createBundleB() throws IOException {
+		createBundle(
+				name(BUNDLE_B), 
+				provideCapability("a;b=c;d=e;f:String=g;h:Long=21474836470"
+						+ ";i:Double=3.4028234663852886E39;j:Version=2.1;"
+						+ "k:List=\"foo,bar,acme\";l:List<Version>=\"1.1,2.2,3.3\""));
+	}
+	
+	private void createBundleC() throws IOException {
+		createBundle(
+				name(BUNDLE_C), 
+				requireCapability("a;filter:=\"(&(b=c)(d=e)(f=g)(h<=21474836470)"
+						+ "(!(i>=3.4028234663852886E40))(&(j>=2)(!(version>=3)))"
+						+ "(|(k=foo)(k=bar)(k=acme))(&(l=1.1.0)(l=2.2)))\""));
+	}
+	
+	private static void createApplicationA() throws IOException {
+        createApplicationAManifest();
+        createSubsystem(APPLICATION_A, BUNDLE_A);
+    }
+    
+    private static void createApplicationAManifest() throws IOException {
+        Map<String, String> attributes = new HashMap<String, String>();
+        attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_A);
+        createManifest(APPLICATION_A + ".mf", attributes);
+    }
+    
+    private static void createApplicationB() throws IOException {
+        createApplicationBManifest();
+        createSubsystem(APPLICATION_B, BUNDLE_C);
+    }
+    
+    private static void createApplicationBManifest() throws IOException {
+        Map<String, String> attributes = new HashMap<String, String>();
+        attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_B);
+        createManifest(APPLICATION_B + ".mf", attributes);
+    }
+    
+    @Test
+    public void testRequireCapability() throws Exception {
+    	Subsystem applicationA = installSubsystemFromFile(APPLICATION_A);
+    	try {
+    		Bundle bundleA = getConstituentAsBundle(applicationA, BUNDLE_A, null, null);
+    		String expectedStr = bundleA.getHeaders().get(Constants.REQUIRE_CAPABILITY);
+    		RequireCapabilityHeader expected = new RequireCapabilityHeader(expectedStr);
+    		Map<String, String> headers = ((BasicSubsystem)applicationA).getDeploymentHeaders();
+    		String actualStr = headers.get(Constants.REQUIRE_CAPABILITY);
+    		RequireCapabilityHeader actual = new RequireCapabilityHeader(actualStr);
+    		assertEquals("Wrong header", expected, actual);
+    	}
+    	finally {
+    		uninstallSubsystemSilently(applicationA);
+    	}
+    }
+    
+    @Test
+    public void testProvideCapability() throws Exception {
+    	Bundle bundleB = installBundleFromFile(BUNDLE_B);
+    	try {
+    		Subsystem applicationB = installSubsystemFromFile(APPLICATION_B);
+    		uninstallSubsystemSilently(applicationB);
+    	}
+    	catch (SubsystemException e) {
+    		e.printStackTrace();
+    		fail("Subsystem should have installed");
+    	}
+    	finally {
+    		uninstallSilently(bundleB);
+    	}
+    }
+}