001    package ezvcard.types;
002    
003    import java.util.List;
004    import java.util.regex.Matcher;
005    import java.util.regex.Pattern;
006    
007    import ezvcard.VCard;
008    import ezvcard.VCardSubTypes;
009    import ezvcard.VCardVersion;
010    import ezvcard.io.CompatibilityMode;
011    import ezvcard.parameters.TelephoneTypeParameter;
012    import ezvcard.parameters.ValueParameter;
013    import ezvcard.util.HCardElement;
014    import ezvcard.util.VCardStringUtils;
015    import ezvcard.util.XCardElement;
016    
017    /*
018     Copyright (c) 2012, Michael Angstadt
019     All rights reserved.
020    
021     Redistribution and use in source and binary forms, with or without
022     modification, are permitted provided that the following conditions are met: 
023    
024     1. Redistributions of source code must retain the above copyright notice, this
025     list of conditions and the following disclaimer. 
026     2. Redistributions in binary form must reproduce the above copyright notice,
027     this list of conditions and the following disclaimer in the documentation
028     and/or other materials provided with the distribution. 
029    
030     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
031     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
032     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
033     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
034     ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
035     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
036     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
037     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
038     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
039     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
040    
041     The views and conclusions contained in the software and documentation are those
042     of the authors and should not be interpreted as representing official policies, 
043     either expressed or implied, of the FreeBSD Project.
044     */
045    
046    /**
047     * A telephone number.
048     * 
049     * <pre>
050     * VCard vcard = new VCard();
051     * TelephoneType tel = new TelephoneType(&quot;+1 123-555-6789&quot;);
052     * tel.addType(TelephoneTypeParameter.HOME);
053     * tel.setPref(2); //the second-most preferred
054     * vcard.addTelephoneNumber(tel);
055     * tel = new TelephoneType(&quot;+1 800-555-9876;ext=111&quot;);
056     * tel.addType(TelephoneTypeParameter.WORK);
057     * tel.setPref(1); //the most preferred
058     * vcard.addTelephoneNumber(tel);
059     * </pre>
060     * 
061     * <p>
062     * vCard property name: TEL
063     * </p>
064     * <p>
065     * vCard versions: 2.1, 3.0, 4.0
066     * </p>
067     * @author Michael Angstadt
068     */
069    public class TelephoneType extends MultiValuedTypeParameterType<TelephoneTypeParameter> {
070            public static final String NAME = "TEL";
071    
072            private String value;
073    
074            public TelephoneType() {
075                    this(null);
076            }
077    
078            /**
079             * @param telNumber the telephone number
080             */
081            public TelephoneType(String telNumber) {
082                    super(NAME);
083                    this.value = telNumber;
084            }
085    
086            /**
087             * Gets the telephone number.
088             * @return the telephone number
089             */
090            public String getValue() {
091                    return value;
092            }
093    
094            /**
095             * Sets the telephone number.
096             * @param value the telephone number
097             */
098            public void setValue(String value) {
099                    this.value = value;
100            }
101    
102            /**
103             * Gets all PID parameter values.
104             * <p>
105             * vCard versions: 4.0
106             * </p>
107             * @return the PID values or empty set if there are none
108             * @see VCardSubTypes#getPids
109             */
110            public List<Integer[]> getPids() {
111                    return subTypes.getPids();
112            }
113    
114            /**
115             * Adds a PID value.
116             * <p>
117             * vCard versions: 4.0
118             * </p>
119             * @param localId the local ID
120             * @param clientPidMapRef the ID used to reference the property's globally
121             * unique identifier in the CLIENTPIDMAP property.
122             * @see VCardSubTypes#addPid(int, int)
123             */
124            public void addPid(int localId, int clientPidMapRef) {
125                    subTypes.addPid(localId, clientPidMapRef);
126            }
127    
128            /**
129             * Removes all PID values.
130             * <p>
131             * vCard versions: 4.0
132             * </p>
133             * @see VCardSubTypes#removePids
134             */
135            public void removePids() {
136                    subTypes.removePids();
137            }
138    
139            /**
140             * Gets the preference value.
141             * <p>
142             * vCard versions: 4.0
143             * </p>
144             * @return the preference value or null if it doesn't exist
145             * @see VCardSubTypes#getPref
146             */
147            public Integer getPref() {
148                    return subTypes.getPref();
149            }
150    
151            /**
152             * Sets the preference value.
153             * <p>
154             * vCard versions: 4.0
155             * </p>
156             * @param pref the preference value or null to remove
157             * @see VCardSubTypes#setPref
158             */
159            public void setPref(Integer pref) {
160                    subTypes.setPref(pref);
161            }
162    
163            /**
164             * Gets the ALTID.
165             * <p>
166             * vCard versions: 4.0
167             * </p>
168             * @return the ALTID or null if it doesn't exist
169             * @see VCardSubTypes#getAltId
170             */
171            public String getAltId() {
172                    return subTypes.getAltId();
173            }
174    
175            /**
176             * Sets the ALTID.
177             * <p>
178             * vCard versions: 4.0
179             * </p>
180             * @param altId the ALTID or null to remove
181             * @see VCardSubTypes#setAltId
182             */
183            public void setAltId(String altId) {
184                    subTypes.setAltId(altId);
185            }
186    
187            @Override
188            protected TelephoneTypeParameter buildTypeObj(String type) {
189                    TelephoneTypeParameter param = TelephoneTypeParameter.valueOf(type);
190                    if (param == null) {
191                            param = new TelephoneTypeParameter(type);
192                    }
193                    return param;
194            }
195    
196            @Override
197            protected void doMarshalSubTypes(VCardSubTypes copy, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode, VCard vcard) {
198                    if (version == VCardVersion.V4_0) {
199                            copy.setValue(ValueParameter.URI);
200                    } else {
201                            copy.setValue(null);
202                    }
203    
204                    //replace "TYPE=pref" with "PREF=1"
205                    if (version == VCardVersion.V4_0) {
206                            if (getTypes().contains(TelephoneTypeParameter.PREF)) {
207                                    copy.removeType(TelephoneTypeParameter.PREF.getValue());
208                                    copy.setPref(1);
209                            }
210                    } else {
211                            copy.setPref(null);
212    
213                            //find the TEL with the lowest PREF value in the vCard
214                            TelephoneType mostPreferred = null;
215                            for (TelephoneType tel : vcard.getTelephoneNumbers()) {
216                                    Integer pref = tel.getPref();
217                                    if (pref != null) {
218                                            if (mostPreferred == null || pref < mostPreferred.getPref()) {
219                                                    mostPreferred = tel;
220                                            }
221                                    }
222                            }
223                            if (this == mostPreferred) {
224                                    copy.addType(TelephoneTypeParameter.PREF.getValue());
225                            }
226                    }
227            }
228    
229            @Override
230            protected void doMarshalText(StringBuilder sb, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
231                    String value = writeValue(version);
232                    sb.append(VCardStringUtils.escape(value));
233            }
234    
235            @Override
236            protected void doUnmarshalText(String value, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
237                    value = VCardStringUtils.unescape(value.trim());
238                    parseValue(value);
239            }
240    
241            @Override
242            protected void doMarshalXml(XCardElement parent, List<String> warnings, CompatibilityMode compatibilityMode) {
243                    String value = writeValue(parent.version());
244                    parent.uri(value);
245            }
246    
247            @Override
248            protected void doUnmarshalXml(XCardElement element, List<String> warnings, CompatibilityMode compatibilityMode) {
249                    String value = element.get("text", "uri");
250                    if (value != null) {
251                            parseValue(value);
252                    }
253            }
254    
255            @Override
256            protected void doUnmarshalHtml(HCardElement element, List<String> warnings) {
257                    List<String> types = element.types();
258                    for (String type : types) {
259                            subTypes.addType(type);
260                    }
261    
262                    String tel = null;
263                    String href = element.attr("href");
264                    if (href.length() > 0) {
265                            Pattern p = Pattern.compile("^tel:(.*?)$", Pattern.CASE_INSENSITIVE);
266                            Matcher m = p.matcher(href);
267                            if (m.find()) {
268                                    tel = m.group(1);
269                            }
270                    }
271                    if (tel == null) {
272                            tel = element.value();
273                    }
274                    setValue(tel);
275            }
276    
277            private void parseValue(String value) {
278                    if (value.matches("(?i)tel:.*")) {
279                            //remove "tel:"
280                            value = (value.length() > 4) ? value.substring(4) : "";
281                    }
282                    setValue(value);
283            }
284    
285            private String writeValue(VCardVersion version) {
286                    String value = this.value;
287                    if (version == VCardVersion.V4_0) {
288                            value = "tel:" + value;
289                    }
290                    return value;
291            }
292    }