Use local copy of latest bndlib code for pre-release testing purposes

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1347815 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/libg/header/Attrs.java b/bundleplugin/src/main/java/aQute/libg/header/Attrs.java
new file mode 100644
index 0000000..d11b6d1
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/header/Attrs.java
@@ -0,0 +1,295 @@
+package aQute.libg.header;
+
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.lib.collections.*;
+import aQute.libg.version.*;
+
+public class Attrs implements Map<String, String> {
+	public enum Type {
+		STRING(null), LONG(null), VERSION(null), DOUBLE(null), STRINGS(STRING), LONGS(LONG), VERSIONS(VERSION), DOUBLES(DOUBLE);
+
+		Type	sub;
+
+		Type(Type sub) {
+			this.sub = sub;
+		}
+
+	}
+
+	/**
+	 * <pre>
+	 * Provide-Capability ::= capability ::=
+	 * name-space ::= typed-attr ::= type ::= scalar ::=
+	 * capability ( ',' capability )*
+	 * name-space
+	 *     ( ’;’ directive | typed-attr )*
+	 * symbolic-name
+	 * extended ( ’:’ type ) ’=’ argument
+	 * scalar | list
+	 * ’String’ | ’Version’ | ’Long’
+	 * list ::=
+	 * ’List<’ scalar ’>’
+	 * </pre>
+	 */
+	static String					EXTENDED	= "[\\-0-9a-zA-Z\\._]+";
+	static String					SCALAR		= "String|Version|Long|Double";
+	static String					LIST		= "List\\s*<\\s*(" + SCALAR + ")\\s*>";
+	public static final Pattern		TYPED		= Pattern.compile("\\s*(" + EXTENDED + ")\\s*:\\s*("+ SCALAR + "|" + LIST + ")\\s*");
+
+	private LinkedHashMap<String, String>	map;
+	private Map<String, Type>		types;
+	static Map<String, String>		EMPTY		= Collections.emptyMap();
+
+	public Attrs(Attrs... attrs) {
+		for (Attrs a : attrs) {
+			if (a != null) {
+				putAll(a);
+			}
+		}
+	}
+
+	public void clear() {
+		map.clear();
+	}
+
+	public boolean containsKey(String name) {
+		if (map == null)
+			return false;
+
+		return map.containsKey(name);
+	}
+
+	@SuppressWarnings("cast")
+	@Deprecated public boolean containsKey(Object name) {
+		assert name instanceof String;
+		if (map == null)
+			return false;
+
+		return map.containsKey((String) name);
+	}
+
+	public boolean containsValue(String value) {
+		if (map == null)
+			return false;
+
+		return map.containsValue(value);
+	}
+
+	@SuppressWarnings("cast")
+	@Deprecated public boolean containsValue(Object value) {
+		assert value instanceof String;
+		if (map == null)
+			return false;
+
+		return map.containsValue((String) value);
+	}
+
+	public Set<java.util.Map.Entry<String, String>> entrySet() {
+		if (map == null)
+			return EMPTY.entrySet();
+
+		return map.entrySet();
+	}
+
+	@SuppressWarnings("cast")
+	@Deprecated public String get(Object key) {
+		assert key instanceof String;
+		if (map == null)
+			return null;
+
+		return map.get((String) key);
+	}
+
+	public String get(String key) {
+		if (map == null)
+			return null;
+
+		return map.get(key);
+	}
+
+	public String get(String key, String deflt) {
+		String s = get(key);
+		if (s == null)
+			return deflt;
+		return s;
+	}
+
+	public boolean isEmpty() {
+		return map == null || map.isEmpty();
+	}
+
+	public Set<String> keySet() {
+		if (map == null)
+			return EMPTY.keySet();
+
+		return map.keySet();
+	}
+
+	public String put(String key, String value) {
+		if (map == null)
+			map = new LinkedHashMap<String, String>();
+
+		Matcher m = TYPED.matcher(key);
+		if (m.matches()) {
+			key = m.group(1);
+			String type = m.group(2);
+			Type t = Type.STRING;
+
+			if ( type.startsWith("List")) {
+				type = m.group(3);
+				if ( "String".equals(type))
+					t = Type.STRINGS;
+				else if ( "Long".equals(type))
+					t = Type.LONGS;
+				else if ( "Double".equals(type))
+					t = Type.DOUBLES;
+				else if ( "Version".equals(type))
+					t = Type.VERSIONS;				
+			} else {
+				if ( "String".equals(type))
+					t = Type.STRING;
+				else if ( "Long".equals(type))
+					t = Type.LONG;
+				else if ( "Double".equals(type))
+					t = Type.DOUBLE;
+				else if ( "Version".equals(type))
+					t = Type.VERSION;
+			}
+			if (types == null)
+				types = new LinkedHashMap<String, Type>();
+			types.put(key, t);
+
+			// TODO verify value?
+		}
+
+		return map.put(key, value);
+	}
+
+	public Type getType(String key) {
+		if (types == null)
+			return Type.STRING;
+		Type t = types.get(key);
+		if (t == null)
+			return Type.STRING;
+		return t;
+	}
+
+	public void putAll(Map<? extends String, ? extends String> map) {
+		for (Map.Entry<? extends String, ? extends String> e : map.entrySet())
+			put(e.getKey(), e.getValue());
+	}
+
+	@SuppressWarnings("cast")
+	@Deprecated public String remove(Object var0) {
+		assert var0 instanceof String;
+		if (map == null)
+			return null;
+
+		return map.remove((String) var0);
+	}
+
+	public String remove(String var0) {
+		if (map == null)
+			return null;
+		return map.remove(var0);
+	}
+
+	public int size() {
+		if (map == null)
+			return 0;
+		return map.size();
+	}
+
+	public Collection<String> values() {
+		if (map == null)
+			return EMPTY.values();
+
+		return map.values();
+	}
+
+	public String getVersion() {
+		return get("version");
+	}
+
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		append(sb);
+		return sb.toString();
+	}
+
+	public void append(StringBuilder sb) {
+		String del = "";
+		for (Map.Entry<String, String> e : entrySet()) {
+			sb.append(del);
+			sb.append(e.getKey());
+			sb.append("=");
+			sb.append(e.getValue());
+			del = ";";
+		}
+	}
+
+	@Deprecated public boolean equals(Object other) {
+		return super.equals(other);
+	}
+
+	@Deprecated public int hashCode() {
+		return super.hashCode();
+	}
+
+	public boolean isEqual(Attrs o) {
+		if (this == o)
+			return true;
+
+		Attrs other = o;
+
+		if (size() != other.size())
+			return false;
+
+		if (isEmpty())
+			return true;
+
+		SortedList<String> l = new SortedList<String>(keySet());
+		SortedList<String> lo = new SortedList<String>(other.keySet());
+		if (!l.isEqual(lo))
+			return false;
+
+		for (String key : keySet()) {
+			if (!get(key).equals(other.get(key)))
+				return false;
+		}
+		return true;
+
+	}
+
+	public Object getTyped(String adname) {
+		String s = get(adname);
+		if (s == null)
+			return null;
+
+		Type t = getType(adname);
+		return convert(t, s);
+	}
+
+	private Object convert(Type t, String s) {
+		if (t.sub == null) {
+			switch (t) {
+			case STRING:
+				return s;
+			case LONG:
+				return Long.parseLong(s.trim());
+			case VERSION:
+				return Version.parseVersion(s);
+			}
+			return null;
+		}
+		List<Object> list = new ArrayList<Object>();
+		String split[] = s.split("\\s*\\(\\?!\\),\\s*");
+		for (String p : split) {
+			p = p.replaceAll("\\\\", "");
+			list.add(convert(t.sub, p));
+		}
+		return list;
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/header/OSGiHeader.java b/bundleplugin/src/main/java/aQute/libg/header/OSGiHeader.java
new file mode 100755
index 0000000..7b26f6f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/header/OSGiHeader.java
@@ -0,0 +1,138 @@
+package aQute.libg.header;
+
+import java.util.*;
+
+import aQute.libg.generics.*;
+import aQute.libg.qtokens.*;
+import aQute.libg.reporter.*;
+
+public class OSGiHeader {
+
+	static public Parameters parseHeader(String value) {
+		return parseHeader(value, null);
+	}
+
+	/**
+	 * Standard OSGi header parser. This parser can handle the format clauses
+	 * ::= clause ( ',' clause ) + clause ::= name ( ';' name ) (';' key '='
+	 * value )
+	 * 
+	 * This is mapped to a Map { name => Map { attr|directive => value } }
+	 * 
+	 * @param value
+	 *            A string
+	 * @return a Map<String,Map<String,String>>
+	 */
+	static public Parameters parseHeader(String value, Reporter logger) {
+		return parseHeader(value, logger, new Parameters());
+	}
+
+	static public Parameters parseHeader(String value, Reporter logger, Parameters result) {
+		if (value == null || value.trim().length() == 0)
+			return result;
+
+		QuotedTokenizer qt = new QuotedTokenizer(value, ";=,");
+		char del = 0;
+		do {
+			boolean hadAttribute = false;
+			Attrs clause = new Attrs();
+			List<String> aliases = Create.list();
+			String name = qt.nextToken(",;");
+
+			del = qt.getSeparator();
+			if (name == null || name.length() == 0) {
+				if (logger != null && logger.isPedantic()) {
+					logger.warning("Empty clause, usually caused by repeating a comma without any name field or by having spaces after the backslash of a property file: "
+							+ value);
+				}
+				if (name == null)
+					break;
+			} else {
+				name = name.trim();
+
+				aliases.add(name);
+				while (del == ';') {
+					String adname = qt.nextToken();
+					if ((del = qt.getSeparator()) != '=') {
+						if (hadAttribute)
+							if (logger != null) {
+								logger.error("Header contains name field after attribute or directive: "
+										+ adname
+										+ " from "
+										+ value
+										+ ". Name fields must be consecutive, separated by a ';' like a;b;c;x=3;y=4");
+							}
+						if (adname != null && adname.length() > 0)
+							aliases.add(adname.trim());
+					} else {
+						String advalue = qt.nextToken();
+						if (clause.containsKey(adname)) {
+							if (logger != null && logger.isPedantic())
+								logger.warning("Duplicate attribute/directive name " + adname
+										+ " in " + value
+										+ ". This attribute/directive will be ignored");
+						}
+						if (advalue == null) {
+							if (logger != null)
+								logger.error("No value after '=' sign for attribute " + adname);
+							advalue = "";
+						}
+						clause.put(adname.trim(), advalue.trim());
+						del = qt.getSeparator();
+						hadAttribute = true;
+					}
+				}
+
+				// Check for duplicate names. The aliases list contains
+				// the list of nams, for each check if it exists. If so,
+				// add a number of "~" to make it unique.
+				for (String clauseName : aliases) {
+					if (result.containsKey(clauseName)) {
+						if (logger != null && logger.isPedantic())
+							logger.warning("Duplicate name "
+									+ clauseName
+									+ " used in header: '"
+									+ clauseName
+									+ "'. Duplicate names are specially marked in Bnd with a ~ at the end (which is stripped at printing time).");
+						while (result.containsKey(clauseName))
+							clauseName += "~";
+					}
+					result.put(clauseName, clause);
+				}
+			}
+		} while (del == ',');
+		return result;
+	}
+
+	public static Attrs parseProperties(String input) {
+		return parseProperties(input, null);
+	}
+
+	public static Attrs parseProperties(String input, Reporter logger) {
+		if (input == null || input.trim().length() == 0)
+			return new Attrs();
+
+		Attrs result = new Attrs();
+		QuotedTokenizer qt = new QuotedTokenizer(input, "=,");
+		char del = ',';
+
+		while (del == ',') {
+			String key = qt.nextToken(",=");
+			String value = "";
+			del = qt.getSeparator();
+			if (del == '=') {
+				value = qt.nextToken(",=");
+				del = qt.getSeparator();
+			}
+			result.put(key, value);
+		}
+		if (del != 0) {
+			if (logger == null)
+				throw new IllegalArgumentException("Invalid syntax for properties: " + input);
+			logger.error("Invalid syntax for properties: " + input);
+		}
+
+		return result;
+	}
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/header/Parameters.java b/bundleplugin/src/main/java/aQute/libg/header/Parameters.java
new file mode 100644
index 0000000..96f0d08
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/header/Parameters.java
@@ -0,0 +1,203 @@
+package aQute.libg.header;
+
+import java.util.*;
+
+import aQute.lib.collections.*;
+import aQute.libg.reporter.*;
+
+public class Parameters implements Map<String, Attrs> {
+	private LinkedHashMap<String, Attrs>	map;
+	static Map<String, Attrs>				EMPTY	= Collections.emptyMap();
+	String									error;
+
+	public Parameters() {
+	}
+
+	public Parameters(String header) {
+		OSGiHeader.parseHeader(header, null, this);
+	}
+
+	public Parameters(String header, Reporter reporter) {
+		OSGiHeader.parseHeader(header, reporter, this);
+	}
+
+	public void clear() {
+		map.clear();
+	}
+
+	public boolean containsKey(final String name) {
+		if (map == null)
+			return false;
+
+		return map.containsKey(name);
+	}
+
+	@SuppressWarnings("cast")
+	@Deprecated public boolean containsKey(Object name) {
+		assert name instanceof String;
+		if (map == null)
+			return false;
+
+		return map.containsKey((String) name);
+	}
+
+	public boolean containsValue(Attrs value) {
+		if (map == null)
+			return false;
+
+		return map.containsValue(value);
+	}
+
+	@SuppressWarnings("cast")
+	@Deprecated public boolean containsValue(Object value) {
+		assert value instanceof Attrs;
+		if (map == null)
+			return false;
+
+		return map.containsValue((Attrs) value);
+	}
+
+	public Set<java.util.Map.Entry<String, Attrs>> entrySet() {
+		if (map == null)
+			return EMPTY.entrySet();
+
+		return map.entrySet();
+	}
+
+	@SuppressWarnings("cast")
+	@Deprecated public Attrs get(Object key) {
+		assert key instanceof String;
+		if (map == null)
+			return null;
+
+		return map.get((String) key);
+	}
+
+	public Attrs get(String key) {
+		if (map == null)
+			return null;
+
+		return map.get(key);
+	}
+
+	public boolean isEmpty() {
+		return map == null || map.isEmpty();
+	}
+
+	public Set<String> keySet() {
+		if (map == null)
+			return EMPTY.keySet();
+
+		return map.keySet();
+	}
+
+	public Attrs put(String key, Attrs value) {
+		assert key != null;
+		assert value != null;
+
+		if (map == null)
+			map = new LinkedHashMap<String, Attrs>();
+
+		return map.put(key, value);
+	}
+
+	public void putAll(Map<? extends String, ? extends Attrs> map) {
+		if (this.map == null) {
+			if (map.isEmpty())
+				return;
+			this.map = new LinkedHashMap<String, Attrs>();
+		}
+		this.map.putAll(map);
+	}
+	public void putAllIfAbsent(Map<String, ? extends Attrs> map) {
+		for(Map.Entry<String, ? extends Attrs> entry : map.entrySet() ) {
+			if ( !containsKey(entry.getKey()))
+				put(entry.getKey(), entry.getValue());
+		}
+	}
+
+	@SuppressWarnings("cast")
+	@Deprecated public Attrs remove(Object var0) {
+		assert var0 instanceof String;
+		if (map == null)
+			return null;
+
+		return map.remove((String) var0);
+	}
+
+	public Attrs remove(String var0) {
+		if (map == null)
+			return null;
+		return map.remove(var0);
+	}
+
+	public int size() {
+		if (map == null)
+			return 0;
+		return map.size();
+	}
+
+	public Collection<Attrs> values() {
+		if (map == null)
+			return EMPTY.values();
+
+		return map.values();
+	}
+
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		append(sb);
+		return sb.toString();
+	}
+
+	public void append(StringBuilder sb) {
+		String del = "";
+		for (Map.Entry<String, Attrs> s : entrySet()) {
+			sb.append(del);
+			sb.append(s.getKey());
+			if (!s.getValue().isEmpty()) {
+				sb.append(';');
+				s.getValue().append(sb);
+			}
+
+			del = ",";
+		}
+	}
+
+	@Deprecated
+	public boolean equals(Object other) {
+		return super.equals(other);
+	}
+	
+	@Deprecated
+	public int hashCode() {
+		return super.hashCode();
+	}
+	
+
+	public boolean isEqual(Parameters other) {
+		if (this == other)
+			return true;
+
+		if (size() != other.size())
+			return false;
+
+		if (isEmpty())
+			return true;
+
+		SortedList<String> l = new SortedList<String>(keySet());
+		SortedList<String> lo = new SortedList<String>(other.keySet());
+		if (!l.isEqual(lo))
+			return false;
+
+		for (String key : keySet()) {
+			if (!get(key).isEqual(other.get(key)))
+				return false;
+		}
+		return true;
+	}
+
+	public Map<String,? extends Map<String,String>> asMapMap() {
+		return this;
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/header/packageinfo b/bundleplugin/src/main/java/aQute/libg/header/packageinfo
new file mode 100644
index 0000000..e39f616
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/header/packageinfo
@@ -0,0 +1 @@
+version 1.1.0