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 }