aQute.bnd.header.Attrs Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.aries.spifly.dynamic.framework.extension Show documentation
Show all versions of org.apache.aries.spifly.dynamic.framework.extension Show documentation
This framework extension fragment contains an extender that facilitates
the use of JRE SPI providers (components typically plugged in to the
JRE through META-INF/services resources).
package aQute.bnd.header;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
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 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");
final Type sub;
final String toString;
Type(Type sub, String toString) {
this.sub = sub;
this.toString = toString;
}
@Override
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;
}
}
}
public interface DataType {
Type type();
}
public static final DataType STRING = () -> Type.STRING;
public static final DataType LONG = () -> Type.LONG;
public static final DataType DOUBLE = () -> Type.DOUBLE;
public static final DataType VERSION = () -> Type.VERSION;
public static final DataType> LIST_STRING = () -> Type.STRINGS;
public static final DataType> LIST_LONG = () -> Type.LONGS;
public static final DataType> LIST_DOUBLE = () -> Type.DOUBLES;
public static final DataType> LIST_VERSION = () -> Type.VERSIONS;
/**
*
* 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 ’>’
*
*/
private static final String EXTENDED = "[\\-0-9a-zA-Z\\._]+";
private static final String SCALAR = "String|Version|Long|Double";
private static final String LIST = "List\\s*<\\s*(" + SCALAR + ")\\s*>";
public static final Pattern TYPED = Pattern
.compile("\\s*(" + EXTENDED + ")\\s*:\\s*(" + SCALAR + "|" + LIST + ")\\s*");
private final Map map;
private final Map types;
public static final Attrs EMPTY_ATTRS = new Attrs(Collections.emptyMap(), Collections.emptyMap());
private Attrs(Map map, Map types) {
this.map = map;
this.types = types;
}
public Attrs() {
this(new LinkedHashMap<>(), new HashMap<>());
}
public Attrs(Attrs... attrs) {
this();
for (Attrs a : attrs) {
if (a != null) {
putAll(a);
}
}
}
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);
}
if (!key.endsWith(":")) { // directives are only String type
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;
}
@Override
public void clear() {
map.clear();
types.clear();
}
public boolean containsKey(String name) {
return map.containsKey(name);
}
@Override
@SuppressWarnings("cast")
@Deprecated
public boolean containsKey(Object name) {
assert name instanceof String;
return map.containsKey(name);
}
public boolean containsValue(String value) {
return map.containsValue(value);
}
@Override
@SuppressWarnings("cast")
@Deprecated
public boolean containsValue(Object value) {
assert value instanceof String;
return map.containsValue(value);
}
@Override
public Set> entrySet() {
return map.entrySet();
}
@Override
@SuppressWarnings("cast")
@Deprecated
public String get(Object key) {
assert key instanceof String;
return map.get(key);
}
public String get(String key) {
return map.get(key);
}
public String get(String key, String deflt) {
String s = get(key);
if (s == null)
return deflt;
return s;
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public Set keySet() {
return map.keySet();
}
@Override
public String put(String key, String value) {
if (key == null)
return null;
Matcher m = TYPED.matcher(key);
if (m.matches()) { // typed-attr
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 (t != Type.STRING) {
types.put(key, t);
} else {
types.remove(key);
}
// TODO verify value?
} else {
types.remove(key); // default String type
}
return map.put(key, value);
}
public Type getType(String key) {
Type t = types.get(key);
if (t == null)
return Type.STRING;
return t;
}
public void putAll(Attrs attrs) {
types.keySet()
.removeAll(attrs.map.keySet());
map.putAll(attrs.map);
types.putAll(attrs.types);
}
@Override
public void putAll(Map extends String, ? extends String> other) {
if (other instanceof Attrs) {
putAll((Attrs) other);
return;
}
for (Map.Entry extends String, ? extends String> e : other.entrySet()) {
put(e.getKey(), e.getValue());
}
}
@Override
@SuppressWarnings("cast")
@Deprecated
public String remove(Object var0) {
assert var0 instanceof String;
types.remove(var0);
return map.remove(var0);
}
public String remove(String var0) {
types.remove(var0);
return map.remove(var0);
}
@Override
public int size() {
return map.size();
}
@Override
public Collection 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) {
String del = "";
for (Map.Entry e : entrySet()) {
sb.append(del);
append(sb, e);
del = ";";
}
}
public void append(StringBuilder sb, Map.Entry e) {
append(sb, e.getKey(), e.getValue());
}
public String toString(String key) {
StringBuilder sb = new StringBuilder();
append(sb, key, get(key));
return sb.toString();
}
private void append(StringBuilder sb, String key, String value) {
sb.append(key);
Type type = getType(key);
if (type != Type.STRING) {
sb.append(":")
.append(type);
}
sb.append("=");
OSGiHeader.quote(sb, value);
}
@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()) {
if (!Objects.equals(get(key), other.get(key))) {
return false;
}
if (getType(key) != other.getType(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);
}
@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
© 2015 - 2025 Weber Informatics LLC | Privacy Policy