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 biz.aQute.bndlib Show documentation
Show all versions of biz.aQute.bndlib Show documentation
bndlib: A Swiss Army Knife for OSGi
The newest version!
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.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import aQute.bnd.stream.MapStream;
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() {
return switch (this) {
case DOUBLE -> DOUBLES;
case LONG -> LONGS;
case STRING -> STRINGS;
case VERSION -> VERSIONS;
default -> 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;
/**
* Pattern for List with list type
*/
public static final Pattern TYPED = Pattern
.compile("List\\s*<\\s*(String|Version|Long|Double)\\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);
}
}
}
// do not remove, used to make sure Attrs use this one and not the next that
// takes a Map
public Attrs(Attrs attrs) {
this();
putAll(attrs);
}
// This constructor is also used by reflective cloning in assertj testing
public Attrs(Map extends String, ? extends String> map) {
this();
if (map != null) {
putAll(map);
}
}
public void putAllTyped(Map extends String, ? extends Object> attrs) {
attrs.forEach(this::putTyped);
}
public void putTyped(String key, Object value) {
if (value == null) {
put(key, null);
return;
}
if (!(value instanceof String)) {
Type type;
if (value instanceof Collection> collection) {
value = collection.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 || member instanceof org.osgi.framework.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();
}
public MapStream stream() {
return MapStream.of(this);
}
@Override
public void forEach(BiConsumer super String, ? super String> action) {
map.forEach(action);
}
@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();
}
public String put(String key, Type type, String value) {
if (key == null) {
return null;
}
if ((type == null) || (type == Type.STRING)) {
types.remove(key);
} else {
types.put(key, type);
}
return map.put(key, value);
}
@Override
public String put(String key, String value) {
if (key == null)
return null;
return map.put(putType(key), value);
}
private String putType(String key) {
int colon = key.indexOf(':');
if (colon >= 0) {
String type = key.substring(colon + 1)
.trim();
typed_attribute: if (!type.isEmpty()) { // typed attribute
String attribute = key.substring(0, colon)
.trim();
switch (type) {
case "String" :
types.remove(attribute);
break;
case "Long" :
types.put(attribute, Type.LONG);
break;
case "Double" :
types.put(attribute, Type.DOUBLE);
break;
case "Version" :
types.put(attribute, Type.VERSION);
break;
case "List" :
case "List" :
types.put(attribute, Type.STRINGS);
break;
case "List" :
types.put(attribute, Type.LONGS);
break;
case "List" :
types.put(attribute, Type.DOUBLES);
break;
case "List" :
types.put(attribute, Type.VERSIONS);
break;
default :
Matcher m = TYPED.matcher(type);
if (!m.matches()) {
break typed_attribute;
}
switch (m.group(1)) {
case "String" :
types.put(attribute, Type.STRINGS);
break;
case "Long" :
types.put(attribute, Type.LONGS);
break;
case "Double" :
types.put(attribute, Type.DOUBLES);
break;
case "Version" :
types.put(attribute, Type.VERSIONS);
break;
}
break;
}
return attribute;
}
}
// default String type
types.remove(key);
return key;
}
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 attrs) {
putAll(attrs);
return;
}
other.forEach(this::put);
}
@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;
if (!keySet().equals(other.keySet()))
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;
}
/**
* Return this attrs as typed valuesx
*/
public Map toTyped() {
Map result = new HashMap<>();
for (String k : keySet()) {
result.put(k, getTyped(k));
}
return result;
}
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) {
if (type == null) {
return null;
}
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) {
return switch (t) {
case STRING -> s;
case LONG -> Long.parseLong(s.trim());
case VERSION -> Version.parseVersion(s);
case DOUBLE -> Double.parseDouble(s.trim());
/*
* Cannot happen since the sub is null
*/
case DOUBLES, LONGS, STRINGS, VERSIONS -> null;
};
}
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy