001    package ezvcard.types;
002    
003    import java.util.ArrayList;
004    import java.util.Arrays;
005    import java.util.List;
006    
007    import ezvcard.VCardSubTypes;
008    import ezvcard.VCardVersion;
009    import ezvcard.io.CompatibilityMode;
010    import ezvcard.util.HCardElement;
011    import ezvcard.util.VCardStringUtils;
012    import ezvcard.util.XCardElement;
013    
014    /*
015     Copyright (c) 2012, Michael Angstadt
016     All rights reserved.
017    
018     Redistribution and use in source and binary forms, with or without
019     modification, are permitted provided that the following conditions are met: 
020    
021     1. Redistributions of source code must retain the above copyright notice, this
022     list of conditions and the following disclaimer. 
023     2. Redistributions in binary form must reproduce the above copyright notice,
024     this list of conditions and the following disclaimer in the documentation
025     and/or other materials provided with the distribution. 
026    
027     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
028     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
029     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
030     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
031     ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
032     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
033     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
034     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
035     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
036     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037    
038     The views and conclusions contained in the software and documentation are those
039     of the authors and should not be interpreted as representing official policies, 
040     either expressed or implied, of the FreeBSD Project.
041     */
042    
043    /**
044     * <p>
045     * Contains the separated components of the person's name.
046     * </p>
047     * 
048     * <p>
049     * Multiple instances of this type can be added ONLY if each instance has an
050     * ALTID parameter and the value of the ALTID parameter is the same across all
051     * instances. However, this is a border-case; under most circumstances, you will
052     * only need to add one instance.
053     * </p>
054     * 
055     * <pre>
056     * VCard vcard = new VCard();
057     * StructuredNameType n = new StructuredNameType();
058     * n.setFamily(&quot;House&quot;);
059     * n.setGiven(&quot;Gregory&quot;);
060     * n.addPrefix(&quot;Dr&quot;);
061     * n.addSuffix(&quot;MD&quot;);
062     * vcard.setStructuredName(n);
063     * </pre>
064     * 
065     * <p>
066     * vCard property name: N
067     * </p>
068     * <p>
069     * vCard versions: 2.1, 3.0, 4.0
070     * </p>
071     * @author Michael Angstadt
072     */
073    public class StructuredNameType extends VCardType {
074            public static final String NAME = "N";
075    
076            private String family;
077            private String given;
078            private List<String> additional = new ArrayList<String>();
079            private List<String> prefixes = new ArrayList<String>();
080            private List<String> suffixes = new ArrayList<String>();
081    
082            public StructuredNameType() {
083                    super(NAME);
084            }
085    
086            /**
087             * Gets the family name (aka "last name").
088             * @return the family name or null if not set
089             */
090            public String getFamily() {
091                    return family;
092            }
093    
094            /**
095             * Sets the family name (aka "last name").
096             * @param family the family name or null to remove
097             */
098            public void setFamily(String family) {
099                    this.family = family;
100            }
101    
102            /**
103             * Gets the given name (aka "first name").
104             * @return the given name or null if not set
105             */
106            public String getGiven() {
107                    return given;
108            }
109    
110            /**
111             * Sets the given name (aka "first name").
112             * @param given the given name or null to remove
113             */
114            public void setGiven(String given) {
115                    this.given = given;
116            }
117    
118            /**
119             * Gets any additional names the person goes by.
120             * @return the additional names or empty list if there are none
121             */
122            public List<String> getAdditional() {
123                    return additional;
124            }
125    
126            /**
127             * Adds an additional name the person goes by.
128             * @param additional the additional name to add
129             */
130            public void addAdditional(String additional) {
131                    this.additional.add(additional);
132            }
133    
134            /**
135             * Gets the prefixes.
136             * @return the prefixes (e.g. "Mr.") or empty list if there are none
137             */
138            public List<String> getPrefixes() {
139                    return prefixes;
140            }
141    
142            /**
143             * Adds a prefix.
144             * @param prefix the prefix to add (e.g. "Mr.")
145             */
146            public void addPrefix(String prefix) {
147                    this.prefixes.add(prefix);
148            }
149    
150            /**
151             * Gets the suffixes.
152             * @return the suffixes (e.g. "Jr.") or empty list if there are none
153             */
154            public List<String> getSuffixes() {
155                    return suffixes;
156            }
157    
158            /**
159             * Adds a suffix.
160             * @param suffix the suffix to add (e.g. "Jr.")
161             */
162            public void addSuffix(String suffix) {
163                    this.suffixes.add(suffix);
164            }
165    
166            /**
167             * Gets the string(s) that define how to sort the vCard.
168             * <p>
169             * 2.1 and 3.0 vCards should use the {@link SortStringType SORT-STRING}
170             * property instead.
171             * </p>
172             * <p>
173             * vCard versions: 4.0
174             * </p>
175             * @return the sort string(s) (e.g. ["Aboville", "Christine"] if the family
176             * name is "d'Aboville" and the given name is "Christine") or empty list if
177             * there are none
178             * @see VCardSubTypes#getSortAs
179             */
180            public List<String> getSortAs() {
181                    return subTypes.getSortAs();
182            }
183    
184            /**
185             * Sets the string that defines how to sort the vCard.
186             * <p>
187             * 2.1 and 3.0 vCards should use the {@link SortStringType SORT-STRING}
188             * property instead.
189             * </p>
190             * <p>
191             * vCard versions: 4.0
192             * </p>
193             * @param family the sorttable family name (e.g. "Adboville" if the family
194             * name is "d'Aboville") or null to remove
195             */
196            public void setSortAs(String family) {
197                    if (family == null) {
198                            subTypes.setSortAs();
199                    } else {
200                            subTypes.setSortAs(family);
201                    }
202            }
203    
204            /**
205             * Sets the strings that define how to sort the vCard.
206             * <p>
207             * 2.1 and 3.0 vCards should use the {@link SortStringType SORT-STRING}
208             * property instead.
209             * </p>
210             * <p>
211             * vCard versions: 4.0
212             * </p>
213             * @param family the sortable family name (e.g. "Adboville" if the family
214             * name is "d'Aboville")
215             * @param given the sortable given name
216             */
217            public void setSortAs(String family, String given) {
218                    subTypes.setSortAs(family, given);
219            }
220    
221            /**
222             * Gets the language the name is written in.
223             * @return the language or null if not set
224             * @see VCardSubTypes#getLanguage
225             */
226            public String getLanguage() {
227                    return subTypes.getLanguage();
228            }
229    
230            /**
231             * Sets the language the name is written in.
232             * @param language the language or null to remove
233             * @see VCardSubTypes#setLanguage
234             */
235            public void setLanguage(String language) {
236                    subTypes.setLanguage(language);
237            }
238    
239            /**
240             * Gets the ALTID.
241             * <p>
242             * vCard versions: 4.0
243             * </p>
244             * @return the ALTID or null if it doesn't exist
245             * @see VCardSubTypes#getAltId
246             */
247            public String getAltId() {
248                    return subTypes.getAltId();
249            }
250    
251            /**
252             * Sets the ALTID.
253             * <p>
254             * vCard versions: 4.0
255             * </p>
256             * @param altId the ALTID or null to remove
257             * @see VCardSubTypes#setAltId
258             */
259            public void setAltId(String altId) {
260                    subTypes.setAltId(altId);
261            }
262    
263            @Override
264            protected void doMarshalText(StringBuilder value, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
265                    if (family != null) {
266                            value.append(VCardStringUtils.escape(family));
267                    }
268    
269                    value.append(';');
270                    if (given != null) {
271                            value.append(VCardStringUtils.escape(given));
272                    }
273    
274                    value.append(';');
275                    if (!additional.isEmpty()) {
276                            for (String s : additional) {
277                                    value.append(VCardStringUtils.escape(s)).append(',');
278                            }
279                            value.deleteCharAt(value.length() - 1);
280                    }
281    
282                    value.append(';');
283                    if (!prefixes.isEmpty()) {
284                            for (String s : prefixes) {
285                                    value.append(VCardStringUtils.escape(s)).append(',');
286                            }
287                            value.deleteCharAt(value.length() - 1);
288                    }
289    
290                    value.append(';');
291                    if (!suffixes.isEmpty()) {
292                            for (String s : suffixes) {
293                                    value.append(VCardStringUtils.escape(s)).append(',');
294                            }
295                            value.deleteCharAt(value.length() - 1);
296                    }
297            }
298    
299            @Override
300            protected void doUnmarshalText(String value, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
301                    //preserve empty items and don't unescape escaped characters(e.g. "additional" might have escaped commas)
302                    String split[] = VCardStringUtils.splitBy(value, ';', false, false);
303    
304                    int i = 0;
305    
306                    family = (split.length > i && split[i].length() > 0) ? VCardStringUtils.unescape(split[i]) : null;
307                    i++;
308    
309                    given = (split.length > i && split[i].length() > 0) ? VCardStringUtils.unescape(split[i]) : null;
310                    i++;
311    
312                    if (split.length > i && split[i].length() > 0) {
313                            additional = new ArrayList<String>(Arrays.asList(VCardStringUtils.splitBy(split[i], ',', true, true)));
314                    } else {
315                            additional = new ArrayList<String>();
316                    }
317                    i++;
318    
319                    if (split.length > i && split[i].length() > 0) {
320                            prefixes = new ArrayList<String>(Arrays.asList(VCardStringUtils.splitBy(split[i], ',', true, true)));
321                    } else {
322                            prefixes = new ArrayList<String>();
323                    }
324                    i++;
325    
326                    if (split.length > i && split[i].length() > 0) {
327                            suffixes = new ArrayList<String>(Arrays.asList(VCardStringUtils.splitBy(split[i], ',', true, true)));
328                    } else {
329                            suffixes = new ArrayList<String>();
330                    }
331                    i++;
332            }
333    
334            @Override
335            protected void doMarshalXml(XCardElement parent, List<String> warnings, CompatibilityMode compatibilityMode) {
336                    if (family != null) {
337                            parent.append("surname", family);
338                    }
339                    if (given != null) {
340                            parent.append("given", given);
341                    }
342                    parent.append("additional", additional);
343                    parent.append("prefix", prefixes);
344                    parent.append("suffix", suffixes);
345            }
346    
347            @Override
348            protected void doUnmarshalXml(XCardElement element, List<String> warnings, CompatibilityMode compatibilityMode) {
349                    family = element.get("surname");
350                    given = element.get("given");
351    
352                    additional.clear();
353                    additional.addAll(element.getAll("additional"));
354    
355                    prefixes.clear();
356                    prefixes.addAll(element.getAll("prefix"));
357    
358                    suffixes.clear();
359                    suffixes.addAll(element.getAll("suffix"));
360            }
361    
362            @Override
363            protected void doUnmarshalHtml(HCardElement element, List<String> warnings) {
364                    family = element.firstValue("family-name");
365                    given = element.firstValue("given-name");
366    
367                    additional.clear();
368                    additional.addAll(element.allValues("additional-name"));
369    
370                    prefixes.clear();
371                    prefixes.addAll(element.allValues("honorific-prefix"));
372    
373                    suffixes.clear();
374                    suffixes.addAll(element.allValues("honorific-suffix"));
375            }
376    }