All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aQute.bnd.header.Attrs Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
package aQute.bnd.header;

import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import aQute.bnd.version.Version;

public class Attrs implements Map {
	public interface DataType {
		Type type();
	}

	public static DataType			STRING			= new DataType() {

																public Type type() {
																	return Type.STRING;
																}
															};
	public static DataType			LONG			= new DataType() {

																public Type type() {
																	return Type.LONG;
																}
															};;
	public static DataType			DOUBLE			= new DataType() {

																public Type type() {
																	return Type.DOUBLE;
																}
															};;
	public static DataType			VERSION			= new DataType() {

																public Type type() {
																	return Type.VERSION;
																}
															};;
	public static DataType>	LIST_STRING		= new DataType>() {

																public Type type() {
																	return Type.STRINGS;
																}
															};;
	public static DataType>		LIST_LONG		= new DataType>() {

																public Type type() {
																	return Type.LONGS;
																}
															};;
	public static DataType>	LIST_DOUBLE		= new DataType>() {

																public Type type() {
																	return Type.DOUBLES;
																}
															};;
	public static DataType>	LIST_VERSION	= new DataType>() {

																public Type type() {
																	return Type.VERSIONS;
																}
															};;

	public enum Type {
		STRING(null, "String"), LONG(null, "Long"), VERSION(null, "Version"), DOUBLE(null, "Double"), STRINGS(STRING,
				"List"), LONGS(LONG, "List"), VERSIONS(VERSION, "List"), DOUBLES(DOUBLE,
						"List");

		Type	sub;
		String	toString;

		Type(Type sub, String toString) {
			this.sub = sub;
			this.toString = toString;
		}

		public String toString() {
			return toString;
		}

		public Type plural() {
			switch (this) {
				case DOUBLE :
					return DOUBLES;

				case LONG :
					return LONGS;
				case STRING :
					return STRINGS;
				case VERSION :
					return VERSIONS;
				default :
					return null;
			}
		}
	}

	/**
	 * 
	 *  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 ’>’
	 * 
*/ 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 Map map; private Map types = new LinkedHashMap(); static Map EMPTY = Collections.emptyMap(); public static Attrs EMPTY_ATTRS = new Attrs(); static { EMPTY_ATTRS.map = Collections.emptyMap(); } public Attrs() {} public Attrs(Attrs... attrs) { for (Attrs a : attrs) { if (a != null) { putAll(a); if (a.types != null) types.putAll(a.types); } } } public void putAllTyped(Map attrs) { for (Map.Entry entry : attrs.entrySet()) { Object value = entry.getValue(); String key = entry.getKey(); putTyped(key, value); } } public void putTyped(String key, Object value) { if (value == null) { put(key, null); return; } if (!(value instanceof String)) { Type type; if (value instanceof Collection) value = ((Collection< ? >) value).toArray(); if (value.getClass().isArray()) { type = Type.STRINGS; int l = Array.getLength(value); StringBuilder sb = new StringBuilder(); String del = ""; boolean first = true; for (int i = 0; i < l; i++) { Object member = Array.get(value, i); if (member == null) { // TODO What do we do with null members? continue; } else if (first) { type = getObjectType(member).plural(); first = true; } sb.append(del); int n = sb.length(); sb.append(member); while (n < sb.length()) { char c = sb.charAt(n); if (c == '\\' || c == ',') { sb.insert(n, '\\'); n++; } n++; } del = ","; } value = sb; } else { type = getObjectType(value); } key += ":" + type.toString(); } put(key, value.toString()); } private Type getObjectType(Object member) { if (member instanceof Double || member instanceof Float) return Type.DOUBLE; if (member instanceof Number) return Type.LONG; if (member instanceof Version) return Type.VERSION; return Type.STRING; } 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(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(value); } public Set> 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(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 keySet() { if (map == null) return EMPTY.keySet(); return map.keySet(); } public String put(String key, String value) { if (key == null) return null; if (map == null) map = new LinkedHashMap(); 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; } 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(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 values() { if (map == null) return EMPTY.values(); return map.values(); } public String getVersion() { return get("version"); } @Override public String toString() { StringBuilder sb = new StringBuilder(); append(sb); return sb.toString(); } public void append(StringBuilder sb) { try { String del = ""; for (Map.Entry e : entrySet()) { sb.append(del); append(sb, e); del = ";"; } } catch (Exception e) { // Cannot happen e.printStackTrace(); } } public void append(StringBuilder sb, Map.Entry e) throws IOException { sb.append(e.getKey()); if (types != null) { Type type = types.get(e.getKey()); if (type != null) { sb.append(":").append(type); } } sb.append("="); OSGiHeader.quote(sb, e.getValue()); } @Override @Deprecated public boolean equals(Object other) { return super.equals(other); } @Override @Deprecated public int hashCode() { return super.hashCode(); } public boolean isEqual(Attrs other) { if (this == other) return true; if (other == null || size() != other.size()) return false; if (isEmpty()) return true; TreeSet l = new TreeSet(keySet()); TreeSet lo = new TreeSet(other.keySet()); if (!l.equals(lo)) return false; for (String key : keySet()) { String value = get(key); String valueo = other.get(key); if (!(value == valueo || (value != null && value.equals(valueo)))) 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); } @SuppressWarnings("unchecked") public T getTyped(DataType type, String adname) { String s = get(adname); if (s == null) return null; Type t = getType(adname); if (t != type.type()) throw new IllegalArgumentException( "For key " + adname + ", expected " + type.type() + " but had a " + t + ". Value is " + s); return (T) convert(t, s); } public static Type toType(String type) { for (Type t : Type.values()) { if (t.toString.equals(type)) return t; } return null; } public static Object convert(String t, String s) { if (s == null) return null; Type type = toType(t); if (type == null) return s; return convert(type, s); } public static 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); case DOUBLE : return Double.parseDouble(s.trim()); case DOUBLES : case LONGS : case STRINGS : case VERSIONS : // Cannot happen since the sub is null return null; } return null; } List list = new ArrayList(); List split = splitListAttribute(s); for (String p : split) list.add(convert(t.sub, p)); return list; } static List splitListAttribute(String input) throws IllegalArgumentException { List result = new LinkedList(); StringBuilder builder = new StringBuilder(); for (int i = 0; i < input.length(); i++) { char c = input.charAt(i); switch (c) { case '\\' : i++; if (i >= input.length()) throw new IllegalArgumentException("Trailing blackslash in multi-valued attribute value"); c = input.charAt(i); builder.append(c); break; case ',' : result.add(builder.toString()); builder = new StringBuilder(); break; default : builder.append(c); break; } } result.add(builder.toString()); return result; } /** * Merge the attributes */ public void mergeWith(Attrs other, boolean override) { for (Map.Entry e : other.entrySet()) { String local = get(e.getKey()); if (override || local == null) put(e.getKey(), e.getValue()); } } /** * Check if a directive, if so, return directive name otherwise null */ public static String toDirective(String key) { if (key == null || !key.endsWith(":")) return null; return key.substring(0, key.length() - 1); } public static Attrs create(String key, String value) { Attrs attrs = new Attrs(); attrs.put(key, value); return attrs; } public Attrs with(String key, String value) { put(key, value); return this; } }