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 }