001    package biweekly.component;
002    
003    import java.util.Date;
004    import java.util.List;
005    
006    import biweekly.Warning;
007    import biweekly.property.Comment;
008    import biweekly.property.DateStart;
009    import biweekly.property.ExceptionDates;
010    import biweekly.property.RecurrenceDates;
011    import biweekly.property.RecurrenceRule;
012    import biweekly.property.TimezoneName;
013    import biweekly.property.TimezoneOffsetFrom;
014    import biweekly.property.TimezoneOffsetTo;
015    import biweekly.util.DateTimeComponents;
016    import 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     * Parent class for the "daylight" and "standard" timezone observances.
045     * @author Michael Angstadt
046     * @see DaylightSavingsTime
047     * @see StandardTime
048     * @rfc 5545 p.62-71
049     */
050    public abstract class Observance extends ICalComponent {
051            /**
052             * Gets the date that the timezone observance starts.
053             * @return the start date or null if not set
054             * @rfc 5545 p.97-8
055             */
056            public DateStart getDateStart() {
057                    return getProperty(DateStart.class);
058            }
059    
060            /**
061             * Sets the date that the timezone observance starts.
062             * @param dateStart the start date or null to remove
063             * @rfc 5545 p.97-8
064             */
065            public void setDateStart(DateStart dateStart) {
066                    if (dateStart != null) {
067                            dateStart.setLocalTime(true);
068                    }
069                    setProperty(DateStart.class, dateStart);
070            }
071    
072            /**
073             * Sets the date that the timezone observance starts.
074             * @param components the raw components of the start date or null to remove
075             * @return the property that was created
076             * @rfc 5545 p.97-8
077             */
078            public DateStart setDateStart(DateTimeComponents components) {
079                    DateStart prop = (components == null) ? null : new DateStart(components);
080                    setDateStart(prop);
081                    return prop;
082            }
083    
084            /**
085             * Gets the UTC offset that the timezone observance transitions to.
086             * @return the UTC offset or null if not set
087             * @rfc 5545 p.105-6
088             */
089            public TimezoneOffsetTo getTimezoneOffsetTo() {
090                    return getProperty(TimezoneOffsetTo.class);
091            }
092    
093            /**
094             * Sets the UTC offset that the timezone observance transitions to.
095             * @param timezoneOffsetTo the UTC offset or null to remove
096             * @rfc 5545 p.105-6
097             */
098            public void setTimezoneOffsetTo(TimezoneOffsetTo timezoneOffsetTo) {
099                    setProperty(TimezoneOffsetTo.class, timezoneOffsetTo);
100            }
101    
102            /**
103             * Sets the UTC offset that the timezone observance transitions to.
104             * @param hour the hour offset (e.g. "-5")
105             * @param minute the minute offset (e.g. "0")
106             * @return the property that was created
107             * @rfc 5545 p.105-6
108             */
109            public TimezoneOffsetTo setTimezoneOffsetTo(Integer hour, Integer minute) {
110                    TimezoneOffsetTo prop = new TimezoneOffsetTo(hour, minute);
111                    setTimezoneOffsetTo(prop);
112                    return prop;
113            }
114    
115            /**
116             * Gets the UTC offset that the timezone observance transitions from.
117             * @return the UTC offset or null if not set
118             * @rfc 5545 p.104-5
119             */
120            public TimezoneOffsetFrom getTimezoneOffsetFrom() {
121                    return getProperty(TimezoneOffsetFrom.class);
122            }
123    
124            /**
125             * Sets the UTC offset that the timezone observance transitions from.
126             * @param timezoneOffsetFrom the UTC offset or null to remove
127             * @rfc 5545 p.104-5
128             */
129            public void setTimezoneOffsetFrom(TimezoneOffsetFrom timezoneOffsetFrom) {
130                    setProperty(TimezoneOffsetFrom.class, timezoneOffsetFrom);
131            }
132    
133            /**
134             * Sets the UTC offset that the timezone observance transitions from.
135             * @param hour the hour offset (e.g. "-5")
136             * @param minute the minute offset (e.g. "0")
137             * @return the property that was created
138             * @rfc 5545 p.104-5
139             */
140            public TimezoneOffsetFrom setTimezoneOffsetFrom(Integer hour, Integer minute) {
141                    TimezoneOffsetFrom prop = new TimezoneOffsetFrom(hour, minute);
142                    setTimezoneOffsetFrom(prop);
143                    return prop;
144            }
145    
146            /**
147             * Gets how often the timezone observance repeats.
148             * @return the recurrence rule or null if not set
149             * @rfc 5545 p.122-32
150             */
151            public RecurrenceRule getRecurrenceRule() {
152                    return getProperty(RecurrenceRule.class);
153            }
154    
155            /**
156             * Sets how often the timezone observance repeats.
157             * @param recur the recurrence rule or null to remove
158             * @return the property that was created
159             * @rfc 5545 p.122-32
160             */
161            public RecurrenceRule setRecurrenceRule(Recurrence recur) {
162                    RecurrenceRule prop = (recur == null) ? null : new RecurrenceRule(recur);
163                    setRecurrenceRule(prop);
164                    return prop;
165            }
166    
167            /**
168             * Sets how often the timezone observance repeats.
169             * @param recurrenceRule the recurrence rule or null to remove
170             * @rfc 5545 p.122-32
171             */
172            public void setRecurrenceRule(RecurrenceRule recurrenceRule) {
173                    setProperty(RecurrenceRule.class, recurrenceRule);
174            }
175    
176            /**
177             * Gets the comments attached to the timezone observance.
178             * @return the comments
179             * @rfc 5545 p.83-4
180             */
181            public List<Comment> getComments() {
182                    return getProperties(Comment.class);
183            }
184    
185            /**
186             * Adds a comment to the timezone observance.
187             * @param comment the comment to add
188             * @rfc 5545 p.83-4
189             */
190            public void addComment(Comment comment) {
191                    addProperty(comment);
192            }
193    
194            /**
195             * Adds a comment to the timezone observance.
196             * @param comment the comment to add
197             * @return the property that was created
198             * @rfc 5545 p.83-4
199             */
200            public Comment addComment(String comment) {
201                    Comment prop = new Comment(comment);
202                    addComment(prop);
203                    return prop;
204            }
205    
206            /**
207             * Gets the list of dates/periods that help define the recurrence rule of
208             * this timezone observance (if one is defined).
209             * @return the recurrence dates
210             * @rfc 5545 p.120-2
211             */
212            public List<RecurrenceDates> getRecurrenceDates() {
213                    return getProperties(RecurrenceDates.class);
214            }
215    
216            /**
217             * Adds a list of dates/periods that help define the recurrence rule of this
218             * timezone observance (if one is defined).
219             * @param recurrenceDates the recurrence dates
220             * @rfc 5545 p.120-2
221             */
222            public void addRecurrenceDates(RecurrenceDates recurrenceDates) {
223                    addProperty(recurrenceDates);
224            }
225    
226            /**
227             * Gets the traditional, non-standard names for the timezone observance.
228             * @return the timezone observance names
229             * @rfc 5545 p.103-4
230             */
231            public List<TimezoneName> getTimezoneNames() {
232                    return getProperties(TimezoneName.class);
233            }
234    
235            /**
236             * Adds a traditional, non-standard name for the timezone observance.
237             * @param timezoneName the timezone observance name
238             * @rfc 5545 p.103-4
239             */
240            public void addTimezoneName(TimezoneName timezoneName) {
241                    addProperty(timezoneName);
242            }
243    
244            /**
245             * Adds a traditional, non-standard name for the timezone observance.
246             * @param timezoneName the timezone observance name (e.g. "EST")
247             * @return the property that was created
248             * @rfc 5545 p.103-4
249             */
250            public TimezoneName addTimezoneName(String timezoneName) {
251                    TimezoneName prop = new TimezoneName(timezoneName);
252                    addTimezoneName(prop);
253                    return prop;
254            }
255    
256            /**
257             * Gets the list of exceptions to the timezone observance.
258             * @return the list of exceptions
259             * @rfc 5545 p.118-20
260             */
261            public List<ExceptionDates> getExceptionDates() {
262                    return getProperties(ExceptionDates.class);
263            }
264    
265            /**
266             * Adds a list of exceptions to the timezone observance. Note that this
267             * property can contain multiple dates.
268             * @param exceptionDates the list of exceptions
269             * @rfc 5545 p.118-20
270             */
271            public void addExceptionDates(ExceptionDates exceptionDates) {
272                    addProperty(exceptionDates);
273            }
274    
275            @SuppressWarnings("unchecked")
276            @Override
277            protected void validate(List<ICalComponent> components, List<Warning> warnings) {
278                    checkRequiredCardinality(warnings, DateStart.class, TimezoneOffsetTo.class, TimezoneOffsetFrom.class);
279    
280                    //RFC 5545 p. 167
281                    DateStart dateStart = getDateStart();
282                    RecurrenceRule rrule = getRecurrenceRule();
283                    if (dateStart != null && rrule != null) {
284                            Date start = dateStart.getValue();
285                            Recurrence recur = rrule.getValue();
286                            if (start != null && recur != null) {
287                                    if (!dateStart.hasTime() && (!recur.getByHour().isEmpty() || !recur.getByMinute().isEmpty() || !recur.getBySecond().isEmpty())) {
288                                            warnings.add(Warning.validate(5));
289                                    }
290                            }
291                    }
292    
293                    //RFC 5545 p. 167
294                    if (getProperties(RecurrenceRule.class).size() > 1) {
295                            warnings.add(Warning.validate(6));
296                    }
297            }
298    }