001package ezvcard.io.json; 002 003import java.util.ArrayList; 004import java.util.Arrays; 005import java.util.Collections; 006import java.util.List; 007 008import ezvcard.property.Categories; 009import ezvcard.property.Note; 010import ezvcard.property.StructuredName; 011 012/* 013 Copyright (c) 2012-2023, 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 The views and conclusions contained in the software and documentation are those 037 of the authors and should not be interpreted as representing official policies, 038 either expressed or implied, of the FreeBSD Project. 039 */ 040 041/** 042 * Holds the data type and value of a jCard property. 043 * @author Michael Angstadt 044 */ 045public class JCardValue { 046 private final List<JsonValue> values; 047 048 /** 049 * Creates a new jCard value. 050 * @param values the values 051 */ 052 public JCardValue(List<JsonValue> values) { 053 this.values = Collections.unmodifiableList(values); 054 } 055 056 /** 057 * Creates a new jCard value. 058 * @param values the values 059 */ 060 public JCardValue(JsonValue... values) { 061 this.values = Arrays.asList(values); //unmodifiable 062 } 063 064 /** 065 * Creates a single-valued value. 066 * @param value the value 067 * @return the jCard value 068 */ 069 public static JCardValue single(Object value) { 070 return new JCardValue(new JsonValue(value)); 071 } 072 073 /** 074 * Creates a multi-valued value. 075 * @param values the values 076 * @return the jCard value 077 */ 078 public static JCardValue multi(Object... values) { 079 return multi(Arrays.asList(values)); 080 } 081 082 /** 083 * Creates a multi-valued value. 084 * @param values the values 085 * @return the jCard value 086 */ 087 public static JCardValue multi(List<?> values) { 088 List<JsonValue> multiValues = new ArrayList<>(values.size()); 089 for (Object value : values) { 090 multiValues.add(new JsonValue(value)); 091 } 092 return new JCardValue(multiValues); 093 } 094 095 /** 096 * <p> 097 * Creates a structured value. 098 * </p> 099 * <p> 100 * This method accepts a vararg of {@link Object} instances. {@link List} 101 * objects will be treated as multi-valued components. Null objects will be 102 * treated as empty components. 103 * </p> 104 * @param values the values 105 * @return the jCard value 106 */ 107 public static JCardValue structured(Object... values) { 108 List<List<?>> valuesList = new ArrayList<>(values.length); 109 for (Object value : values) { 110 List<?> list = (value instanceof List) ? (List<?>) value : Collections.singletonList(value); 111 valuesList.add(list); 112 } 113 return structured(valuesList); 114 } 115 116 /** 117 * Creates a structured value. 118 * @param values the values 119 * @return the jCard value 120 */ 121 public static JCardValue structured(List<List<?>> values) { 122 List<JsonValue> array = new ArrayList<>(values.size()); 123 124 for (List<?> list : values) { 125 if (list.isEmpty()) { 126 array.add(new JsonValue("")); 127 continue; 128 } 129 130 if (list.size() == 1) { 131 Object value = list.get(0); 132 if (value == null) { 133 value = ""; 134 } 135 array.add(new JsonValue(value)); 136 continue; 137 } 138 139 List<JsonValue> subArray = new ArrayList<>(list.size()); 140 for (Object value : list) { 141 if (value == null) { 142 value = ""; 143 } 144 subArray.add(new JsonValue(value)); 145 } 146 array.add(new JsonValue(subArray)); 147 } 148 149 return new JCardValue(new JsonValue(array)); 150 } 151 152 /** 153 * Gets all the JSON values. 154 * @return the JSON values 155 */ 156 public List<JsonValue> getValues() { 157 return values; 158 } 159 160 /** 161 * Gets the value of a single-valued property (such as {@link Note}). 162 * @return the value or empty string if not found 163 */ 164 public String asSingle() { 165 if (values.isEmpty()) { 166 return ""; 167 } 168 169 JsonValue first = values.get(0); 170 if (first.isNull()) { 171 return ""; 172 } 173 174 Object obj = first.getValue(); 175 if (obj != null) { 176 return obj.toString(); 177 } 178 179 //get the first element of the array 180 List<JsonValue> array = first.getArray(); 181 if (array != null && !array.isEmpty()) { 182 obj = array.get(0).getValue(); 183 if (obj != null) { 184 return obj.toString(); 185 } 186 } 187 188 return ""; 189 } 190 191 /** 192 * Gets the value of a structured property (such as {@link StructuredName}). 193 * @return the values or empty list if not found 194 */ 195 public List<List<String>> asStructured() { 196 if (values.isEmpty()) { 197 return Collections.emptyList(); 198 } 199 200 JsonValue first = values.get(0); 201 202 //["gender", {}, "text", ["M", "text"] ] 203 List<JsonValue> array = first.getArray(); 204 if (array != null) { 205 List<List<String>> components = new ArrayList<>(array.size()); 206 for (JsonValue value : array) { 207 if (value.isNull()) { 208 components.add(Collections.<String>emptyList()); 209 continue; 210 } 211 212 Object obj = value.getValue(); 213 if (obj != null) { 214 String s = obj.toString(); 215 List<String> component = s.isEmpty() ? Collections.<String>emptyList() : Collections.singletonList(s); 216 components.add(component); 217 continue; 218 } 219 220 List<JsonValue> subArray = value.getArray(); 221 if (subArray != null) { 222 List<String> component = new ArrayList<>(subArray.size()); 223 for (JsonValue subArrayValue : subArray) { 224 if (subArrayValue.isNull()) { 225 component.add(""); 226 continue; 227 } 228 229 obj = subArrayValue.getValue(); 230 if (obj != null) { 231 component.add(obj.toString()); 232 continue; 233 } 234 } 235 236 if (component.size() == 1 && component.get(0).isEmpty()) { 237 component.clear(); 238 } 239 components.add(component); 240 } 241 } 242 return components; 243 } 244 245 //get the first value if it's not enclosed in an array 246 //["gender", {}, "text", "M"] 247 Object obj = first.getValue(); 248 if (obj != null) { 249 List<List<String>> components = new ArrayList<>(1); 250 String s = obj.toString(); 251 List<String> component = s.isEmpty() ? Collections.<String>emptyList() : Collections.singletonList(s); 252 components.add(component); 253 return components; 254 } 255 256 //["gender", {}, "text", null] 257 if (first.isNull()) { 258 List<List<String>> components = new ArrayList<>(1); 259 components.add(Collections.<String>emptyList()); 260 return components; 261 } 262 263 return Collections.emptyList(); 264 } 265 266 /** 267 * Gets the value of a multi-valued property (such as {@link Categories} ). 268 * @return the values or empty list if not found 269 */ 270 public List<String> asMulti() { 271 if (values.isEmpty()) { 272 return Collections.emptyList(); 273 } 274 275 List<String> multi = new ArrayList<>(values.size()); 276 for (JsonValue value : values) { 277 if (value.isNull()) { 278 multi.add(""); 279 continue; 280 } 281 282 Object obj = value.getValue(); 283 if (obj != null) { 284 multi.add(obj.toString()); 285 continue; 286 } 287 } 288 return multi; 289 } 290 291 @Override 292 public boolean equals(Object o) { 293 if (this == o) { 294 return true; 295 } 296 if (o == null || getClass() != o.getClass()) { 297 return false; 298 } 299 300 JCardValue that = (JCardValue) o; 301 302 if (values != null ? !values.equals(that.values) : that.values != null) { 303 return false; 304 } 305 306 return true; 307 } 308 309 @Override 310 public int hashCode() { 311 return values != null ? values.hashCode() : 0; 312 } 313}