001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.acacia.shared; 029 030import org.opencms.util.CmsUUID; 031 032import java.util.Date; 033import java.util.Objects; 034import java.util.SortedSet; 035import java.util.TreeSet; 036 037/** The base class for implementations of serial date values. */ 038public class A_CmsSerialDateValue implements I_CmsSerialDateValue { 039 040 /** Start date and time of the first event in the series. */ 041 private Date m_start; 042 /** End date and time of the first event in the series. */ 043 private Date m_end; 044 /** Last day events of the series should take place. */ 045 private Date m_seriesEndDate; 046 /** Maximal number of occurrences of the event. */ 047 private int m_seriesOccurrences; 048 /** The interval between two events (e.g., number of days, weeks, month, years). */ 049 private int m_interval; 050 /** The day of the month when the event should happen. */ 051 private int m_dayOfMonth; 052 /** The weekdays at which the event should happen. */ 053 private final SortedSet<WeekDay> m_weekDays = new TreeSet<>(); 054 /** The recursion pattern of the event series. */ 055 private PatternType m_patterntype; 056 /** The weeks in a month where the event should happen. */ 057 private final SortedSet<WeekOfMonth> m_weeksOfMonth = new TreeSet<>(); 058 /** Dates in the event series, where the event is not taking place. */ 059 private final SortedSet<Date> m_exceptions = new TreeSet<>(); 060 /** Individual dates, where the event takes place. */ 061 private final SortedSet<Date> m_individualDates = new TreeSet<>(); 062 /** Flag, indicating if the event should take place on every working day. */ 063 private boolean m_isEveryWorkingDay; 064 /** Flag, indicating if the event lasts all the day. */ 065 private boolean m_isWholeDay; 066 /** Month in which the event takes place. */ 067 private Month m_month = Month.JANUARY; 068 /** The end type of the series. */ 069 private EndType m_endType; 070 /** The series content, the current value is extracted from. */ 071 private CmsUUID m_parentSeriesId; 072 /** Flag, indicating if the events are "current" till their end. */ 073 private boolean m_currentTillEnd = true; 074 075 /** 076 * Add a date where the event should not take place, even if they are part of the series. 077 * @param date the date to add as exception. 078 */ 079 public void addException(Date date) { 080 081 if (null != date) { 082 m_exceptions.add(date); 083 } 084 085 } 086 087 /** 088 * Add a week of month. 089 * @param week the week to add. 090 */ 091 public final void addWeekOfMonth(WeekOfMonth week) { 092 093 m_weeksOfMonth.add(week); 094 } 095 096 /** 097 * Clear the exceptions. 098 */ 099 public final void clearExceptions() { 100 101 m_exceptions.clear(); 102 103 } 104 105 /** 106 * Clear the individual dates. 107 */ 108 public final void clearIndividualDates() { 109 110 m_individualDates.clear(); 111 112 } 113 114 /** 115 * Clear the week days. 116 */ 117 public final void clearWeekDays() { 118 119 m_weekDays.clear(); 120 121 } 122 123 /** 124 * Clear the weeks of month. 125 */ 126 public final void clearWeeksOfMonth() { 127 128 m_weeksOfMonth.clear(); 129 130 } 131 132 /** 133 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#endsAtMidNight() 134 */ 135 @SuppressWarnings("deprecation") 136 public boolean endsAtMidNight() { 137 138 Date end = getEnd(); 139 return (end != null) 140 && (end.getHours() == 0) 141 && (end.getMinutes() == 0) 142 && (end.getSeconds() == 0) 143 && ((end.getTime() % 1000) == 0); 144 } 145 146 /** 147 * @see java.lang.Object#equals(java.lang.Object) 148 */ 149 //TODO: Rework! 150 @Override 151 public final boolean equals(Object o) { 152 153 if (o instanceof I_CmsSerialDateValue) { 154 I_CmsSerialDateValue val = (I_CmsSerialDateValue)o; 155 return (val.getDayOfMonth() == this.getDayOfMonth()) 156 && (val.isEveryWorkingDay() == this.isEveryWorkingDay()) 157 && (val.isWholeDay() == this.isWholeDay()) 158 && Objects.equals(val.getEnd(), this.getEnd()) 159 && Objects.equals(val.getEndType(), this.getEndType()) 160 && Objects.equals(val.getExceptions(), this.getExceptions()) 161 && Objects.equals(val.getIndividualDates(), this.getIndividualDates()) 162 && (val.getInterval() == this.getInterval()) 163 && Objects.equals(val.getMonth(), this.getMonth()) 164 && (val.getOccurrences() == this.getOccurrences()) 165 && Objects.equals(val.getPatternType(), this.getPatternType()) 166 && Objects.equals(val.getSeriesEndDate(), this.getSeriesEndDate()) 167 && Objects.equals(val.getStart(), this.getStart()) 168 && Objects.equals(val.getWeekDay(), this.getWeekDay()) 169 && Objects.equals(val.getWeekDays(), this.getWeekDays()) 170 && Objects.equals(val.getWeekOfMonth(), this.getWeekOfMonth()) 171 && Objects.equals(val.getWeeksOfMonth(), this.getWeeksOfMonth()) 172 && Objects.equals(val.getParentSeriesId(), this.getParentSeriesId()); 173 } 174 return false; 175 } 176 177 /** 178 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getDateType() 179 */ 180 public DateType getDateType() { 181 182 if (!Objects.equals(getPatternType(), PatternType.NONE)) { 183 return DateType.SERIES; 184 } 185 if (isFromOtherSeries()) { 186 return DateType.EXTRACTED; 187 } 188 return DateType.SINGLE; 189 } 190 191 /** 192 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getDayOfMonth() 193 */ 194 public final int getDayOfMonth() { 195 196 return m_dayOfMonth; 197 } 198 199 /** 200 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getEnd() 201 */ 202 public final Date getEnd() { 203 204 return m_end; 205 } 206 207 /** 208 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getEndType() 209 */ 210 public final EndType getEndType() { 211 212 return m_endType; 213 } 214 215 /** 216 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getExceptions() 217 */ 218 public final SortedSet<Date> getExceptions() { 219 220 return new TreeSet<>(m_exceptions); 221 } 222 223 /** 224 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getIndividualDates() 225 */ 226 public final SortedSet<Date> getIndividualDates() { 227 228 return new TreeSet<>(m_individualDates); 229 } 230 231 /** 232 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getInterval() 233 */ 234 public final int getInterval() { 235 236 return m_interval; 237 } 238 239 /** 240 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getMonth() 241 */ 242 public final Month getMonth() { 243 244 return m_month; 245 } 246 247 /** 248 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getOccurrences() 249 */ 250 public final int getOccurrences() { 251 252 return m_seriesOccurrences; 253 } 254 255 /** 256 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getParentSeriesId() 257 */ 258 public CmsUUID getParentSeriesId() { 259 260 return m_parentSeriesId; 261 } 262 263 /** 264 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getPatternType() 265 */ 266 public final PatternType getPatternType() { 267 268 return m_patterntype; 269 } 270 271 /** 272 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getSeriesEndDate() 273 */ 274 public final Date getSeriesEndDate() { 275 276 return m_seriesEndDate; 277 } 278 279 /** 280 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getStart() 281 */ 282 public final Date getStart() { 283 284 return m_start; 285 } 286 287 /** 288 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getWeekDay() 289 */ 290 public final WeekDay getWeekDay() { 291 292 if (m_weekDays.size() > 0) { 293 return m_weekDays.first(); 294 } 295 return null; 296 } 297 298 /** 299 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getWeekDays() 300 */ 301 public final SortedSet<WeekDay> getWeekDays() { 302 303 return new TreeSet<>(m_weekDays); 304 } 305 306 /** 307 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getWeekOfMonth() 308 */ 309 public final WeekOfMonth getWeekOfMonth() { 310 311 if (m_weeksOfMonth.size() > 0) { 312 return m_weeksOfMonth.iterator().next(); 313 } 314 return null; 315 } 316 317 /** 318 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#getWeeksOfMonth() 319 */ 320 @Override 321 public final SortedSet<WeekOfMonth> getWeeksOfMonth() { 322 323 return new TreeSet<>(m_weeksOfMonth); 324 } 325 326 /** 327 * Returns a flag, indicating if exceptions are present. 328 * @return a flag, indicating if exceptions are present. 329 */ 330 public final boolean hasExceptions() { 331 332 return !getExceptions().isEmpty(); 333 } 334 335 /** 336 * @see java.lang.Object#hashCode() 337 */ 338 // TODO: Rework 339 @Override 340 public final int hashCode() { 341 342 return Objects.hash( 343 Boolean.valueOf(this.isEveryWorkingDay()), 344 Boolean.valueOf(this.isWholeDay()), 345 Integer.valueOf(this.getDayOfMonth()), 346 this.getEnd(), 347 this.getEndType(), 348 this.getExceptions(), 349 this.getIndividualDates(), 350 Integer.valueOf(this.getInterval()), 351 this.getMonth(), 352 Integer.valueOf(this.getOccurrences()), 353 this.getPatternType(), 354 this.getSeriesEndDate(), 355 this.getStart(), 356 this.getWeekDay(), 357 this.getWeekDays(), 358 this.getWeekOfMonth(), 359 this.getWeeksOfMonth(), 360 Boolean.valueOf(this.isCurrentTillEnd()), 361 this.getParentSeriesId()); 362 363 } 364 365 /** 366 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#isCurrentTillEnd() 367 */ 368 public boolean isCurrentTillEnd() { 369 370 return m_currentTillEnd; 371 } 372 373 /** 374 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#isEveryWorkingDay() 375 */ 376 public final boolean isEveryWorkingDay() { 377 378 return m_isEveryWorkingDay; 379 } 380 381 /** 382 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#isFromOtherSeries() 383 */ 384 @Override 385 public boolean isFromOtherSeries() { 386 387 return null != m_parentSeriesId; 388 } 389 390 /** 391 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#isValid() 392 */ 393 public final boolean isValid() { 394 395 return isStartSet() && isEndValid() && isPatternValid() && isDurationValid(); 396 } 397 398 /** 399 * @see org.opencms.acacia.shared.I_CmsSerialDateValue#isWholeDay() 400 */ 401 public final boolean isWholeDay() { 402 403 return m_isWholeDay; 404 } 405 406 /** 407 * Remove a week of month. 408 * @param week the week to remove. 409 */ 410 public final void removeWeekOfMonth(WeekOfMonth week) { 411 412 m_weeksOfMonth.remove(week); 413 } 414 415 /** 416 * Set the flag, indicating if the event is treated as "current" till the end. 417 * @param isCurrentTillEnd the flag, indicating if the event is treated as "current" till the end. 418 */ 419 public final void setCurrentTillEnd(Boolean isCurrentTillEnd) { 420 421 m_currentTillEnd = (null == isCurrentTillEnd) || isCurrentTillEnd.booleanValue(); 422 } 423 424 /** 425 * Set the day of month. 426 * @param dayOfMonth the day of month to set. 427 */ 428 public final void setDayOfMonth(int dayOfMonth) { 429 430 m_dayOfMonth = dayOfMonth; 431 } 432 433 /** 434 * Set the end time for the event. 435 * @param date the end time to set. 436 */ 437 public final void setEnd(Date date) { 438 439 m_end = date; 440 } 441 442 /** 443 * Set the end type of the series. 444 * @param endType the end type to set. 445 */ 446 public final void setEndType(EndType endType) { 447 448 m_endType = null == endType ? EndType.SINGLE : endType; 449 } 450 451 /** 452 * Set the flag, indicating if the event should take place every working day. 453 * @param isEveryWorkingDay the flag, indicating if the event should take place every working day. 454 */ 455 public final void setEveryWorkingDay(Boolean isEveryWorkingDay) { 456 457 m_isEveryWorkingDay = null == isEveryWorkingDay ? false : isEveryWorkingDay.booleanValue(); 458 459 } 460 461 /** 462 * Set dates where the event should not take place, even if they are part of the series. 463 * @param dates dates to set. 464 */ 465 public final void setExceptions(SortedSet<Date> dates) { 466 467 m_exceptions.clear(); 468 if (null != dates) { 469 m_exceptions.addAll(dates); 470 } 471 472 } 473 474 /** 475 * Set the individual dates where the event should take place. 476 * @param dates the dates to set. 477 */ 478 public final void setIndividualDates(SortedSet<Date> dates) { 479 480 m_individualDates.clear(); 481 if (null != dates) { 482 m_individualDates.addAll(dates); 483 } 484 for (Date d : getExceptions()) { 485 if (!m_individualDates.contains(d)) { 486 m_exceptions.remove(d); 487 } 488 } 489 490 } 491 492 /** 493 * Set the pattern type specific interval between two events, e.g., number of days, weeks, month, years. 494 * @param interval the interval to set. 495 */ 496 public final void setInterval(int interval) { 497 498 m_interval = interval; 499 } 500 501 /** 502 * Set the month in which the event should take place. 503 * @param month the month to set. 504 */ 505 public final void setMonth(Month month) { 506 507 m_month = null == month ? Month.JANUARY : month; 508 509 } 510 511 /** 512 * Set the number of occurrences of the event. 513 * @param occurrences the number of occurrences to set. 514 */ 515 public final void setOccurrences(int occurrences) { 516 517 m_seriesOccurrences = occurrences; 518 519 } 520 521 /** 522 * Set the series, the current event (series) is extracted from. 523 * @param structureId the structure id of the series content, the event is extracted from. 524 */ 525 public final void setParentSeriesId(CmsUUID structureId) { 526 527 m_parentSeriesId = structureId; 528 529 } 530 531 /** 532 * Set the pattern type of the event series.<p> 533 * 534 * All pattern specific values are reset. 535 * 536 * @param type the pattern type to set. 537 */ 538 public final void setPatternType(PatternType type) { 539 540 m_patterntype = null == type ? PatternType.NONE : type; 541 } 542 543 /** 544 * Set the last day events of the series should occur. 545 * @param date the day to set. 546 */ 547 public final void setSeriesEndDate(Date date) { 548 549 m_seriesEndDate = date; 550 551 } 552 553 /** 554 * Set the start time of the events. Unless you specify a single event, the day information is discarded. 555 * @param date the time to set. 556 */ 557 public final void setStart(Date date) { 558 559 m_start = date; 560 } 561 562 /** 563 * Set the week day the events should occur. 564 * @param weekDay the week day to set. 565 */ 566 public final void setWeekDay(WeekDay weekDay) { 567 568 SortedSet<WeekDay> wds = new TreeSet<>(); 569 if (null != weekDay) { 570 wds.add(weekDay); 571 } 572 setWeekDays(wds); 573 574 } 575 576 /** 577 * Set the week days the events should occur. 578 * @param weekDays the week days to set. 579 */ 580 public final void setWeekDays(SortedSet<WeekDay> weekDays) { 581 582 m_weekDays.clear(); 583 if (null != weekDays) { 584 m_weekDays.addAll(weekDays); 585 } 586 } 587 588 /** 589 * Set the week of the month the events should occur. 590 * @param weekOfMonth the week of month to set (first to fifth, where fifth means last). 591 */ 592 public final void setWeekOfMonth(WeekOfMonth weekOfMonth) { 593 594 SortedSet<WeekOfMonth> woms = new TreeSet<>(); 595 if (null != weekOfMonth) { 596 woms.add(weekOfMonth); 597 } 598 setWeeksOfMonth(woms); 599 } 600 601 /** 602 * Set the weeks of the month the events should occur. 603 * @param weeksOfMonth the weeks of month to set (first to fifth, where fifth means last). 604 */ 605 public final void setWeeksOfMonth(SortedSet<WeekOfMonth> weeksOfMonth) { 606 607 m_weeksOfMonth.clear(); 608 if (null != weeksOfMonth) { 609 m_weeksOfMonth.addAll(weeksOfMonth); 610 } 611 612 } 613 614 /** 615 * Set the flag, indicating if the event last the whole day/whole days. 616 * @param isWholeDay the flag to set 617 */ 618 public final void setWholeDay(Boolean isWholeDay) { 619 620 m_isWholeDay = (null != isWholeDay) && isWholeDay.equals(Boolean.TRUE); 621 622 } 623 624 /** 625 * Checks, if a valid day of month is set. 626 * @return a flag, indicating if the set day of month is valid. 627 */ 628 protected final boolean isDayOfMonthValid() { 629 630 return (getDayOfMonth() > 0) && (getDayOfMonth() <= (getMonth() == null ? 31 : getMonth().getMaximalDay())); 631 } 632 633 /** 634 * Checks if the duration option is valid. 635 * 636 * NOTE: This does NOT check, if too many events are specified. 637 * 638 * @return a flag, indicating if the duration option is valid. 639 */ 640 protected final boolean isDurationValid() { 641 642 if (isValidEndTypeForPattern()) { 643 switch (getEndType()) { 644 case DATE: 645 return (getStart().getTime() < (getSeriesEndDate().getTime() + DAY_IN_MILLIS)); 646 case TIMES: 647 return getOccurrences() > 0; 648 case SINGLE: 649 return true; 650 default: 651 return false; 652 } 653 } else { 654 return false; 655 } 656 } 657 658 /** Check, if the end date of the single event is valid, i.e., either not set or not before the start date. 659 * 660 * @return a flag, indicating if the end date is set. 661 */ 662 protected final boolean isEndValid() { 663 664 return (getEnd() == null) || !getEnd().before(getStart()); 665 } 666 667 /** Checks, if a valid interval is specified. 668 * 669 * @return a flag, indicating if the specified interval is valid. 670 */ 671 protected final boolean isIntervalValid() { 672 673 return getInterval() > 0; 674 } 675 676 /** 677 * Checks, if a month is specified. 678 * @return flag, indicating if a month is specified. 679 */ 680 protected final boolean isMonthSet() { 681 682 return getMonth() != null; 683 } 684 685 /** 686 * Checks, if all values necessary for a specific pattern are valid. 687 * @return a flag, indicating if all values required for the pattern are valid. 688 */ 689 protected final boolean isPatternValid() { 690 691 switch (getPatternType()) { 692 case DAILY: 693 return isEveryWorkingDay() || isIntervalValid(); 694 case WEEKLY: 695 return isIntervalValid() && isWeekDaySet(); 696 case MONTHLY: 697 return isIntervalValid() && isWeekDaySet() ? isWeekOfMonthSet() : isDayOfMonthValid(); 698 case YEARLY: 699 return isMonthSet() && isWeekDaySet() ? isWeekOfMonthSet() : isDayOfMonthValid(); 700 case INDIVIDUAL: 701 case NONE: 702 return true; 703 default: 704 return false; 705 } 706 } 707 708 /** Check, if the start time stamp is set. 709 * 710 * @return a flag, indicating if a start date is set. 711 */ 712 protected final boolean isStartSet() { 713 714 return null != getStart(); 715 } 716 717 /** 718 * Checks, if the end type is valid for the set pattern type. 719 * @return a flag, indicating if the end type is valid for the pattern type. 720 */ 721 protected final boolean isValidEndTypeForPattern() { 722 723 if (getEndType() == null) { 724 return false; 725 } 726 switch (getPatternType()) { 727 case DAILY: 728 case WEEKLY: 729 case MONTHLY: 730 case YEARLY: 731 return (getEndType().equals(EndType.DATE) || getEndType().equals(EndType.TIMES)); 732 case INDIVIDUAL: 733 case NONE: 734 return getEndType().equals(EndType.SINGLE); 735 default: 736 return false; 737 } 738 } 739 740 /** 741 * Checks if at least one weekday is specified. 742 * @return a flag, indicating if at least one weekday is specified. 743 */ 744 protected final boolean isWeekDaySet() { 745 746 return !m_weekDays.isEmpty(); 747 } 748 749 /** 750 * Checks, if at least one week of month is set. 751 * @return a flag, indicating if at least one week of month is set. 752 */ 753 protected final boolean isWeekOfMonthSet() { 754 755 return !m_weeksOfMonth.isEmpty(); 756 } 757 758 /** 759 * Sets the value to a default. 760 */ 761 protected final void setDefaultValue() { 762 763 m_start = null; 764 m_end = null; 765 m_patterntype = PatternType.NONE; 766 m_dayOfMonth = 0; 767 m_exceptions.clear(); 768 m_individualDates.clear(); 769 m_interval = 0; 770 m_isEveryWorkingDay = false; 771 m_isWholeDay = false; 772 m_month = Month.JANUARY; 773 m_seriesEndDate = null; 774 m_seriesOccurrences = 0; 775 m_weekDays.clear(); 776 m_weeksOfMonth.clear(); 777 m_endType = EndType.SINGLE; 778 m_parentSeriesId = null; 779 } 780 781 /** 782 * Set the end type as derived from other values. 783 */ 784 protected final void setDerivedEndType() { 785 786 m_endType = getPatternType().equals(PatternType.NONE) || getPatternType().equals(PatternType.INDIVIDUAL) 787 ? EndType.SINGLE 788 : null != getSeriesEndDate() ? EndType.DATE : EndType.TIMES; 789 } 790}