001/* 002 * MIT License 003 * 004 * Copyright (c) 2016 Michael Angstadt 005 * 006 * Permission is hereby granted, free of charge, to any person obtaining a copy 007 * of this software and associated documentation files (the "Software"), to deal 008 * in the Software without restriction, including without limitation the rights 009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010 * copies of the Software, and to permit persons to whom the Software is 011 * furnished to do so, subject to the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be included in 014 * all copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 022 * SOFTWARE. 023 */ 024 025package com.github.mangstadt.vinnie; 026 027import java.nio.charset.Charset; 028import java.nio.charset.IllegalCharsetNameException; 029import java.nio.charset.UnsupportedCharsetException; 030import java.util.ArrayList; 031import java.util.Arrays; 032import java.util.Iterator; 033import java.util.LinkedHashMap; 034import java.util.List; 035import java.util.Map; 036import java.util.Map.Entry; 037 038/** 039 * A simple multimap implementation for holding the parameters of a 040 * {@link VObjectProperty}. Enforces case-insensitivity of parameter names by 041 * converting them to uppercase. 042 * @author Michael Angstadt 043 */ 044public class VObjectParameters implements Iterable<Map.Entry<String, List<String>>> { 045 private final Map<String, List<String>> multimap; 046 047 /** 048 * Creates an empty list of parameters. 049 */ 050 public VObjectParameters() { 051 multimap = new LinkedHashMap<String, List<String>>(); //preserve insertion order of keys 052 } 053 054 /** 055 * <p> 056 * Creates a list of parameters backed by the given map. Any changes made to 057 * the given map will effect the parameter list and vice versa. 058 * </p> 059 * <p> 060 * If the given map is not empty, care should be taken to ensure that all of 061 * its keys are in uppercase before passing it into this constructor. 062 * </p> 063 * @param map the map 064 */ 065 public VObjectParameters(Map<String, List<String>> map) { 066 multimap = map; 067 } 068 069 /** 070 * Copies an existing list of parameters. 071 * @param original the existing list 072 */ 073 public VObjectParameters(VObjectParameters original) { 074 this(); 075 for (Map.Entry<String, List<String>> entry : original) { 076 String name = entry.getKey(); 077 List<String> values = entry.getValue(); 078 multimap.put(name, new ArrayList<String>(values)); 079 } 080 } 081 082 /** 083 * Gets the values that are assigned to a key. 084 * @param key the key 085 * @return the values or null if the key does not exist 086 */ 087 public List<String> get(String key) { 088 key = sanitizeKey(key); 089 return _get(key); 090 } 091 092 /** 093 * @param key assumed to already be in uppercase 094 */ 095 private List<String> _get(String key) { 096 return multimap.get(key); 097 } 098 099 /** 100 * Inserts a value. 101 * @param key the key 102 * @param value the value to add 103 */ 104 public void put(String key, String value) { 105 key = sanitizeKey(key); 106 _put(key, value); 107 } 108 109 /** 110 * @param key assumed to already be in uppercase 111 * @param value the value to add 112 */ 113 private void _put(String key, String value) { 114 List<String> list = _get(key); 115 if (list == null) { 116 list = new ArrayList<String>(); 117 multimap.put(key, list); 118 } 119 list.add(value); 120 } 121 122 /** 123 * Inserts multiple values. 124 * @param key the key 125 * @param values the values to add 126 */ 127 public void putAll(String key, String... values) { 128 if (values.length == 0) { 129 return; 130 } 131 key = sanitizeKey(key); 132 _putAll(key, values); 133 } 134 135 /** 136 * @param key assumed to already be in uppercase 137 * @param values the values to add 138 */ 139 private void _putAll(String key, String... values) { 140 List<String> list = _get(key); 141 if (list == null) { 142 list = new ArrayList<String>(); 143 multimap.put(key, list); 144 } 145 list.addAll(Arrays.asList(values)); 146 } 147 148 /** 149 * Replaces all the values of the given key with the given value. 150 * @param key the key 151 * @param value the value 152 * @return the replaced values or null if the key didn't exist 153 */ 154 public List<String> replace(String key, String value) { 155 key = sanitizeKey(key); 156 List<String> replaced = _removeAll(key); 157 _put(key, value); 158 return replaced; 159 } 160 161 /** 162 * Replaces all the values of the given key with the given values. 163 * @param key the key 164 * @param values the values 165 * @return the replaced values or null if the key didn't exist 166 */ 167 public List<String> replaceAll(String key, String... values) { 168 key = sanitizeKey(key); 169 List<String> replaced = _removeAll(key); 170 if (values.length > 0) { 171 _putAll(key, values); 172 } 173 return replaced; 174 } 175 176 /** 177 * Removes a value. 178 * @param key the key 179 * @param value the value to remove 180 * @return true if the value was found, false if not 181 */ 182 public boolean remove(String key, String value) { 183 List<String> values = get(key); 184 return (values == null) ? false : values.remove(value); 185 } 186 187 /** 188 * Removes all values associated with a key, along with the key itself. 189 * @param key the key 190 * @return the removed values or null if the key didn't exist 191 */ 192 public List<String> removeAll(String key) { 193 key = sanitizeKey(key); 194 return _removeAll(key); 195 } 196 197 /** 198 * @param key assumed to already be in uppercase 199 */ 200 private List<String> _removeAll(String key) { 201 return multimap.remove(key); 202 } 203 204 /** 205 * Clears the multimap. 206 */ 207 public void clear() { 208 multimap.clear(); 209 } 210 211 /** 212 * Gets the first value assigned to the given key. 213 * @param key the key 214 * @return the value or null if the given key does not have any values 215 */ 216 public String first(String key) { 217 List<String> values = get(key); 218 return (values == null || values.isEmpty()) ? null : values.get(0); 219 } 220 221 /** 222 * Determines if a "quoted-printable encoding" parameter exists. 223 * @return true if the parameter exists, false if not 224 */ 225 public boolean isQuotedPrintable() { 226 for (String key : new String[] { "ENCODING", null }) { 227 List<String> values = _get(key); 228 if (values == null) { 229 continue; 230 } 231 232 for (String value : values) { 233 if ("QUOTED-PRINTABLE".equalsIgnoreCase(value)) { 234 return true; 235 } 236 } 237 } 238 239 return false; 240 } 241 242 /** 243 * Gets the CHARSET parameter. 244 * @return the character set or null if a character set is not defined 245 * @throws IllegalCharsetNameException if the character set name contains 246 * illegal characters 247 * @throws UnsupportedCharsetException if the local JVM does not recognized 248 * the character set 249 */ 250 public Charset getCharset() throws IllegalCharsetNameException, UnsupportedCharsetException { 251 String charsetStr = first("CHARSET"); 252 return (charsetStr == null) ? null : Charset.forName(charsetStr); 253 } 254 255 /** 256 * Gets the map that backs this parameters list. 257 * @return the map 258 */ 259 public Map<String, List<String>> getMap() { 260 return multimap; 261 } 262 263 /** 264 * Creates an iterator over all the parameters (for use in foreach loops). 265 * @return the iterator 266 */ 267 public Iterator<Entry<String, List<String>>> iterator() { 268 return multimap.entrySet().iterator(); 269 } 270 271 /** 272 * Converts the given key to uppercase. Call this method before passing a 273 * key to the multimap. 274 * @param key the key 275 * @return the sanitized key 276 */ 277 private String sanitizeKey(String key) { 278 return (key == null) ? null : key.toUpperCase(); 279 } 280 281 @Override 282 public int hashCode() { 283 return multimap.hashCode(); 284 } 285 286 @Override 287 public boolean equals(Object obj) { 288 if (this == obj) return true; 289 if (obj == null) return false; 290 if (getClass() != obj.getClass()) return false; 291 VObjectParameters other = (VObjectParameters) obj; 292 return multimap.equals(other.multimap); 293 } 294 295 @Override 296 public String toString() { 297 return multimap.toString(); 298 } 299}