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.bnd.runtime.snapshot Show documentation
Show all versions of biz.aQute.bnd.runtime.snapshot Show documentation
biz.aQute.bnd.runtime.snapshot
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