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/17 16:12:24 UTC

svn commit: r1703631 [1/3] - in /aries/trunk/subsystem: subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ subsystem-core/src/main/java/org/apache/aries/subsystem/core/capabilityset/ subsystem-core/src/main/java/org/apache/aries/subs...

Author: jwross
Date: Thu Sep 17 14:12:21 2015
New Revision: 1703631

URL: http://svn.apache.org/r1703631
Log:
ARIES-1387 Make equals and hashCode comparisons within the header, clause, and parameter hierarchies based on equivalency rather than string equals.

This commit is a rebasing of two commits made by Cristiano Gavião <cv...@gmail.com> with modifications.

This closes #27

https://github.com/apache/aries/pull/27
https://github.com/cvgaviao/aries ARIES-1387
https://github.com/apache/aries/pull/27.patch

commit 0a825a12f7b503a49c36cc16daa6c5fbb017d87d
Author: Cristiano Gavião <cv...@gmail.com>
Date:   Tue Sep 1 07:28:02 2015 -0300

    added missed class

commit dceab1743016c08e68922c1971b6ab34c98b6db8
Author: Cristiano Gavião <cv...@gmail.com>
Date:   Sat Aug 29 08:29:20 2015 -0300

    factored headers and clauses classes and added equals() and hashCode()

    issue #ARIES-1387

Plus the following modifications.

Reinforce the immutablity and safe publication of the header, clause, and parameter hierarchies: (1) no default constructors; (2) all fields are final; (3) no subclass methods are called in the constructor; (4) synchronize access to mutable fields that are visible to subclasses (for example, the Provide-Capability header does its own parameter parsing and modifies the clause collection after it has been published).

Pull out more common functionality within the header, clause, and parameter hierarchies.

Do not use the original header value as part of the equals and hashCode methods; otherwise, it basically amounts to a string equals comparison.

Remove unnecessary implementations of equals and hashCode and rely on implementations in the abstract classes as much as possible. Currently, the only exception is the filter directive so that the filter comparison is not string equals.

Rewrite equals and hashCode in a format more familiar to this committer. There may have been nothing wrong with the original implementations.

Support equivalent filter comparisons rather than just string equals:
(1) Cache a simple filter and override equals/hashCode in the filter directive rather than rely on a string comparison in the abstract parameter.
(2) Override equals/hashCode in the simple filter rather than rely on an identity comparison.
(3) Use sets rather than lists in the simple filter so that equivalent filters are equal and have the same hashCode. For example, the following two filters are equal: (&(a=b)(y=z)) and (&(y=z)(a=b)).

In the equality test:
(1) Ensure that equivalent versions and clauses are equal.
(2) Include default values for the effective directive.
(3) Use the correct version range attribute name (i.e. "bundle-version" instead of "version") for the Require-Bundle header.
(4) Make the subsystem manifest equality check more robust so that default values and equivalent orderings are included.
(5) Use valid clause syntax. For example, the path must come first.
(6) Remove unnecessary references to header names. The header name is implied based on which header class is instantiated and is ignored as part of processing the value.

Add Subsystem-Type to the abstract header hierarchy.

Convert constructors taking a Requirement argument into static valueOf methods which translate requirements back into their respective manifest headers. Implement unfinished functionality, particularly with regard to extracting attributes from filters and creating filters from attributes.

Fix Subsystem-ImportService header and tests. Previously, the osgi.service requirement namespace was used for the path, whereas the correct path is the value of the namespace attribute.

Fix the filter generation within OsgiExecutionEnvironmentRequirement and tests. Previously, the filter would contain unnecessary operators for unary attributes. This broke ManifestHeaderProce

Support both "version" and "bundle-version" as version range attribute names.

Add default value for version range attribute to Require-Bundle and convert versions into ranges.

Add resolution directive default value to the Import-Package header.

Added:
    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/AbstractClauseBasedHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/Patterns.java
    aries/trunk/subsystem/subsystem-core/src/test/java/org/apache/aries/subsystem/core/archive/SubsystemManifestEqualityTest.java
    aries/trunk/subsystem/subsystem-core/src/test/resources/
    aries/trunk/subsystem/subsystem-core/src/test/resources/files/
    aries/trunk/subsystem/subsystem-core/src/test/resources/files/SUBSYSTEM.MF.1
    aries/trunk/subsystem/subsystem-core/src/test/resources/files/SUBSYSTEM.MF.2
Modified:
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractAttribute.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractDirective.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractParameter.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AriesSubsystemParentsHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AttributeFactory.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedContentHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedVersionAttribute.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeploymentManifest.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DynamicImportPackageHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ExportPackageHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/FilterDirective.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/FragmentHostHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericClause.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ImportPackageHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/OsgiExecutionEnvironmentRequirement.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/PreferredProviderHeader.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/ProvisionResourceHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ReferenceDirective.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/RequireBundleHeader.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/ResolutionDirective.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemContentHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemExportServiceHeader.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/SubsystemManifest.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/SubsystemTypeHeader.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/VersionRangeAttribute.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/capabilityset/SimpleFilter.java
    aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/RawSubsystemResource.java
    aries/trunk/subsystem/subsystem-core/src/test/java/org/apache/aries/subsystem/core/archive/BundleRequiredExecutionEnvironmentHeaderTest.java
    aries/trunk/subsystem/subsystem-core/src/test/java/org/apache/aries/subsystem/core/archive/ImportPackageHeaderTest.java
    aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/ResolutionTest.java
    aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/ServiceDependencyTest.java

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractAttribute.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractAttribute.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractAttribute.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractAttribute.java Thu Sep 17 14:12:21 2015
@@ -18,11 +18,13 @@ public abstract class AbstractAttribute
 		super(name, value);
 	}
 	
