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