1 package expo.modules.structuredheaders;
2 
3 import android.os.Build;
4 
5 import java.math.BigDecimal;
6 import java.util.Collection;
7 import java.util.Collections;
8 import java.util.LinkedHashMap;
9 import java.util.Map;
10 import java.util.Objects;
11 import java.util.Set;
12 import java.util.function.BiConsumer;
13 import java.util.function.BiFunction;
14 import java.util.function.Function;
15 
16 import androidx.annotation.RequiresApi;
17 
18 /**
19  * Represents the Parameters of an Item or an Inner List.
20  *
21  * @see <a href=
22  *      "https://greenbytes.de/tech/webdav/draft-ietf-httpbis-header-structure-19.html#param">Section
23  *      3.1.2 of draft-ietf-httpbis-header-structure-19</a>
24  */
25 public class Parameters implements Map<String, Item<? extends Object>> {
26 
27     private final Map<String, Item<? extends Object>> delegate;
28 
29     public static final Parameters EMPTY = new Parameters(Collections.<String, Object>emptyMap());
30 
31     private Parameters(Map<String, Object> value) {
32         this.delegate = Collections.unmodifiableMap(checkAndTransformMap(value));
33     }
34 
35     /**
36      * Creates a {@link Parameters} instance representing the specified
37      * {@code Map<String, Item>} value.
38      * <p>
39      * Note that the {@link Map} implementation that is used here needs to
40      * iterate predictably based on insertion order, such as
41      * {@link java.util.LinkedHashMap}.
42      *
43      * @param value
44      *            a {@code Map<String, Item>} value
45      * @return a {@link Parameters} representing {@code value}.
46      */
47     public static Parameters valueOf(Map<String, Object> value) {
48         return new Parameters(value);
49     }
50 
51     public StringBuilder serializeTo(StringBuilder sb) {
52         for (Map.Entry<String, Item<? extends Object>> e : delegate.entrySet()) {
53             sb.append(';').append(e.getKey());
54             if (!(e.getValue().get().equals(Boolean.TRUE))) {
55                 sb.append('=');
56                 e.getValue().serializeTo(sb);
57             }
58         }
59         return sb;
60     }
61 
62     private static Map<String, Item<? extends Object>> checkAndTransformMap(Map<String, Object> map) {
63         Map<String, Item<? extends Object>> result = new LinkedHashMap<>(
64                 Objects.requireNonNull(map, "Map must not be null").size());
65         for (Map.Entry<String, Object> entry : map.entrySet()) {
66             String key = Utils.checkKey(entry.getKey());
67             Item<? extends Object> value = asItem(key, entry.getValue());
68             if (!value.getParams().isEmpty()) {
69                 throw new IllegalArgumentException("Parameter value for '" + key + "' must be bare item (no parameters)");
70             }
71             result.put(entry.getKey(), value);
72         }
73         return result;
74     }
75 
76     @SuppressWarnings("unchecked")
77     private static Item<? extends Object> asItem(String key, Object o) {
78         if (o instanceof Item) {
79             return (Item<? extends Object>) o;
80         } else if (o instanceof Integer) {
81             return IntegerItem.valueOf(((Integer) o).longValue());
82         } else if (o instanceof Long) {
83             return IntegerItem.valueOf(((Long) o).longValue());
84         } else if (o instanceof String) {
85             return StringItem.valueOf((String) o);
86         } else if (o instanceof Boolean) {
87             return BooleanItem.valueOf(((Boolean) o).booleanValue());
88         } else if (o instanceof byte[]) {
89             return ByteSequenceItem.valueOf((byte[]) o);
90         } else if (o instanceof BigDecimal) {
91             return DecimalItem.valueOf((BigDecimal)o);
92         } else {
93             throw new IllegalArgumentException("Can't map value for parameter '" + key + "': " + o.getClass());
94         }
95     }
96 
97     // delegate methods, autogenerated
98 
99     public void clear() {
100         delegate.clear();
101     }
102 
103     @RequiresApi(api = Build.VERSION_CODES.N)
104     public Item<? extends Object> compute(String key,
105                                           BiFunction<? super String, ? super Item<? extends Object>, ? extends Item<? extends Object>> remappingFunction) {
106         return delegate.compute(key, remappingFunction);
107     }
108 
109     @RequiresApi(api = Build.VERSION_CODES.N)
110     public Item<? extends Object> computeIfAbsent(String key,
111                                                   Function<? super String, ? extends Item<? extends Object>> mappingFunction) {
112         return delegate.computeIfAbsent(key, mappingFunction);
113     }
114 
115     @RequiresApi(api = Build.VERSION_CODES.N)
116     public Item<? extends Object> computeIfPresent(String key,
117                                                    BiFunction<? super String, ? super Item<? extends Object>, ? extends Item<? extends Object>> remappingFunction) {
118         return delegate.computeIfPresent(key, remappingFunction);
119     }
120 
121     public boolean containsKey(Object key) {
122         return delegate.containsKey(key);
123     }
124 
125     public boolean containsValue(Object value) {
126         return delegate.containsValue(value);
127     }
128 
129     public Set<Entry<String, Item<? extends Object>>> entrySet() {
130         return delegate.entrySet();
131     }
132 
133     public boolean equals(Object o) {
134         return delegate.equals(o);
135     }
136 
137     @RequiresApi(api = Build.VERSION_CODES.N)
138     public void forEach(BiConsumer<? super String, ? super Item<? extends Object>> action) {
139         delegate.forEach(action);
140     }
141 
142     public Item<? extends Object> get(Object key) {
143         return delegate.get(key);
144     }
145 
146     @RequiresApi(api = Build.VERSION_CODES.N)
147     public Item<? extends Object> getOrDefault(Object key, Item<? extends Object> defaultValue) {
148         return delegate.getOrDefault(key, defaultValue);
149     }
150 
151     public int hashCode() {
152         return delegate.hashCode();
153     }
154 
155     public boolean isEmpty() {
156         return delegate.isEmpty();
157     }
158 
159     public Set<String> keySet() {
160         return delegate.keySet();
161     }
162 
163     @RequiresApi(api = Build.VERSION_CODES.N)
164     public Item<? extends Object> merge(String key, Item<? extends Object> value,
165                                         BiFunction<? super Item<? extends Object>, ? super Item<? extends Object>, ? extends Item<? extends Object>> remappingFunction) {
166         return delegate.merge(key, value, remappingFunction);
167     }
168 
169     public Item<? extends Object> put(String key, Item<? extends Object> value) {
170         return delegate.put(key, value);
171     }
172 
173     public void putAll(Map<? extends String, ? extends Item<? extends Object>> m) {
174         delegate.putAll(m);
175     }
176 
177     @RequiresApi(api = Build.VERSION_CODES.N)
178     public Item<? extends Object> putIfAbsent(String key, Item<? extends Object> value) {
179         return delegate.putIfAbsent(key, value);
180     }
181 
182     public boolean remove(Object key, Object value) {
183         return delegate.remove(key, value);
184     }
185 
186     public Item<? extends Object> remove(Object key) {
187         return delegate.remove(key);
188     }
189 
190     @RequiresApi(api = Build.VERSION_CODES.N)
191     public boolean replace(String key, Item<? extends Object> oldValue, Item<? extends Object> newValue) {
192         return delegate.replace(key, oldValue, newValue);
193     }
194 
195     @RequiresApi(api = Build.VERSION_CODES.N)
196     public Item<? extends Object> replace(String key, Item<? extends Object> value) {
197         return delegate.replace(key, value);
198     }
199 
200     @RequiresApi(api = Build.VERSION_CODES.N)
201     public void replaceAll(BiFunction<? super String, ? super Item<? extends Object>, ? extends Item<? extends Object>> function) {
202         delegate.replaceAll(function);
203     }
204 
205     public int size() {
206         return delegate.size();
207     }
208 
209     public Collection<Item<? extends Object>> values() {
210         return delegate.values();
211     }
212 
213     public String serialize() {
214         return serializeTo(new StringBuilder()).toString();
215     }
216 }
217