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 m_startDate = new GregorianCalendar(); 091 m_endDate = new GregorianCalendar(); 092 m_startDate.setTime(startDate); 093 m_endDate.setTime(endDate == null ? startDate : endDate); 094 if (isWholeDay) { 095 m_startDate.set(Calendar.HOUR_OF_DAY, 0); 096 m_startDate.set(Calendar.MINUTE, 0); 097 m_startDate.set(Calendar.SECOND, 0); 098 m_startDate.set(Calendar.MILLISECOND, 0); 099 m_endDate.set(Calendar.HOUR_OF_DAY, 0); 100 m_endDate.set(Calendar.MINUTE, 0); 101 m_endDate.set(Calendar.SECOND, 0); 102 m_endDate.set(Calendar.MILLISECOND, 0); 103 m_endDate.add(Calendar.DATE, 1); 104 } 105 m_endType = endType; 106 switch (m_endType) { 107 case DATE: 108 m_serialEndDate = new GregorianCalendar(); 109 m_serialEndDate.setTime(serialEndDate); 110 Calendar dayAfterEnd = new GregorianCalendar( 111 m_serialEndDate.get(Calendar.YEAR), 112 m_serialEndDate.get(Calendar.MONTH), 113 m_serialEndDate.get(Calendar.DATE)); 114 dayAfterEnd.add(Calendar.DATE, 1); 115 m_endMillis = dayAfterEnd.getTimeInMillis(); 116 break; 117 case TIMES: 118 m_occurrences = occurrences; 119 break; 120 case SINGLE: 121 m_occurrences = 1; 122 break; 123 default: 124 break; 125 } 126 if (null != exceptions) { 127 m_exceptions.addAll(exceptions); 128 } 129 } 130 131 /** 132 * @see org.opencms.widgets.serialdate.I_CmsSerialDateBean#getDates() 133 */ 134 @Override 135 public SortedSet<Date> getDates() { 136 137 if (null == m_dates) { 138 m_dates = filterExceptions(calculateDates()); 139 } 140 return m_dates; 141 } 142 143 /** 144 * @see org.opencms.widgets.serialdate.I_CmsSerialDateBean#getDatesAsLong() 145 */ 146 @Override 147 public SortedSet<Long> getDatesAsLong() { 148 149 if (null == m_datesInMillis) { 150 SortedSet<Date> dates = getDates(); 151 m_datesInMillis = new TreeSet<>(); 152 for (Date d : dates) { 153 m_datesInMillis.add(Long.valueOf(d.getTime())); 154 } 155 } 156 return m_datesInMillis; 157 } 158 159 /** 160 * @see org.opencms.widgets.serialdate.I_CmsSerialDateBean#getEventDuration() 161 */ 162 @Override 163 public Long getEventDuration() { 164 165 return (null != m_endDate) && (null != m_startDate) 166 ? Long.valueOf(m_endDate.getTimeInMillis() - m_startDate.getTimeInMillis()) 167 : null; 168 } 169 170 /** 171 * @see org.opencms.widgets.serialdate.I_CmsSerialDateBean#getExceptions() 172 */ 173 @Override 174 public SortedSet<Date> getExceptions() { 175 176 return m_exceptions; 177 } 178 179 /** 180 * Returns the occurrences of a defined series interval, used for the series end type.<p> 181 * 182 * @return the occurrences of a defined series interval, used for the series end type 183 */ 184 public int getOccurrences() { 185 186 return Math.min(m_occurrences, CmsSerialDateUtil.getMaxEvents()); 187 } 188 189 /** 190 * Returns the serial end date if the series is of type: ending at specific date.<p> 191 * 192 * @return the serial end date if the series is of type: ending at specific date 193 */ 194 public Calendar getSerialEndDate() { 195 196 return m_serialEndDate; 197 } 198 199 /** 200 * Returns the end type of the date series (never, n times, specific date).<p> 201 * 202 * @return the end type of the date series 203 */ 204 public EndType getSerialEndType() { 205 206 return m_endType; 207 } 208 209 /** 210 * Returns the date provided as the earliest date the event should take place. 211 * The time is set to the starting time of the event. 212 * 213 * @return date where the event should take place earliest with time set to the date's starting time. 214 */ 215 public Calendar getStartDate() { 216 217 return m_startDate; 218 } 219 220 /** 221 * @see org.opencms.widgets.serialdate.I_CmsSerialDateBean#hasTooManyDates() 222 */ 223 @Override 224 public boolean hasTooManyDates() { 225 226 if (null == m_hasTooManyOccurrences) { 227 switch (getSerialEndType()) { 228 case SINGLE: 229 m_hasTooManyOccurrences = Boolean.FALSE; 230 break; 231 case TIMES: 232 m_hasTooManyOccurrences = Boolean.valueOf(m_occurrences > CmsSerialDateUtil.getMaxEvents()); 233 break; 234 case DATE: 235 m_hasTooManyOccurrences = Boolean.FALSE; 236 calculateDates(); // this will set the value automatically to TRUE in case there are too many dates. 237 break; 238 default: 239 throw new IllegalArgumentException(); 240 } 241 } 242 return m_hasTooManyOccurrences.booleanValue(); 243 } 244 245 /** 246 * Generates the first date of the series. 247 * 248 * @return the first date of the series. 249 */ 250 abstract protected Calendar getFirstDate(); 251 252 /** 253 * Check, if the series can have at least one event/date. 254 * @return <code>true</code> if the series can be non-empty, <code>false</code> otherwise. 255 */ 256 abstract protected boolean isAnyDatePossible(); 257 258 /** 259 * Check if the provided date or any date after it are part of the series. 260 * @param nextDate the current date to check. 261 * @param previousOccurrences the number of events of the series that took place before the date to check. 262 * @return <code>true</code> if more dates (including the provided one) could be in the series, <code>false</code> otherwise. 263 */ 264 protected boolean showMoreEntries(Calendar nextDate, int previousOccurrences) { 265 266 switch (getSerialEndType()) { 267 case DATE: 268 boolean moreByDate = nextDate.getTimeInMillis() < m_endMillis; 269 boolean moreByOccurrences = previousOccurrences < CmsSerialDateUtil.getMaxEvents(); 270 if (moreByDate && !moreByOccurrences) { 271 m_hasTooManyOccurrences = Boolean.TRUE; 272 } 273 return moreByDate && moreByOccurrences; 274 case TIMES: 275 case SINGLE: 276 return previousOccurrences < getOccurrences(); 277 default: 278 throw new IllegalArgumentException(); 279 } 280 } 281 282 /** 283 * Starting with a date that's in the series, the next date is created. 284 * @param date the current event date for a event in the series, which is adjusted to the next date potentially in the series. 285 */ 286 abstract protected void toNextDate(Calendar date); 287 288 /** 289 * Calculates all dates of the series. 290 * @return all dates of the series in milliseconds. 291 */ 292 private SortedSet<Date> calculateDates() { 293 294 if (null == m_allDates) { 295 SortedSet<Date> result = new TreeSet<>(); 296 if (isAnyDatePossible()) { 297 Calendar date = getFirstDate(); 298 int previousOccurrences = 0; 299 while (showMoreEntries(date, previousOccurrences)) { 300 result.add(date.getTime()); 301 toNextDate(date); 302 previousOccurrences++; 303 } 304 } 305 m_allDates = result; 306 } 307 return m_allDates; 308 } 309 310 /** 311 * Filters all exceptions from the provided dates. 312 * @param dates the dates to filter. 313 * @return the provided dates, except the ones that match some exception. 314 */ 315 private SortedSet<Date> filterExceptions(SortedSet<Date> dates) { 316 317 SortedSet<Date> result = new TreeSet<Date>(); 318 for (Date d : dates) { 319 if (!m_exceptions.contains(d)) { 320 result.add(d); 321 } 322 } 323 return result; 324 } 325 326}