001package biweekly.io.json; 002 003import java.util.ArrayList; 004import java.util.Arrays; 005import java.util.Collections; 006import java.util.LinkedHashMap; 007import java.util.List; 008import java.util.Map; 009 010import biweekly.util.ListMultimap; 011 012/* 013 Copyright (c) 2013-2015, Michael Angstadt 014 All rights reserved. 015 016 Redistribution and use in source and binary forms, with or without 017 modification, are permitted provided that the following conditions are met: 018 019 1. Redistributions of source code must retain the above copyright notice, this 020 list of conditions and the following disclaimer. 021 2. Redistributions in binary form must reproduce the above copyright notice, 022 this list of conditions and the following disclaimer in the documentation 023 and/or other materials provided with the distribution. 024 025 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 026 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 027 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 028 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 029 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 030 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 031 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 032 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 033 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 034 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 035 */ 036 037/** 038 * Holds the value of a jCal property. 039 * @author Michael Angstadt 040 */ 041public class JCalValue { 042 private final List<JsonValue> values; 043 044 /** 045 * Creates a new jCal value. 046 * @param values the values 047 */ 048 public JCalValue(List<JsonValue> values) { 049 this.values = Collections.unmodifiableList(values); 050 } 051 052 /** 053 * Creates a new jCal value. 054 * @param values the values 055 */ 056 public JCalValue(JsonValue... values) { 057 this.values = Arrays.asList(values); //unmodifiable 058 } 059 060 /** 061 * Creates a single-valued value. 062 * @param value the value 063 * @return the jCal value 064 */ 065 public static JCalValue single(Object value) { 066 return new JCalValue(new JsonValue(value)); 067 } 068 069 /** 070 * Creates a multi-valued value. 071 * @param values the values 072 * @return the jCal value 073 */ 074 public static JCalValue multi(Object... values) { 075 return multi(Arrays.asList(values)); 076 } 077 078 /** 079 * Creates a multi-valued value. 080 * @param values the values 081 * @return the jCal value 082 */ 083 public static JCalValue multi(List<?> values) { 084 List<JsonValue> multiValues = new ArrayList<JsonValue>(values.size()); 085 for (Object value : values) { 086 multiValues.add(new JsonValue(value)); 087 } 088 return new JCalValue(multiValues); 089 } 090 091 /** 092 * <p> 093 * Creates a structured value. 094 * </p> 095 * <p> 096 * This method accepts a vararg of {@link Object} instances. {@link List} 097 * objects will be treated as multi-valued components. All other objects. 098 * Null values will be treated as empty components. 099 * </p> 100 * @param values the values 101 * @return the jCal value 102 */ 103 public static JCalValue structured(Object... values) { 104 List<List<?>> valuesList = new ArrayList<List<?>>(values.length); 105 for (Object value : values) { 106 List<?> list = (value instanceof List) ? (List<?>) value : Arrays.asList(value); 107 valuesList.add(list); 108 } 109 return structured(valuesList); 110 } 111 112 /** 113 * Creates a structured value. 114 * @param values the values 115 * @return the jCal value 116 */ 117 public static JCalValue structured(List<List<?>> values) { 118 List<JsonValue> array = new ArrayList<JsonValue>(values.size()); 119 120 for (List<?> list : values) { 121 if (list.isEmpty()) { 122 array.add(new JsonValue("")); 123 continue; 124 } 125 126 if (list.size() == 1) { 127 Object value = list.get(0); 128 if (value == null) { 129 value = ""; 130 } 131 array.add(new JsonValue(value)); 132 continue; 133 } 134 135 List<JsonValue> subArray = new ArrayList<JsonValue>(list.size()); 136 for (Object value : list) { 137 if (value == null) { 138 value = ""; 139 } 140 subArray.add(new JsonValue(value)); 141 } 142 array.add(new JsonValue(subArray)); 143 } 144 145 return new JCalValue(new JsonValue(array)); 146 } 147 148 /** 149 * Creates an object value. 150 * @param value the object 151 * @return the jCal value 152 */ 153 public static JCalValue object(ListMultimap<String, Object> value) { 154 Map<String, JsonValue> object = new LinkedHashMap<String, JsonValue>(); 155 for (Map.Entry<String, List<Object>> entry : value) { 156 String key = entry.getKey(); 157 List<Object> list = entry.getValue(); 158 159 JsonValue v; 160 if (list.size() == 1) { 161 v = new JsonValue(list.get(0)); 162 } else { 163 List<JsonValue> array = new ArrayList<JsonValue>(list.size()); 164 for (Object element : list) { 165 array.add(new JsonValue(element)); 166 } 167 v = new JsonValue(array); 168 } 169 object.put(key, v); 170 } 171 return new JCalValue(new JsonValue(object)); 172 } 173 174 /** 175 * Gets the raw JSON values. Use one of the "{@code as*}" methods to parse 176 * the values as one of the standard jCal values. 177 * @return the JSON values 178 */ 179 public List<JsonValue> getValues() { 180 return values; 181 } 182 183 /** 184 * Parses this jCal value as a single-valued property value. 185 * @return the value or empty string if not found 186 */ 187 public String asSingle() { 188 if (values.isEmpty()) { 189 return ""; 190 } 191 192 JsonValue first = values.get(0); 193 if (first.isNull()) { 194 return ""; 195 } 196 197 Object obj = first.getValue(); 198 if (obj != null) { 199 return obj.toString(); 200 } 201 202 //get the first element of the array 203 List<JsonValue> array = first.getArray(); 204 if (array != null && !array.isEmpty()) { 205 obj = array.get(0).getValue(); 206 if (obj != null) { 207 return obj.toString(); 208 } 209 } 210 211 return ""; 212 } 213 214 /** 215 * Parses this jCal value as a structured property value. 216 * @return the structured values or empty list if not found 217 */ 218 public List<List<String>> asStructured() { 219 if (values.isEmpty()) { 220 return Collections.emptyList(); 221 } 222 223 JsonValue first = values.get(0); 224 225 //["request-status", {}, "text", ["2.0", "Success"] ] 226 List<JsonValue> array = first.getArray(); 227 if (array != null) { 228 List<List<String>> valuesStr = new ArrayList<List<String>>(array.size()); 229 for (JsonValue value : array) { 230 if (value.isNull()) { 231 valuesStr.add(Arrays.asList("")); 232 continue; 233 } 234 235 Object obj = value.getValue(); 236 if (obj != null) { 237 valuesStr.add(Arrays.asList(obj.toString())); 238 continue; 239 } 240 241 List<JsonValue> subArray = value.getArray(); 242 if (subArray != null) { 243 List<String> subValuesStr = new ArrayList<String>(subArray.size()); 244 for (JsonValue subArrayValue : subArray) { 245 if (subArrayValue.isNull()) { 246 subValuesStr.add(""); 247 continue; 248 } 249 250 obj = subArrayValue.getValue(); 251 if (obj != null) { 252 subValuesStr.add(obj.toString()); 253 continue; 254 } 255 } 256 valuesStr.add(subValuesStr); 257 } 258 } 259 return valuesStr; 260 } 261 262 //get the first value if it's not enclosed in an array 263 //["request-status", {}, "text", "2.0"] 264 Object obj = first.getValue(); 265 if (obj != null) { 266 List<List<String>> values = new ArrayList<List<String>>(1); 267 values.add(Arrays.asList(obj.toString())); 268 return values; 269 } 270 271 //["request-status", {}, "text", null] 272 if (first.isNull()) { 273 List<List<String>> values = new ArrayList<List<String>>(1); 274 values.add(Arrays.asList("")); 275 return values; 276 } 277 278 return Collections.emptyList(); 279 } 280 281 /** 282 * Parses this jCal value as a multi-valued property value. 283 * @return the values or empty list if not found 284 */ 285 public List<String> asMulti() { 286 if (values.isEmpty()) { 287 return Collections.emptyList(); 288 } 289 290 List<String> multi = new ArrayList<String>(values.size()); 291 for (JsonValue value : values) { 292 if (value.isNull()) { 293 multi.add(""); 294 continue; 295 } 296 297 Object obj = value.getValue(); 298 if (obj != null) { 299 multi.add(obj.toString()); 300 continue; 301 } 302 } 303 return multi; 304 } 305 306 /** 307 * Parses this jCal value as an object property value. 308 * @return the object or an empty map if not found 309 */ 310 public ListMultimap<String, String> asObject() { 311 if (values.isEmpty()) { 312 return new ListMultimap<String, String>(0); 313 } 314 315 Map<String, JsonValue> map = values.get(0).getObject(); 316 if (map == null) { 317 return new ListMultimap<String, String>(0); 318 } 319 320 ListMultimap<String, String> values = new ListMultimap<String, String>(); 321 for (Map.Entry<String, JsonValue> entry : map.entrySet()) { 322 String key = entry.getKey(); 323 JsonValue value = entry.getValue(); 324 325 if (value.isNull()) { 326 values.put(key, ""); 327 continue; 328 } 329 330 Object obj = value.getValue(); 331 if (obj != null) { 332 values.put(key, obj.toString()); 333 continue; 334 } 335 336 List<JsonValue> array = value.getArray(); 337 if (array != null) { 338 for (JsonValue element : array) { 339 if (element.isNull()) { 340 values.put(key, ""); 341 continue; 342 } 343 344 obj = element.getValue(); 345 if (obj != null) { 346 values.put(key, obj.toString()); 347 } 348 } 349 } 350 } 351 return values; 352 } 353}