001    package ezvcard.types;
002    
003    import java.util.List;
004    
005    import ezvcard.VCard;
006    import ezvcard.VCardSubTypes;
007    import ezvcard.VCardVersion;
008    import ezvcard.io.CompatibilityMode;
009    import ezvcard.parameters.AddressTypeParameter;
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     * A mailing address.
045     * 
046     * <p>
047     * <b>Adding an address</b>
048     * </p>
049     * 
050     * <pre>
051     * VCard vcard = new VCard();
052     * AddressType adr = new AddressType();
053     * adr.setStreetAddress(&quot;123 Main St.&quot;);
054     * adr.setLocality(&quot;Austin&quot;);
055     * adr.setRegion(&quot;TX&quot;);
056     * adr.setPostalCode(&quot;12345&quot;);
057     * adr.setCountry(&quot;USA&quot;);
058     * adr.addType(AddressTypeParameter.WORK);
059     * adr.addType(AddressTypeParameter.DOM);
060     * 
061     * //optionally, provide the exact text to print out on the mailing label
062     * adr.setLabel(&quot;123 Main St.\nAustin, Tx 12345\nUSA&quot;);
063     * 
064     * vcard.addAddress(adr);
065     * </pre>
066     * 
067     * <p>
068     * <b>Getting the addresses</b>
069     * </p>
070     * 
071     * <pre>
072     * VCard vcard = ...
073     * for (AddressType adr : vcard.getAddresses()){
074     *   ...
075     * }
076     * </pre>
077     * 
078     * <p>
079     * vCard property name: ADR
080     * </p>
081     * <p>
082     * vCard versions: 2.1, 3.0, 4.0
083     * </p>
084     * @author Michael Angstadt
085     */
086    public class AddressType extends MultiValuedTypeParameterType<AddressTypeParameter> {
087            public static final String NAME = "ADR";
088    
089            private String poBox;
090            private String extendedAddress;
091            private String streetAddress;
092            private String locality;
093            private String region;
094            private String postalCode;
095            private String country;
096    
097            public AddressType() {
098                    super(NAME);
099            }
100    
101            @Override
102            protected AddressTypeParameter buildTypeObj(String type) {
103                    AddressTypeParameter param = AddressTypeParameter.valueOf(type);
104                    if (param == null) {
105                            param = new AddressTypeParameter(type);
106                    }
107                    return param;
108            }
109    
110            /**
111             * Gets the P.O. (post office) box.
112             * @return the P.O. box or null if not set
113             */
114            public String getPoBox() {
115                    return poBox;
116            }
117    
118            /**
119             * Sets the P.O. (post office) box.
120             * @param poBox the P.O. box or null to remove
121             */
122            public void setPoBox(String poBox) {
123                    this.poBox = poBox;
124            }
125    
126            /**
127             * Gets the extended address.
128             * @return the extended address (e.g. "Suite 200") or null if not set
129             */
130            public String getExtendedAddress() {
131                    return extendedAddress;
132            }
133    
134            /**
135             * Sets the extended address.
136             * @param extendedAddress the extended address (e.g. "Suite 200") or null to
137             * remove
138             */
139            public void setExtendedAddress(String extendedAddress) {
140                    this.extendedAddress = extendedAddress;
141            }
142    
143            /**
144             * Gets the street address
145             * @return the street address (e.g. "123 Main St")
146             */
147            public String getStreetAddress() {
148                    return streetAddress;
149            }
150    
151            /**
152             * Sets the street address.
153             * @param streetAddress the street address (e.g. "123 Main St") or null to
154             * remove
155             */
156            public void setStreetAddress(String streetAddress) {
157                    this.streetAddress = streetAddress;
158            }
159    
160            /**
161             * Gets the locality (city)
162             * @return the locality (e.g. "Boston") or null if not set
163             */
164            public String getLocality() {
165                    return locality;
166            }
167    
168            /**
169             * Sets the locality (city).
170             * @param locality the locality or null to remove
171             */
172            public void setLocality(String locality) {
173                    this.locality = locality;
174            }
175    
176            /**
177             * Gets the region.
178             * @return the region (e.g. "Texas") or null if not set
179             */
180            public String getRegion() {
181                    return region;
182            }
183    
184            /**
185             * Sets the region.
186             * @param region the region (e.g. "Texas") or null to remove
187             */
188            public void setRegion(String region) {
189                    this.region = region;
190            }
191    
192            /**
193             * Gets the postal code.
194             * @return the postal code (e.g. "90210") or null if not set
195             */
196            public String getPostalCode() {
197                    return postalCode;
198            }
199    
200            /**
201             * Sets the postal code.
202             * @param postalCode the postal code (e.g. "90210") or null to remove
203             */
204            public void setPostalCode(String postalCode) {
205                    this.postalCode = postalCode;
206            }
207    
208            /**
209             * Gets the country.
210             * @return the country (e.g. "USA") or null if not set
211             */
212            public String getCountry() {
213                    return country;
214            }
215    
216            /**
217             * Sets the country.
218             * @param country the country (e.g. "USA") or null to remove
219             */
220            public void setCountry(String country) {
221                    this.country = country;
222            }
223    
224            /**
225             * Gets the language that the address is written in.
226             * @return the language or null if not set
227             * @see VCardSubTypes#getLanguage
228             */
229            public String getLanguage() {
230                    return subTypes.getLanguage();
231            }
232    
233            /**
234             * Sets the language that the address is written in.
235             * @param language the language or null to remove
236             * @see VCardSubTypes#setLanguage
237             */
238            public void setLanguage(String language) {
239                    subTypes.setLanguage(language);
240            }
241    
242            /**
243             * Gets the label of the address.
244             * @return the label or null if it doesn't have one
245             */
246            public String getLabel() {
247                    return subTypes.getFirst("LABEL");
248            }
249    
250            /**
251             * Sets the label of the address.
252             * @param label the label or null to remove
253             */
254            public void setLabel(String label) {
255                    subTypes.replace("LABEL", label);
256            }
257    
258            /**
259             * Gets the global positioning coordinates that are associated with this
260             * address.
261             * <p>
262             * vCard versions: 4.0
263             * </p>
264             * @return the latitude (index 0) and longitude (index 1) or null if not set
265             * or null if the parameter value was in an incorrect format
266             * @see VCardSubTypes#getGeo
267             */
268            public double[] getGeo() {
269                    return subTypes.getGeo();
270            }
271    
272            /**
273             * Sets the global positioning coordinates that are associated with this
274             * address.
275             * <p>
276             * vCard versions: 4.0
277             * </p>
278             * @param latitude the latitude
279             * @param longitude the longitude
280             * @see VCardSubTypes#setGeo
281             */
282            public void setGeo(double latitude, double longitude) {
283                    subTypes.setGeo(latitude, longitude);
284            }
285    
286            /**
287             * Gets all PID parameter values.
288             * <p>
289             * vCard versions: 4.0
290             * </p>
291             * @return the PID values or empty set if there are none
292             * @see VCardSubTypes#getPids
293             */
294            public List<Integer[]> getPids() {
295                    return subTypes.getPids();
296            }
297    
298            /**
299             * Adds a PID value.
300             * <p>
301             * vCard versions: 4.0
302             * </p>
303             * @param localId the local ID
304             * @param clientPidMapRef the ID used to reference the property's globally
305             * unique identifier in the CLIENTPIDMAP property.
306             * @see VCardSubTypes#addPid(int, int)
307             */
308            public void addPid(int localId, int clientPidMapRef) {
309                    subTypes.addPid(localId, clientPidMapRef);
310            }
311    
312            /**
313             * Removes all PID values.
314             * <p>
315             * vCard versions: 4.0
316             * </p>
317             * @see VCardSubTypes#removePids
318             */
319            public void removePids() {
320                    subTypes.removePids();
321            }
322    
323            /**
324             * Gets the preference value.
325             * <p>
326             * vCard versions: 4.0
327             * </p>
328             * @return the preference value or null if it doesn't exist
329             * @see VCardSubTypes#getPref
330             */
331            public Integer getPref() {
332                    return subTypes.getPref();
333            }
334    
335            /**
336             * Sets the preference value.
337             * <p>
338             * vCard versions: 4.0
339             * </p>
340             * @param pref the preference value or null to remove
341             * @see VCardSubTypes#setPref
342             */
343            public void setPref(Integer pref) {
344                    subTypes.setPref(pref);
345            }
346    
347            /**
348             * Gets the ALTID.
349             * <p>
350             * vCard versions: 4.0
351             * </p>
352             * @return the ALTID or null if it doesn't exist
353             * @see VCardSubTypes#getAltId
354             */
355            public String getAltId() {
356                    return subTypes.getAltId();
357            }
358    
359            /**
360             * Sets the ALTID.
361             * <p>
362             * vCard versions: 4.0
363             * </p>
364             * @param altId the ALTID or null to remove
365             * @see VCardSubTypes#setAltId
366             */
367            public void setAltId(String altId) {
368                    subTypes.setAltId(altId);
369            }
370    
371            /**
372             * Gets the timezone that's associated with this address.
373             * <p>
374             * vCard versions: 4.0
375             * </p>
376             * @return the timezone (e.g. "America/New_York") or null if it doesn't
377             * exist
378             */
379            public String getTimezone() {
380                    String value = subTypes.getFirst("TZ");
381                    if (value.matches("(?i)tz:.*")) {
382                            //remove the "tz:"
383                            value = (value.length() > 3) ? value.substring(3) : "";
384                    }
385                    return value;
386            }
387    
388            /**
389             * Sets the timezone that's associated with this address.
390             * <p>
391             * vCard versions: 4.0
392             * </p>
393             * @param timezone the timezone (e.g. "America/New_York") or null to remove
394             */
395            public void setTimezone(String timezone) {
396                    subTypes.replace("TZ", "tz:" + timezone);
397            }
398    
399            @Override
400            protected void doMarshalSubTypes(VCardSubTypes copy, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode, VCard vcard) {
401                    //replace "TYPE=pref" with "PREF=1"
402                    if (version == VCardVersion.V4_0) {
403                            if (getTypes().contains(AddressTypeParameter.PREF)) {
404                                    copy.removeType(AddressTypeParameter.PREF.getValue());
405                                    copy.setPref(1);
406                            }
407                    } else {
408                            copy.setPref(null);
409    
410                            //find the ADR with the lowest PREF value in the vCard
411                            AddressType mostPreferred = null;
412                            for (AddressType adr : vcard.getAddresses()) {
413                                    Integer pref = adr.getPref();
414                                    if (pref != null) {
415                                            if (mostPreferred == null || pref < mostPreferred.getPref()) {
416                                                    mostPreferred = adr;
417                                            }
418                                    }
419                            }
420                            if (this == mostPreferred) {
421                                    copy.addType(AddressTypeParameter.PREF.getValue());
422                            }
423                    }
424    
425                    //remove the LABEL parameter
426                    if (version != VCardVersion.V4_0) {
427                            copy.removeAll("LABEL");
428                    }
429            }
430    
431            @Override
432            protected void doMarshalText(StringBuilder sb, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
433                    if (poBox != null) {
434                            sb.append(VCardStringUtils.escape(poBox));
435                    }
436                    sb.append(';');
437    
438                    if (extendedAddress != null) {
439                            sb.append(VCardStringUtils.escape(extendedAddress));
440                    }
441                    sb.append(';');
442    
443                    if (streetAddress != null) {
444                            sb.append(VCardStringUtils.escape(streetAddress));
445                    }
446                    sb.append(';');
447    
448                    if (locality != null) {
449                            sb.append(VCardStringUtils.escape(locality));
450                    }
451                    sb.append(';');
452    
453                    if (region != null) {
454                            sb.append(VCardStringUtils.escape(region));
455                    }
456                    sb.append(';');
457    
458                    if (postalCode != null) {
459                            sb.append(VCardStringUtils.escape(postalCode));
460                    }
461                    sb.append(';');
462    
463                    if (country != null) {
464                            sb.append(VCardStringUtils.escape(country));
465                    }
466            }
467    
468            @Override
469            protected void doUnmarshalText(String value, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
470                    String split[] = VCardStringUtils.splitBy(value, ';', false, true);
471    
472                    int i = 0;
473    
474                    poBox = (split.length > i && split[i].length() > 0) ? split[i] : null;
475                    i++;
476    
477                    extendedAddress = (split.length > i && split[i].length() > 0) ? split[i] : null;
478                    i++;
479    
480                    streetAddress = (split.length > i && split[i].length() > 0) ? split[i] : null;
481                    i++;
482    
483                    locality = (split.length > i && split[i].length() > 0) ? split[i] : null;
484                    i++;
485    
486                    region = (split.length > i && split[i].length() > 0) ? split[i] : null;
487                    i++;
488    
489                    postalCode = (split.length > i && split[i].length() > 0) ? split[i] : null;
490                    i++;
491    
492                    country = (split.length > i && split[i].length() > 0) ? split[i] : null;
493            }
494    
495            @Override
496            protected void doMarshalXml(XCardElement parent, List<String> warnings, CompatibilityMode compatibilityMode) {
497                    if (poBox != null) {
498                            parent.append("pobox", poBox);
499                    }
500                    if (extendedAddress != null) {
501                            parent.append("ext", extendedAddress);
502                    }
503                    if (streetAddress != null) {
504                            parent.append("street", streetAddress);
505                    }
506                    if (locality != null) {
507                            parent.append("locality", locality);
508                    }
509                    if (region != null) {
510                            parent.append("region", region);
511                    }
512                    if (postalCode != null) {
513                            parent.append("code", postalCode);
514                    }
515                    if (country != null) {
516                            parent.append("country", country);
517                    }
518            }
519    
520            @Override
521            protected void doUnmarshalXml(XCardElement element, List<String> warnings, CompatibilityMode compatibilityMode) {
522                    poBox = element.get("pobox");
523                    extendedAddress = element.get("ext");
524                    streetAddress = element.get("street");
525                    locality = element.get("locality");
526                    region = element.get("region");
527                    postalCode = element.get("code");
528                    country = element.get("country");
529            }
530    
531            @Override
532            protected void doUnmarshalHtml(HCardElement element, List<String> warnings) {
533                    poBox = element.firstValue("post-office-box");
534                    extendedAddress = element.firstValue("extended-address");
535                    streetAddress = element.firstValue("street-address");
536                    locality = element.firstValue("locality");
537                    region = element.firstValue("region");
538                    postalCode = element.firstValue("postal-code");
539                    country = element.firstValue("country-name");
540                    List<String> types = element.types();
541                    for (String type : types) {
542                            subTypes.addType(type);
543                    }
544            }
545    }