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