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 offset the offset
123         * @return the property that was created
124         * @see <a href="http://tools.ietf.org/html/rfc5545#page-105">RFC 5545
125         * p.105-6</a>
126         */
127        public TimezoneOffsetTo setTimezoneOffsetTo(UtcOffset offset) {
128                TimezoneOffsetTo prop = new TimezoneOffsetTo(offset);
129                setTimezoneOffsetTo(prop);
130                return prop;
131        }
132
133        /**
134         * Gets the UTC offset that the timezone observance transitions from.
135         * @return the UTC offset or null if not set
136         * @see <a href="http://tools.ietf.org/html/rfc5545#page-104">RFC 5545
137         * p.104-5</a>
138         */
139        public TimezoneOffsetFrom getTimezoneOffsetFrom() {
140                return getProperty(TimezoneOffsetFrom.class);
141        }
142
143        /**
144         * Sets the UTC offset that the timezone observance transitions from.
145         * @param timezoneOffsetFrom the UTC offset or null to remove
146         * @see <a href="http://tools.ietf.org/html/rfc5545#page-104">RFC 5545
147         * p.104-5</a>
148         */
149        public void setTimezoneOffsetFrom(TimezoneOffsetFrom timezoneOffsetFrom) {
150                setProperty(TimezoneOffsetFrom.class, timezoneOffsetFrom);
151        }
152
153        /**
154         * Sets the UTC offset that the timezone observance transitions from.
155         * @param offset the offset
156         * @return the property that was created
157         * @see <a href="http://tools.ietf.org/html/rfc5545#page-104">RFC 5545
158         * p.104-5</a>
159         */
160        public TimezoneOffsetFrom setTimezoneOffsetFrom(UtcOffset offset) {
161                TimezoneOffsetFrom prop = new TimezoneOffsetFrom(offset);
162                setTimezoneOffsetFrom(prop);
163                return prop;
164        }
165
166        /**
167         * Gets how often the timezone observance repeats.
168         * @return the recurrence rule or null if not set
169         * @see <a href="http://tools.ietf.org/html/rfc5545#page-122">RFC 5545
170         * p.122-32</a>
171         */
172        public RecurrenceRule getRecurrenceRule() {
173                return getProperty(RecurrenceRule.class);
174        }
175
176        /**
177         * Sets how often the timezone observance repeats.
178         * @param recur the recurrence rule or null to remove
179         * @return the property that was created
180         * @see <a href="http://tools.ietf.org/html/rfc5545#page-122">RFC 5545
181         * p.122-32</a>
182         */
183        public RecurrenceRule setRecurrenceRule(Recurrence recur) {
184                RecurrenceRule prop = (recur == null) ? null : new RecurrenceRule(recur);
185                setRecurrenceRule(prop);
186                return prop;
187        }
188
189        /**
190         * Sets how often the timezone observance repeats.
191         * @param recurrenceRule the recurrence rule or null to remove
192         * @see <a href="http://tools.ietf.org/html/rfc5545#page-122">RFC 5545
193         * p.122-32</a>
194         */
195        public void setRecurrenceRule(RecurrenceRule recurrenceRule) {
196                setProperty(RecurrenceRule.class, recurrenceRule);
197        }
198
199        /**
200         * Gets the comments attached to the timezone observance.
201         * @return the comments
202         * @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
203         * p.83-4</a>
204         */
205        public List<Comment> getComments() {
206                return getProperties(Comment.class);
207        }
208
209        /**
210         * Adds a comment to the timezone observance.
211         * @param comment the comment to add
212         * @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
213         * p.83-4</a>
214         */
215        public void addComment(Comment comment) {
216                addProperty(comment);
217        }
218
219        /**
220         * Adds a comment to the timezone observance.
221         * @param comment the comment to add
222         * @return the property that was created
223         * @see <a href="http://tools.ietf.org/html/rfc5545#page-83">RFC 5545
224         * p.83-4</a>
225         */
226        public Comment addComment(String comment) {
227                Comment prop = new Comment(comment);
228                addComment(prop);
229                return prop;
230        }
231
232        /**
233         * Gets the list of dates/periods that help define the recurrence rule of
234         * this timezone observance (if one is defined).
235         * @return the recurrence dates
236         * @see <a href="http://tools.ietf.org/html/rfc5545#page-120">RFC 5545
237         * p.120-2</a>
238         */
239        public List<RecurrenceDates> getRecurrenceDates() {
240                return getProperties(RecurrenceDates.class);
241        }
242
243        /**
244         * Adds a list of dates/periods that help define the recurrence rule of this
245         * timezone observance (if one is defined).
246         * @param recurrenceDates the recurrence dates
247         * @see <a href="http://tools.ietf.org/html/rfc5545#page-120">RFC 5545
248         * p.120-2</a>
249         */
250        public void addRecurrenceDates(RecurrenceDates recurrenceDates) {
251                addProperty(recurrenceDates);
252        }
253
254        /**
255         * Gets the traditional, non-standard names for the timezone observance.
256         * @return the timezone observance names
257         * @see <a href="http://tools.ietf.org/html/rfc5545#page-103">RFC 5545
258         * p.103-4</a>
259         */
260        public List<TimezoneName> getTimezoneNames() {
261                return getProperties(TimezoneName.class);
262        }
263
264        /**
265         * Adds a traditional, non-standard name for the timezone observance.
266         * @param timezoneName the timezone observance name
267         * @see <a href="http://tools.ietf.org/html/rfc5545#page-103">RFC 5545
268         * p.103-4</a>
269         */
270        public void addTimezoneName(TimezoneName timezoneName) {
271                addProperty(timezoneName);
272        }
273
274        /**
275         * Adds a traditional, non-standard name for the timezone observance.
276         * @param timezoneName the timezone observance name (e.g. "EST")
277         * @return the property that was created
278         * @see <a href="http://tools.ietf.org/html/rfc5545#page-103">RFC 5545
279         * p.103-4</a>
280         */
281        public TimezoneName addTimezoneName(String timezoneName) {
282                TimezoneName prop = new TimezoneName(timezoneName);
283                addTimezoneName(prop);
284                return prop;
285        }
286
287        /**
288         * Gets the list of exceptions to the timezone observance.
289         * @return the list of exceptions
290         * @see <a href="http://tools.ietf.org/html/rfc5545#page-118">RFC 5545
291         * p.118-20</a>
292         */
293        public List<ExceptionDates> getExceptionDates() {
294                return getProperties(ExceptionDates.class);
295        }
296
297        /**
298         * Adds a list of exceptions to the timezone observance. Note that this
299         * property can contain multiple dates.
300         * @param exceptionDates the list of exceptions
301         * @see <a href="http://tools.ietf.org/html/rfc5545#page-118">RFC 5545
302         * p.118-20</a>
303         */
304        public void addExceptionDates(ExceptionDates exceptionDates) {
305                addProperty(exceptionDates);
306        }
307
308        @SuppressWarnings("unchecked")
309        @Override
310        protected void validate(List<ICalComponent> components, ICalVersion version, List<Warning> warnings) {
311                if (version == ICalVersion.V1_0) {
312                        warnings.add(Warning.validate(48, version));
313                }
314
315                checkRequiredCardinality(warnings, DateStart.class, TimezoneOffsetTo.class, TimezoneOffsetFrom.class);
316
317                //BYHOUR, BYMINUTE, and BYSECOND cannot be specified in RRULE if DTSTART's data type is "date"
318                //RFC 5545 p. 167
319                DateStart dateStart = getDateStart();
320                RecurrenceRule rrule = getRecurrenceRule();
321                if (dateStart != null && rrule != null) {
322                        ICalDate start = dateStart.getValue();
323                        Recurrence recur = rrule.getValue();
324                        if (start != null && recur != null) {
325                                if (!start.hasTime() && (!recur.getByHour().isEmpty() || !recur.getByMinute().isEmpty() || !recur.getBySecond().isEmpty())) {
326                                        warnings.add(Warning.validate(5));
327                                }
328                        }
329                }
330
331                //there *should* be only 1 instance of RRULE
332                //RFC 5545 p. 167
333                if (getProperties(RecurrenceRule.class).size() > 1) {
334                        warnings.add(Warning.validate(6));
335                }
336        }
337}