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