001    package ezvcard.io.json;
002    
003    import java.util.ArrayList;
004    import java.util.Arrays;
005    import java.util.Collections;
006    import java.util.List;
007    
008    import ezvcard.property.Categories;
009    import ezvcard.property.Note;
010    import ezvcard.property.StructuredName;
011    
012    /*
013     Copyright (c) 2013, 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     */
045    public 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<JsonValue>(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<List<?>>(values.length);
109                    for (Object value : values) {
110                            List<?> list = (value instanceof List) ? (List<?>) value : Arrays.asList(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<JsonValue>(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<JsonValue>(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
193             * {@link StructuredName}).
194             * @return the values or empty list if not found
195             */
196            public List<List<String>> asStructured() {
197                    if (values.isEmpty()) {
198                            return Collections.emptyList();
199                    }
200    
201                    JsonValue first = values.get(0);
202    
203                    //["gender", {}, "text", ["M", "text"] ]
204                    List<JsonValue> array = first.getArray();
205                    if (array != null) {
206                            List<List<String>> valuesStr = new ArrayList<List<String>>(array.size());
207                            for (JsonValue value : array) {
208                                    if (value.isNull()) {
209                                            valuesStr.add(Arrays.asList(""));
210                                            continue;
211                                    }
212    
213                                    Object obj = value.getValue();
214                                    if (obj != null) {
215                                            valuesStr.add(Arrays.asList(obj.toString()));
216                                            continue;
217                                    }
218    
219                                    List<JsonValue> subArray = value.getArray();
220                                    if (subArray != null) {
221                                            List<String> subValuesStr = new ArrayList<String>(subArray.size());
222                                            for (JsonValue subArrayValue : subArray) {
223                                                    if (subArrayValue.isNull()) {
224                                                            subValuesStr.add("");
225                                                            continue;
226                                                    }
227    
228                                                    obj = subArrayValue.getValue();
229                                                    if (obj != null) {
230                                                            subValuesStr.add(obj.toString());
231                                                            continue;
232                                                    }
233                                            }
234                                            valuesStr.add(subValuesStr);
235                                    }
236                            }
237                            return valuesStr;
238                    }
239    
240                    //get the first value if it's not enclosed in an array
241                    //["gender", {}, "text", "M"]
242                    Object obj = first.getValue();
243                    if (obj != null) {
244                            List<List<String>> values = new ArrayList<List<String>>(1);
245                            values.add(Arrays.asList(obj.toString()));
246                            return values;
247                    }
248    
249                    //["gender", {}, "text", null]
250                    if (first.isNull()) {
251                            List<List<String>> values = new ArrayList<List<String>>(1);
252                            values.add(Arrays.asList(""));
253                            return values;
254                    }
255    
256                    return Collections.emptyList();
257            }
258    
259            /**
260             * Gets the value of a multi-valued property (such as {@link Categories}
261             * ).
262             * @return the values or empty list if not found
263             */
264            public List<String> asMulti() {
265                    if (values.isEmpty()) {
266                            return Collections.emptyList();
267                    }
268    
269                    List<String> multi = new ArrayList<String>(values.size());
270                    for (JsonValue value : values) {
271                            if (value.isNull()) {
272                                    multi.add("");
273                                    continue;
274                            }
275    
276                            Object obj = value.getValue();
277                            if (obj != null) {
278                                    multi.add(obj.toString());
279                                    continue;
280                            }
281                    }
282                    return multi;
283            }
284    }