|
Modified: ofbiz/branches/release09.04/framework/service/src/org/ofbiz/service/calendar/TemporalExpressions.java
URL: http://svn.apache.org/viewvc/ofbiz/branches/release09.04/framework/service/src/org/ofbiz/service/calendar/TemporalExpressions.java?rev=903125&r1=903124&r2=903125&view=diff ============================================================================== --- ofbiz/branches/release09.04/framework/service/src/org/ofbiz/service/calendar/TemporalExpressions.java (original) +++ ofbiz/branches/release09.04/framework/service/src/org/ofbiz/service/calendar/TemporalExpressions.java Tue Jan 26 08:28:39 2010 @@ -24,7 +24,9 @@ import java.util.Date; import java.util.Set; import java.util.TreeSet; + import org.ofbiz.base.util.Debug; +import org.ofbiz.base.util.UtilValidate; /** A collection of TemporalExpression classes. * <p>For the most part, these classes are immutable - with the exception @@ -35,259 +37,418 @@ public class TemporalExpressions implements Serializable { public static final String module = TemporalExpressions.class.getName(); public static final TemporalExpression NullExpression = new Null(); + // Expressions are evaluated from smallest unit of time to largest. + // When unit of time is the same, then they are evaluated from + // least ambiguous to most. Frequency should always be first - + // since it is the most specific. Date range should always be last. + // The idea is to evaluate all other expressions, then check to see + // if the result falls within the date range. + // Difference: adopts the sequence of its include expression + // Intersection: aggregates member expression sequence values + // Union: adopts the sequence of its first member expression + public static final int SEQUENCE_DATE_RANGE = 800; + public static final int SEQUENCE_DAY_IN_MONTH = 460; + public static final int SEQUENCE_DOM_RANGE = 400; + public static final int SEQUENCE_DOW_RANGE = 450; + public static final int SEQUENCE_FREQ = 100; + public static final int SEQUENCE_HOUR_RANGE = 300; + public static final int SEQUENCE_MINUTE_RANGE = 200; + public static final int SEQUENCE_MONTH_RANGE = 600; + public static final int SEQUENCE_TOD_RANGE = 350; - /** This class represents a null expression. */ - public static class Null extends TemporalExpression { - public Calendar first(Calendar cal) { - return null; - } - public boolean includesDate(Calendar cal) { - return false; - } - public Calendar next(Calendar cal) { - return null; - } - public void accept(TemporalExpressionVisitor visitor) { - visitor.visit(this); - } - } + /** A temporal expression that represents a range of dates. */ + public static class DateRange extends TemporalExpression { + protected final org.ofbiz.base.util.DateRange range; - /** This class represents a mathematical union of all of its - * member expressions. */ - public static class Union extends TemporalExpression { - protected final Set<TemporalExpression> expressionSet; + public DateRange(Date date) { + this(date, date); + } - public Union(Set<TemporalExpression> expressionSet) { - if (expressionSet == null) { - throw new IllegalArgumentException("expressionSet argument cannot be null"); - } - this.expressionSet = expressionSet; - if (containsExpression(this)) { - throw new IllegalArgumentException("recursive expression"); - } - if (this.expressionSet.size() > 0) { - TemporalExpression that = this.expressionSet.iterator().next(); - if (this.compareTo(that) > 0) { - this.sequence = that.sequence; - this.subSequence = that.subSequence; - } - } + public DateRange(Date start, Date end) { + this.range = new org.ofbiz.base.util.DateRange(start, end); + this.sequence = SEQUENCE_DATE_RANGE; if (Debug.verboseOn()) { Debug.logVerbose("Created " + this, module); } } + @Override + public void accept(TemporalExpressionVisitor visitor) { + visitor.visit(this); + } + + @Override public boolean equals(Object obj) { if (obj == this) { return true; } try { - return this.expressionSet.equals(((Union) obj).expressionSet); - } catch (Exception e) {} - return false; - } - - public String toString() { - return super.toString() + ", size = " + this.expressionSet.size(); - } - - public boolean includesDate(Calendar cal) { - for (TemporalExpression expression : this.expressionSet) { - if (expression.includesDate(cal)) { - return true; - } - } + return this.range.equals(((DateRange) obj).range); + } catch (ClassCastException e) {} return false; } + @Override public Calendar first(Calendar cal) { - for (TemporalExpression expression : this.expressionSet) { - Calendar first = expression.first(cal); - if (first != null && includesDate(first)) { - return first; - } - } - return null; - } - - public Calendar next(Calendar cal) { - Calendar result = null; - for (TemporalExpression expression : this.expressionSet) { - Calendar next = expression.next(cal); - if (next != null && includesDate(next)) { - if (result == null || next.before(result)) { - result = next; - } - } - } - return result; + return includesDate(cal) ? cal : null; } - public void accept(TemporalExpressionVisitor visitor) { - visitor.visit(this); + /** Returns the contained <code>org.ofbiz.base.util.DateRange</code>. + * @return The contained <code>org.ofbiz.base.util.DateRange</code> + */ + public org.ofbiz.base.util.DateRange getDateRange() { + return this.range; } - public Set<Date> getRange(org.ofbiz.base.util.DateRange range, Calendar cal) { - Set<Date> rawSet = new TreeSet<Date>(); - Set<Date> finalSet = new TreeSet<Date>(); - for (TemporalExpression expression : this.expressionSet) { - rawSet.addAll(expression.getRange(range, cal)); - } - Calendar checkCal = (Calendar) cal.clone(); - for (Date date : rawSet) { - checkCal.setTime(date); - if (includesDate(checkCal)) { - finalSet.add(date); - } - } - return finalSet; + @Override + public boolean includesDate(Calendar cal) { + return this.range.includesDate(cal.getTime()); } - /** Returns the member expression <code>Set</code>. The - * returned set is unmodifiable. - * @return The member expression <code>Set</code> - */ - public Set<TemporalExpression> getExpressionSet() { - return Collections.unmodifiableSet(this.expressionSet); + @Override + public Calendar next(Calendar cal, ExpressionContext context) { + return includesDate(cal) ? cal : null; } - protected boolean containsExpression(TemporalExpression expression) { - for (TemporalExpression setItem : this.expressionSet) { - if (setItem.containsExpression(expression)) { - return true; - } - } - return false; + @Override + public String toString() { + return super.toString() + ", start = " + this.range.start() + ", end = " + this.range.end(); } } - /** This class represents a mathematical intersection of all of its - * member expressions. */ - public static class Intersection extends TemporalExpression { - protected final Set<TemporalExpression> expressionSet; + /** A temporal expression that represents a day in the month. */ + public static class DayInMonth extends TemporalExpression { + protected final int dayOfWeek; + protected final int occurrence; - public Intersection(Set<TemporalExpression> expressionSet) { - if (expressionSet == null) { - throw new IllegalArgumentException("expressionSet argument cannot be null"); + /** + * @param dayOfWeek An integer in the range of <code>Calendar.SUNDAY</code> + * to <code>Calendar.SATURDAY</code> + * @param occurrence An integer in the range of -5 to 5, excluding zero + */ + public DayInMonth(int dayOfWeek, int occurrence) { + if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) { + throw new IllegalArgumentException("Invalid day argument"); } - this.expressionSet = expressionSet; - if (containsExpression(this)) { - throw new IllegalArgumentException("recursive expression"); + if (occurrence < -5 || occurrence == 0 || occurrence > 5) { + throw new IllegalArgumentException("Invalid occurrence argument"); } - if (this.expressionSet.size() > 0) { - TemporalExpression that = this.expressionSet.iterator().next(); - if (this.compareTo(that) > 0) { - this.sequence = that.sequence; - this.subSequence = that.subSequence; - } + this.dayOfWeek = dayOfWeek; + this.occurrence = occurrence; + int result = occurrence; + if (result < 0) { + // Make negative values a higher sequence + // Example: Last Monday should come after first Monday + result += 11; } + this.sequence = SEQUENCE_DAY_IN_MONTH + (result * 10) + dayOfWeek; if (Debug.verboseOn()) { Debug.logVerbose("Created " + this, module); } } + @Override + public void accept(TemporalExpressionVisitor visitor) { + visitor.visit(this); + } + + protected Calendar alignDayOfWeek(Calendar cal) { + cal.set(Calendar.DAY_OF_MONTH, 1); + if (this.occurrence > 0) { + while (cal.get(Calendar.DAY_OF_WEEK) != this.dayOfWeek) { + cal.add(Calendar.DAY_OF_MONTH, 1); + } + cal.add(Calendar.DAY_OF_MONTH, (this.occurrence - 1) * 7); + } else { + cal.add(Calendar.MONTH, 1); + cal.add(Calendar.DAY_OF_MONTH, -1); + while (cal.get(Calendar.DAY_OF_WEEK) != this.dayOfWeek) { + cal.add(Calendar.DAY_OF_MONTH, -1); + } + cal.add(Calendar.DAY_OF_MONTH, (this.occurrence + 1) * 7); + } + return cal; + } + + @Override public boolean equals(Object obj) { if (obj == this) { return true; } try { - return this.expressionSet.equals(((Intersection) obj).expressionSet); - } catch (Exception e) {} + DayInMonth that = (DayInMonth) obj; + return this.dayOfWeek == that.dayOfWeek && this.occurrence == that.occurrence; + } catch (ClassCastException e) {} return false; } - public String toString() { - return super.toString() + ", size = " + this.expressionSet.size(); + @Override + public Calendar first(Calendar cal) { + int month = cal.get(Calendar.MONTH); + Calendar first = alignDayOfWeek((Calendar) cal.clone()); + if (first.before(cal)) { + first.set(Calendar.DAY_OF_MONTH, 1); + if (first.get(Calendar.MONTH) == month) { + first.add(Calendar.MONTH, 1); + } + alignDayOfWeek(first); + } + return first; + } + + /** Returns the day of week in this expression. + * @return The day of week in this expression + */ + public int getDayOfWeek() { + return this.dayOfWeek; + } + + /** Returns the occurrence in this expression. + * @return The occurrence in this expression + */ + public int getOccurrence() { + return this.occurrence; } + @Override public boolean includesDate(Calendar cal) { - for (TemporalExpression expression : this.expressionSet) { - if (!expression.includesDate(cal)) { - return false; - } + if (cal.get(Calendar.DAY_OF_WEEK) != this.dayOfWeek) { + return false; } - return true; + int month = cal.get(Calendar.MONTH); + int dom = cal.get(Calendar.DAY_OF_MONTH); + Calendar next = (Calendar) cal.clone(); + alignDayOfWeek(next); + return dom == next.get(Calendar.DAY_OF_MONTH) && next.get(Calendar.MONTH) == month; } - public Calendar first(Calendar cal) { - Calendar first = (Calendar) cal.clone(); - for (TemporalExpression expression : this.expressionSet) { - first = expression.first(first); - if (first == null) { - return null; + @Override + public Calendar next(Calendar cal, ExpressionContext context) { + int month = cal.get(Calendar.MONTH); + Calendar next = alignDayOfWeek((Calendar) cal.clone()); + if (next.before(cal) || next.equals(cal)) { + next.set(Calendar.DAY_OF_MONTH, 1); + if (next.get(Calendar.MONTH) == month) { + next.add(Calendar.MONTH, 1); } + alignDayOfWeek(next); } - if (includesDate(first)) { - return first; - } else { - return null; - } + return next; } - public Calendar next(Calendar cal) { - Calendar next = (Calendar) cal.clone(); - for (TemporalExpression expression : this.expressionSet) { - next = expression.next(next); - if (next == null) { - return null; - } + @Override + public String toString() { + return super.toString() + ", dayOfWeek = " + this.dayOfWeek + ", occurrence = " + this.occurrence; + } + } + + /** A temporal expression that represents a day of month range. */ + public static class DayOfMonthRange extends TemporalExpression { + protected final int end; + protected final int start; + + public DayOfMonthRange(int dom) { + this(dom, dom); + } + + /** + * @param start An integer in the range of 1 to 31 + * @param end An integer in the range of 1 to 31 + */ + public DayOfMonthRange(int start, int end) { + if (start < 1 || start > end) { + throw new IllegalArgumentException("Invalid start argument"); } - if (includesDate(next)) { - return next; - } else { - return null; + if (end < 1 || end > 31) { + throw new IllegalArgumentException("Invalid end argument"); + } + this.sequence = SEQUENCE_DOM_RANGE + start; + this.start = start; + this.end = end; + if (Debug.verboseOn()) { + Debug.logVerbose("Created " + this, module); } } + @Override public void accept(TemporalExpressionVisitor visitor) { visitor.visit(this); } - public Set<Date> getRange(org.ofbiz.base.util.DateRange range, Calendar cal) { - Set<Date> finalSet = new TreeSet<Date>(); - Set<Date> rawSet = new TreeSet<Date>(); - Date last = range.start(); - Calendar next = first(cal); - while (next != null && range.includesDate(next.getTime())) { - last = next.getTime(); - rawSet.add(last); - next = next(next); - if (next != null && last.equals(next.getTime())) { - break; - } - } - Calendar checkCal = (Calendar) cal.clone(); - for (Date date : rawSet) { - checkCal.setTime(date); - if (includesDate(checkCal)) { - finalSet.add(date); - } + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; } - return finalSet; + try { + DayOfMonthRange that = (DayOfMonthRange) obj; + return this.start == that.start && this.end == that.end; + } catch (ClassCastException e) {} + return false; } - /** Returns the member expression <code>Set</code>. The - * returned set is unmodifiable. - * @return The member expression <code>Set</code> + @Override + public Calendar first(Calendar cal) { + Calendar first = (Calendar) cal.clone(); + while (!includesDate(first)) { + first.add(Calendar.DAY_OF_MONTH, 1); + } + return first; + } + + /** Returns the ending day of this range. + * @return The ending day of this range */ - public Set<TemporalExpression> getExpressionSet() { - return Collections.unmodifiableSet(this.expressionSet); + public int getEndDay() { + return this.end; } - protected boolean containsExpression(TemporalExpression expression) { - for (TemporalExpression setItem : this.expressionSet) { - if (setItem.containsExpression(expression)) { + /** Returns the starting day of this range. + * @return The starting day of this range + */ + public int getStartDay() { + return this.start; + } + + @Override + public boolean includesDate(Calendar cal) { + int dom = cal.get(Calendar.DAY_OF_MONTH); + return dom >= this.start && dom <= this.end; + } + + @Override + public Calendar next(Calendar cal, ExpressionContext context) { + Calendar next = (Calendar) cal.clone(); + next.add(Calendar.DAY_OF_MONTH, 1); + while (!includesDate(next)) { + next.add(Calendar.DAY_OF_MONTH, 1); + } + return next; + } + + @Override + public String toString() { + return super.toString() + ", start = " + this.start + ", end = " + this.end; + } + } + + /** A temporal expression that represents a day of week range. */ + public static class DayOfWeekRange extends TemporalExpression { + protected final int end; + protected final int start; + + public DayOfWeekRange(int dow) { + this(dow, dow); + } + + /** + * @param start An integer in the range of <code>Calendar.SUNDAY</code> + * to <code>Calendar.SATURDAY</code> + * @param end An integer in the range of <code>Calendar.SUNDAY</code> + * to <code>Calendar.SATURDAY</code> + */ + public DayOfWeekRange(int start, int end) { + if (start < Calendar.SUNDAY || start > Calendar.SATURDAY) { + throw new IllegalArgumentException("Invalid start argument"); + } + if (end < Calendar.SUNDAY || end > Calendar.SATURDAY) { + throw new IllegalArgumentException("Invalid end argument"); + } + this.sequence = SEQUENCE_DOW_RANGE + start; + this.start = start; + this.end = end; + if (Debug.verboseOn()) { + Debug.logVerbose("Created " + this, module); + } + } + + @Override + public void accept(TemporalExpressionVisitor visitor) { + visitor.visit(this); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + try { + DayOfWeekRange that = (DayOfWeekRange) obj; + return this.start == that.start && this.end == that.end; + } catch (ClassCastException e) {} + return false; + } + + @Override + public Calendar first(Calendar cal) { + Calendar first = (Calendar) cal.clone(); + while (!includesDate(first)) { + first.add(Calendar.DAY_OF_MONTH, 1); + } + return first; + } + + /** Returns the ending day of this range. + * @return The ending day of this range + */ + public int getEndDay() { + return this.end; + } + + /** Returns the starting day of this range. + * @return The starting day of this range + */ + public int getStartDay() { + return this.start; + } + + @Override + public boolean includesDate(Calendar cal) { + int dow = cal.get(Calendar.DAY_OF_WEEK); + if (dow == this.start || dow == this.end) { + return true; + } + Calendar compareCal = (Calendar) cal.clone(); + while (compareCal.get(Calendar.DAY_OF_WEEK) != this.start) { + compareCal.add(Calendar.DAY_OF_MONTH, 1); + } + while (compareCal.get(Calendar.DAY_OF_WEEK) != this.end) { + if (compareCal.get(Calendar.DAY_OF_WEEK) == dow) { return true; } + compareCal.add(Calendar.DAY_OF_MONTH, 1); } return false; } + + @Override + public Calendar next(Calendar cal, ExpressionContext context) { + Calendar next = (Calendar) cal.clone(); + if (includesDate(next)) { + if (context.dayBumped) { + return next; + } + next.add(Calendar.DAY_OF_MONTH, 1); + } + while (!includesDate(next)) { + next.add(Calendar.DAY_OF_MONTH, 1); + } + if (cal.get(Calendar.MONTH) != next.get(Calendar.MONTH)) { + context.monthBumped = true; + } + return next; + } + + @Override + public String toString() { + return super.toString() + ", start = " + this.start + ", end = " + this.end; + } } - /** This class represents a difference of two temporal expressions. */ + /** A temporal expression that represents a difference of two temporal expressions. */ public static class Difference extends TemporalExpression { - protected final TemporalExpression included; protected final TemporalExpression excluded; + protected final TemporalExpression included; public Difference(TemporalExpression included, TemporalExpression excluded) { if (included == null) { @@ -298,15 +459,23 @@ if (containsExpression(this)) { throw new IllegalArgumentException("recursive expression"); } - if (this.compareTo(included) > 0) { - this.sequence = included.sequence; - this.subSequence = included.subSequence; - } + this.sequence = included.sequence; if (Debug.verboseOn()) { Debug.logVerbose("Created " + this, module); } } + @Override + public void accept(TemporalExpressionVisitor visitor) { + visitor.visit(this); + } + + @Override + protected boolean containsExpression(TemporalExpression expression) { + return this.included.containsExpression(expression) || this.excluded.containsExpression(expression); + } + + @Override public boolean equals(Object obj) { if (obj == this) { return true; @@ -314,18 +483,11 @@ try { Difference that = (Difference) obj; return this.included.equals(that.included) && this.excluded.equals(that.excluded); - } catch (Exception e) {} + } catch (ClassCastException e) {} return false; } - public String toString() { - return super.toString() + ", included = " + this.included + ", excluded = " + this.excluded; - } - - public boolean includesDate(Calendar cal) { - return this.included.includesDate(cal) && !this.excluded.includesDate(cal); - } - + @Override public Calendar first(Calendar cal) { Calendar first = this.included.first(cal); while (first != null && this.excluded.includesDate(first)) { @@ -334,31 +496,6 @@ return first; } - public Calendar next(Calendar cal) { - Calendar next = this.included.next(cal); - while (next != null && this.excluded.includesDate(next)) { - next = this.included.next(next); - } - return next; - } - - public void accept(TemporalExpressionVisitor visitor) { - visitor.visit(this); - } - - public Set<Date> getRange(org.ofbiz.base.util.DateRange range, Calendar cal) { - Set<Date> finalSet = new TreeSet<Date>(); - Set<Date> rawSet = this.included.getRange(range, cal); - Calendar checkCal = (Calendar) cal.clone(); - for (Date date : rawSet) { - checkCal.setTime(date); - if (!this.excluded.includesDate(checkCal)) { - finalSet.add(date); - } - } - return finalSet; - } - /** Returns the excluded expression. * @return The excluded <code>TemporalExpression</code> */ @@ -373,742 +510,915 @@ return this.included; } - protected boolean containsExpression(TemporalExpression expression) { - return this.included.containsExpression(expression) || this.excluded.containsExpression(expression); + @Override + public boolean includesDate(Calendar cal) { + return this.included.includesDate(cal) && !this.excluded.includesDate(cal); + } + + @Override + public Calendar next(Calendar cal, ExpressionContext context) { + Calendar next = this.included.next(cal, context); + while (next != null && this.excluded.includesDate(next)) { + next = this.included.next(next, context); + } + return next; + } + + @Override + public String toString() { + return super.toString() + ", included = " + this.included + ", excluded = " + this.excluded; } } - /** A temporal expression that represents a range of dates. */ - public static class DateRange extends TemporalExpression { - protected final org.ofbiz.base.util.DateRange range; + /** A temporal expression that represents a frequency. */ + public static class Frequency extends TemporalExpression { + protected final int freqCount; + protected final int freqType; + protected final Date start; - public DateRange(Date start, Date end) { - this.sequence = 1000; - this.range = new org.ofbiz.base.util.DateRange(start, end); + /** + * @param start Starting date, defaults to current system time + * @param freqType One of the following integer values: <code>Calendar.SECOND + * Calendar.MINUTE Calendar.HOUR Calendar.DAY_OF_MONTH Calendar.MONTH + * Calendar.YEAR</code> + * @param freqCount A positive integer + */ + public Frequency(Date start, int freqType, int freqCount) { + if (freqType != Calendar.SECOND && freqType != Calendar.MINUTE + && freqType != Calendar.HOUR && freqType != Calendar.DAY_OF_MONTH + && freqType != Calendar.MONTH && freqType != Calendar.YEAR) { + throw new IllegalArgumentException("Invalid freqType argument"); + } + if (freqCount < 1) { + throw new IllegalArgumentException("freqCount argument must be a positive integer"); + } + if (start != null) { + this.start = start; + } else { + this.start = new Date(); + } + this.sequence = SEQUENCE_FREQ + freqType; + this.freqType = freqType; + this.freqCount = freqCount; if (Debug.verboseOn()) { Debug.logVerbose("Created " + this, module); } } + @Override + public void accept(TemporalExpressionVisitor visitor) { + visitor.visit(this); + } + + @Override public boolean equals(Object obj) { if (obj == this) { return true; } try { - return this.range.equals(((DateRange) obj).range); - } catch (Exception e) {} + Frequency that = (Frequency) obj; + return this.start.equals(that.start) && this.freqType == that.freqType && this.freqCount == that.freqCount; + } catch (ClassCastException e) {} return false; } - public String toString() { - return super.toString() + ", start = " + this.range.start() + ", end = " + this.range.end(); - } - - public boolean includesDate(Calendar cal) { - return this.range.includesDate(cal.getTime()); - } - + @Override public Calendar first(Calendar cal) { - return includesDate(cal) ? cal : null; + Calendar first = prepareCal(cal); + while (first.before(cal)) { + first.add(this.freqType, this.freqCount); + } + return first; } - public Calendar next(Calendar cal) { - return includesDate(cal) ? cal : null; + /** Returns the frequency count of this expression. + * @return The frequency count of this expression + */ + public int getFreqCount() { + return this.freqCount; } - public void accept(TemporalExpressionVisitor visitor) { - visitor.visit(this); + /** Returns the frequency type of this expression. + * @return The frequency type of this expression + */ + public int getFreqType() { + return this.freqType; } - /** Returns the contained <code>org.ofbiz.base.util.DateRange</code>. - * @return The contained <code>org.ofbiz.base.util.DateRange</code> + /** Returns the start date of this expression. + * @return The start date of this expression */ - public org.ofbiz.base.util.DateRange getDateRange() { - return this.range; + public Date getStartDate() { + return (Date) this.start.clone(); } - } - /** A temporal expression that represents a time of day range. */ - public static class TimeOfDayRange extends TemporalExpression { - protected final String startStr; - protected final String endStr; - protected final int interval; - protected final int count; - protected final int startSecs; - protected final int startMins; - protected final int startHrs; - protected final int endSecs; - protected final int endMins; - protected final int endHrs; - - /** - * @param start A time String in the form of hh:mm:ss (24 hr clock) - * @param end A time String in the form of hh:mm:ss (24 hr clock) - * @param interval The range interval - must be one of <code> - * Calendar.SECOND Calendar.MINUTE Calendar.HOUR_OF_DAY</code> - * @param count The interval count - must be greater than zero - */ - public TimeOfDayRange(String start, String end, int interval, int count) { - if (start == null || start.length() == 0) { - throw new IllegalArgumentException("start argument cannot be null or empty"); - } - if (end == null || end.length() == 0) { - throw new IllegalArgumentException("end argument cannot be null or empty"); - } - if (interval != Calendar.SECOND && interval != Calendar.MINUTE && interval != Calendar.HOUR_OF_DAY) { - throw new IllegalArgumentException("invalid interval argument"); - } - if (count < 1) { - throw new IllegalArgumentException("invalid count argument"); - } - this.startStr = start; - this.endStr = end; - this.interval = interval; - this.count = count; - String strArray[] = this.startStr.split(":"); - if (strArray.length == 0 || strArray.length > 3) { - throw new IllegalArgumentException("Invalid start time argument"); - } - this.startHrs = Integer.valueOf(strArray[0]); - this.startMins = strArray.length > 1 ? Integer.valueOf(strArray[1]) : 0; - this.startSecs = strArray.length > 2 ? Integer.valueOf(strArray[2]) : 0; - if (this.startHrs > 23 || this.startMins > 59 || this.startSecs > 59) { - throw new IllegalArgumentException("Invalid start time argument"); - } - strArray = this.endStr.split(":"); - if (strArray.length == 0 || strArray.length > 3) { - throw new IllegalArgumentException("Invalid end time argument"); - } - this.endHrs = Integer.valueOf(strArray[0]); - this.endMins = strArray.length > 1 ? Integer.valueOf(strArray[1]) : 0; - this.endSecs = strArray.length > 2 ? Integer.valueOf(strArray[2]) : 0; - if (this.endHrs > 23 || this.endMins > 59 || this.endSecs > 59) { - throw new IllegalArgumentException("Invalid end time argument"); - } - this.sequence = 600; - this.subSequence = (this.startHrs * 4000) + (this.startMins * 60) + this.startSecs; - if (Debug.verboseOn()) { - Debug.logVerbose("Created " + this, module); - } + @Override + public boolean includesDate(Calendar cal) { + Calendar next = first(cal); + return next.equals(cal); } - public boolean equals(Object obj) { - if (obj == this) { - return true; + @Override + public Calendar next(Calendar cal, ExpressionContext context) { + Calendar next = first(cal); + if (next.equals(cal)) { + next.add(this.freqType, this.freqCount); } - try { - TimeOfDayRange that = (TimeOfDayRange) obj; - return this.startStr.equals(that.startStr) && this.endStr.equals(that.endStr); - } catch (Exception e) {} - return false; - } - - public String toString() { - return super.toString() + ", start = " + this.startStr + ", end = " + this.endStr - + ", interval = " + this.interval + ", count = " + this.count; + return next; } - public boolean includesDate(Calendar cal) { - long millis = cal.getTimeInMillis(); - Calendar startCal = setStart(cal); - Calendar endCal = setEnd(startCal); - if (endCal.before(startCal)) { - endCal.add(Calendar.DAY_OF_MONTH, 1); + protected Calendar prepareCal(Calendar cal) { + // Performs a "sane" skip forward in time - avoids time consuming loops + // like incrementing every second from Jan 1 2000 until today + Calendar skip = (Calendar) cal.clone(); + skip.setTime(this.start); + long deltaMillis = cal.getTimeInMillis() - this.start.getTime(); + if (deltaMillis < 1000) { + return skip; } - long startMillis = startCal.getTimeInMillis(); - long endMillis = endCal.getTimeInMillis(); - return millis >= startMillis && millis <= endMillis; - } - - public Calendar first(Calendar cal) { - if (includesDate(cal)) { - return cal; + long divisor = deltaMillis; + if (this.freqType == Calendar.DAY_OF_MONTH) { + divisor = 86400000; + } else if (this.freqType == Calendar.HOUR) { + divisor = 3600000; + } else if (this.freqType == Calendar.MINUTE) { + divisor = 60000; + } else if (this.freqType == Calendar.SECOND) { + divisor = 1000; + } else { + return skip; } - return next(cal); - } - - public Calendar next(Calendar cal) { - Calendar next = (Calendar) cal.clone(); - next.add(this.interval, this.count); - if (!includesDate(next)) { - Calendar last = next; - next = setStart(next); - if (next.before(last)) { - next.add(Calendar.DAY_OF_MONTH, 1); - } + float units = deltaMillis / divisor; + units = (units / this.freqCount) * this.freqCount; + skip.add(this.freqType, (int)units); + while (skip.after(cal)) { + skip.add(this.freqType, -this.freqCount); } - return next; - } - - public int getCount() { - return this.count; - } - - public int getEndHours() { - return this.endHrs; - } - - public int getEndMins() { - return this.endMins; - } - - public int getEndSecs() { - return this.endSecs; - } - - public int getInterval() { - return this.interval; - } - - public int getStartHours() { - return this.startHrs; - } - - public int getStartMins() { - return this.startMins; - } - - public int getStartSecs() { - return this.startSecs; + return skip; } - public void accept(TemporalExpressionVisitor visitor) { - visitor.visit(this); + @Override + public String toString() { + return super.toString() + ", start = " + this.start + ", freqType = " + this.freqType + ", freqCount = " + this.freqCount; } + } - protected Calendar setCalendar(Calendar cal, int hrs, int mins, int secs) { - Calendar newCal = (Calendar) cal.clone(); - newCal.set(Calendar.MILLISECOND, 0); - newCal.set(Calendar.SECOND, secs); - newCal.set(Calendar.MINUTE, mins); - newCal.set(Calendar.HOUR_OF_DAY, hrs); - return newCal; - } - protected Calendar setStart(Calendar cal) { - return setCalendar(cal, this.startHrs, this.startMins, this.startSecs); - } + /** A temporal expression that represents an hour range. */ + public static class HourRange extends TemporalExpression { + protected final int end; + protected final int start; - protected Calendar setEnd(Calendar cal) { - return setCalendar(cal, this.endHrs, this.endMins, this.endSecs); + /** + * @param hour An integer in the range of 0 to 23. + */ + public HourRange(int hour) { + this(hour, hour); } - } - - /** A temporal expression that represents a day of week range. */ - public static class DayOfWeekRange extends TemporalExpression { - protected final int start; - protected final int end; /** - * @param start An integer in the range of <code>Calendar.SUNDAY</code> - * to <code>Calendar.SATURDAY</code> - * @param end An integer in the range of <code>Calendar.SUNDAY</code> - * to <code>Calendar.SATURDAY</code> + * @param start An integer in the range of 0 to 23. + * @param end An integer in the range of 0 to 23. */ - public DayOfWeekRange(int start, int end) { - if (start < Calendar.SUNDAY || start > Calendar.SATURDAY) { + public HourRange(int start, int end) { + if (start < 0 || start > 23) { throw new IllegalArgumentException("Invalid start argument"); } - if (end < Calendar.SUNDAY || end > Calendar.SATURDAY) { + if (end < 0 || end > 23) { throw new IllegalArgumentException("Invalid end argument"); } - this.sequence = 500; - this.subSequence = start; this.start = start; this.end = end; + this.sequence = SEQUENCE_HOUR_RANGE + start; if (Debug.verboseOn()) { Debug.logVerbose("Created " + this, module); } } + @Override + public void accept(TemporalExpressionVisitor visitor) { + visitor.visit(this); + } + + @Override public boolean equals(Object obj) { if (obj == this) { return true; } try { - DayOfWeekRange that = (DayOfWeekRange) obj; + HourRange that = (HourRange) obj; return this.start == that.start && this.end == that.end; - } catch (Exception e) {} + } catch (ClassCastException e) {} return false; } - public String toString() { - return super.toString() + ", start = " + this.start + ", end = " + this.end; + @Override + public Calendar first(Calendar cal) { + Calendar first = (Calendar) cal.clone(); + while (!includesDate(first)) { + first.add(Calendar.HOUR_OF_DAY, 1); + } + return first; + } + + /** Returns the ending hour of this range. + * @return The ending hour of this range + */ + public int getEndHour() { + return this.end; } + public Set<Integer> getHourRangeAsSet() { + Set<Integer> rangeSet = new TreeSet<Integer>(); + if (this.start == this.end) { + rangeSet.add(this.start); + } else { + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, this.start); + while (cal.get(Calendar.HOUR_OF_DAY) != this.end) { + rangeSet.add(cal.get(Calendar.HOUR_OF_DAY)); + cal.add(Calendar.HOUR_OF_DAY, 1); + } + } + return rangeSet; + } + + /** Returns the starting hour of this range. + * @return The starting hour of this range + */ + public int getStartHour() { + return this.start; + } + + @Override public boolean includesDate(Calendar cal) { - int dow = cal.get(Calendar.DAY_OF_WEEK); - if (dow == this.start || dow == this.end) { + int hour = cal.get(Calendar.HOUR_OF_DAY); + if (hour == this.start || hour == this.end) { return true; } Calendar compareCal = (Calendar) cal.clone(); - while (compareCal.get(Calendar.DAY_OF_WEEK) != this.start) { - compareCal.add(Calendar.DAY_OF_MONTH, 1); - } - while (compareCal.get(Calendar.DAY_OF_WEEK) != this.end) { - if (compareCal.get(Calendar.DAY_OF_WEEK) == dow) { + compareCal.set(Calendar.HOUR_OF_DAY, this.start); + while (compareCal.get(Calendar.HOUR_OF_DAY) != this.end) { + if (compareCal.get(Calendar.HOUR_OF_DAY) == hour) { return true; } - compareCal.add(Calendar.DAY_OF_MONTH, 1); + compareCal.add(Calendar.HOUR_OF_DAY, 1); } return false; } - public Calendar first(Calendar cal) { - Calendar first = (Calendar) cal.clone(); - while (!includesDate(first)) { - first.add(Calendar.DAY_OF_MONTH, 1); - } - return setStartOfDay(first); - } - public Calendar next(Calendar cal) { + @Override + public Calendar next(Calendar cal, ExpressionContext context) { Calendar next = (Calendar) cal.clone(); - next.add(Calendar.DAY_OF_MONTH, 1); + if (includesDate(next)) { + if (context.hourBumped) { + return next; + } + next.add(Calendar.HOUR_OF_DAY, 1); + } while (!includesDate(next)) { - next.add(Calendar.DAY_OF_MONTH, 1); + next.add(Calendar.HOUR_OF_DAY, 1); } - return setStartOfDay(next); - } - - public void accept(TemporalExpressionVisitor visitor) { - visitor.visit(this); - } - - /** Returns the starting day of this range. - * @return The starting day of this range - */ - public int getStartDay() { - return this.start; + if (cal.get(Calendar.DAY_OF_MONTH) != next.get(Calendar.DAY_OF_MONTH)) { + context.dayBumped = true; + } + return next; } - /** Returns the ending day of this range. - * @return The ending day of this range - */ - public int getEndDay() { - return this.end; + @Override + public String toString() { + return super.toString() + ", start = " + this.start + ", end = " + this.end; } } - /** A temporal expression that represents a month range. */ - public static class MonthRange extends TemporalExpression { - protected final int start; - protected final int end; + /** A temporal expression that represents a mathematical intersection of all of its + * member expressions. */ + public static class Intersection extends TemporalExpression { + protected final Set<TemporalExpression> expressionSet; - /** - * @param start An integer in the range of <code>Calendar.JANUARY</code> - * to <code>Calendar.UNDECIMBER</code> - * @param end An integer in the range of <code>Calendar.JANUARY</code> - * to <code>Calendar.UNDECIMBER</code> - */ - public MonthRange(int start, int end) { - if (start < Calendar.JANUARY || start > Calendar.UNDECIMBER) { - throw new IllegalArgumentException("Invalid start argument"); + public Intersection(Set<TemporalExpression> expressionSet) { + if (expressionSet == null) { + throw new IllegalArgumentException("expressionSet argument cannot be null"); } - if (end < Calendar.JANUARY || end > Calendar.UNDECIMBER) { - throw new IllegalArgumentException("Invalid end argument"); + this.expressionSet = expressionSet; + if (containsExpression(this)) { + throw new IllegalArgumentException("recursive expression"); + } + if (this.expressionSet.size() > 0) { + // Aggregate member expression sequences in a way that will + // ensure the proper evaluation sequence for the entire collection + int result = 0; + TemporalExpression[] exprArray = this.expressionSet.toArray(new TemporalExpression[this.expressionSet.size()]); + for (int i = exprArray.length - 1; i >= 0; i--) { + result *= 10; + result += exprArray[i].sequence; + } + this.sequence = result; } - this.sequence = 200; - this.subSequence = start; - this.start = start; - this.end = end; if (Debug.verboseOn()) { Debug.logVerbose("Created " + this, module); } } + @Override + public void accept(TemporalExpressionVisitor visitor) { + visitor.visit(this); + } + + @Override + protected boolean containsExpression(TemporalExpression expression) { + for (TemporalExpression setItem : this.expressionSet) { + if (setItem.containsExpression(expression)) { + return true; + } + } + return false; + } + + @Override public boolean equals(Object obj) { if (obj == this) { return true; } try { - MonthRange that = (MonthRange) obj; - return this.start == that.start && this.end == that.end; - } catch (Exception e) {} + return this.expressionSet.equals(((Intersection) obj).expressionSet); + } catch (ClassCastException e) {} return false; } - public String toString() { - return super.toString() + ", start = " + this.start + ", end = " + this.end; - } - - public boolean includesDate(Calendar cal) { - int month = cal.get(Calendar.MONTH); - if (month == this.start || month == this.end) { - return true; - } - Calendar compareCal = (Calendar) cal.clone(); - while (compareCal.get(Calendar.MONTH) != this.start) { - compareCal.add(Calendar.MONTH, 1); - } - while (compareCal.get(Calendar.MONTH) != this.end) { - if (compareCal.get(Calendar.MONTH) == month) { - return true; + @Override + public Calendar first(Calendar cal) { + Calendar first = (Calendar) cal.clone(); + for (TemporalExpression expression : this.expressionSet) { + first = expression.first(first); + if (first == null) { + return null; } - compareCal.add(Calendar.MONTH, 1); } - return false; + if (includesDate(first)) { + return first; + } else { + return null; + } } - public Calendar first(Calendar cal) { - Calendar first = (Calendar) cal.clone(); - first.set(Calendar.DAY_OF_MONTH, 1); - while (!includesDate(first)) { - first.add(Calendar.MONTH, 1); + /** Returns the member expression <code>Set</code>. The + * returned set is unmodifiable. + * @return The member expression <code>Set</code> + */ + public Set<TemporalExpression> getExpressionSet() { + return Collections.unmodifiableSet(this.expressionSet); + } + + @Override + public boolean includesDate(Calendar cal) { + for (TemporalExpression expression : this.expressionSet) { + if (!expression.includesDate(cal)) { + return false; + } } - return first; + return true; } - public Calendar next(Calendar cal) { + @Override + public Calendar next(Calendar cal, ExpressionContext context) { Calendar next = (Calendar) cal.clone(); - next.set(Calendar.DAY_OF_MONTH, 1); - next.add(Calendar.MONTH, 1); - while (!includesDate(next)) { - next.add(Calendar.MONTH, 1); + for (TemporalExpression expression : this.expressionSet) { + next = expression.next(next, context); + if (next == null) { + return null; + } } return next; } - public void accept(TemporalExpressionVisitor visitor) { - visitor.visit(this); + @Override + public String toString() { + return super.toString() + ", size = " + this.expressionSet.size(); } + } - /** Returns the starting month of this range. - * @return The starting month of this range - */ - public int getStartMonth() { - return this.start; - } + /** A temporal expression that represents a minute range. */ + public static class MinuteRange extends TemporalExpression { + protected final int end; + protected final int start; - /** Returns the ending month of this range. - * @return The ending month of this range + /** + * @param hour An integer in the range of 0 to 59. */ - public int getEndMonth() { - return this.end; + public MinuteRange(int minute) { + this(minute, minute); } - } - - /** A temporal expression that represents a day of month range. */ - public static class DayOfMonthRange extends TemporalExpression { - protected final int start; - protected final int end; /** - * @param start An integer in the range of 1 to 31 - * @param end An integer in the range of 1 to 31 + * @param start An integer in the range of 0 to 59. + * @param end An integer in the range of 0 to 59. */ - public DayOfMonthRange(int start, int end) { - if (start < 1 || start > end) { + public MinuteRange(int start, int end) { + if (start < 0 || start > 59) { throw new IllegalArgumentException("Invalid start argument"); } - if (end < 1 || end > 31) { + if (end < 0 || end > 59) { throw new IllegalArgumentException("Invalid end argument"); } - this.sequence = 300; - this.subSequence = start; this.start = start; this.end = end; + this.sequence = SEQUENCE_MINUTE_RANGE + start; if (Debug.verboseOn()) { Debug.logVerbose("Created " + this, module); } } + @Override + public void accept(TemporalExpressionVisitor visitor) { + visitor.visit(this); + } + + @Override public boolean equals(Object obj) { if (obj == this) { return true; } try { - DayOfMonthRange that = (DayOfMonthRange) obj; + MinuteRange that = (MinuteRange) obj; return this.start == that.start && this.end == that.end; - } catch (Exception e) {} + } catch (ClassCastException e) {} return false; } - public String toString() { - return super.toString() + ", start = " + this.start + ", end = " + this.end; - } - - public boolean includesDate(Calendar cal) { - int dom = cal.get(Calendar.DAY_OF_MONTH); - return dom >= this.start && dom <= this.end; - } - + @Override public Calendar first(Calendar cal) { - Calendar first = setStartOfDay((Calendar) cal.clone()); + Calendar first = (Calendar) cal.clone(); while (!includesDate(first)) { - first.add(Calendar.DAY_OF_MONTH, 1); + first.add(Calendar.MINUTE, 1); } return first; } - public Calendar next(Calendar cal) { - Calendar next = setStartOfDay((Calendar) cal.clone()); - next.add(Calendar.DAY_OF_MONTH, 1); - while (!includesDate(next)) { - next.add(Calendar.DAY_OF_MONTH, 1); - } - return next; + /** Returns the ending minute of this range. + * @return The ending minute of this range + */ + public int getEndMinute() { + return this.end; } - public void accept(TemporalExpressionVisitor visitor) { - visitor.visit(this); + public Set<Integer> getMinuteRangeAsSet() { + Set<Integer> rangeSet = new TreeSet<Integer>(); + if (this.start == this.end) { + rangeSet.add(this.start); + } else { + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, this.start); + while (cal.get(Calendar.HOUR_OF_DAY) != this.end) { + rangeSet.add(cal.get(Calendar.HOUR_OF_DAY)); + cal.add(Calendar.HOUR_OF_DAY, 1); + } + } + return rangeSet; } - /** Returns the starting day of this range. - * @return The starting day of this range + /** Returns the starting minute of this range. + * @return The starting minute of this range */ - public int getStartDay() { + public int getStartMinute() { return this.start; } - /** Returns the ending day of this range. - * @return The ending day of this range - */ - public int getEndDay() { - return this.end; + @Override + public boolean includesDate(Calendar cal) { + int minute = cal.get(Calendar.MINUTE); + if (minute == this.start || minute == this.end) { + return true; + } + Calendar compareCal = (Calendar) cal.clone(); + compareCal.set(Calendar.MINUTE, this.start); + while (compareCal.get(Calendar.MINUTE) != this.end) { + if (compareCal.get(Calendar.MINUTE) == minute) { + return true; + } + compareCal.add(Calendar.MINUTE, 1); + } + return false; + } + + + @Override + public Calendar next(Calendar cal, ExpressionContext context) { + Calendar next = (Calendar) cal.clone(); + if (includesDate(next)) { + next.add(Calendar.MINUTE, 1); + } + while (!includesDate(next)) { + next.add(Calendar.MINUTE, 1); + } + if (cal.get(Calendar.HOUR_OF_DAY) != next.get(Calendar.HOUR_OF_DAY)) { + context.hourBumped = true; + } + return next; + } + + @Override + public String toString() { + return super.toString() + ", start = " + this.start + ", end = " + this.end; } } - /** A temporal expression that represents a day in the month. */ - public static class DayInMonth extends TemporalExpression { - protected final int dayOfWeek; - protected final int occurrence; + /** A temporal expression that represents a month range. */ + public static class MonthRange extends TemporalExpression { + protected final int end; + protected final int start; + + public MonthRange(int month) { + this(month, month); + } /** - * @param dayOfWeek An integer in the range of <code>Calendar.SUNDAY</code> - * to <code>Calendar.SATURDAY</code> - * @param occurrence An integer in the range of -5 to 5, excluding zero + * @param start An integer in the range of <code>Calendar.JANUARY</code> + * to <code>Calendar.UNDECIMBER</code> + * @param end An integer in the range of <code>Calendar.JANUARY</code> + * to <code>Calendar.UNDECIMBER</code> */ - public DayInMonth(int dayOfWeek, int occurrence) { - if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) { - throw new IllegalArgumentException("Invalid day argument"); + public MonthRange(int start, int end) { + if (start < Calendar.JANUARY || start > Calendar.UNDECIMBER) { + throw new IllegalArgumentException("Invalid start argument"); } - if (occurrence < -5 || occurrence == 0 || occurrence > 5) { - throw new IllegalArgumentException("Invalid occurrence argument"); + if (end < Calendar.JANUARY || end > Calendar.UNDECIMBER) { + throw new IllegalArgumentException("Invalid end argument"); } - this.sequence = 400; - this.subSequence = dayOfWeek; - this.dayOfWeek = dayOfWeek; - this.occurrence = occurrence; + this.sequence = SEQUENCE_MONTH_RANGE + start; + this.start = start; + this.end = end; if (Debug.verboseOn()) { Debug.logVerbose("Created " + this, module); } } + @Override + public void accept(TemporalExpressionVisitor visitor) { + visitor.visit(this); + } + + @Override public boolean equals(Object obj) { if (obj == this) { return true; } try { - DayInMonth that = (DayInMonth) obj; - return this.dayOfWeek == that.dayOfWeek && this.occurrence == that.occurrence; - } catch (Exception e) {} + MonthRange that = (MonthRange) obj; + return this.start == that.start && this.end == that.end; + } catch (ClassCastException e) {} return false; } - public String toString() { - return super.toString() + ", dayOfWeek = " + this.dayOfWeek + ", occurrence = " + this.occurrence; - } - - public boolean includesDate(Calendar cal) { - if (cal.get(Calendar.DAY_OF_WEEK) != this.dayOfWeek) { - return false; - } - int month = cal.get(Calendar.MONTH); - int dom = cal.get(Calendar.DAY_OF_MONTH); - Calendar next = (Calendar) cal.clone(); - alignDayOfWeek(next); - return dom == next.get(Calendar.DAY_OF_MONTH) && next.get(Calendar.MONTH) == month; - } - + @Override public Calendar first(Calendar cal) { - int month = cal.get(Calendar.MONTH); - Calendar first = setStartOfDay(alignDayOfWeek((Calendar) cal.clone())); - if (first.before(cal)) { - first.set(Calendar.DAY_OF_MONTH, 1); - if (first.get(Calendar.MONTH) == month) { - first.add(Calendar.MONTH, 1); - } - alignDayOfWeek(first); + Calendar first = (Calendar) cal.clone(); + first.set(Calendar.DAY_OF_MONTH, 1); + while (!includesDate(first)) { + first.add(Calendar.MONTH, 1); } return first; } - public Calendar next(Calendar cal) { - int month = cal.get(Calendar.MONTH); - Calendar next = setStartOfDay(alignDayOfWeek((Calendar) cal.clone())); - if (next.before(cal) || next.equals(cal)) { - next.set(Calendar.DAY_OF_MONTH, 1); - if (next.get(Calendar.MONTH) == month) { - next.add(Calendar.MONTH, 1); + /** Returns the ending month of this range. + * @return The ending month of this range + */ + public int getEndMonth() { + return this.end; + } + + /** Returns the starting month of this range. + * @return The starting month of this range + */ + public int getStartMonth() { + return this.start; + } + + @Override + public boolean includesDate(Calendar cal) { + int month = cal.get(Calendar.MONTH); + if (month == this.start || month == this.end) { + return true; + } + Calendar compareCal = (Calendar) cal.clone(); + while (compareCal.get(Calendar.MONTH) != this.start) { + compareCal.add(Calendar.MONTH, 1); + } + while (compareCal.get(Calendar.MONTH) != this.end) { + if (compareCal.get(Calendar.MONTH) == month) { + return true; } - alignDayOfWeek(next); + compareCal.add(Calendar.MONTH, 1); + } + return false; + } + + @Override + public Calendar next(Calendar cal, ExpressionContext context) { + Calendar next = (Calendar) cal.clone(); + next.set(Calendar.DAY_OF_MONTH, 1); + next.add(Calendar.MONTH, 1); + while (!includesDate(next)) { + next.add(Calendar.MONTH, 1); } return next; } + @Override + public String toString() { + return super.toString() + ", start = " + this.start + ", end = " + this.end; + } + } + + /** A temporal expression that represents a null expression. */ + public static class Null extends TemporalExpression { + @Override public void accept(TemporalExpressionVisitor visitor) { visitor.visit(this); } - - /** Returns the day of week in this expression. - * @return The day of week in this expression - */ - public int getDayOfWeek() { - return this.dayOfWeek; + @Override + public Calendar first(Calendar cal) { + return null; } - - /** Returns the occurrence in this expression. - * @return The occurrence in this expression - */ - public int getOccurrence() { - return this.occurrence; + @Override + public boolean includesDate(Calendar cal) { + return false; } - - protected Calendar alignDayOfWeek(Calendar cal) { - cal.set(Calendar.DAY_OF_MONTH, 1); - if (this.occurrence > 0) { - while (cal.get(Calendar.DAY_OF_WEEK) != this.dayOfWeek) { - cal.add(Calendar.DAY_OF_MONTH, 1); - } - cal.add(Calendar.DAY_OF_MONTH, (this.occurrence - 1) * 7); - } else { - cal.add(Calendar.MONTH, 1); - cal.add(Calendar.DAY_OF_MONTH, -1); - while (cal.get(Calendar.DAY_OF_WEEK) != this.dayOfWeek) { - cal.add(Calendar.DAY_OF_MONTH, -1); - } - cal.add(Calendar.DAY_OF_MONTH, (this.occurrence + 1) * 7); - } - return cal; + @Override + public Calendar next(Calendar cal, ExpressionContext context) { + return null; } } - /** A temporal expression that represents a frequency. */ - public static class Frequency extends TemporalExpression { - protected final Date start; - protected final int freqType; - protected final int freqCount; + /** A temporal expression that represents a time of day range. + * @deprecated + */ + @Deprecated + public static class TimeOfDayRange extends TemporalExpression { + protected final int count; + protected final int endHrs; + protected final int endMins; + protected final int endSecs; + protected final String endStr; + protected final int interval; + protected final int startHrs; + protected final int startMins; + protected final int startSecs; + protected final String startStr; /** - * @param start Starting date, defaults to current system time - * @param freqType One of the following integer values: <code>Calendar.SECOND - * Calendar.MINUTE Calendar.HOUR Calendar.DAY_OF_MONTH Calendar.MONTH - * Calendar.YEAR</code> - * @param freqCount A positive integer + * @param start A time String in the form of hh:mm:ss (24 hr clock) + * @param end A time String in the form of hh:mm:ss (24 hr clock) + * @param interval The range interval - must be one of <code> + * Calendar.SECOND Calendar.MINUTE Calendar.HOUR_OF_DAY</code> + * @param count The interval count - must be greater than zero */ - public Frequency(Date start, int freqType, int freqCount) { - if (freqType != Calendar.SECOND && freqType != Calendar.MINUTE - && freqType != Calendar.HOUR && freqType != Calendar.DAY_OF_MONTH - && freqType != Calendar.MONTH && freqType != Calendar.YEAR) { - throw new IllegalArgumentException("Invalid freqType argument"); + public TimeOfDayRange(String start, String end, int interval, int count) { + if (UtilValidate.isEmpty(start)) { + throw new IllegalArgumentException("start argument cannot be null or empty"); } - if (freqCount < 1) { - throw new IllegalArgumentException("freqCount argument must be a positive integer"); + if (UtilValidate.isEmpty(end)) { + throw new IllegalArgumentException("end argument cannot be null or empty"); } - if (start != null) { - this.start = start; - } else { - this.start = new Date(); + if (interval != Calendar.SECOND && interval != Calendar.MINUTE && interval != Calendar.HOUR_OF_DAY) { + throw new IllegalArgumentException("invalid interval argument"); } - this.sequence = 100; - this.subSequence = freqType; - this.freqType = freqType; - this.freqCount = freqCount; + if (count < 1) { + throw new IllegalArgumentException("invalid count argument"); + } + this.startStr = start; + this.endStr = end; + this.interval = interval; + this.count = count; + String strArray[] = this.startStr.split(":"); + if (strArray.length == 0 || strArray.length > 3) { + throw new IllegalArgumentException("Invalid start time argument"); + } + this.startHrs = Integer.valueOf(strArray[0]); + this.startMins = strArray.length > 1 ? Integer.valueOf(strArray[1]) : 0; + this.startSecs = strArray.length > 2 ? Integer.valueOf(strArray[2]) : 0; + if (this.startHrs > 23 || this.startMins > 59 || this.startSecs > 59) { + throw new IllegalArgumentException("Invalid start time argument"); + } + strArray = this.endStr.split(":"); + if (strArray.length == 0 || strArray.length > 3) { + throw new IllegalArgumentException("Invalid end time argument"); + } + this.endHrs = Integer.valueOf(strArray[0]); + this.endMins = strArray.length > 1 ? Integer.valueOf(strArray[1]) : 0; + this.endSecs = strArray.length > 2 ? Integer.valueOf(strArray[2]) : 0; + if (this.endHrs > 23 || this.endMins > 59 || this.endSecs > 59) { + throw new IllegalArgumentException("Invalid end time argument"); + } + this.sequence = SEQUENCE_TOD_RANGE + this.startHrs; if (Debug.verboseOn()) { Debug.logVerbose("Created " + this, module); } } + @Override + public void accept(TemporalExpressionVisitor visitor) { + visitor.visit(this); + } + + @Override public boolean equals(Object obj) { if (obj == this) { return true; } try { - Frequency that = (Frequency) obj; - return this.start.equals(that.start) && this.freqType == that.freqType && this.freqCount == that.freqCount; - } catch (Exception e) {} + TimeOfDayRange that = (TimeOfDayRange) obj; + return this.startStr.equals(that.startStr) && this.endStr.equals(that.endStr); + } catch (ClassCastException e) {} return false; } - public String toString() { - return super.toString() + ", start = " + this.start + ", freqType = " + this.freqType + ", freqCount = " + this.freqCount; + @Override + public Calendar first(Calendar cal) { + if (includesDate(cal)) { + return cal; + } + return next(cal); } - public boolean includesDate(Calendar cal) { - Calendar next = first(cal); - return next.equals(cal); + public int getCount() { + return this.count; } - public Calendar first(Calendar cal) { - Calendar first = prepareCal(cal); - while (first.before(cal)) { - first.add(this.freqType, this.freqCount); + public int getEndHours() { + return this.endHrs; + } + + public int getEndMins() { + return this.endMins; + } + + public int getEndSecs() { + return this.endSecs; + } + + public int getInterval() { + return this.interval; + } + + public int getStartHours() { + return this.startHrs; + } + + public int getStartMins() { + return this.startMins; + } + + public int getStartSecs() { + return this.startSecs; + } + + @Override + public boolean includesDate(Calendar cal) { + long millis = cal.getTimeInMillis(); + Calendar startCal = setStart(cal); + Calendar endCal = setEnd(startCal); + if (endCal.before(startCal)) { + endCal.add(Calendar.DAY_OF_MONTH, 1); } - return first; + long startMillis = startCal.getTimeInMillis(); + long endMillis = endCal.getTimeInMillis(); + return millis >= startMillis && millis <= endMillis; } - public Calendar next(Calendar cal) { - Calendar next = first(cal); - if (next.equals(cal)) { - next.add(this.freqType, this.freqCount); + @Override + public Calendar next(Calendar cal, ExpressionContext context) { + Calendar next = (Calendar) cal.clone(); + next.add(this.interval, this.count); + if (!includesDate(next)) { + Calendar last = next; + next = setStart(next); + if (next.before(last)) { + next.add(Calendar.DAY_OF_MONTH, 1); + } } return next; } + protected Calendar setCalendar(Calendar cal, int hrs, int mins, int secs) { + Calendar newCal = (Calendar) cal.clone(); + newCal.set(Calendar.MILLISECOND, 0); + newCal.set(Calendar.SECOND, secs); + newCal.set(Calendar.MINUTE, mins); + newCal.set(Calendar.HOUR_OF_DAY, hrs); + return newCal; + } + + protected Calendar setEnd(Calendar cal) { + return setCalendar(cal, this.endHrs, this.endMins, this.endSecs); + } + + protected Calendar setStart(Calendar cal) { + return setCalendar(cal, this.startHrs, this.startMins, this.startSecs); + } + + @Override + public String toString() { + return super.toString() + ", start = " + this.startStr + ", end = " + this.endStr + + ", interval = " + this.interval + ", count = " + this.count; + } + } + + /** A temporal expression that represents a mathematical union of all of its + * member expressions. */ + public static class Union extends TemporalExpression { + protected final Set<TemporalExpression> expressionSet; + + public Union(Set<TemporalExpression> expressionSet) { + if (expressionSet == null) { + throw new IllegalArgumentException("expressionSet argument cannot be null"); + } + this.expressionSet = expressionSet; + if (containsExpression(this)) { + throw new IllegalArgumentException("recursive expression"); + } + if (this.expressionSet.size() > 0) { + TemporalExpression that = this.expressionSet.iterator().next(); + this.sequence = that.sequence; + } + if (Debug.verboseOn()) { + Debug.logVerbose("Created " + this, module); + } + } + + @Override public void accept(TemporalExpressionVisitor visitor) { visitor.visit(this); } - /** Returns the start date of this expression. - * @return The start date of this expression - */ - public Date getStartDate() { - return (Date) this.start.clone(); + @Override + protected boolean containsExpression(TemporalExpression expression) { + for (TemporalExpression setItem : this.expressionSet) { + if (setItem.containsExpression(expression)) { + return true; + } + } + return false; } - /** Returns the frequency type of this expression. - * @return The frequency type of this expression - */ - public int getFreqType() { - return this.freqType; + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + try { + return this.expressionSet.equals(((Union) obj).expressionSet); + } catch (ClassCastException e) {} + return false; } - /** Returns the frequency count of this expression. - * @return The frequency count of this expression + @Override + public Calendar first(Calendar cal) { + for (TemporalExpression expression : this.expressionSet) { + Calendar first = expression.first(cal); + if (first != null && includesDate(first)) { + return first; + } + } + return null; + } + + /** Returns the member expression <code>Set</code>. The + * returned set is unmodifiable. + * @return The member expression <code>Set</code> */ - public int getFreqCount() { - return this.freqCount; + public Set<TemporalExpression> getExpressionSet() { + return Collections.unmodifiableSet(this.expressionSet); } - protected Calendar prepareCal(Calendar cal) { - // Performs a "sane" skip forward in time - avoids time consuming loops - // like incrementing every second from Jan 1 2000 until today - Calendar skip = (Calendar) cal.clone(); - skip.setTime(this.start); - long deltaMillis = cal.getTimeInMillis() - this.start.getTime(); - if (deltaMillis < 1000) { - return skip; - } - long divisor = deltaMillis; - if (this.freqType == Calendar.DAY_OF_MONTH) { - divisor = 86400000; - } else if (this.freqType == Calendar.HOUR) { - divisor = 3600000; - } else if (this.freqType == Calendar.MINUTE) { - divisor = 60000; - } else if (this.freqType == Calendar.SECOND) { - divisor = 1000; - } else { - return skip; + @Override + public boolean includesDate(Calendar cal) { + for (TemporalExpression expression : this.expressionSet) { + if (expression.includesDate(cal)) { + return true; + } } - float units = deltaMillis / divisor; - units = (units / this.freqCount) * this.freqCount; - skip.add(this.freqType, (int)units); - while (skip.after(cal)) { - skip.add(this.freqType, -this.freqCount); + return false; + } + + @Override + public Calendar next(Calendar cal, ExpressionContext context) { + Calendar result = null; + for (TemporalExpression expression : this.expressionSet) { + Calendar next = expression.next(cal, context); + if (next != null) { + if (result == null || next.before(result)) { + result = next; + } + } } - return skip; + return result; + } + + @Override + public String toString() { + return super.toString() + ", size = " + this.expressionSet.size(); } } } Modified: ofbiz/branches/release09.04/framework/webtools/webapp/webtools/tempexpr/tempExprMacros.ftl URL: http://svn.apache.org/viewvc/ofbiz/branches/release09.04/framework/webtools/webapp/webtools/tempexpr/tempExprMacros.ftl?rev=903125&r1=903124&r2=903125&view=diff ============================================================================== --- ofbiz/branches/release09.04/framework/webtools/webapp/webtools/tempexpr/tempExprMacros.ftl (original) +++ ofbiz/branches/release09.04/framework/webtools/webapp/webtools/tempexpr/tempExprMacros.ftl Tue Jan 26 08:28:39 2010 @@ -78,6 +78,17 @@ </tr> </#macro> +<#macro HourOfDayRange fromHour=1 toHour=31> + <tr> + <td class="label">${uiLabelMap.CommonFrom}</td> + <td><@HourOfDayField fieldName="integer1" fieldValue=fromHour/></td> + </tr> + <tr> + <td class="label">${uiLabelMap.CommonTo}</td> + <td><@HourOfDayField fieldName="integer2" fieldValue=toHour/></td> + </tr> +</#macro> + <#macro DayOfWeekRange fromDay=firstDayOfWeek toDay=lastDayOfWeek> <tr> <td class="label">${uiLabelMap.CommonFrom}</td> @@ -118,6 +129,17 @@ </tr> </#macro> +<#macro MinuteRange fromMinute=1 toMinute=31> + <tr> + <td class="label">${uiLabelMap.CommonFrom}</td> + <td><@MinuteField fieldName="integer1" fieldValue=fromMinute/></td> + </tr> + <tr> + <td class="label">${uiLabelMap.CommonTo}</td> + <td><@MinuteField fieldName="integer2" fieldValue=toMinute/></td> + </tr> +</#macro> + <#macro MonthRange fromMonth=0 toMonth=11> <tr> <td class="label">${uiLabelMap.CommonFrom}</td> Modified: ofbiz/branches/release09.04/framework/webtools/webapp/webtools/tempexpr/tempExprMaint.ftl URL: http://svn.apache.org/viewvc/ofbiz/branches/release09.04/framework/webtools/webapp/webtools/tempexpr/tempExprMaint.ftl?rev=903125&r1=903124&r2=903125&view=diff ============================================================================== --- ofbiz/branches/release09.04/framework/webtools/webapp/webtools/tempexpr/tempExprMaint.ftl (original) +++ ofbiz/branches/release09.04/framework/webtools/webapp/webtools/tempexpr/tempExprMaint.ftl Tue Jan 26 08:28:39 2010 @@ -44,6 +44,12 @@ <@DayOfWeekRange fromDay=temporalExpression.integer1 toDay=temporalExpression.integer2/> <#elseif temporalExpression.tempExprTypeId == "FREQUENCY"> <@Frequency formName="updateExpression" fromDate=temporalExpression.date1 freqType=temporalExpression.integer1 freqValue=temporalExpression.integer2/> + <#elseif temporalExpression.tempExprTypeId == "DAY_OF_WEEK_RANGE"> + <@DayOfWeekRange fromDay=temporalExpression.integer1 toDay=temporalExpression.integer2/> + <#elseif temporalExpression.tempExprTypeId == "HOUR_RANGE"> + <@HourOfDayRange fromHour=temporalExpression.integer1 toHour=temporalExpression.integer2/> + <#elseif temporalExpression.tempExprTypeId == "MINUTE_RANGE"> + <@MinuteRange fromMinute=temporalExpression.integer1 toMinute=temporalExpression.integer2/> <#elseif temporalExpression.tempExprTypeId == "MONTH_RANGE"> <@MonthRange fromMonth=temporalExpression.integer1 toMonth=temporalExpression.integer2/> <#elseif temporalExpression.tempExprTypeId == "TIME_OF_DAY_RANGE"> @@ -100,6 +106,10 @@ <hr/> <@CreateForm "FREQUENCY" Frequency/> <hr/> + <@CreateForm "HOUR_RANGE" HourOfDayRange/> + <hr/> + <@CreateForm "MINUTE_RANGE" MinuteRange/> + <hr/> <@CreateForm "MONTH_RANGE" MonthRange/> <hr/> <@CreateForm "TIME_OF_DAY_RANGE" TimeOfDayRange/> |
| Free forum by Nabble | Edit this page |
