001package biweekly.component;
002
003import java.util.List;
004
005import biweekly.ICalVersion;
006import biweekly.Warning;
007import biweekly.property.Comment;
008import biweekly.property.DateStart;
009import biweekly.property.ExceptionDates;
010import biweekly.property.RecurrenceDates;
011import biweekly.property.RecurrenceRule;
012import biweekly.property.TimezoneName;
013import biweekly.property.TimezoneOffsetFrom;
014import biweekly.property.TimezoneOffsetTo;
015import biweekly.util.DateTimeComponents;
016import biweekly.util.ICalDate;
017import biweekly.util.Recurrence;
018import biweekly.util.UtcOffset;
019
020/*
021 Copyright (c) 2013-2015, Michael Angstadt
022 All rights reserved.
023
024 Redistribution and use in source and binary forms, with or without
025 modification, are permitted provided that the following conditions are met: 
026
027 1. Redistributions of source code must retain the above copyright notice, this
028 list of conditions and the following disclaimer. 
029 2. Redistributions in binary form must reproduce the above copyright notice,
030 this list of conditions and the following disclaimer in the documentation
031 and/or other materials provided with the distribution. 
032
033 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
034 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
035 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
037 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
039 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
040 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
041 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
042 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
043 */
044
045/**
046 * Represents a timezone observance (i.e. "daylight savings" and "standard"
047 * times).
048 * @author Michael Angstadt
049 * @see DaylightSavingsTime
050 * @see StandardTime
051 * @see <a href="http://tools.ietf.org/html/rfc5545#page-62">RFC 5545
052 * p.62-71</a>
053 * @see <a href="http://tools.ietf.org/html/rfc2445#page-60">RFC 2445 p.60-7</a>
054 */
055public class Observance extends ICalComponent {
056        /**
057         * Gets the date that the timezone observance starts.
058         * @return the start date or null if not set
059         * @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
060         * p.97-8</a>
061         */
062        public DateStart getDateStart() {
063                return getProperty(DateStart.class);
064        }
065
066        /**
067         * Sets the date that the timezone observance starts.
068         * @param dateStart the start date or null to remove
069         * @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
070         * p.97-8</a>
071         */
072        public void setDateStart(DateStart dateStart) {
073                setProperty(DateStart.class, dateStart);
074        }
075
076        /**
077         * Sets the date that the timezone observance starts.
078         * @param date the start date or null to remove
079         * @return the property that was created
080         * @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
081         * p.97-8</a>
082         */
083        public DateStart setDateStart(ICalDate date) {
084                DateStart prop = (date == null) ? null : new DateStart(date);
085                setDateStart(prop);
086                return prop;
087        }
088
089        /**
090         * Sets the date that the timezone observance starts.
091         * @param rawComponents the start date or null to remove
092         * @return the property that was created
093         * @see <a href="http://tools.ietf.org/html/rfc5545#page-97">RFC 5545
094         * p.97-8</a>
095         */
096        public DateStart setDateStart(DateTimeComponents rawComponents) {
097                return setDateStart((rawComponents == null) ? null : new ICalDate(rawComponents, true));
098        }
099
100        /**
101         * Gets the UTC offset that the timezone observance transitions to.
102         * @return the UTC offset or null if not set
103         * @see <a href="http://tools.ietf.org/html/rfc5545#page-105">RFC 5545
104         * p.105-6</a>
105         */
106        public TimezoneOffsetTo getTimezoneOffsetTo() {
107                return getProperty(TimezoneOffsetTo.class);
108        }
109
110        /**
111         * Sets the UTC offset that the timezone observance transitions to.
112         * @param timezoneOffsetTo the UTC offset or null to remove
113         * @see <a href="http://tools.ietf.org/html/rfc5545#page-105">RFC 5545
114         * p.105-6</a>
115         */
116        public void setTimezoneOffsetTo(TimezoneOffsetTo timezoneOffsetTo) {
117                setProperty(TimezoneOffsetTo.class, timezoneOffsetTo);
118        }
119
120        /**
121         * Sets the UTC offset that the timezone observance transitions to.
122         * @param hour the hour offset (e.g. "-5")
123         * @param minute the minute offset (e.g. "0")
124         * @return the property that was created
125         * @see <a href="http://tools.ietf.org/html/rfc5545#page-105">RFC 5545
126         * p.105-6</a>
127         */
128        public TimezoneOffsetTo setTimezoneOffsetTo(Integer hour, Integer minute) {
129                return setTimezoneOffsetTo(new UtcOffset(hour, minute));
130        }
131
132        /**
133         * Sets the UTC offset that the timezone observance transitions to.
134         * @param offset the offset
135         * @return the property that was created
136         * @see <a href="http://tools.ietf.org/html/rfc5545#page-105">RFC 5545
137         * p.105-6</a>
138         */
139        public TimezoneOffsetTo setTimezoneOffsetTo(UtcOffset offset) {
140                TimezoneOffsetTo prop = new TimezoneOffsetTo(offset);
141                setTimezoneOffsetTo(prop);
142                return prop;
143        }
144
145        /**
146         * Gets the UTC offset that the timezone observance transitions from.
147         * @return the UTC offset or null if not set
148         * @see <a href="http://tools.ietf.org/html/rfc5545#page-104">RFC 5545
149         * p.104-5</a>
150         */
151        public TimezoneOffsetFrom getTimezoneOffsetFrom() {
152                return getProperty(TimezoneOffsetFrom.class);
153        }
154
155        /**
156         * Sets the UTC offset that the timezone observance transitions from.
157         * @param timezoneOffsetFrom the UTC offset or null to remove
158         * @see <a href="http://tools.ietf.org/html/rfc5545#page-104">RFC 5545
159         * p.104-5</a>
160         */
161        public void setTimezoneOffsetFrom(TimezoneOffsetFrom timezoneOffsetFrom) {
162                setProperty(TimezoneOffsetFrom.class, timezoneOffsetFrom);
163        }
164
165        /**
166         * Sets the UTC offset that the timezone observance transitions from.
167         * @param hour the hour offset (e.g. "-5")
168         * @param minute the minute offset (e.g. "0")
169         * @return the property that was created
170         * @see <a href="http://tools.ietf.org/html/rfc5545#page-104">RFC 5545
171         * p.104-5</a>
172         */
173        public TimezoneOffsetFrom setTimezoneOffsetFrom(Integer hour, Integer minute) {
174                return setTimezoneOffsetFrom(new UtcOffset(hour, minute));
175        }
176
177        /**
178         * Sets the UTC offset that the timezone observance transitions from.
179         * @param offset the offset
180         * @return the property that was created
181         * @see <a href="http://tools.ietf.org/html/rfc5545#page-104">RFC 5545
182         * p.104-5</a>
183         */
184        public TimezoneOffsetFrom setTimezoneOffsetFrom(UtcOffset offset) {
185                TimezoneOffsetFrom prop = new TimezoneOffsetFrom(offset);
186                setTimezoneOffsetFrom(prop);
187                return prop;
188        }
189
190        /**
191         * Gets how often the timezone observance repeats.
192         * @return the recurrence rule or null if not set
193         * @see <a href="http://tools.ietf.org/html/rfc5545#page-122">RFC 5545
194         * p.122-32</a>
195         */
196        public RecurrenceRule getRecurrenceRule() {
197                return getProperty(RecurrenceRule.class);
198        }
199
200        /**
201         * Sets how often the timezone observance repeats.
202         * @param recur the recurrence rule or null to remove
203         * @return the property that was created
204         * @see <a href="http://tools.ietf.org/html/rfc5545#page-122">RFC 5545
205         * p.122-32</a>
206         */
207        public RecurrenceRule setRecurrenceRule(Recurrence recur) {
208                RecurrenceRule prop = (recur == null) ? null : new RecurrenceRule(recur);
209                setRecurrenceRule(prop);
210                return prop;
211        }
212
213        /**
214         * Sets how often the timezone observance repeats.
215         * @param recurrenceRule the recurrence rule or null to remove
216         * @see <a href="http://tools.ietf.org/html/rfc5545#page-122">RFC 5545
217         * p.122-32</a>
218         */
219        public void setRecurrenceRule(RecurrenceRule recurrenceRule) {
220                setProperty(RecurrenceRule.class, recurrenceRule);
221        }
222
223        /**
224         * Gets the comments attached to the timezone observance.
225         * @return the comments
226         * @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
227         * p.83-4</a>
228         */
229        public List<Comment> getComments() {
230                return getProperties(Comment.class);
231        }
232
233        /**
234         * Adds a comment to the timezone observance.
235         * @param comment the comment to add
236         * @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
237         * p.83-4</a>
238         */
239        public void addComment(Comment comment) {
240                addProperty(comment);
241        }
242
243        /**
244         * Adds a comment to the timezone observance.
245         * @param comment the comment to add
246         * @return the property that was created
247         * @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
248         * p.83-4</a>
249         */
250        public Comment addComment(String comment) {
251                Comment prop = new Comment(comment);
252                addComment(prop);
253                return prop;
254        }
255
256        /**
257         * Gets the list of dates/periods that help define the recurrence rule of
258         * this timezone observance (if one is defined).
259         * @return the recurrence dates
260         * @see <a href="http://tools.ietf.org/html/rfc5545#page-120">RFC 5545
261         * p.120-2</a>
262         */
263        public List<RecurrenceDates> getRecurrenceDates() {
264                return getProperties(RecurrenceDates.class);
265        }
266
267        /**
268         * Adds a list of dates/periods that help define the recurrence rule of this
269         * timezone observance (if one is defined).
270         * @param recurrenceDates the recurrence dates
271         * @see <a href="http://tools.ietf.org/html/rfc5545#page-120">RFC 5545
272         * p.120-2</a>
273         */
274        public void addRecurrenceDates(RecurrenceDates recurrenceDates) {
275                addProperty(recurrenceDates);
276        }
277
278        /**
279         * Gets the traditional, non-standard names for the timezone observance.
280         * @return the timezone observance names
281         * @see <a href="http://tools.ietf.org/html/rfc5545#page-103">RFC 5545
282         * p.103-4</a>
283         */
284        public List<TimezoneName> getTimezoneNames() {
285                return getProperties(TimezoneName.class);
286        }
287
288        /**
289         * Adds a traditional, non-standard name for the timezone observance.
290         * @param timezoneName the timezone observance name
291         * @see <a href="http://tools.ietf.org/html/rfc5545#page-103">RFC 5545
292         * p.103-4</a>
293         */
294        public void addTimezoneName(TimezoneName timezoneName) {
295                addProperty(timezoneName);
296        }
297
298        /**
299         * Adds a traditional, non-standard name for the timezone observance.
300         * @param timezoneName the timezone observance name (e.g. "EST")
301         * @return the property that was created
302         * @see <a href="http://tools.ietf.org/html/rfc5545#page-103">RFC 5545
303         * p.103-4</a>
304         */
305        public TimezoneName addTimezoneName(String timezoneName) {
306                TimezoneName prop = new TimezoneName(timezoneName);
307                addTimezoneName(prop);
308                return prop;
309        }
310
311        /**
312         * Gets the list of exceptions to the timezone observance.
313         * @return the list of exceptions
314         * @see <a href="http://tools.ietf.org/html/rfc5545#page-118">RFC 5545
315         * p.118-20</a>
316         */
317        public List<ExceptionDates> getExceptionDates() {
318                return getProperties(ExceptionDates.class);
319        }
320
321        /**
322         * Adds a list of exceptions to the timezone observance. Note that this
323         * property can contain multiple dates.
324         * @param exceptionDates the list of exceptions
325         * @see <a href="http://tools.ietf.org/html/rfc5545#page-118">RFC 5545
326         * p.118-20</a>
327         */
328        public void addExceptionDates(ExceptionDates exceptionDates) {
329                addProperty(exceptionDates);
330        }
331
332        @SuppressWarnings("unchecked")
333        @Override
334        protected void validate(List<ICalComponent> components, ICalVersion version, List<Warning> warnings) {
335                if (version == ICalVersion.V1_0) {
336                        warnings.add(Warning.validate(48, version));
337                }
338
339                checkRequiredCardinality(warnings, DateStart.class, TimezoneOffsetTo.class, TimezoneOffsetFrom.class);
340
341                //BYHOUR, BYMINUTE, and BYSECOND cannot be specified in RRULE if DTSTART's data type is "date"
342                //RFC 5545 p. 167
343                DateStart dateStart = getDateStart();
344                RecurrenceRule rrule = getRecurrenceRule();
345                if (dateStart != null && rrule != null) {
346                        ICalDate start = dateStart.getValue();
347                        Recurrence recur = rrule.getValue();
348                        if (start != null && recur != null) {
349                                if (!start.hasTime() && (!recur.getByHour().isEmpty() || !recur.getByMinute().isEmpty() || !recur.getBySecond().isEmpty())) {
350                                        warnings.add(Warning.validate(5));
351                                }
352                        }
353                }
354
355                //there *should* be only 1 instance of RRULE
356                //RFC 5545 p. 167
357                if (getProperties(RecurrenceRule.class).size() > 1) {
358                        warnings.add(Warning.validate(6));
359                }
360        }
361}