Dictionary.java
package org.greenbytes.http.sfv;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
/**
* Represents a Dictionary.
*
* @see <a href=
* "https://www.rfc-editor.org/rfc/rfc9651.html#dictionary">Section 3.2 of
* RFC 9651</a>
*/
public class Dictionary implements Type<Map<String, ListElement<?>>> {
private final Map<String, ListElement<?>> value;
private Dictionary(Map<String, ListElement<?>> value) {
this.value = Collections.unmodifiableMap(Utils.checkKeys(value));
}
@Override
public SfDataType getType() {
return SfDataType.DICTIONARY;
}
/**
* @see #parse(List<String>)
*/
public static Dictionary parse(String input) {
return new Parser(input).parseDictionary();
}
/**
* Implementation of "Parsing a Dictionary"
*
* @param input values of field values.
* @return result of parse as {@link Dictionary}.
*
* @see <a href=
* "https://www.rfc-editor.org/rfc/rfc9651.html#parse-dictionary">Section
* 4.2.2 of RFC 9651</a>
*/
public static Dictionary parse(List<String> input) {
return new Parser(input).parseDictionary();
}
/**
* Creates a {@link Dictionary} 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 Dictionary} representing {@code value}.
*/
public static Dictionary of(Map<String, ListElement<?>> value) {
return new Dictionary(value);
}
/**
* Creates a {@link Dictionary} instance representing the values
* (key/value pairs)
*
* @param obs
* a sequence of key/value pairs
* @return a {@link Dictionary} representing the {@code values}.
*/
public static Dictionary valueOf(Object... obs) {
if (obs.length % 2 != 0) {
throw new IllegalArgumentException("requires even number of arguments, got: " + obs.length);
} else {
Map<String, ListElement<?>> map = new LinkedHashMap<>();
for (int i = 0; i < obs.length; i += 2) {
String key = obs[i].toString();
Object value = obs[i + 1];
if (map.containsKey(key)) {
throw new IllegalArgumentException("key " + key + " already exists");
}
if (value instanceof ListElement) {
map.put(key, (ListElement<?>) value);
} else {
map.put(key, Utils.asItem(obs[i + 1]));
}
}
return of(map);
}
}
@Override
public Map<String, ListElement<?>> get() {
return value;
}
@Override
public StringBuilder serializeTo(StringBuilder sb) {
String separator = "";
for (Map.Entry<String, ListElement<?>> e : value.entrySet()) {
sb.append(separator);
separator = ", ";
String name = e.getKey();
ListElement<?> dict = e.getValue();
sb.append(name);
if (Boolean.TRUE.equals(dict.get())) {
dict.params().serializeTo(sb);
} else {
sb.append("=");
dict.serializeTo(sb);
}
}
return sb;
}
@Override
public String serialize() {
return serializeTo(new StringBuilder()).toString();
}
@Override
public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function<Class, String> formatter) {
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, ListElement<?>> e : value.entrySet()) {
sb.append(indent +" ").append(e.getKey()).append(" -> \n");
e.getValue().serializeToForDebug(sb, indentLevel + 2, formatter);
}
return sb;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Dictionary)) {
return false;
} else {
Dictionary that = (Dictionary) o;
return Objects.equals(value, that.value);
}
}
@Override
public int hashCode() {
return Objects.hashCode(value);
}
}