-	public StringBuilder appendToFilter(StringBuilder builder) {
+	@Override
+    public StringBuilder appendToFilter(StringBuilder builder) {
 		return builder.append('(').append(getName()).append('=').append(getValue()).append(')');
 	}
 
-	public String toString() {
+	@Override
+    public String toString() {
 		return new StringBuilder()
 				.append(getName())
 				.append('=')

Added: 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=1703631&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractClause.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractClause.java Thu Sep 17 14:12:21 2015
@@ -0,0 +1,168 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.osgi.framework.VersionRange;
+
+public abstract class AbstractClause implements Clause {
+	protected static Collection<Parameter> generateDefaultParameters(Parameter... parameters) {
+		if (parameters == null || parameters.length == 0) {
+			return Collections.emptyList();
+		}
+		Collection<Parameter> defaults = new ArrayList<Parameter>(parameters.length);
+		for (Parameter parameter : parameters) {
+			defaults.add(parameter);
+		}
+		return defaults;
+	}
+	
+	protected static Map<String, Parameter> parseParameters(String clause, boolean replaceVersionWithVersionRange) {
+    	Map<String, Parameter> parameters = new HashMap<String, Parameter>();
+		Matcher matcher = Patterns.PARAMETER.matcher(clause);
+		while (matcher.find()) {
+            Parameter parameter = ParameterFactory.create(matcher.group());
+            if (replaceVersionWithVersionRange && (parameter instanceof VersionAttribute)) {
+                parameter = new VersionRangeAttribute(new VersionRange(String.valueOf(parameter.getValue())));
+            }
+            parameters.put(parameter.getName(), parameter);
+        }
+		return parameters;
+    }
+    
+    protected static String parsePath(String clause, Pattern pattern, boolean replaceAllWhitespace) {
+    	Matcher matcher = pattern.matcher(clause);
+        if (!matcher.find())
+            throw new IllegalArgumentException("Invalid path: " + clause);
+        String path = matcher.group();
+        if (replaceAllWhitespace) {
+        	path = path.replaceAll("\\s", "");
+        }
+        return path;
+    }
+	
+	protected final Map<String, Parameter> parameters;
+	protected final String path;
+    
+    public AbstractClause(String path, Map<String, Parameter> parameters, Collection<Parameter> defaultParameters) {
+    	if (path == null) {
+    		throw new NullPointerException();
+    	}
+    	for (Parameter parameter : defaultParameters) {
+        	String name = parameter.getName();
+        	if (parameters.containsKey(name)) {
+        		continue;
+        	}
+        	parameters.put(name, parameter);
+    	}
+    	this.path = path;
+    	this.parameters = Collections.synchronizedMap(parameters);
+    }
+
+    @Override
+    public Attribute getAttribute(String name) {
+        Parameter result = parameters.get(name);
+        if (result instanceof Attribute) {
+            return (Attribute) result;
+        }
+        return null;
+    }
+
+    @Override
+    public Collection<Attribute> getAttributes() {
+        ArrayList<Attribute> attributes = new ArrayList<Attribute>(
+                parameters.size());
+        for (Parameter parameter : parameters.values()) {
+            if (parameter instanceof Attribute) {
+                attributes.add((Attribute) parameter);
+            }
+        }
+        attributes.trimToSize();
+        return attributes;
+    }
+
+    @Override
+    public Directive getDirective(String name) {
+        Parameter result = parameters.get(name);
+        if (result instanceof Directive) {
+            return (Directive) result;
+        }
+        return null;
+    }
+
+    @Override
+    public Collection<Directive> getDirectives() {
+        ArrayList<Directive> directives = new ArrayList<Directive>(
+                parameters.size());
+        for (Parameter parameter : parameters.values()) {
+            if (parameter instanceof Directive) {
+                directives.add((Directive) parameter);
+            }
+        }
+        directives.trimToSize();
+        return directives;
+    }
+
+    @Override
+    public Parameter getParameter(String name) {
+        return parameters.get(name);
+    }
+
+    @Override
+    public Collection<Parameter> getParameters() {
+        return Collections.unmodifiableCollection(parameters.values());
+    }
+
+    @Override
+    public String getPath() {
+        return path;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + path.hashCode();
+        result = 31 * result + parameters.hashCode();
+        return result;
+    }
+    
+    @Override
+    public boolean equals(Object o) {
+    	if (o == this) {
+    		return true;
+    	}
+    	if (!(o instanceof AbstractClause)) {
+    		return false;
+    	}
+    	AbstractClause that = (AbstractClause)o;
+    	return that.path.equals(this.path)
+    			&& that.parameters.equals(this.parameters);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder().append(getPath());
+        for (Parameter parameter : getParameters()) {
+            builder.append(';').append(parameter);
+        }
+        return builder.toString();
+    }
+}

Added: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractClauseBasedHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractClauseBasedHeader.java?rev=1703631&view=auto
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractClauseBasedHeader.java (added)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractClauseBasedHeader.java Thu Sep 17 14:12:21 2015
@@ -0,0 +1,82 @@
+/*
+ * 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 java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class AbstractClauseBasedHeader<C extends Clause> implements Header<C> {
+	public interface ClauseFactory<C> {
+		public C newInstance(String clause);
+	}
+	
+    protected final Set<C> clauses;
+    
+    public AbstractClauseBasedHeader(Collection<C> clauses) {
+        if (clauses.isEmpty()) {
+            throw new IllegalArgumentException(String.format(
+                    "The header %s must have at least one clause.", getName()));
+        }
+        this.clauses = Collections.synchronizedSet(new HashSet<C>(clauses));
+    }
+
+    public AbstractClauseBasedHeader(String header, ClauseFactory<C> factory) {
+    	Collection<String> clauseStrs = new ClauseTokenizer(header).getClauses();
+		Set<C> clauses = new HashSet<C>(clauseStrs.size());
+		for (String clause : new ClauseTokenizer(header).getClauses()) {
+			clauses.add(factory.newInstance(clause));
+		}
+		this.clauses = Collections.synchronizedSet(clauses);
+    }
+
+    @Override
+    public final Collection<C> getClauses() {
+        return Collections.unmodifiableCollection(clauses);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + getName().hashCode();
+        result = 31 * result + clauses.hashCode();
+        return result;
+    }
+    
+    @Override
+    public boolean equals(Object o) {
+    	if (o == this) {
+    		return true;
+    	}
+    	if (!(o instanceof AbstractClauseBasedHeader)) {
+    		return false;
+    	}
+    	AbstractClauseBasedHeader<?> that = (AbstractClauseBasedHeader<?>)o;
+    	return that.getName().equals(this.getName())
+    			&& that.clauses.equals(this.clauses);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        for (C clause : getClauses()) {
+            builder.append(clause).append(',');
+        }
+        // Remove the trailing comma. Note at least one clause is guaranteed to
+        // exist.
+        builder.deleteCharAt(builder.length() - 1);
+        return builder.toString();
+    }
+}

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractDirective.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractDirective.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractDirective.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractDirective.java Thu Sep 17 14:12:21 2015
@@ -18,11 +18,13 @@ public abstract class AbstractDirective
 		super(name, value);
 	}
 	
-	public String getValue() {
+	@Override
+    public String getValue() {
 		return (String)super.getValue();
 	}
 
-	public String toString() {
+	@Override
+    public String toString() {
 		return new StringBuilder()
 				.append(getName())
 				.append(":=")

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractHeader.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractHeader.java Thu Sep 17 14:12:21 2015
@@ -17,17 +17,12 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.osgi.framework.Version;
 import org.osgi.framework.namespace.IdentityNamespace;
 import org.osgi.resource.Resource;
 
-public abstract class AbstractHeader implements Header {
-	protected static final String REGEX = Grammar.CLAUSE + "(?=,|\\z)";
-	protected static final Pattern PATTERN = Pattern.compile(REGEX);
-	
+public abstract class AbstractHeader implements Header<Clause> {
 	// TODO This is specific to deployment manifests and shouldn't be at this level.
 	protected static void appendResource(Resource resource, StringBuilder builder) {
 		Map<String, Object> attributes = resource.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE).get(0).getAttributes();
@@ -45,33 +40,65 @@ public abstract class AbstractHeader imp
 			.append(namespace);
 	}
 	
-	protected final List<Clause> clauses = new ArrayList<Clause>();
+	protected final List<Clause> clauses;
 	protected final String name;
 	protected final String value;
 	
 	public AbstractHeader(String name, String value) {
+		if (name == null) {
+			throw new NullPointerException();
+		}
+		ClauseTokenizer tokenizer = new ClauseTokenizer(value);
+		List<Clause> clauses = new ArrayList<Clause>(tokenizer.getClauses().size());
+		for (String clause : tokenizer.getClauses()) {
+			clauses.add(new GenericClause(clause));
+		}
+		if (clauses.isEmpty()) {
+			throw new IllegalArgumentException("Invalid header syntax -> " + name + ": " + value);
+		}
 		this.name = name;
 		this.value = value;
-		Matcher matcher = PATTERN.matcher(value);
-		while (matcher.find())
-			clauses.add(new GenericClause(matcher.group()));
-		if (clauses.isEmpty())
-			throw new IllegalArgumentException("Invalid header syntax -> " + name + ": " + value);
+		this.clauses = Collections.synchronizedList(clauses);
 	}
-	
-	public List<Clause> getClauses() {
+
+	@Override
+    public List<Clause> getClauses() {
 		return Collections.unmodifiableList(clauses);
 	}
 
-	public String getName() {
+	@Override
+    public String getName() {
 		return name;
 	}
 	
-	public String getValue() {
+	@Override
+    public String getValue() {
 		return value;
 	}
 	
-	public String toString() {
+	@Override
+    public boolean equals(Object o) {
+    	if (o == this) {
+    		return true;
+    	}
+    	if (!(o instanceof AbstractHeader)) {
+    		return false;
+    	}
+    	AbstractHeader that = (AbstractHeader)o;
+    	return that.name.equals(this.name)
+    			&& that.clauses.equals(this.clauses);
+    }
+	
+	@Override
+	public int hashCode() {
+	    int result = 17;
+	    result = 31 * result + name.hashCode();
+	    result = 31 * result + clauses.hashCode();
+	    return result;
+	}
+
+	@Override
+    public String toString() {
 		return new StringBuilder(getClass().getName())
 		.append(": name=")
 		.append(name)

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractParameter.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractParameter.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractParameter.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AbstractParameter.java Thu Sep 17 14:12:21 2015
@@ -14,19 +14,45 @@
 package org.apache.aries.subsystem.core.archive;
 
 public abstract class AbstractParameter implements Parameter {
-	private final String myName;
-	private final Object myValue;
+	protected final String name;
+	protected final Object value;
 	
 	public AbstractParameter(String name, Object value) {
-		myName = name;
-		myValue = value;
+		if (name == null || value == null) {
+			throw new NullPointerException();
+		}
+		this.name = name;
+		this.value = value;
 	}
 	
-	public String getName() {
-		return myName;
+	@Override
+    public String getName() {
+		return name;
 	}
 	
-	public Object getValue() {
-		return myValue;
+	@Override
+    public Object getValue() {
+		return value;
+	}
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + name.hashCode();
+        result = 31 * result + value.hashCode();
+        return result;
+    }
+
+    @Override
+	public boolean equals(Object o) {
+		if (o == this) {
+    		return true;
+    	}
+    	if (!(o instanceof AbstractParameter)) {
+    		return false;
+    	}
+    	AbstractParameter that = (AbstractParameter)o;
+    	return that.name.equals(this.name)
+    			&& that.value.equals(this.value);
 	}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AriesSubsystemParentsHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AriesSubsystemParentsHeader.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AriesSubsystemParentsHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AriesSubsystemParentsHeader.java Thu Sep 17 14:12:21 2015
@@ -15,57 +15,28 @@ package org.apache.aries.subsystem.core.
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.apache.aries.subsystem.core.internal.BasicSubsystem;
 import org.apache.aries.subsystem.core.internal.OsgiIdentityRequirement;
 import org.osgi.framework.Version;
-import org.osgi.framework.VersionRange;
 import org.osgi.resource.Requirement;
 import org.osgi.resource.Resource;
 import org.osgi.service.subsystem.SubsystemConstants;
 
-public class AriesSubsystemParentsHeader implements RequirementHeader<AriesSubsystemParentsHeader.Clause> {
-	public static class Clause implements org.apache.aries.subsystem.core.archive.Clause {
-		public static final String ATTRIBUTE_VERSION = VersionRangeAttribute.NAME;
+public class AriesSubsystemParentsHeader extends AbstractClauseBasedHeader<AriesSubsystemParentsHeader.Clause> implements RequirementHeader<AriesSubsystemParentsHeader.Clause> {
+    public static class Clause extends AbstractClause {
+		public static final String ATTRIBUTE_VERSION = VersionRangeAttribute.NAME_VERSION;
 		public static final String ATTRIBUTE_RESOURCEID = "resourceId";
 		public static final String ATTRIBUTE_TYPE = TypeAttribute.NAME;
 		
-		private static final Pattern PATTERN_SYMBOLICNAME = Pattern.compile('(' + Grammar.SYMBOLICNAME + ")(?=;|\\z)");
-		private static final Pattern PATTERN_PARAMETER = Pattern.compile('(' + Grammar.PARAMETER + ")(?=;|\\z)");
-		
-		private static void fillInDefaults(Map<String, Parameter> parameters) {
-			Parameter parameter = parameters.get(ATTRIBUTE_TYPE);
-			if (parameter == null)
-				parameters.put(ATTRIBUTE_TYPE, TypeAttribute.newInstance(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION));
-			parameter = parameters.get(ATTRIBUTE_VERSION);
-			if (parameter == null)
-				parameters.put(ATTRIBUTE_VERSION, VersionAttribute.DEFAULT);
-		}
-		
-		private final String path;
-		private final Map<String, Parameter> parameters = new HashMap<String, Parameter>();
-		
 		public Clause(String clause) {
-			Matcher matcher = PATTERN_SYMBOLICNAME.matcher(clause);
-			if (!matcher.find())
-				throw new IllegalArgumentException("Missing symbolic name path: " + clause);
-			path = matcher.group();
-			matcher.usePattern(PATTERN_PARAMETER);
-			while (matcher.find()) {
-				Parameter parameter = ParameterFactory.create(matcher.group());
-				if (parameter instanceof VersionAttribute)
-					parameter = new VersionRangeAttribute(new VersionRange(String.valueOf(parameter.getValue())));
-				parameters.put(parameter.getName(), parameter);
-			}
-			fillInDefaults(parameters);
+			super(
+					parsePath(clause, Patterns.SYMBOLIC_NAME, false), 
+					parseParameters(clause, true), 
+					generateDefaultParameters(
+							TypeAttribute.newInstance(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION),
+							VersionRangeAttribute.DEFAULT_VERSION));
 		}
 		
 		public Clause(BasicSubsystem subsystem, boolean referenceCount) {
@@ -80,43 +51,7 @@ public class AriesSubsystemParentsHeader
 					&& getType().equals(
 							subsystem.getType());
 		}
-		
-		@Override
-		public Attribute getAttribute(String name) {
-			Parameter result = parameters.get(name);
-			if (result instanceof Attribute)
-				return (Attribute)result;
-			return null;
-		}
-
-		@Override
-		public Collection<Attribute> getAttributes() {
-			ArrayList<Attribute> attributes = new ArrayList<Attribute>(parameters.size());
-			for (Parameter parameter : parameters.values())
-				if (parameter instanceof Attribute)
-					attributes.add((Attribute)parameter);
-			attributes.trimToSize();
-			return attributes;
-		}
-
-		@Override
-		public Directive getDirective(String name) {
-			Parameter result = parameters.get(name);
-			if (result instanceof Directive)
-				return (Directive)result;
-			return null;
-		}
-
-		@Override
-		public Collection<Directive> getDirectives() {
-			ArrayList<Directive> directives = new ArrayList<Directive>(parameters.size());
-			for (Parameter parameter : parameters.values())
-				if (parameter instanceof Directive)
-					directives.add((Directive)parameter);
-			directives.trimToSize();
-			return directives;
-		}
-		
+
 		public long getId() {
 			Attribute attribute = getAttribute(ATTRIBUTE_RESOURCEID);
 			if (attribute == null)
@@ -124,21 +59,6 @@ public class AriesSubsystemParentsHeader
 			return Long.valueOf(String.valueOf(attribute.getValue()));
 		}
 
-		@Override
-		public Parameter getParameter(String name) {
-			return parameters.get(name);
-		}
-
-		@Override
-		public Collection<Parameter> getParameters() {
-			return Collections.unmodifiableCollection(parameters.values());
-		}
-
-		@Override
-		public String getPath() {
-			return path;
-		}
-		
 		public String getSymbolicName() {
 			return path;
 		}
@@ -154,16 +74,6 @@ public class AriesSubsystemParentsHeader
 		public OsgiIdentityRequirement toRequirement(Resource resource) {
 			return new OsgiIdentityRequirement(getSymbolicName(), getVersion(), getType(), false);
 		}
-		
-		@Override
-		public String toString() {
-			StringBuilder builder = new StringBuilder()
-					.append(getPath());
-			for (Parameter parameter : getParameters()) {
-				builder.append(';').append(parameter);
-			}
-			return builder.toString();
-		}
 	}
 	
 	public static final String NAME = "AriesSubsystem-Parents";
@@ -188,24 +98,19 @@ public class AriesSubsystemParentsHeader
 		return builder;
 	}
 	
-	private static Collection<Clause> processHeader(String value) {
-		Collection<String> clauseStrs = new ClauseTokenizer(value).getClauses();
-		Set<Clause> clauses = new HashSet<Clause>(clauseStrs.size());
-		for (String clause : new ClauseTokenizer(value).getClauses())
-			clauses.add(new Clause(clause));
-		return clauses;
-	}
-	
-	private final Set<Clause> clauses;
-	
 	public AriesSubsystemParentsHeader(Collection<Clause> clauses) {
-		if (clauses.isEmpty())
-			throw new IllegalArgumentException("A " + NAME + " header must have at least one clause");
-		this.clauses = new HashSet<Clause>(clauses);
+		super(clauses);
 	}
 	
 	public AriesSubsystemParentsHeader(String value) {
-		this(processHeader(value));
+		super(
+				value, 
+				new ClauseFactory<Clause>() {
+					@Override
+					public Clause newInstance(String clause) {
+						return new Clause(clause);
+					}
+				});
 	}
 	
 	public boolean contains(BasicSubsystem subsystem) {
@@ -226,11 +131,6 @@ public class AriesSubsystemParentsHeader
 	}
 	
 	@Override
-	public Collection<AriesSubsystemParentsHeader.Clause> getClauses() {
-		return Collections.unmodifiableSet(clauses);
-	}
-
-	@Override
 	public String getName() {
 		return NAME;
 	}
@@ -247,15 +147,4 @@ public class AriesSubsystemParentsHeader
 			requirements.add(clause.toRequirement(resource));
 		return requirements;
 	}
-	
-	@Override
-	public String toString() {
-		StringBuilder builder = new StringBuilder();
-		for (Clause clause : getClauses()) {
-			builder.append(clause).append(',');
-		}
-		// Remove the trailing comma. Note at least one clause is guaranteed to exist.
-		builder.deleteCharAt(builder.length() - 1);
-		return builder.toString();
-	}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AttributeFactory.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AttributeFactory.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AttributeFactory.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/AttributeFactory.java Thu Sep 17 14:12:21 2015
@@ -20,8 +20,7 @@ public class AttributeFactory {
 		if (Constants.VERSION_ATTRIBUTE.equals(name)) {
 			if (Character.isDigit(value.charAt(0)))
 				return new VersionAttribute(value);
-			else
-				return new VersionRangeAttribute(value);
+            return new VersionRangeAttribute(value);
 		}
 		if (TypeAttribute.NAME.equals(name))
 			return new TypeAttribute(value);

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedContentHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedContentHeader.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedContentHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedContentHeader.java Thu Sep 17 14:12:21 2015
@@ -15,14 +15,7 @@ package org.apache.aries.subsystem.core.
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.apache.aries.subsystem.core.internal.ResourceHelper;
 import org.apache.aries.subsystem.core.internal.Utils;
@@ -31,282 +24,162 @@ import org.osgi.resource.Requirement;
 import org.osgi.resource.Resource;
 import org.osgi.service.subsystem.SubsystemConstants;
 
-public class DeployedContentHeader implements RequirementHeader<DeployedContentHeader.Clause> {
-	public static class Clause implements org.apache.aries.subsystem.core.archive.Clause {
-		public static final String ATTRIBUTE_DEPLOYEDVERSION = DeployedVersionAttribute.NAME;
-		public static final String ATTRIBUTE_RESOURCEID = "resourceId";
-		public static final String ATTRIBUTE_TYPE = TypeAttribute.NAME;
-		public static final String DIRECTIVE_REFERENCE = ReferenceDirective.NAME;
-		public static final String DIRECTIVE_STARTORDER = StartOrderDirective.NAME;
-		
-		private static final Pattern PATTERN_SYMBOLICNAME = Pattern.compile('(' + Grammar.SYMBOLICNAME + ")(?=;|\\z)");
-		private static final Pattern PATTERN_PARAMETER = Pattern.compile('(' + Grammar.PARAMETER + ")(?=;|\\z)");
-		
-		private static void fillInDefaults(Map<String, Parameter> parameters) {
-			Parameter parameter = parameters.get(ATTRIBUTE_TYPE);
-			if (parameter == null)
-				parameters.put(ATTRIBUTE_TYPE, TypeAttribute.DEFAULT);
-			parameter = parameters.get(DIRECTIVE_REFERENCE);
-			if (parameter == null)
-				parameters.put(DIRECTIVE_REFERENCE, ReferenceDirective.TRUE);
-		}
-		
-		private final String path;
-		private final Map<String, Parameter> parameters = new HashMap<String, Parameter>();
-		
-		public Clause(String clause) {
-			Matcher matcher = PATTERN_SYMBOLICNAME.matcher(clause);
-			if (!matcher.find())
-				throw new IllegalArgumentException("Missing symbolic name path: " + clause);
-			path = matcher.group();
-			matcher.usePattern(PATTERN_PARAMETER);
-			while (matcher.find()) {
-				Parameter parameter = ParameterFactory.create(matcher.group());
-				parameters.put(parameter.getName(), parameter);
-			}
-			fillInDefaults(parameters);
-		}
-		
-		public Clause(Resource resource) {
-			this(resource, true);
-		}
-		
-		public Clause(Resource resource, boolean referenced) {
-			this(appendResource(resource, new StringBuilder(), referenced).toString());
-		}
-		
-		public boolean contains(Resource resource) {
-			return getSymbolicName().equals(
-					ResourceHelper.getSymbolicNameAttribute(resource))
-					&& getDeployedVersion().equals(
-							ResourceHelper.getVersionAttribute(resource))
-					&& getType().equals(
-							ResourceHelper.getTypeAttribute(resource));
-		}
-		
-		@Override
-		public boolean equals(Object o) {
-			if (o == this)
-				return true;
-			if (!(o instanceof Clause))
-				return false;
-			Clause that = (Clause)o;
-			return getSymbolicName().equals(that.getSymbolicName())
-					&& getDeployedVersion().equals(that.getDeployedVersion())
-					&& getType().equals(that.getType());
-		}
-		
-		@Override
-		public int hashCode() {
-			int result = 17;
-			result = 31 * result + getSymbolicName().hashCode();
-			result = 31 * result + getDeployedVersion().hashCode();
-			result = 31 * result + getType().hashCode();
-			return result;
-		}
-		
-		@Override
-		public Attribute getAttribute(String name) {
-			Parameter result = parameters.get(name);
-			if (result instanceof Attribute)
-				return (Attribute)result;
-			return null;
-		}
-
-		@Override
-		public Collection<Attribute> getAttributes() {
-			ArrayList<Attribute> attributes = new ArrayList<Attribute>(parameters.size());
-			for (Parameter parameter : parameters.values())
-				if (parameter instanceof Attribute)
-					attributes.add((Attribute)parameter);
-			attributes.trimToSize();
-			return attributes;
-		}
-		
-		public Version getDeployedVersion() {
-			return ((DeployedVersionAttribute)getAttribute(ATTRIBUTE_DEPLOYEDVERSION)).getVersion();
-		}
-
-		@Override
-		public Directive getDirective(String name) {
-			Parameter result = parameters.get(name);
-			if (result instanceof Directive)
-				return (Directive)result;
-			return null;
-		}
-
-		@Override
-		public Collection<Directive> getDirectives() {
-			ArrayList<Directive> directives = new ArrayList<Directive>(parameters.size());
-			for (Parameter parameter : parameters.values())
-				if (parameter instanceof Directive)
-					directives.add((Directive)parameter);
-			directives.trimToSize();
-			return directives;
-		}
-
-		@Override
-		public Parameter getParameter(String name) {
-			return parameters.get(name);
-		}
-
-		@Override
-		public Collection<Parameter> getParameters() {
-			return Collections.unmodifiableCollection(parameters.values());
-		}
-
-		@Override
-		public String getPath() {
-			return path;
-		}
-		
-		public String getSymbolicName() {
-			return path;
-		}
-		
-		public int getStartOrder() {
-			return ((StartOrderDirective)getAttribute(DIRECTIVE_STARTORDER)).getStartOrder();
-		}
-		
-		public String getType() {
-			return ((TypeAttribute)getAttribute(ATTRIBUTE_TYPE)).getType();
-		}
-		
-		public boolean isReferenced() {
-			return ((ReferenceDirective)getDirective(DIRECTIVE_REFERENCE)).isReferenced();
-		}
-		
-		public DeployedContentRequirement toRequirement(Resource resource) {
-			return new DeployedContentRequirement(this, resource);
-		}
-		
-		@Override
-		public String toString() {
-			StringBuilder builder = new StringBuilder()
-					.append(getPath());
-			for (Parameter parameter : getParameters()) {
-				builder.append(';').append(parameter);
-			}
-			return builder.toString();
-		}
-	}
-	
-	public static final String NAME = SubsystemConstants.DEPLOYED_CONTENT;
-	
-	public static DeployedContentHeader newInstance(Collection<Resource> resources) {
-		StringBuilder builder = new StringBuilder();
-		for (Resource resource : resources) {
-			appendResource(resource, builder, true);
-			builder.append(',');
-		}
-		// Remove the trailing comma.
-		// TODO Intentionally letting the exception propagate since there must be at least one resource.
-		builder.deleteCharAt(builder.length() - 1);
-		return new DeployedContentHeader(builder.toString());
-	}
-	
-	private static StringBuilder appendResource(Resource resource, StringBuilder builder, boolean referenced) {
-		String symbolicName = ResourceHelper.getSymbolicNameAttribute(resource);
-		Version version = ResourceHelper.getVersionAttribute(resource);
-		String type = ResourceHelper.getTypeAttribute(resource);
-		builder.append(symbolicName)
-			.append(';')
-			.append(Clause.ATTRIBUTE_DEPLOYEDVERSION)
-			.append('=')
-			.append(version.toString())
-			.append(';')
-			.append(Clause.ATTRIBUTE_TYPE)
-			.append('=')
-			.append(type)
-			.append(';')
-			.append(Clause.ATTRIBUTE_RESOURCEID)
-			.append('=')
-			.append(Utils.getId(resource))
-			.append(';')
-			.append(Clause.DIRECTIVE_REFERENCE)
-			.append(":=")
-			.append(referenced);
-		return builder;
-	}
-	
-	private static Collection<Clause> processHeader(String value) {
-		Collection<String> clauseStrs = new ClauseTokenizer(value).getClauses();
-		Set<Clause> clauses = new HashSet<Clause>(clauseStrs.size());
-		for (String clause : new ClauseTokenizer(value).getClauses())
-			clauses.add(new Clause(clause));
-		return clauses;
-	}
-	
-	private final Set<Clause> clauses;
-	
-	public DeployedContentHeader(Collection<Clause> clauses) {
-		if (clauses.isEmpty())
-			throw new IllegalArgumentException("A " + NAME + " header must have at least one clause");
-		this.clauses = new HashSet<Clause>(clauses);
-	}
-	
-	public DeployedContentHeader(String value) {
-		this(processHeader(value));
-	}
-	
-	public boolean contains(Resource resource) {
-		for (Clause clause : getClauses())
-			if (clause.contains(resource))
-				return true;
-		return false;
-	}
-	
-	public Clause getClause(Resource resource) {
-		String symbolicName = ResourceHelper.getSymbolicNameAttribute(resource);
-		Version version = ResourceHelper.getVersionAttribute(resource);
-		String type = ResourceHelper.getTypeAttribute(resource);
-		for (Clause clause : clauses) {
-			if (symbolicName.equals(clause.getPath())
-					&& clause.getDeployedVersion().equals(version)
-					&& type.equals(clause.getType()))
-				return clause;
-		}
-		return null;
-	}
-	
-	@Override
-	public Collection<DeployedContentHeader.Clause> getClauses() {
-		return Collections.unmodifiableSet(clauses);
-	}
-
-	@Override
-	public String getName() {
-		return NAME;
-	}
-
-	@Override
-	public String getValue() {
-		return toString();
-	}
-	
-	public boolean isReferenced(Resource resource) {
-		DeployedContentHeader.Clause clause = getClause(resource);
-		if (clause == null)
-			return false;
-		return clause.isReferenced();
-	}
-	
-	@Override
-	public List<Requirement> toRequirements(Resource resource) {
-		List<Requirement> requirements = new ArrayList<Requirement>(clauses.size());
-		for (Clause clause : clauses)
-			requirements.add(clause.toRequirement(resource));
-		return requirements;
-	}
-	
-	@Override
-	public String toString() {
-		StringBuilder builder = new StringBuilder();
-		for (Clause clause : getClauses()) {
-			builder.append(clause).append(',');
-		}
-		// Remove the trailing comma. Note at least one clause is guaranteed to exist.
-		builder.deleteCharAt(builder.length() - 1);
-		return builder.toString();
-	}
+public class DeployedContentHeader
+        extends AbstractClauseBasedHeader<DeployedContentHeader.Clause>
+        implements RequirementHeader<DeployedContentHeader.Clause> {
+
+    public static class Clause extends AbstractClause {
+        public static final String ATTRIBUTE_DEPLOYEDVERSION = DeployedVersionAttribute.NAME;
+        public static final String ATTRIBUTE_RESOURCEID = "resourceId";
+        public static final String ATTRIBUTE_TYPE = TypeAttribute.NAME;
+        public static final String DIRECTIVE_REFERENCE = ReferenceDirective.NAME;
+        public static final String DIRECTIVE_STARTORDER = StartOrderDirective.NAME;
+
+        public Clause(String clause) {
+            super( 
+            		parsePath(clause, Patterns.SYMBOLIC_NAME, false), 
+            		parseParameters(clause, false), 
+            		generateDefaultParameters(
+            				TypeAttribute.DEFAULT,
+            				ReferenceDirective.TRUE));
+        }
+
+        public Clause(Resource resource) {
+            this(resource, true);
+        }
+
+        public Clause(Resource resource, boolean referenced) {
+            this(appendResource(resource, new StringBuilder(), referenced)
+                    .toString());
+        }
+
+        public boolean contains(Resource resource) {
+            return getSymbolicName()
+                    .equals(ResourceHelper.getSymbolicNameAttribute(resource))
+                    && getDeployedVersion().equals(
+                            ResourceHelper.getVersionAttribute(resource))
+                    && getType()
+                            .equals(ResourceHelper.getTypeAttribute(resource));
+        }
+
+        public Version getDeployedVersion() {
+            return ((DeployedVersionAttribute) getAttribute(
+                    ATTRIBUTE_DEPLOYEDVERSION)).getVersion();
+        }
+
+        public String getSymbolicName() {
+            return path;
+        }
+
+        public int getStartOrder() {
+            return ((StartOrderDirective) getAttribute(DIRECTIVE_STARTORDER))
+                    .getStartOrder();
+        }
+
+        public String getType() {
+            return ((TypeAttribute) getAttribute(ATTRIBUTE_TYPE)).getType();
+        }
+
+        public boolean isReferenced() {
+            return ((ReferenceDirective) getDirective(DIRECTIVE_REFERENCE))
+                    .isReferenced();
+        }
+
+        public DeployedContentRequirement toRequirement(Resource resource) {
+            return new DeployedContentRequirement(this, resource);
+        }
+    }
+
+    public static final String NAME = SubsystemConstants.DEPLOYED_CONTENT;
+
+    public static DeployedContentHeader newInstance(
+            Collection<Resource> resources) {
+        StringBuilder builder = new StringBuilder();
+        for (Resource resource : resources) {
+            appendResource(resource, builder, true);
+            builder.append(',');
+        }
+        // Remove the trailing comma.
+        // TODO Intentionally letting the exception propagate since there must
+        // be at least one resource.
+        builder.deleteCharAt(builder.length() - 1);
+        return new DeployedContentHeader(builder.toString());
+    }
+
+    private static StringBuilder appendResource(Resource resource,
+            StringBuilder builder, boolean referenced) {
+        String symbolicName = ResourceHelper.getSymbolicNameAttribute(resource);
+        Version version = ResourceHelper.getVersionAttribute(resource);
+        String type = ResourceHelper.getTypeAttribute(resource);
+        builder.append(symbolicName).append(';')
+                .append(Clause.ATTRIBUTE_DEPLOYEDVERSION).append('=')
+                .append(version.toString()).append(';')
+                .append(Clause.ATTRIBUTE_TYPE).append('=').append(type)
+                .append(';').append(Clause.ATTRIBUTE_RESOURCEID).append('=')
+                .append(Utils.getId(resource)).append(';')
+                .append(Clause.DIRECTIVE_REFERENCE).append(":=")
+                .append(referenced);
+        return builder;
+    }
+
+    public DeployedContentHeader(Collection<Clause> clauses) {
+        super(clauses);
+    }
+
+    public DeployedContentHeader(String value) {
+    	super(
+				value, 
+				new ClauseFactory<Clause>() {
+					@Override
+					public Clause newInstance(String clause) {
+						return new Clause(clause);
+					}
+				});
+    }
+
+    public boolean contains(Resource resource) {
+        for (Clause clause : getClauses())
+            if (clause.contains(resource))
+                return true;
+        return false;
+    }
+
+    public Clause getClause(Resource resource) {
+        String symbolicName = ResourceHelper.getSymbolicNameAttribute(resource);
+        Version version = ResourceHelper.getVersionAttribute(resource);
+        String type = ResourceHelper.getTypeAttribute(resource);
+        for (Clause clause : clauses) {
+            if (symbolicName.equals(clause.getPath())
+                    && clause.getDeployedVersion().equals(version)
+                    && type.equals(clause.getType()))
+                return clause;
+        }
+        return null;
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public String getValue() {
+        return toString();
+    }
+
+    public boolean isReferenced(Resource resource) {
+        DeployedContentHeader.Clause clause = getClause(resource);
+        if (clause == null)
+            return false;
+        return clause.isReferenced();
+    }
+
+    @Override
+    public List<Requirement> toRequirements(Resource resource) {
+        List<Requirement> requirements = new ArrayList<Requirement>(
+                clauses.size());
+        for (Clause clause : clauses)
+            requirements.add(clause.toRequirement(resource));
+        return requirements;
+    }
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedVersionAttribute.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedVersionAttribute.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedVersionAttribute.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeployedVersionAttribute.java Thu Sep 17 14:12:21 2015
@@ -30,7 +30,7 @@ public class DeployedVersionAttribute ex
 	@Override
 	public StringBuilder appendToFilter(StringBuilder builder) {
 		VersionRange versionRange = new VersionRange(VersionRange.LEFT_CLOSED, getVersion(), getVersion(), VersionRange.RIGHT_CLOSED);
-		return builder.append(versionRange.toFilterString(VersionRangeAttribute.NAME));
+		return builder.append(versionRange.toFilterString(VersionRangeAttribute.NAME_VERSION));
 	}
 
 	public Version getDeployedVersion() {

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeploymentManifest.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeploymentManifest.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeploymentManifest.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DeploymentManifest.java Thu Sep 17 14:12:21 2015
@@ -288,4 +288,21 @@ public class DeploymentManifest {
 	private Map<String, Header<?>> computeHeaders(SubsystemManifest manifest) {
 		return new HashMap<String, Header<?>>(manifest.getHeaders());
 	}
+
+    @Override
+    public int hashCode() {
+    	return 31 * 17 + headers.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+    	if (o == this) {
+    		return true;
+    	}
+    	if (!(o instanceof SubsystemManifest)) {
+    		return false;
+    	}
+    	DeploymentManifest that = (DeploymentManifest)o;
+    	return that.headers.equals(this.headers);
+    }
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DynamicImportPackageHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DynamicImportPackageHeader.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DynamicImportPackageHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/DynamicImportPackageHeader.java Thu Sep 17 14:12:21 2015
@@ -16,120 +16,30 @@ package org.apache.aries.subsystem.core.
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.osgi.framework.Constants;
 import org.osgi.framework.namespace.PackageNamespace;
 import org.osgi.resource.Resource;
 
-public class DynamicImportPackageHeader implements RequirementHeader<DynamicImportPackageHeader.Clause> {
-	public static class Clause implements org.apache.aries.subsystem.core.archive.Clause {
-		private static final String REGEX1 = '(' + Grammar.WILDCARD_NAMES + ")(?=;|\\z)";
-		private static final String REGEX2 = '(' + Grammar.PARAMETER + ")(?=;|\\z)";
-		private static final Pattern PATTERN1 = Pattern.compile(REGEX1);
-		private static final Pattern PATTERN2 = Pattern.compile(REGEX2);
-
-		private static void fillInDefaults(Map<String, Parameter> parameters) {
-			Parameter parameter = parameters.get(Constants.VERSION_ATTRIBUTE);
-			if (parameter == null)
-				parameters.put(Constants.VERSION_ATTRIBUTE, new VersionRangeAttribute());
-		}
-		
-		private final Map<String, Parameter> myParameters = new HashMap<String, Parameter>();
-		private final String myPath;
-		
+public class DynamicImportPackageHeader extends AbstractClauseBasedHeader<DynamicImportPackageHeader.Clause> implements RequirementHeader<DynamicImportPackageHeader.Clause> {
+    public static class Clause extends AbstractClause {
 		public Clause(String clause) {
-			Matcher matcher = PATTERN1.matcher(clause);
-			if (matcher.find())
-				myPath = matcher.group().replaceAll("\\s", "");
-			else
-				throw new IllegalArgumentException("Invalid " + Constants.IMPORT_PACKAGE + " header clause: " + clause);
-			matcher.usePattern(PATTERN2);
-			while (matcher.find()) {
-				Parameter parameter = ParameterFactory.create(matcher.group());
-				// TODO Revisit the following fix.
-				// All version attributes on an ImportPackage header are ranges. The ParameterFactory will return
-				// a VersionAttribute when the value is a single version (e.g., version=1.0.0). This causes a
-				// ClassCastException in getVersionRangeAttribute().
-				if (parameter instanceof VersionAttribute)
-					parameter = new VersionRangeAttribute(String.valueOf(parameter.getValue()));
-				myParameters.put(parameter.getName(), parameter);
-			}
-			fillInDefaults(myParameters);
-		}
-		
-		@Override
-		public Attribute getAttribute(String name) {
-			Parameter result = myParameters.get(name);
-			if (result instanceof Attribute) {
-				return (Attribute)result;
-			}
-			return null;
-		}
-		
-		@Override
-		public Collection<Attribute> getAttributes() {
-			ArrayList<Attribute> attributes = new ArrayList<Attribute>(myParameters.size());
-			for (Parameter parameter : myParameters.values()) {
-				if (parameter instanceof Attribute) {
-					attributes.add((Attribute)parameter);
-				}
-			}
-			attributes.trimToSize();
-			return attributes;
-		}
-		
-		@Override
-		public Directive getDirective(String name) {
-			Parameter result = myParameters.get(name);
-			if (result instanceof Directive) {
-				return (Directive)result;
-			}
-			return null;
-		}
-		
-		@Override
-		public Collection<Directive> getDirectives() {
-			ArrayList<Directive> directives = new ArrayList<Directive>(myParameters.size());
-			for (Parameter parameter : myParameters.values()) {
-				if (parameter instanceof Directive) {
-					directives.add((Directive)parameter);
-				}
-			}
-			directives.trimToSize();
-			return directives;
+			super(
+					parsePath(clause, Patterns.WILDCARD_NAMES, true),
+					parseParameters(clause, true), 
+            		generateDefaultParameters(
+            				VersionRangeAttribute.DEFAULT_VERSION));
 		}
 		
 		public Collection<String> getPackageNames() {
-			return Arrays.asList(myPath.split(";"));
-		}
-		
-		@Override
-		public Parameter getParameter(String name) {
-			return myParameters.get(name);
-		}
-		
-		@Override
-		public Collection<Parameter> getParameters() {
-			return Collections.unmodifiableCollection(myParameters.values());
-		}
-		
-		@Override
-		public String getPath() {
-			return myPath;
+			return Arrays.asList(path.split(";"));
 		}
 		
 		public VersionRangeAttribute getVersionRangeAttribute() {
-			return (VersionRangeAttribute)myParameters.get(Constants.VERSION_ATTRIBUTE);
+			return (VersionRangeAttribute)parameters.get(Constants.VERSION_ATTRIBUTE);
 		}
-		
+        
 		public List<DynamicImportPackageRequirement> toRequirements(Resource resource) {
 			Collection<String> pkgs = getPackageNames();
 			List<DynamicImportPackageRequirement> result = new ArrayList<DynamicImportPackageRequirement>(pkgs.size());
@@ -138,16 +48,6 @@ public class DynamicImportPackageHeader
 			}
 			return result;
 		}
-		
-		@Override
-		public String toString() {
-			StringBuilder builder = new StringBuilder()
-					.append(getPath());
-			for (Parameter parameter : getParameters()) {
-				builder.append(';').append(parameter);
-			}
-			return builder.toString();
-		}
 	}
 	
 	public static final String ATTRIBUTE_BUNDLE_SYMBOLICNAME = PackageNamespace.CAPABILITY_BUNDLE_SYMBOLICNAME_ATTRIBUTE;
@@ -155,30 +55,23 @@ public class DynamicImportPackageHeader
 	public static final String ATTRIBUTE_VERSION = PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE;
 	public static final String NAME = Constants.DYNAMICIMPORT_PACKAGE;
 	
-	private static Collection<Clause> processHeader(String header) {
-		Set<Clause> clauses = new HashSet<Clause>();
-		for (String clause : new ClauseTokenizer(header).getClauses())
-			clauses.add(new Clause(clause));
-		return clauses;
-	}
-	
-	private final Set<Clause> clauses;
-	
 	public DynamicImportPackageHeader(Collection<Clause> clauses) {
-		if (clauses.isEmpty())
-			throw new IllegalArgumentException("A DynamicImport-Package header must have at least one clause");
-		this.clauses = new HashSet<Clause>(clauses);
+		super(clauses);
 	}
 	
-	public DynamicImportPackageHeader(String header) {
-		this(processHeader(header));
+	public DynamicImportPackageHeader(String value) {
+		super(
+				value, 
+				new ClauseFactory<Clause>() {
+					@Override
+					public Clause newInstance(String clause) {
+						return new Clause(clause);
+					}
+				});
 	}
 	
-	public Collection<DynamicImportPackageHeader.Clause> getClauses() {
-		return Collections.unmodifiableSet(clauses);
-	}
-
-	public String getName() {
+	@Override
+    public String getName() {
 		return Constants.IMPORT_PACKAGE;
 	}
 	
@@ -196,15 +89,4 @@ public class DynamicImportPackageHeader
 		}
 		return result;
 	}
-	
-	@Override
-	public String toString() {
-		StringBuilder builder = new StringBuilder();
-		for (Clause clause : getClauses()) {
-			builder.append(clause).append(',');
-		}
-		// Remove the trailing comma. Note at least one clause is guaranteed to exist.
-		builder.deleteCharAt(builder.length() - 1);
-		return builder.toString();
-	}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ExportPackageHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ExportPackageHeader.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ExportPackageHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/ExportPackageHeader.java Thu Sep 17 14:12:21 2015
@@ -14,55 +14,28 @@
 package org.apache.aries.subsystem.core.archive;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.osgi.framework.Constants;
 import org.osgi.resource.Resource;
 
-public class ExportPackageHeader implements CapabilityHeader<ExportPackageHeader.Clause> {
-	public static class Clause implements org.apache.aries.subsystem.core.archive.Clause {
+public class ExportPackageHeader extends AbstractClauseBasedHeader<ExportPackageHeader.Clause> implements CapabilityHeader<ExportPackageHeader.Clause> {
+    public static class Clause extends AbstractClause {
 		public static final String ATTRIBUTE_VERSION = Constants.VERSION_ATTRIBUTE;
 		public static final String DIRECTIVE_EXCLUDE = Constants.EXCLUDE_DIRECTIVE;
 		public static final String DIRECTIVE_INCLUDE = Constants.INCLUDE_DIRECTIVE;
 		public static final String DIRECTIVE_MANDATORY = Constants.MANDATORY_DIRECTIVE;
 		public static final String DIRECTIVE_USES = Constants.USES_DIRECTIVE;
 		
-		private static final Pattern PATTERN_PACKAGENAME = Pattern.compile('(' + Grammar.PACKAGENAME + ")(?=;|\\z)");
-		private static final Pattern PATTERN_PACKAGENAMES = Pattern.compile('(' + Grammar.PACKAGENAMES + ")(?=;|\\z)");
-		private static final Pattern PATTERN_PARAMETER = Pattern.compile('(' + Grammar.PARAMETER + ")(?=;|\\z)");
-		
-		private static void fillInDefaults(Map<String, Parameter> parameters) {
-			Parameter parameter = parameters.get(ATTRIBUTE_VERSION);
-			if (parameter == null)
-				parameters.put(ATTRIBUTE_VERSION, VersionAttribute.DEFAULT);
-		}
-		
-		private final Collection<String> packageNames = new HashSet<String>();
-		private final String path;
-		private final Map<String, Parameter> parameters = new HashMap<String, Parameter>();
-		
 		public Clause(String clause) {
-			Matcher main = PATTERN_PACKAGENAMES.matcher(clause);
-			if (!main.find())
-				throw new IllegalArgumentException("Missing package names path: " + clause);
-			path = main.group();
-			Matcher path = PATTERN_PACKAGENAME.matcher(this.path);
-			while (path.find())
-				packageNames.add(main.group());
-			main.usePattern(PATTERN_PARAMETER);
-			while (main.find()) {
-				Parameter parameter = ParameterFactory.create(main.group());
-				parameters.put(parameter.getName(), parameter);
-			}
-			fillInDefaults(parameters);
+            super(
+            		parsePath(clause, Patterns.PACKAGE_NAMES, false), 
+            		parseParameters(clause, false), 
+            		generateDefaultParameters(
+            				VersionAttribute.DEFAULT));
 		}
 		
 		@Override
@@ -108,7 +81,7 @@ public class ExportPackageHeader impleme
 		}
 		
 		public Collection<String> getPackageNames() {
-			return Collections.unmodifiableCollection(packageNames);
+			return Arrays.asList(path.split(";"));
 		}
 
 		@Override
@@ -125,42 +98,31 @@ public class ExportPackageHeader impleme
 		public String getPath() {
 			return path;
 		}
-		
+        
 		public Collection<ExportPackageCapability> toCapabilities(Resource resource) {
+			Collection<String> packageNames = getPackageNames();
 			Collection<ExportPackageCapability> result = new ArrayList<ExportPackageCapability>(packageNames.size());
-			for (String packageName : packageNames)
+			for (String packageName : packageNames) {
 				result.add(new ExportPackageCapability(packageName, parameters.values(), resource));
-			return result;
-		}
-		
-		@Override
-		public String toString() {
-			StringBuilder builder = new StringBuilder()
-					.append(getPath());
-			for (Parameter parameter : getParameters()) {
-				builder.append(';').append(parameter);
 			}
-			return builder.toString();
+			return result;
 		}
 	}
 	
 	public static final String NAME = Constants.EXPORT_PACKAGE;
 	
-	private final Set<Clause> clauses = new HashSet<Clause>();
-	
 	public ExportPackageHeader(String value) {
-		for (String clause : new ClauseTokenizer(value).getClauses())
-			clauses.add(new Clause(clause));
-		if (clauses.isEmpty())
-			throw new IllegalArgumentException("An " + NAME + " header must have at least one clause");
-	}
+		super(
+				value, 
+				new ClauseFactory<Clause>() {
+					@Override
+					public Clause newInstance(String clause) {
+						return new Clause(clause);
+					}
+				});
+	}	
 	
 	@Override
-	public Collection<ExportPackageHeader.Clause> getClauses() {
-		return Collections.unmodifiableSet(clauses);
-	}
-
-	@Override
 	public String getName() {
 		return NAME;
 	}

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/FilterDirective.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/FilterDirective.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/FilterDirective.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/FilterDirective.java Thu Sep 17 14:12:21 2015
@@ -13,24 +13,21 @@
  */
 package org.apache.aries.subsystem.core.archive;
 
+import org.apache.aries.subsystem.core.capabilityset.SimpleFilter;
 import org.osgi.framework.Constants;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
 
 public class FilterDirective extends AbstractDirective {
 	public static final String NAME = Constants.FILTER_DIRECTIVE;
 	
+	private final SimpleFilter filter;
+	
 	public FilterDirective(String value) {
 		super(NAME, value);
-		try {
-			FrameworkUtil.createFilter(value);
-		}
-		catch (InvalidSyntaxException e) {
-			throw new IllegalArgumentException("Invalid filter: " + value, e);
-		}
+		filter = SimpleFilter.parse(value);
 	}
 
-	public String toString() {
+	@Override
+    public String toString() {
 		return new StringBuilder()
 		.append(getName())
 		.append(":=\"")
@@ -38,4 +35,21 @@ public class FilterDirective extends Abs
 		.append('\"')
 		.toString();
 	}
+	
+	@Override
+    public int hashCode() {
+        return 31 * 17 + filter.hashCode();
+    }
+    
+    @Override
+    public boolean equals(Object o) {
+    	if (o == this) {
+    		return true;
+    	}
+    	if (!(o instanceof FilterDirective)) {
+    		return false;
+    	}
+    	FilterDirective that = (FilterDirective)o;
+    	return that.filter.equals(this.filter);
+    }
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/FragmentHostHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/FragmentHostHeader.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/FragmentHostHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/FragmentHostHeader.java Thu Sep 17 14:12:21 2015
@@ -20,113 +20,31 @@ package org.apache.aries.subsystem.core.
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
 import org.osgi.framework.VersionRange;
 import org.osgi.resource.Resource;
 
-public class FragmentHostHeader implements RequirementHeader<FragmentHostHeader.Clause> {
-	public static class Clause implements org.apache.aries.subsystem.core.archive.Clause {
+public class FragmentHostHeader extends AbstractClauseBasedHeader<FragmentHostHeader.Clause> implements RequirementHeader<FragmentHostHeader.Clause> {
+	public static class Clause extends AbstractClause {
 		public static final String ATTRIBUTE_BUNDLEVERSION = Constants.BUNDLE_VERSION_ATTRIBUTE;
 		
-		private static final Pattern PATTERN_SYMBOLICNAME = Pattern.compile('(' + Grammar.SYMBOLICNAME + ")(?=;|\\z)");
-		private static final Pattern PATTERN_PARAMETER = Pattern.compile('(' + Grammar.PARAMETER + ")(?=;|\\z)");
-		
-		private static void fillInDefaults(Map<String, Parameter> parameters) {
-		    Parameter parameter = parameters.get(ATTRIBUTE_BUNDLEVERSION);
-            if (parameter == null) {
-                parameters.put(ATTRIBUTE_BUNDLEVERSION, 
-                        new BundleVersionAttribute(
-                                new VersionRange(
-                                        VersionRange.LEFT_CLOSED, 
-                                        new Version("0"), 
-                                        null, 
-                                        VersionRange.RIGHT_OPEN)));
-            }
-		}
-		
-		private final String path;
-		private final Map<String, Parameter> parameters = new HashMap<String, Parameter>();
-		
 		public Clause(String clause) {
-			Matcher matcher = PATTERN_SYMBOLICNAME.matcher(clause);
-			if (!matcher.find())
-				throw new IllegalArgumentException("Missing symbolic-name: " + clause);
-			path = matcher.group();
-			matcher.usePattern(PATTERN_PARAMETER);
-			while (matcher.find()) {
-				Parameter parameter = ParameterFactory.create(matcher.group());
-				parameters.put(parameter.getName(), parameter);
-			}
-			fillInDefaults(parameters);
+		    super(
+            		parsePath(clause, Patterns.SYMBOLIC_NAME, false), 
+            		parseParameters(clause, false), 
+            		generateDefaultParameters(
+            				new BundleVersionAttribute(
+        		    				new VersionRange(
+        		    						VersionRange.LEFT_CLOSED, 
+        		    						new Version("0"), 
+        		    						null, 
+                                            VersionRange.RIGHT_OPEN))));
 		}
 		
-		@Override
-		public Attribute getAttribute(String name) {
-			Parameter result = parameters.get(name);
-			if (result instanceof Attribute) {
-				return (Attribute)result;
-			}
-			return null;
-		}
-
-		@Override
-		public Collection<Attribute> getAttributes() {
-			ArrayList<Attribute> attributes = new ArrayList<Attribute>(parameters.size());
-			for (Parameter parameter : parameters.values()) {
-				if (parameter instanceof Attribute) {
-					attributes.add((Attribute)parameter);
-				}
-			}
-			attributes.trimToSize();
-			return attributes;
-		}
-
-		@Override
-		public Directive getDirective(String name) {
-			Parameter result = parameters.get(name);
-			if (result instanceof Directive) {
-				return (Directive)result;
-			}
-			return null;
-		}
 
-		@Override
-		public Collection<Directive> getDirectives() {
-			ArrayList<Directive> directives = new ArrayList<Directive>(parameters.size());
-			for (Parameter parameter : parameters.values()) {
-				if (parameter instanceof Directive) {
-					directives.add((Directive)parameter);
-				}
-			}
-			directives.trimToSize();
-			return directives;
-		}
-
-		@Override
-		public Parameter getParameter(String name) {
-			return parameters.get(name);
-		}
-
-		@Override
-		public Collection<Parameter> getParameters() {
-			return Collections.unmodifiableCollection(parameters.values());
-		}
-
-		@Override
-		public String getPath() {
-			return path;
-		}
-		
 		public String getSymbolicName() {
 			return path;
 		}
@@ -134,46 +52,32 @@ public class FragmentHostHeader implemen
 		public FragmentHostRequirement toRequirement(Resource resource) {
 			return new FragmentHostRequirement(this, resource);
 		}
-		
-		@Override
-		public String toString() {
-			StringBuilder builder = new StringBuilder()
-					.append(getPath());
-			for (Parameter parameter : getParameters()) {
-				builder.append(';').append(parameter);
-			}
-			return builder.toString();
-		}
 	}
 	
 	public static final String NAME = Constants.FRAGMENT_HOST;
 	
-	private static Collection<Clause> processHeader(String header) {
-		Set<Clause> clauses = new HashSet<Clause>();
-		for (String clause : new ClauseTokenizer(header).getClauses())
-			clauses.add(new Clause(clause));
-		return clauses;
-	}
-	
-	private final Set<Clause> clauses;
-	
 	public FragmentHostHeader(Collection<Clause> clauses) {
-		if (clauses.size() != 1) {
-		    throw new IllegalArgumentException("A " + NAME + " header must have one and only one clause");
-		}
-		this.clauses = new HashSet<Clause>(clauses);
+	    super(clauses);
+        if (clauses.size() != 1) {
+            throw new IllegalArgumentException("A " + NAME + " header must have one and only one clause");
+        }
 	}
 	
 	public FragmentHostHeader(String value) {
-		this(processHeader(value));
+		super(
+				value, 
+				new ClauseFactory<Clause>() {
+					@Override
+					public Clause newInstance(String clause) {
+						return new Clause(clause);
+					}
+				});
+		if (clauses.size() != 1) {
+            throw new IllegalArgumentException("A " + NAME + " header must have one and only one clause");
+        }
 	}
 	
 	@Override
-	public Collection<FragmentHostHeader.Clause> getClauses() {
-		return Collections.unmodifiableSet(clauses);
-	}
-
-	@Override
 	public String getName() {
 		return NAME;
 	}
@@ -190,15 +94,5 @@ public class FragmentHostHeader implemen
 			requirements.add(clause.toRequirement(resource));
 		return requirements;
 	}
-	
-	@Override
-	public String toString() {
-		StringBuilder builder = new StringBuilder();
-		for (Clause clause : getClauses()) {
-			builder.append(clause).append(',');
-		}
-		// Remove the trailing comma. Note at least one clause is guaranteed to exist.
-		builder.deleteCharAt(builder.length() - 1);
-		return builder.toString();
-	}
+
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericClause.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericClause.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericClause.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericClause.java Thu Sep 17 14:12:21 2015
@@ -13,86 +13,11 @@
  */
 package org.apache.aries.subsystem.core.archive;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class GenericClause implements Clause {
-	private static final String REGEX = '(' + Grammar.PATH + "(?:;" + Grammar.PATH + ")*)(?:;\\s*(" + Grammar.PARAMETER + "))*";
-	
-	private static final Pattern PATTERN = Pattern.compile(REGEX);
-	private static final Pattern PATTERN_PARAMETER = Pattern.compile(Grammar.PARAMETER);
-	
-	private final Map<String, Parameter> parameters = new HashMap<String, Parameter>();
-	private final String path;
-	
+public class GenericClause extends AbstractClause {
 	public GenericClause(String clause) {
-		Matcher matcher = PATTERN.matcher(clause);
-		if (!matcher.matches()) {
-			throw new IllegalArgumentException("Invalid clause: " + clause);
-		}
-		path = matcher.group(1);
-		matcher = PATTERN_PARAMETER.matcher(clause);
-		while(matcher.find()) {
-			String group = matcher.group();
-			if (group == null || group.length() == 0)
-				continue;
-			Parameter parameter = ParameterFactory.create(group);
-			parameters.put(parameter.getName(), parameter);
-		}
-	}
-	
-	public Attribute getAttribute(String name) {
-		Parameter result = parameters.get(name);
-		if (result instanceof Attribute) {
-			return (Attribute)result;
-		}
-		return null;
-	}
-	
-	public Collection<Attribute> getAttributes() {
-		ArrayList<Attribute> attributes = new ArrayList<Attribute>(parameters.size());
-		for (Parameter parameter : parameters.values()) {
-			if (parameter instanceof Attribute) {
-				attributes.add((Attribute)parameter);
-			}
-		}
-		attributes.trimToSize();
-		return attributes;
-	}
-	
-	public Directive getDirective(String name) {
-		Parameter result = parameters.get(name);
-		if (result instanceof Directive) {
-			return (Directive)result;
-		}
-		return null;
-	}
-	
-	public Collection<Directive> getDirectives() {
-		ArrayList<Directive> directives = new ArrayList<Directive>(parameters.size());
-		for (Parameter parameter : parameters.values()) {
-			if (parameter instanceof Directive) {
-				directives.add((Directive)parameter);
-			}
-		}
-		directives.trimToSize();
-		return directives;
-	}
-	
-	public Parameter getParameter(String name) {
-		return parameters.get(name);
-	}
-	
-	public Collection<Parameter> getParameters() {
-		return Collections.unmodifiableCollection(parameters.values());
-	}
-	
-	public String getPath() {
-		return path;
+        super(
+        		parsePath(clause, Patterns.PATHS, false), 
+        		parseParameters(clause, false), 
+        		generateDefaultParameters());
 	}
 }

Modified: aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericHeader.java
URL: http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericHeader.java?rev=1703631&r1=1703630&r2=1703631&view=diff
==============================================================================
--- aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericHeader.java (original)
+++ aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/archive/GenericHeader.java Thu Sep 17 14:12:21 2015
@@ -15,71 +15,34 @@ package org.apache.aries.subsystem.core.
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
 
-public class GenericHeader implements Header<GenericHeader.Clause> {
-	public static class Clause implements org.apache.aries.subsystem.core.archive.Clause {
-		private final String path;
-		
+public class GenericHeader extends AbstractClauseBasedHeader<GenericHeader.Clause> {
+	public static class Clause extends AbstractClause {
 		public Clause(String clause) {
-			path = clause;
-		}
-		
-		public Attribute getAttribute(String name) {
-			return null;
-		}
-		
-		public Collection<Attribute> getAttributes() {
-			return Collections.emptyList();
-		}
-		
-		public Directive getDirective(String name) {
-			return null;
-		}
-		
-		public Collection<Directive> getDirectives() {
-			return Collections.emptyList();
-		}
-		
-		public Parameter getParameter(String name) {
-			return null;
-		}
-		
-		public Collection<Parameter> getParameters() {
-			return Collections.emptyList();
-		}
-		
-		public String getPath() {
-			return path;
-		}
-		
-		@Override
-		public String toString() {
-			StringBuilder builder = new StringBuilder()
-					.append(getPath());
-			for (Parameter parameter : getParameters()) {
-				builder.append(';').append(parameter);
-			}
-			return builder.toString();
+			super(
+					clause,
+					Collections.<String, Parameter>emptyMap(),
+	        		generateDefaultParameters());
 		}
 	}
 	
-	private final Set<Clause> clauses;
 	private final String name;
 	
 	public GenericHeader(String name, Collection<Clause> clauses) {
+	    super(clauses);
 		this.name = name;
-		this.clauses = new HashSet<Clause>(clauses);
 	}
 	
 	public GenericHeader(String name, String value) {
-		this(name, Collections.singletonList(new Clause(value)));
-	}
-
-	@Override
-	public Collection<Clause> getClauses() {
-		return Collections.unmodifiableSet(clauses);
+		super(
+				value, 
+				new ClauseFactory<Clause>() {
+					@Override
+					public Clause newInstance(String clause) {
+						return new Clause(clause);
+					}
+				});
+		this.name = name;
 	}
 
 	@Override
@@ -91,15 +54,4 @@ public class GenericHeader implements He
 	public String getValue() {
 		return toString();
 	}
-	
-	@Override
-	public String toString() {
-		StringBuilder builder = new StringBuilder();
-		for (Clause clause : getClauses()) {
-			builder.append(clause).append(',');
-		}
-		// Remove the trailing comma. Note at least one clause is guaranteed to exist.
-		builder.deleteCharAt(builder.length() - 1);
-		return builder.toString();
-	}
 }