src/net/fortuna/ical4j/model/Period.java

Tue, 10 Feb 2015 18:12:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 10 Feb 2015 18:12:00 +0100
changeset 0
fb9019fb1bf7
child 4
45d57ecba757
permissions
-rw-r--r--

Import initial revisions of existing project AndroidCaldavSyncAdapater,
forked from upstream repository at 27e8a0f8495c92e0780d450bdf0c7cec77a03a55.

michael@0 1 /**
michael@0 2 * Copyright (c) 2012, Ben Fortuna
michael@0 3 * All rights reserved.
michael@0 4 *
michael@0 5 * Redistribution and use in source and binary forms, with or without
michael@0 6 * modification, are permitted provided that the following conditions
michael@0 7 * are met:
michael@0 8 *
michael@0 9 * o Redistributions of source code must retain the above copyright
michael@0 10 * notice, this list of conditions and the following disclaimer.
michael@0 11 *
michael@0 12 * o Redistributions in binary form must reproduce the above copyright
michael@0 13 * notice, this list of conditions and the following disclaimer in the
michael@0 14 * documentation and/or other materials provided with the distribution.
michael@0 15 *
michael@0 16 * o Neither the name of Ben Fortuna nor the names of any other contributors
michael@0 17 * may be used to endorse or promote products derived from this software
michael@0 18 * without specific prior written permission.
michael@0 19 *
michael@0 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
michael@0 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
michael@0 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
michael@0 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
michael@0 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
michael@0 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
michael@0 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
michael@0 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 31 */
michael@0 32 package net.fortuna.ical4j.model;
michael@0 33
michael@0 34 import java.text.ParseException;
michael@0 35 import java.util.Date;
michael@0 36
michael@0 37 import org.apache.commons.lang.builder.EqualsBuilder;
michael@0 38 import org.apache.commons.lang.builder.HashCodeBuilder;
michael@0 39
michael@0 40 /**
michael@0 41 * $Id$ [Apr 14, 2004]
michael@0 42 *
michael@0 43 * Defines a period of time. A period may be specified as either a start date
michael@0 44 * and end date, or a start date and duration. NOTE: End dates and durations are
michael@0 45 * implicitly derived when not explicitly specified. This means that you cannot
michael@0 46 * rely on the returned values from the getters to deduce whether a period has
michael@0 47 * an explicit end date or duration.
michael@0 48 *
michael@0 49 * @author Ben Fortuna
michael@0 50 */
michael@0 51 public class Period extends DateRange implements Comparable {
michael@0 52
michael@0 53 private static final long serialVersionUID = 7321090422911676490L;
michael@0 54
michael@0 55 private Dur duration;
michael@0 56
michael@0 57 /**
michael@0 58 * Constructor.
michael@0 59 *
michael@0 60 * @param aValue
michael@0 61 * a string representation of a period
michael@0 62 * @throws ParseException
michael@0 63 * where the specified string is not a valid representation
michael@0 64 */
michael@0 65 public Period(final String aValue) throws ParseException {
michael@0 66 super(parseStartDate(aValue), parseEndDate(aValue, true));
michael@0 67
michael@0 68 // period may end in either a date-time or a duration..
michael@0 69 try {
michael@0 70 parseEndDate(aValue, false);
michael@0 71 }
michael@0 72 catch (ParseException pe) {
michael@0 73 // duration = DurationFormat.getInstance().parse(aValue);
michael@0 74 duration = parseDuration(aValue);
michael@0 75 }
michael@0 76 normalise();
michael@0 77 }
michael@0 78
michael@0 79 /**
michael@0 80 * Constructs a new period with the specied start and end date.
michael@0 81 *
michael@0 82 * @param start
michael@0 83 * the start date of the period
michael@0 84 * @param end
michael@0 85 * the end date of the period
michael@0 86 */
michael@0 87 public Period(final DateTime start, final DateTime end) {
michael@0 88 super(start, end);
michael@0 89 normalise();
michael@0 90 }
michael@0 91
michael@0 92 /**
michael@0 93 * Constructs a new period with the specified start date and duration.
michael@0 94 *
michael@0 95 * @param start
michael@0 96 * the start date of the period
michael@0 97 * @param duration
michael@0 98 * the duration of the period
michael@0 99 */
michael@0 100 public Period(final DateTime start, final Dur duration) {
michael@0 101 super(start, new DateTime(duration.getTime(start)));
michael@0 102 this.duration = duration;
michael@0 103 normalise();
michael@0 104 }
michael@0 105
michael@0 106 private static DateTime parseStartDate(String value) throws ParseException {
michael@0 107 return new DateTime(value.substring(0, value.indexOf('/')));
michael@0 108 }
michael@0 109
michael@0 110 private static DateTime parseEndDate(String value, boolean resolve) throws ParseException {
michael@0 111 DateTime end = null;
michael@0 112 try {
michael@0 113 end = new DateTime(value.substring(value.indexOf('/') + 1));
michael@0 114 }
michael@0 115 catch (ParseException e) {
michael@0 116 if (resolve) {
michael@0 117 final Dur duration = parseDuration(value);
michael@0 118 end = new DateTime(duration.getTime(parseStartDate(value)));
michael@0 119 }
michael@0 120 else {
michael@0 121 throw e;
michael@0 122 }
michael@0 123 }
michael@0 124 return end;
michael@0 125 }
michael@0 126
michael@0 127 private static Dur parseDuration(String value) {
michael@0 128 return new Dur(value.substring(value.indexOf('/') + 1));
michael@0 129 }
michael@0 130
michael@0 131 private void normalise() {
michael@0 132 // ensure the end timezone is the same as the start..
michael@0 133 if (getStart().isUtc()) {
michael@0 134 getEnd().setUtc(true);
michael@0 135 }
michael@0 136 else {
michael@0 137 getEnd().setTimeZone(getStart().getTimeZone());
michael@0 138 }
michael@0 139 }
michael@0 140
michael@0 141 /**
michael@0 142 * Returns the duration of this period. If an explicit duration is not
michael@0 143 * specified, the duration is derived from the end date.
michael@0 144 *
michael@0 145 * @return the duration of this period in milliseconds.
michael@0 146 */
michael@0 147 public final Dur getDuration() {
michael@0 148 if (duration == null) {
michael@0 149 return new Dur(getStart(), getEnd());
michael@0 150 }
michael@0 151 return duration;
michael@0 152 }
michael@0 153
michael@0 154 /**
michael@0 155 * Returns the end date of this period. If an explicit end date is not
michael@0 156 * specified, the end date is derived from the duration.
michael@0 157 *
michael@0 158 * @return the end date of this period.
michael@0 159 */
michael@0 160 public final DateTime getEnd() {
michael@0 161 return (DateTime) getRangeEnd();
michael@0 162 }
michael@0 163
michael@0 164 /**
michael@0 165 * @return Returns the start.
michael@0 166 */
michael@0 167 public final DateTime getStart() {
michael@0 168 return (DateTime) getRangeStart();
michael@0 169 }
michael@0 170
michael@0 171 /**
michael@0 172 * @param date a date to test for inclusion
michael@0 173 * @param inclusive indicates if the start and end of the period are included in the test
michael@0 174 * @return true if the specified date occurs within the current period
michael@0 175 * @deprecated use {@link Period#includes(Date, int)} instead.
michael@0 176 */
michael@0 177 public final boolean includes(final Date date, final boolean inclusive) {
michael@0 178 if (inclusive) {
michael@0 179 return includes(date, INCLUSIVE_START | INCLUSIVE_END);
michael@0 180 }
michael@0 181 else {
michael@0 182 return includes(date, 0);
michael@0 183 }
michael@0 184 }
michael@0 185
michael@0 186 /**
michael@0 187 * Creates a period that encompasses both this period and another one. If
michael@0 188 * the other period is null, return a copy of this period. NOTE: Resulting
michael@0 189 * periods are specified by explicitly setting a start date and end date
michael@0 190 * (i.e. durations are implied).
michael@0 191 *
michael@0 192 * @param period
michael@0 193 * the period to add to this one
michael@0 194 * @return a period
michael@0 195 */
michael@0 196 public final Period add(final Period period) {
michael@0 197 DateTime newPeriodStart = null;
michael@0 198 DateTime newPeriodEnd = null;
michael@0 199
michael@0 200 if (period == null) {
michael@0 201 newPeriodStart = getStart();
michael@0 202 newPeriodEnd = getEnd();
michael@0 203 }
michael@0 204 else {
michael@0 205 if (getStart().before(period.getStart())) {
michael@0 206 newPeriodStart = getStart();
michael@0 207 }
michael@0 208 else {
michael@0 209 newPeriodStart = period.getStart();
michael@0 210 }
michael@0 211 if (getEnd().after(period.getEnd())) {
michael@0 212 newPeriodEnd = getEnd();
michael@0 213 }
michael@0 214 else {
michael@0 215 newPeriodEnd = period.getEnd();
michael@0 216 }
michael@0 217 }
michael@0 218
michael@0 219 return new Period(newPeriodStart, newPeriodEnd);
michael@0 220 }
michael@0 221
michael@0 222 /**
michael@0 223 * Creates a set of periods resulting from the subtraction of the specified
michael@0 224 * period from this one. If the specified period is completely contained
michael@0 225 * in this period, the resulting list will contain two periods. Otherwise
michael@0 226 * it will contain one. If the specified period does not interest this period
michael@0 227 * a list containing this period is returned. If this period is completely
michael@0 228 * contained within the specified period an empty period list is returned.
michael@0 229 * @param period a period to subtract from this one
michael@0 230 * @return a list containing zero, one or two periods.
michael@0 231 */
michael@0 232 public final PeriodList subtract(final Period period) {
michael@0 233 final PeriodList result = new PeriodList();
michael@0 234
michael@0 235 if (period.contains(this)) {
michael@0 236 return result;
michael@0 237 }
michael@0 238 else if (!period.intersects(this)) {
michael@0 239 result.add(this);
michael@0 240 return result;
michael@0 241 }
michael@0 242
michael@0 243 DateTime newPeriodStart;
michael@0 244 DateTime newPeriodEnd;
michael@0 245 if (!period.getStart().after(getStart())) {
michael@0 246 newPeriodStart = period.getEnd();
michael@0 247 newPeriodEnd = getEnd();
michael@0 248 }
michael@0 249 else if (!period.getEnd().before(getEnd())) {
michael@0 250 newPeriodStart = getStart();
michael@0 251 newPeriodEnd = period.getStart();
michael@0 252 }
michael@0 253 else {
michael@0 254 // subtraction consumed by this period..
michael@0 255 // initialise and add head period..
michael@0 256 newPeriodStart = getStart();
michael@0 257 newPeriodEnd = period.getStart();
michael@0 258 result.add(new Period(newPeriodStart, newPeriodEnd));
michael@0 259 // initialise tail period..
michael@0 260 newPeriodStart = period.getEnd();
michael@0 261 newPeriodEnd = getEnd();
michael@0 262 }
michael@0 263 result.add(new Period(newPeriodStart, newPeriodEnd));
michael@0 264 return result;
michael@0 265 }
michael@0 266
michael@0 267 /**
michael@0 268 * An empty period is one that consumes no time.
michael@0 269 * @return true if this period consumes no time, otherwise false
michael@0 270 */
michael@0 271 public final boolean isEmpty() {
michael@0 272 return getStart().equals(getEnd());
michael@0 273 }
michael@0 274
michael@0 275 /**
michael@0 276 * Updates the start and (possible) end times of this period to reflect
michael@0 277 * the specified UTC timezone status.
michael@0 278 * @param utc indicates whether the period is in UTC time
michael@0 279 */
michael@0 280 public void setUtc(final boolean utc) {
michael@0 281 getStart().setUtc(utc);
michael@0 282 getEnd().setUtc(utc);
michael@0 283 }
michael@0 284
michael@0 285 /**
michael@0 286 * Updates the start and (possible) end times of this period to reflect
michael@0 287 * the specified timezone status.
michael@0 288 * @param timezone a timezone for the period
michael@0 289 */
michael@0 290 public final void setTimeZone(final TimeZone timezone) {
michael@0 291 getStart().setUtc(false);
michael@0 292 getStart().setTimeZone(timezone);
michael@0 293 getEnd().setUtc(false);
michael@0 294 getEnd().setTimeZone(timezone);
michael@0 295 }
michael@0 296
michael@0 297 /**
michael@0 298 * {@inheritDoc}
michael@0 299 */
michael@0 300 public final String toString() {
michael@0 301 final StringBuffer b = new StringBuffer();
michael@0 302 b.append(getStart());
michael@0 303 b.append('/');
michael@0 304 if (duration == null) {
michael@0 305 b.append(getEnd());
michael@0 306 }
michael@0 307 else {
michael@0 308 // b.append(DurationFormat.getInstance().format(duration));
michael@0 309 b.append(duration);
michael@0 310 }
michael@0 311 return b.toString();
michael@0 312 }
michael@0 313
michael@0 314 /**
michael@0 315 * {@inheritDoc}
michael@0 316 */
michael@0 317 public final int compareTo(final Object arg0) {
michael@0 318 return compareTo((Period) arg0);
michael@0 319 }
michael@0 320
michael@0 321 /**
michael@0 322 * Compares the specified period with this period.
michael@0 323 *
michael@0 324 * @param arg0 a period to compare with this one
michael@0 325 * @return a postive value if this period is greater, negative if the other is
michael@0 326 * greater, or zero if they are equal
michael@0 327 */
michael@0 328 public final int compareTo(final Period arg0) {
michael@0 329 // Throws documented exception if type is wrong or parameter is null
michael@0 330 if (arg0 == null) {
michael@0 331 throw new ClassCastException("Cannot compare this object to null");
michael@0 332 }
michael@0 333 final int startCompare = getStart().compareTo(arg0.getStart());
michael@0 334 if (startCompare != 0) {
michael@0 335 return startCompare;
michael@0 336 }
michael@0 337 // start dates are equal, compare end dates..
michael@0 338 else if (duration == null) {
michael@0 339 final int endCompare = getEnd().compareTo(arg0.getEnd());
michael@0 340 if (endCompare != 0) {
michael@0 341 return endCompare;
michael@0 342 }
michael@0 343 }
michael@0 344 // ..or durations
michael@0 345 return getDuration().compareTo(arg0.getDuration());
michael@0 346 }
michael@0 347
michael@0 348 /**
michael@0 349 * {@inheritDoc}
michael@0 350 */
michael@0 351 public final boolean equals(final Object o) {
michael@0 352 if (this == o) {
michael@0 353 return true;
michael@0 354 }
michael@0 355 if (!(o instanceof Period)) {
michael@0 356 return false;
michael@0 357 }
michael@0 358
michael@0 359 final Period period = (Period) o;
michael@0 360 return new EqualsBuilder().append(getStart(), period.getStart())
michael@0 361 .append(getEnd(), period.getEnd()).isEquals();
michael@0 362 }
michael@0 363
michael@0 364 /**
michael@0 365 * {@inheritDoc}
michael@0 366 */
michael@0 367 public final int hashCode() {
michael@0 368 return new HashCodeBuilder().append(getStart())
michael@0 369 .append((duration == null) ? (Object) getEnd() : duration).toHashCode();
michael@0 370 }
michael@0 371 }

mercurial