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