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.widgets.serialdate; 029 030import org.opencms.acacia.shared.CmsSerialDateUtil; 031import org.opencms.acacia.shared.I_CmsSerialDateValue.EndType; 032 033import java.util.Calendar; 034import java.util.Date; 035import java.util.GregorianCalendar; 036import java.util.SortedSet; 037import java.util.TreeSet; 038 039/** Abstract base class for serial date beans. 040 * It deals with information common for all serial dates and already provides part of the implementation 041 * for calculating all dates of the series. 042 */ 043public abstract class A_CmsSerialDateBean implements I_CmsSerialDateBean { 044 045 /** The maximal number of occurrences that is allowed. */ 046 public static final int MAX_OCCURRENCES = 100; 047 /** The start date and time of the (potentially) first event of the series. */ 048 protected Calendar m_startDate; 049 /** The end date and time of the (potentially) first event of the series. */ 050 protected Calendar m_endDate; 051 /** The maximal number of occurrences of the event. */ 052 protected int m_occurrences; 053 /** The date of the last day, the event should occur. */ 054 protected Calendar m_serialEndDate; 055 /** The exact time the event should occur latest (in milliseconds). */ 056 protected long m_endMillis; 057 /** The end type of the series. */ 058 protected EndType m_endType = null; 059 /** Variable for caching the dates of the event after lazy calculation. */ 060 protected SortedSet<Date> m_dates; 061 /** Variable for caching the dates of the event after lazy calculation. */ 062 protected SortedSet<Date> m_allDates; 063 /** Variable for caching the dates as long. */ 064 protected SortedSet<Long> m_datesInMillis; 065 /** The list of exceptions. */ 066 protected final SortedSet<Date> m_exceptions = new TreeSet<>(); 067 /** A flag, indicating if the configuration specifies too many occurrences. */ 068 private Boolean m_hasTooManyOccurrences; 069 070 /** Constructor for the abstract class for serial date beans. 071 * It takes all the arguments that are common for serial dates and should be called from each sub-class. 072 * 073 * @param startDate the start date of the series as provided by the serial date widget. 074 * @param endDate the end date of the series as provided by the serial date widget. 075 * @param isWholeDay flag, indicating if the event lasts the whole day. 076 * @param endType the end type of the series as provided by the serial date widget. 077 * @param serialEndDate the end date of the series as provided by the serial date widget. 078 * @param occurrences the maximal number of occurrences of the event as provided by the serial date widget. 079 * If endType is DATE, this parameter is ignored. 080 * @param exceptions the dates not part of the list. 081 */ 082 public A_CmsSerialDateBean( 083 Date startDate, 084 Date endDate, 085 boolean isWholeDay, 086 EndType endType, 087 Date serialEndDate, 088 int occurrences, 089 SortedSet<Date> exceptions) { 090 091 m_startDate = new GregorianCalendar(); 092 m_endDate = new GregorianCalendar(); 093 m_startDate.setTime(startDate); 094 m_endDate.setTime(endDate == null ? startDate : endDate); 095 if (isWholeDay) { 096 m_startDate.set(Calendar.HOUR_OF_DAY, 0); 097 m_startDate.set(Calendar.MINUTE, 0); 098 m_startDate.set(Calendar.SECOND, 0); 099 m_startDate.set(Calendar.MILLISECOND, 0); 100 m_endDate.set(Calendar.HOUR_OF_DAY, 0); 101 m_endDate.set(Calendar.MINUTE, 0); 102 m_endDate.set(Calendar.SECOND, 0); 103 m_endDate.set(Calendar.MILLISECOND, 0); 104 m_endDate.add(Calendar.DATE, 1); 105 } 106 m_endType = endType; 107 switch (m_endType) { 108 case DATE: 109 m_serialEndDate = new GregorianCalendar(); 110 m_serialEndDate.setTime(serialEndDate); 111 Calendar dayAfterEnd = new GregorianCalendar( 112 m_serialEndDate.get(Calendar.YEAR), 113 m_serialEndDate.get(Calendar.MONTH), 114 m_serialEndDate.get(Calendar.DATE)); 115 dayAfterEnd.add(Calendar.DATE, 1); 116 m_endMillis = dayAfterEnd.getTimeInMillis(); 117 break; 118 case TIMES: 119 m_occurrences = occurrences; 120 break; 121 case SINGLE: 122 m_occurrences = 1; 123 break; 124 default: 125 break; 126 } 127 if (null != exceptions) { 128 m_exceptions.addAll(exceptions); 129 } 130 } 131 132 /** 133 * @see org.opencms.widgets.serialdate.I_CmsSerialDateBean#getDates() 134 */ 135 @Override 136 public SortedSet<Date> getDates() { 137 138 if (null == m_dates) { 139 m_dates = filterExceptions(calculateDates()); 140 } 141 return m_dates; 142 } 143 144 /** 145 * @see org.opencms.widgets.serialdate.I_CmsSerialDateBean#getDatesAsLong() 146 */ 147 @Override 148 public SortedSet<Long> getDatesAsLong() { 149 150 if (null == m_datesInMillis) { 151 SortedSet<Date> dates = getDates(); 152 m_datesInMillis = new TreeSet<>(); 153 for (Date d : dates) { 154 m_datesInMillis.add(Long.valueOf(d.getTime())); 155 } 156 } 157 return m_datesInMillis; 158 } 159 160 /** 161 * @see org.opencms.widgets.serialdate.I_CmsSerialDateBean#getEventDuration() 162 */ 163 @Override 164 public Long getEventDuration() { 165 166 return (null != m_endDate) && (null != m_startDate) 167 ? Long.valueOf(m_endDate.getTimeInMillis() - m_startDate.getTimeInMillis()) 168 : null; 169 } 170 171 /** 172 * @see org.opencms.widgets.serialdate.I_CmsSerialDateBean#getExceptions() 173 */ 174 @Override 175 public SortedSet<Date> getExceptions() { 176 177 return m_exceptions; 178 } 179 180 /** 181 * Returns the occurrences of a defined series interval, used for the series end type.<p> 182 * 183 * @return the occurrences of a defined series interval, used for the series end type 184 */ 185 public int getOccurrences() { 186 187 return Math.min(m_occurrences, CmsSerialDateUtil.getMaxEvents()); 188 } 189 190 /** 191 * Returns the serial end date if the series is of type: ending at specific date.<p> 192 * 193 * @return the serial end date if the series is of type: ending at specific date 194 */ 195 public Calendar getSerialEndDate() { 196 197 return m_serialEndDate; 198 } 199 200 /** 201 * Returns the end type of the date series (never, n times, specific date).<p> 202 * 203 * @return the end type of the date series 204 */ 205 public EndType getSerialEndType() { 206 207 return m_endType; 208 } 209 210 /** 211 * Returns the date provided as the earliest date the event should take place. 212 * The time is set to the starting time of the event. 213 * 214 * @return date where the event should take place earliest with time set to the date's starting time. 215 */ 216 public Calendar getStartDate() { 217 218 return m_startDate; 219 } 220 221 /** 222 * @see org.opencms.widgets.serialdate.I_CmsSerialDateBean#hasTooManyDates() 223 */ 224 @Override 225 public boolean hasTooManyDates() { 226 227 if (null == m_hasTooManyOccurrences) { 228 switch (getSerialEndType()) { 229 case SINGLE: 230 m_hasTooManyOccurrences = Boolean.FALSE; 231 break; 232 case TIMES: 233 m_hasTooManyOccurrences = Boolean.valueOf(m_occurrences > CmsSerialDateUtil.getMaxEvents()); 234 break; 235 case DATE: 236 m_hasTooManyOccurrences = Boolean.FALSE; 237 calculateDates(); // this will set the value automatically to TRUE in case there are too many dates. 238 break; 239 default: 240 throw new IllegalArgumentException(); 241 } 242 } 243 return m_hasTooManyOccurrences.booleanValue(); 244 } 245 246 /** 247 * Generates the first date of the series. 248 * 249 * @return the first date of the series. 250 */ 251 abstract protected Calendar getFirstDate(); 252 253 /** 254 * Check, if the series can have at least one event/date. 255 * @return <code>true</code> if the series can be non-empty, <code>false</code> otherwise. 256 */ 257 abstract protected boolean isAnyDatePossible(); 258 259 /** 260 * Check if the provided date or any date after it are part of the series. 261 * @param nextDate the current date to check. 262 * @param previousOccurrences the number of events of the series that took place before the date to check. 263 * @return <code>true</code> if more dates (including the provided one) could be in the series, <code>false</code> otherwise. 264 */ 265 protected boolean showMoreEntries(Calendar nextDate, int previousOccurrences) { 266 267 switch (getSerialEndType()) { 268 case DATE: 269 boolean moreByDate = nextDate.getTimeInMillis() < m_endMillis; 270 boolean moreByOccurrences = previousOccurrences < CmsSerialDateUtil.getMaxEvents(); 271 if (moreByDate && !moreByOccurrences) { 272 m_hasTooManyOccurrences = Boolean.TRUE; 273 } 274 return moreByDate && moreByOccurrences; 275 case TIMES: 276 case SINGLE: 277 return previousOccurrences < getOccurrences(); 278 default: 279 throw new IllegalArgumentException(); 280 } 281 } 282 283 /** 284 * Starting with a date that's in the series, the next date is created. 285 * @param date the current event date for a event in the series, which is adjusted to the next date potentially in the series. 286 */ 287 abstract protected void toNextDate(Calendar date); 288 289 /** 290 * Calculates all dates of the series. 291 * @return all dates of the series in milliseconds. 292 */ 293 private SortedSet<Date> calculateDates() { 294 295 if (null == m_allDates) { 296 SortedSet<Date> result = new TreeSet<>(); 297 if (isAnyDatePossible()) { 298 Calendar date = getFirstDate(); 299 int previousOccurrences = 0; 300 while (showMoreEntries(date, previousOccurrences)) { 301 result.add(date.getTime()); 302 toNextDate(date); 303 previousOccurrences++; 304 } 305 } 306 m_allDates = result; 307 } 308 return m_allDates; 309 } 310 311 /** 312 * Filters all exceptions from the provided dates. 313 * @param dates the dates to filter. 314 * @return the provided dates, except the ones that match some exception. 315 */ 316 private SortedSet<Date> filterExceptions(SortedSet<Date> dates) { 317 318 SortedSet<Date> result = new TreeSet<Date>(); 319 for (Date d : dates) { 320 if (!m_exceptions.contains(d)) { 321 result.add(d); 322 } 323 } 324 return result; 325 } 326 327}