Parameters.java
package org.greenbytes.http.sfv;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Represents the Parameters of an Item or an Inner List.
*
* @see <a href=
* "https://www.rfc-editor.org/rfc/rfc9651.html#param">Section
* 3.1.2 of RFC 9651</a>
*/
public class Parameters implements Map<String, Item<?>> {
private final Map<String, Item<?>> delegate;
/** Empty parameters instance. */
protected static final Parameters EMPTY = new Parameters(Collections.emptyMap());
private Parameters(Map<String, Object> value) {
this.delegate = Collections.unmodifiableMap(checkAndTransformMap(value));
}
/**
* Creates an unmodifiable {@link Parameters} instance representing the
* specified {@code Map<String, Item>} value.
* <p>
* Note that the {@link Map} implementation that is used here needs to
* iterate predictably based on insertion order, such as
* {@link java.util.LinkedHashMap}.
*
* @param value
* a {@code Map<String, Item>} value
* @return a {@link Parameters} representing {@code value}.
*/
public static Parameters valueOf(Map<String, Object> value) {
return new Parameters(value);
}
/**
* Serialize this parameter to a {@linkplain StringBuilder}
* @param sb to serialize to
* @return updated {@linkplain StringBuilder}
*/
public StringBuilder serializeTo(StringBuilder sb) {
for (Map.Entry<String, Item<?>> e : delegate.entrySet()) {
sb.append(';').append(e.getKey());
if (!(e.getValue().get().equals(Boolean.TRUE))) {
sb.append('=');
e.getValue().serializeTo(sb);
}
}
return sb;
}
/**
* Serialize this parameter.
* @return serialization
*/
public String serialize() {
return serializeTo(new StringBuilder()).toString();
}
public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function<Class, String> formatter) {
if (!delegate.isEmpty()) {
String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : "";
String classn = formatter.apply(this.getClass());
sb.append(indent).append(serialize()).append(classn).append("\n");
for (Map.Entry<String, Item<?>> e : delegate.entrySet()) {
sb.append(" " + indent).append(e.getKey()).append(" -> ");
e.getValue().serializeToForDebug(sb, 0, formatter);
}
return sb;
} else {
return sb;
}
}
private static Map<String, Item<?>> checkAndTransformMap(Map<String, Object> map) {
Map<String, Item<?>> result = new LinkedHashMap<>(
Objects.requireNonNull(map, "Map must not be null").size());
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = Utils.checkKey(entry.getKey());
Item<?> value = asItem(key, entry.getValue());
if (!value.getParams().isEmpty()) {
throw new IllegalArgumentException("Parameter value for '" + key + "' must be bare item (no parameters)");
}
result.put(entry.getKey(), value);
}
return result;
}
private static Item<?> asItem(String key, Object o) {
if (o instanceof Item) {
if (o instanceof Parameterizable) {
Parameterizable p = ((Parameterizable)o);
if (!p.getParams().isEmpty()) {
throw new IllegalArgumentException("Can't map value for parameter '" + key + "': " + o.getClass() + " carries parameters");
}
}
return (Item<?>) o;
} else if (o instanceof Integer) {
return IntegerItem.valueOf(((Integer) o).longValue());
} else if (o instanceof Long) {
return IntegerItem.valueOf((Long) o);
} else if (o instanceof String) {
return StringItem.valueOf((String) o);
} else if (o instanceof Boolean) {
return BooleanItem.valueOf((Boolean) o);
} else if (o instanceof byte[]) {
return ByteSequenceItem.valueOf((byte[]) o);
} else if (o instanceof BigDecimal) {
return DecimalItem.valueOf((BigDecimal)o);
} else {
throw new IllegalArgumentException("Can't map value for parameter '" + key + "': " + o.getClass());
}
}
// delegate methods, autogenerated
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public Item<?> compute(String key,
BiFunction<? super String, ? super Item<?>, ? extends Item<?>> remappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public Item<?> computeIfAbsent(String key,
Function<? super String, ? extends Item<?>> mappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public Item<?> computeIfPresent(String key,
BiFunction<? super String, ? super Item<?>, ? extends Item<?>> remappingFunction) {
throw new UnsupportedOperationException();
}
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}
public Set<Entry<String, Item<?>>> entrySet() {
return delegate.entrySet();
}
public boolean equals(Object o) {
return Objects.equals(delegate, o);
}
@Override
public void forEach(BiConsumer<? super String, ? super Item<?>> action) {
delegate.forEach(action);
}
public Item<?> get(Object key) {
return delegate.get(key);
}
@Override
public Item<?> getOrDefault(Object key, Item<?> defaultValue) {
return delegate.getOrDefault(key, defaultValue);
}
public int hashCode() {
return delegate.hashCode();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public Set<String> keySet() {
return delegate.keySet();
}
@Override
public Item<?> merge(String key, Item<?> value,
BiFunction<? super Item<?>, ? super Item<?>, ? extends Item<?>> remappingFunction) {
throw new UnsupportedOperationException();
}
public Item<?> put(String key, Item<?> value) {
throw new UnsupportedOperationException();
}
public void putAll(Map<? extends String, ? extends Item<?>> m) {
throw new UnsupportedOperationException();
}
@Override
public Item<?> putIfAbsent(String key, Item<?> value) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException();
}
public Item<?> remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public boolean replace(String key, Item<?> oldValue, Item<?> newValue) {
throw new UnsupportedOperationException();
}
@Override
public Item<?> replace(String key, Item<?> value) {
throw new UnsupportedOperationException();
}
@Override
public void replaceAll(BiFunction<? super String, ? super Item<?>, ? extends Item<?>> function) {
throw new UnsupportedOperationException();
}
public int size() {
return delegate.size();
}
public Collection<Item<?>> values() {
return delegate.values();
}
}