001    package ezvcard.property;
002    
003    import java.util.List;
004    import java.util.SimpleTimeZone;
005    import java.util.TimeZone;
006    
007    import ezvcard.VCard;
008    import ezvcard.VCardVersion;
009    import ezvcard.Warning;
010    import ezvcard.util.UtcOffset;
011    import ezvcard.util.VCardDateFormatter;
012    
013    /*
014     Copyright (c) 2013, Michael Angstadt
015     All rights reserved.
016    
017     Redistribution and use in source and binary forms, with or without
018     modification, are permitted provided that the following conditions are met: 
019    
020     1. Redistributions of source code must retain the above copyright notice, this
021     list of conditions and the following disclaimer. 
022     2. Redistributions in binary form must reproduce the above copyright notice,
023     this list of conditions and the following disclaimer in the documentation
024     and/or other materials provided with the distribution. 
025    
026     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
027     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
028     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
029     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
030     ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
031     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
032     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
033     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
034     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
035     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
036    
037     The views and conclusions contained in the software and documentation are those
038     of the authors and should not be interpreted as representing official policies, 
039     either expressed or implied, of the FreeBSD Project.
040     */
041    
042    /**
043     * Contains the timezone that the person lives/works in.
044     * 
045     * <p>
046     * <b>Code sample</b>
047     * </p>
048     * 
049     * <pre class="brush:java">
050     * VCard vcard = new VCard();
051     * Timezone tz = new Timezone(-5, 0, &quot;America/New_York&quot;);
052     * vcard.addTimezone(tz);
053     * </pre>
054     * 
055     * <p>
056     * <b>Property name:</b> {@code TZ}
057     * </p>
058     * <p>
059     * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0}
060     * </p>
061     * 
062     * @author Michael Angstadt
063     */
064    
065    //@formatter:off
066    /* 
067     * Parsing===================
068     * 
069     * vCard 2.1:
070     * Parse as UTC offset.  If invalid, throw CannotParseException.
071     * 
072     * vCard 3.0, hCard:
073     * VALUE=text:                  Treat as text
074     * No VALUE param:              Parse as UTC offset.  If invalid, add warning and treat as text.
075     * 
076     * vCard 4.0, jCard:
077     * VALUE=text:                  Treat as text
078     * VALUE=utc-offset:    Parse as UTC offset.  If invalid, throw CannotParseException
079     * VALUE=uri:                   Not going to support this, as there is no description of what a timezone URI looks like
080     * No VALUE param:              Parse as UTC offset.  If invalid, treat as text
081     * 
082     * xCard:
083     * text | utc-offset    | result
084     * no   | no                    | throw CannotParseException
085     * yes  | no                    | OK
086     * no   | yes                   | OK
087     * no   | invalid               | throw CannotParseException
088     * yes  | yes                   | Parse text
089     * yes  | invalid               | Parse text
090     * 
091     * Writing===================
092     * 
093     * vCard 2.1:
094     * text | utc-offset    | result
095     * no   | no                    | empty string (validation warning)
096     * no   | yes                   | Write UTC offset
097     * yes  | no                    | empty string (validation warning)
098     * yes  | yes                   | Write UTC offset
099     * 
100     * vCard 3.0:
101     * text | utc-offset    | result
102     * no   | no                    | empty string (validation warning)
103     * no   | yes                   | Write UTC offset
104     * yes  | no                    | Write text, add "VALUE=text" parameter
105     * yes  | yes                   | Write UTC offset
106     * 
107     * vCard 4.0, xCard, jCard:
108     * text | utc-offset    | result
109     * no   | no                    | empty string (validation warning)
110     * no   | yes                   | Write UTC offset, add "VALUE=utc-offset" parameter
111     * yes  | no                    | Write text
112     * yes  | yes                   | Write text
113     */
114    //@formatter:on
115    public class Timezone extends VCardProperty implements HasAltId {
116            private UtcOffset offset;
117            private String text;
118    
119            /**
120             * Creates a timezone property.
121             * @param text a free-form string representing the timezone, preferably a
122             * timezone ID from the <a
123             * href="http://en.wikipedia.org/wiki/List_of_tz_database_time_zones">Olson
124             * Database</a> (e.g. "America/New_York")
125             */
126            public Timezone(String text) {
127                    this(null, text);
128            }
129    
130            /**
131             * Creates a timezone property.
132             * @param hourOffset the hour component of the UTC offset (e.g. -5)
133             * @param minuteOffset the minute component of the UTC offset (e.g. 0)
134             */
135            public Timezone(Integer hourOffset, Integer minuteOffset) {
136                    this(new UtcOffset(hourOffset, minuteOffset));
137            }
138    
139            /**
140             * Creates a timezone property.
141             * @param offset the UTC offset
142             */
143            public Timezone(UtcOffset offset) {
144                    this(offset, null);
145            }
146    
147            /**
148             * Creates a timezone property.
149             * @param hourOffset the hour component of the UTC offset (e.g. -5)
150             * @param minuteOffset the minute component of the UTC offset (e.g. 0)
151             * @param text a free-form string representing the timezone, preferably a
152             * timezone ID from the <a
153             * href="http://en.wikipedia.org/wiki/List_of_tz_database_time_zones">Olson
154             * Database</a> (e.g. "America/New_York")
155             */
156            public Timezone(Integer hourOffset, Integer minuteOffset, String text) {
157                    this(new UtcOffset(hourOffset, minuteOffset), text);
158            }
159    
160            /**
161             * Creates a timezone property.
162             * @param offset the UTC offset
163             * @param text a free-form string representing the timezone, preferably a
164             * timezone ID from the <a
165             * href="http://en.wikipedia.org/wiki/List_of_tz_database_time_zones">Olson
166             * Database</a> (e.g. "America/New_York")
167             */
168            public Timezone(UtcOffset offset, String text) {
169                    setOffset(offset);
170                    setText(text);
171            }
172    
173            /**
174             * Creates a timezone property.
175             * @param timezone the timezone
176             */
177            public Timezone(TimeZone timezone) {
178                    this(UtcOffset.parse(timezone), timezone.getID());
179            }
180    
181            /**
182             * Gets the hour component of the UTC offset.
183             * @return the hour component of the UTC offset or null if not set
184             */
185            public Integer getHourOffset() {
186                    return (offset == null) ? null : offset.getHour();
187            }
188    
189            /**
190             * Gets the minute component of the UTC offset.
191             * @return the minute component of the UTC offset or null if not set
192             */
193            public Integer getMinuteOffset() {
194                    return (offset == null) ? null : offset.getMinute();
195            }
196    
197            /**
198             * Gets the UTC offset.
199             * @return the UTC offset or null if not set
200             */
201            public UtcOffset getOffset() {
202                    return offset;
203            }
204    
205            /**
206             * Sets the UTC offset.
207             * @param hourOffset the hour offset (e.g. -5)
208             * @param minuteOffset the minute offset (e.g. 0)
209             */
210            public void setOffset(int hourOffset, int minuteOffset) {
211                    setOffset(new UtcOffset(hourOffset, minuteOffset));
212            }
213    
214            /**
215             * Sets the UTC offset.
216             * @param offset the UTC offset
217             */
218            public void setOffset(UtcOffset offset) {
219                    this.offset = offset;
220            }
221    
222            /**
223             * Gets the text portion of the timezone.
224             * @return the free-form string representing the timezone, such as a
225             * timezone ID from the <a
226             * href="http://en.wikipedia.org/wiki/List_of_tz_database_time_zones">Olson
227             * Database</a> (e.g. "America/New_York")
228             */
229            public String getText() {
230                    return text;
231            }
232    
233            /**
234             * Sets the text portion of the timezone.
235             * @param text a free-form string representing the timezone, preferably a
236             * timezone ID from the <a
237             * href="http://en.wikipedia.org/wiki/List_of_tz_database_time_zones">Olson
238             * Database</a> (e.g. "America/New_York")
239             */
240            public void setText(String text) {
241                    this.text = text;
242            }
243    
244            /**
245             * Creates a {@link java.util.TimeZone} representation of this class.
246             * @return a {@link TimeZone} object or null if this object contains no
247             * offset data
248             */
249            public TimeZone toTimeZone() {
250                    if (text != null) {
251                            TimeZone timezone = VCardDateFormatter.parseTimeZoneId(text);
252                            if (timezone != null) {
253                                    return timezone;
254                            }
255                    }
256    
257                    if (offset != null) {
258                            int rawHourOffset = offset.getHour() * 60 * 60 * 1000;
259                            int rawMinuteOffset = offset.getMinute() * 60 * 1000;
260                            if (rawHourOffset < 0) {
261                                    rawMinuteOffset *= -1;
262                            }
263                            int rawOffset = rawHourOffset + rawMinuteOffset;
264    
265                            String id = (text == null) ? "" : text;
266    
267                            return new SimpleTimeZone(rawOffset, id);
268                    }
269    
270                    return null;
271            }
272    
273            /**
274             * Gets the TYPE parameter.
275             * <p>
276             * <b>Supported versions:</b> {@code 4.0}
277             * </p>
278             * @return the TYPE value (typically, this will be either "work" or "home")
279             * or null if it doesn't exist
280             */
281            public String getType() {
282                    return parameters.getType();
283            }
284    
285            /**
286             * Sets the TYPE parameter.
287             * <p>
288             * <b>Supported versions:</b> {@code 4.0}
289             * </p>
290             * @param type the TYPE value (this should be either "work" or "home") or
291             * null to remove
292             */
293            public void setType(String type) {
294                    parameters.setType(type);
295            }
296    
297            /**
298             * Gets the MEDIATYPE parameter.
299             * <p>
300             * <b>Supported versions:</b> {@code 4.0}
301             * </p>
302             * @return the media type or null if not set
303             */
304            public String getMediaType() {
305                    return parameters.getMediaType();
306            }
307    
308            /**
309             * Sets the MEDIATYPE parameter.
310             * <p>
311             * <b>Supported versions:</b> {@code 4.0}
312             * </p>
313             * @param mediaType the media type or null to remove
314             */
315            public void setMediaType(String mediaType) {
316                    parameters.setMediaType(mediaType);
317            }
318    
319            @Override
320            public List<Integer[]> getPids() {
321                    return super.getPids();
322            }
323    
324            @Override
325            public void addPid(int localId, int clientPidMapRef) {
326                    super.addPid(localId, clientPidMapRef);
327            }
328    
329            @Override
330            public void removePids() {
331                    super.removePids();
332            }
333    
334            @Override
335            public Integer getPref() {
336                    return super.getPref();
337            }
338    
339            @Override
340            public void setPref(Integer pref) {
341                    super.setPref(pref);
342            }
343    
344            //@Override
345            public String getAltId() {
346                    return parameters.getAltId();
347            }
348    
349            //@Override
350            public void setAltId(String altId) {
351                    parameters.setAltId(altId);
352            }
353    
354            @Override
355            protected void _validate(List<Warning> warnings, VCardVersion version, VCard vcard) {
356                    if (offset == null && text == null) {
357                            warnings.add(new Warning(8));
358                    }
359                    if (offset == null && version == VCardVersion.V2_1) {
360                            warnings.add(new Warning(20));
361                    }
362                    if (offset != null && (offset.getMinute() < 0 || offset.getMinute() > 59)) {
363                            warnings.add(new Warning(21));
364                    }
365            }
366    }