svn commit: r900050 - /ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressions.java

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r900050 - /ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressions.java

adrianc
Author: adrianc
Date: Sun Jan 17 03:13:19 2010
New Revision: 900050

URL: http://svn.apache.org/viewvc?rev=900050&view=rev
Log:
Reorganized code in TemporalExpressions.java. No functional change.

Modified:
    ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressions.java

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressions.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressions.java?rev=900050&r1=900049&r2=900050&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressions.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressions.java Sun Jan 17 03:13:19 2010
@@ -37,224 +37,323 @@
 public class TemporalExpressions implements Serializable {
     public static final String module = TemporalExpressions.class.getName();
     public static final TemporalExpression NullExpression = new Null();
+    // Expressions are evaluated from largest unit of time to smallest.
+    // 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.
+    public static final int SEQUENCE_DATE_RANGE = 10000;
+    public static final int SEQUENCE_DAY_IN_MONTH = 400;
+    public static final int SEQUENCE_DOM_RANGE = 300;
+    public static final int SEQUENCE_DOW_RANGE = 500;
+    public static final int SEQUENCE_FREQ = 100;
+    public static final int SEQUENCE_HOUR_RANGE = 700;
+    public static final int SEQUENCE_MINUTE_RANGE = 800;
+    public static final int SEQUENCE_MONTH_RANGE = 200;
+    public static final int SEQUENCE_TOD_RANGE = 600;
+    
+
+    /** A temporal expression that represents a range of dates. */
+    public static class DateRange extends TemporalExpression {
+        protected final org.ofbiz.base.util.DateRange range;
+
+        public DateRange(Date date) {
+            this(date, date);
+        }
+
+        public DateRange(Date start, Date end) {
+            this.sequence = SEQUENCE_DATE_RANGE;
+            this.range = new org.ofbiz.base.util.DateRange(start, 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 {
+                return this.range.equals(((DateRange) obj).range);
+            } catch (ClassCastException e) {}
+            return false;
+        }
 
-    /** This class represents a null expression. */
-    public static class Null extends TemporalExpression {
         @Override
         public Calendar first(Calendar cal) {
-            return null;
+            return includesDate(cal) ? cal : null;
+        }
+
+        /** 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;
         }
+
         @Override
         public boolean includesDate(Calendar cal) {
-            return false;
+            return this.range.includesDate(cal.getTime());
         }
+
         @Override
         public Calendar next(Calendar cal) {
-            return null;
+            return includesDate(cal) ? cal : null;
         }
+
         @Override
-        public void accept(TemporalExpressionVisitor visitor) {
-            visitor.visit(this);
+        public String toString() {
+            return super.toString() + ", start = " + this.range.start() + ", end = " + this.range.end();
         }
     }
 
-    /** This class represents a mathematical union of all of its
-     * member expressions. */
-    public static class Union 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 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");
+        /**
+         * @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");
             }
-            if (this.expressionSet.size() > 0) {
-                TemporalExpression that = this.expressionSet.iterator().next();
-                if (this.compareTo(that) > 0) {
-                    this.sequence = that.sequence;
-                    this.subSequence = that.subSequence;
-                }
+            if (occurrence < -5 || occurrence == 0 || occurrence > 5) {
+                throw new IllegalArgumentException("Invalid occurrence argument");
             }
+            this.sequence = SEQUENCE_DAY_IN_MONTH;
+            this.subSequence = (occurrence * 7) + dayOfWeek;
+            this.dayOfWeek = dayOfWeek;
+            this.occurrence = occurrence;
             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(((Union) obj).expressionSet);
+                DayInMonth that = (DayInMonth) obj;
+                return this.dayOfWeek == that.dayOfWeek && this.occurrence == that.occurrence;
             } catch (ClassCastException e) {}
             return false;
         }
 
         @Override
-        public String toString() {
-            return super.toString() + ", size = " + this.expressionSet.size();
-        }
-
-        @Override
-        public boolean includesDate(Calendar cal) {
-            for (TemporalExpression expression : this.expressionSet) {
-                if (expression.includesDate(cal)) {
-                    return true;
+        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);
             }
-            return false;
+            return first;
         }
 
-        @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 day of week in this expression.
+         * @return The day of week in this expression
+         */
+        public int getDayOfWeek() {
+            return this.dayOfWeek;
         }
 
-        @Override
-        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;
+        /** Returns the occurrence in this expression.
+         * @return The occurrence in this expression
+         */
+        public int getOccurrence() {
+            return this.occurrence;
         }
 
         @Override
-        public void accept(TemporalExpressionVisitor visitor) {
-            visitor.visit(this);
+        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 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);
+        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);
                 }
+                alignDayOfWeek(next);
             }
-            return finalSet;
-        }
-
-        /** 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);
+            return next;
         }
 
         @Override
-        protected boolean containsExpression(TemporalExpression expression) {
-            for (TemporalExpression setItem : this.expressionSet) {
-                if (setItem.containsExpression(expression)) {
-                    return true;
-                }
-            }
-            return false;
+        public String toString() {
+            return super.toString() + ", dayOfWeek = " + this.dayOfWeek + ", occurrence = " + this.occurrence;
         }
     }
 
-    /** 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 of month range. */
+    public static class DayOfMonthRange extends TemporalExpression {
+        protected final int end;
+        protected final int start;
 
-        public Intersection(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");
+        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 (this.expressionSet.size() > 0) {
-                TemporalExpression that = this.expressionSet.iterator().next();
-                if (this.compareTo(that) > 0) {
-                    this.sequence = that.sequence;
-                    this.subSequence = that.subSequence;
-                }
+            if (end < 1 || end > 31) {
+                throw new IllegalArgumentException("Invalid end argument");
             }
+            this.sequence = SEQUENCE_DOM_RANGE;
+            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
         public boolean equals(Object obj) {
             if (obj == this) {
                 return true;
             }
             try {
-                return this.expressionSet.equals(((Intersection) obj).expressionSet);
+                DayOfMonthRange that = (DayOfMonthRange) obj;
+                return this.start == that.start && this.end == that.end;
             } catch (ClassCastException e) {}
             return false;
         }
 
         @Override
-        public String toString() {
-            return super.toString() + ", size = " + this.expressionSet.size();
+        public Calendar first(Calendar cal) {
+            Calendar first = setStartOfDay((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) {
-            for (TemporalExpression expression : this.expressionSet) {
-                if (!expression.includesDate(cal)) {
-                    return false;
-                }
-            }
-            return true;
+            int dom = cal.get(Calendar.DAY_OF_MONTH);
+            return dom >= this.start && dom <= this.end;
         }
 
         @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;
-                }
-            }
-            if (includesDate(first)) {
-                return first;
-            } else {
-                return null;
+        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;
         }
 
         @Override
-        public Calendar next(Calendar cal) {
-            Calendar next = (Calendar) cal.clone();
-            for (TemporalExpression expression : this.expressionSet) {
-                next = expression.next(next);
-                if (next == null) {
-                    return null;
-                }
+        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 (includesDate(next)) {
-                return next;
-            } else {
-                return null;
+            if (end < Calendar.SUNDAY || end > Calendar.SATURDAY) {
+                throw new IllegalArgumentException("Invalid end argument");
+            }
+            this.sequence = SEQUENCE_DOW_RANGE;
+            this.subSequence = start;
+            this.start = start;
+            this.end = end;
+            if (Debug.verboseOn()) {
+                Debug.logVerbose("Created " + this, module);
             }
         }
 
@@ -264,52 +363,79 @@
         }
 
         @Override
-        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;
-                }
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
             }
-            Calendar checkCal = (Calendar) cal.clone();
-            for (Date date : rawSet) {
-                checkCal.setTime(date);
-                if (includesDate(checkCal)) {
-                    finalSet.add(date);
-                }
+            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 finalSet;
+            return setStartOfDay(first);
         }
 
-        /** Returns the member expression <code>Set</code>. The
-         * returned set is unmodifiable.
-         * @return The member expression <code>Set</code>
+        /** 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;
+        }
+
+        /** Returns the starting day of this range.
+         * @return The starting day of this range
+         */
+        public int getStartDay() {
+            return this.start;
         }
 
         @Override
-        protected boolean containsExpression(TemporalExpression expression) {
-            for (TemporalExpression setItem : this.expressionSet) {
-                if (setItem.containsExpression(expression)) {
+        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) {
+            Calendar next = (Calendar) cal.clone();
+            next.add(Calendar.DAY_OF_MONTH, 1);
+            while (!includesDate(next)) {
+                next.add(Calendar.DAY_OF_MONTH, 1);
+            }
+            return setStartOfDay(next);
+        }
+
+        @Override
+        public String toString() {
+            return super.toString() + ", start = " + this.start + ", end = " + this.end;
+        }
     }
 
     /** This class 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) {
@@ -330,6 +456,16 @@
         }
 
         @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;
@@ -342,16 +478,6 @@
         }
 
         @Override
-        public String toString() {
-            return super.toString() + ", included = " + this.included + ", excluded = " + this.excluded;
-        }
-
-        @Override
-        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)) {
@@ -360,18 +486,18 @@
             return first;
         }
 
-        @Override
-        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;
+        /** Returns the excluded expression.
+         * @return The excluded <code>TemporalExpression</code>
+         */
+        public TemporalExpression getExcluded() {
+            return this.excluded;
         }
 
-        @Override
-        public void accept(TemporalExpressionVisitor visitor) {
-            visitor.visit(this);
+        /** Returns the included expression.
+         * @return The included <code>TemporalExpression</code>
+         */
+        public TemporalExpression getIncluded() {
+            return this.included;
         }
 
         @Override
@@ -388,41 +514,66 @@
             return finalSet;
         }
 
-        /** Returns the excluded expression.
-         * @return The excluded <code>TemporalExpression</code>
-         */
-        public TemporalExpression getExcluded() {
-            return this.excluded;
+        @Override
+        public boolean includesDate(Calendar cal) {
+            return this.included.includesDate(cal) && !this.excluded.includesDate(cal);
         }
 
-        /** Returns the included expression.
-         * @return The included <code>TemporalExpression</code>
-         */
-        public TemporalExpression getIncluded() {
-            return this.included;
+        @Override
+        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;
         }
 
         @Override
-        protected boolean containsExpression(TemporalExpression expression) {
-            return this.included.containsExpression(expression) || this.excluded.containsExpression(expression);
+        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;
-
-        public DateRange(Date date) {
-            this(date, date);
-        }
-
-        public DateRange(Date start, Date end) {
-            this.sequence = 1000;
-            this.range = new org.ofbiz.base.util.DateRange(start, end);
-            if (Debug.verboseOn()) {
-                Debug.logVerbose("Created " + this, module);
-            }
-        }
+    /** 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;
+
+        /**
+         * @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 = 100;
+            this.subSequence = 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) {
@@ -430,48 +581,98 @@
                 return true;
             }
             try {
-                return this.range.equals(((DateRange) obj).range);
+                Frequency that = (Frequency) obj;
+                return this.start.equals(that.start) && this.freqType == that.freqType && this.freqCount == that.freqCount;
             } catch (ClassCastException e) {}
             return false;
         }
 
         @Override
-        public String toString() {
-            return super.toString() + ", start = " + this.range.start() + ", end = " + this.range.end();
+        public Calendar first(Calendar cal) {
+            Calendar first = prepareCal(cal);
+            while (first.before(cal)) {
+                first.add(this.freqType, this.freqCount);
+            }
+            return first;
         }
 
-        @Override
-        public boolean includesDate(Calendar cal) {
-            return this.range.includesDate(cal.getTime());
+        /** Returns the frequency count of this expression.
+         * @return The frequency count of this expression
+         */
+        public int getFreqCount() {
+            return this.freqCount;
+        }
+
+        /** Returns the frequency type of this expression.
+         * @return The frequency type of this expression
+         */
+        public int getFreqType() {
+            return this.freqType;
+        }
+
+        /** Returns the start date of this expression.
+         * @return The start date of this expression
+         */
+        public Date getStartDate() {
+            return (Date) this.start.clone();
         }
 
         @Override
-        public Calendar first(Calendar cal) {
-            return includesDate(cal) ? cal : null;
+        public boolean includesDate(Calendar cal) {
+            Calendar next = first(cal);
+            return next.equals(cal);
         }
 
         @Override
         public Calendar next(Calendar cal) {
-            return includesDate(cal) ? cal : null;
+            Calendar next = first(cal);
+            if (next.equals(cal)) {
+                next.add(this.freqType, this.freqCount);
+            }
+            return next;
         }
 
-        @Override
-        public void accept(TemporalExpressionVisitor visitor) {
-            visitor.visit(this);
+        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;
+            }
+            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 skip;
         }
 
-        /** 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;
+        @Override
+        public String toString() {
+            return super.toString() + ", start = " + this.start + ", freqType = " + this.freqType + ", freqCount = " + this.freqCount;
         }
     }
 
+
     /** A temporal expression that represents an hour range. */
     public static class HourRange extends TemporalExpression {
-        protected final int start;
         protected final int end;
+        protected final int start;
 
         /**
          * @param hour An integer in the range of 0 to 23.
@@ -493,14 +694,19 @@
             }
             this.start = start;
             this.end = end;
-            this.sequence = 600;
-            this.subSequence = start * 4000;
+            this.sequence = SEQUENCE_HOUR_RANGE;
+            this.subSequence = 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;
@@ -513,28 +719,6 @@
         }
 
         @Override
-        public String toString() {
-            return super.toString() + ", start = " + this.start + ", end = " + this.end;
-        }
-
-        @Override
-        public boolean includesDate(Calendar cal) {
-            int hour = cal.get(Calendar.HOUR_OF_DAY);
-            if (hour == this.start || hour == this.end) {
-                return true;
-            }
-            Calendar compareCal = (Calendar) cal.clone();
-            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.HOUR_OF_DAY, 1);
-            }
-            return false;
-        }
-
-        @Override
         public Calendar first(Calendar cal) {
             Calendar first = (Calendar) cal.clone();
             while (!includesDate(first)) {
@@ -543,28 +727,6 @@
             return first;
         }
 
-        @Override
-        public Calendar next(Calendar cal) {
-            Calendar next = (Calendar) cal.clone();
-            next.add(Calendar.HOUR_OF_DAY, 1);
-            while (!includesDate(next)) {
-                next.add(Calendar.HOUR_OF_DAY, 1);
-            }
-            return next;
-        }
-
-        @Override
-        public void accept(TemporalExpressionVisitor visitor) {
-            visitor.visit(this);
-        }
-
-        /** Returns the starting hour of this range.
-         * @return The starting hour of this range
-         */
-        public int getStartHour() {
-            return this.start;
-        }
-
         /** Returns the ending hour of this range.
          * @return The ending hour of this range
          */
@@ -586,410 +748,301 @@
             }
             return rangeSet;
         }
-    }
-
-
-    /** A temporal expression that represents a minute range. */
-    public static class MinuteRange extends TemporalExpression {
-        protected final int start;
-        protected final int end;
-
-        /**
-         * @param hour An integer in the range of 0 to 59.
-         */
-        public MinuteRange(int minute) {
-            this(minute, minute);
-        }
 
-        /**
-         * @param start An integer in the range of 0 to 59.
-         * @param end An integer in the range of 0 to 59.
+        /** Returns the starting hour of this range.
+         * @return The starting hour of this range
          */
-        public MinuteRange(int start, int end) {
-            if (start < 0 || start > 59) {
-                throw new IllegalArgumentException("Invalid start argument");
-            }
-            if (end < 0 || end > 59) {
-                throw new IllegalArgumentException("Invalid end argument");
-            }
-            this.start = start;
-            this.end = end;
-            this.sequence = 600;
-            this.subSequence = start;
-            if (Debug.verboseOn()) {
-                Debug.logVerbose("Created " + this, module);
-            }
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj == this) {
-                return true;
-            }
-            try {
-                MinuteRange that = (MinuteRange) obj;
-                return this.start == that.start && this.end == that.end;
-            } catch (ClassCastException e) {}
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            return super.toString() + ", start = " + this.start + ", end = " + this.end;
+        public int getStartHour() {
+            return this.start;
         }
 
         @Override
         public boolean includesDate(Calendar cal) {
-            int minute = cal.get(Calendar.MINUTE);
-            if (minute == this.start || minute == this.end) {
+            int hour = cal.get(Calendar.HOUR_OF_DAY);
+            if (hour == this.start || hour == 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) {
+            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.MINUTE, 1);
+                compareCal.add(Calendar.HOUR_OF_DAY, 1);
             }
             return false;
         }
 
         @Override
-        public Calendar first(Calendar cal) {
-            Calendar first = (Calendar) cal.clone();
-            while (!includesDate(first)) {
-                first.add(Calendar.MINUTE, 1);
-            }
-            return first;
-        }
-
-        @Override
         public Calendar next(Calendar cal) {
             Calendar next = (Calendar) cal.clone();
-            next.add(Calendar.MINUTE, 1);
+            next.add(Calendar.HOUR_OF_DAY, 1);
             while (!includesDate(next)) {
-                next.add(Calendar.MINUTE, 1);
+                next.add(Calendar.HOUR_OF_DAY, 1);
             }
             return next;
         }
 
         @Override
-        public void accept(TemporalExpressionVisitor visitor) {
-            visitor.visit(this);
+        public String toString() {
+            return super.toString() + ", start = " + this.start + ", end = " + this.end;
         }
+    }
+    /** This class represents a mathematical intersection of all of its
+     * member expressions. */
+    public static class Intersection extends TemporalExpression {
+        protected final Set<TemporalExpression> expressionSet;
 
-        /** Returns the starting minute of this range.
-         * @return The starting minute of this range
-         */
-        public int getStartMinute() {
-            return this.start;
-        }
-
-        /** Returns the ending minute of this range.
-         * @return The ending minute of this range
-         */
-        public int getEndMinute() {
-            return this.end;
-        }
-
-        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;
-        }
-    }
-    /** 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 (UtilValidate.isEmpty(start)) {
-                throw new IllegalArgumentException("start argument cannot be null or empty");
-            }
-            if (UtilValidate.isEmpty(end)) {
-                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");
+        public Intersection(Set<TemporalExpression> expressionSet) {
+            if (expressionSet == null) {
+                throw new IllegalArgumentException("expressionSet argument cannot be null");
             }
-            strArray = this.endStr.split(":");
-            if (strArray.length == 0 || strArray.length > 3) {
-                throw new IllegalArgumentException("Invalid end time argument");
+            this.expressionSet = expressionSet;
+            if (containsExpression(this)) {
+                throw new IllegalArgumentException("recursive expression");
             }
-            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");
+            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.sequence = 600;
-            this.subSequence = (this.startHrs * 4000) + (this.startMins * 60) + this.startSecs;
             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 {
-                TimeOfDayRange that = (TimeOfDayRange) obj;
-                return this.startStr.equals(that.startStr) && this.endStr.equals(that.endStr);
+                return this.expressionSet.equals(((Intersection) obj).expressionSet);
             } catch (ClassCastException e) {}
             return false;
         }
 
         @Override
-        public String toString() {
-            return super.toString() + ", start = " + this.startStr + ", end = " + this.endStr
-            + ", interval = " + this.interval + ", count = " + this.count;
+        public Calendar first(Calendar cal) {
+            Calendar first = (Calendar) cal.clone();
+            for (TemporalExpression expression : this.expressionSet) {
+                first = expression.first(first);
+                if (first == null) {
+                    return null;
+                }
+            }
+            if (includesDate(first)) {
+                return first;
+            } else {
+                return null;
+            }
+        }
+
+        /** 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) {
-            long millis = cal.getTimeInMillis();
-            Calendar startCal = setStart(cal);
-            Calendar endCal = setEnd(startCal);
-            if (endCal.before(startCal)) {
-                endCal.add(Calendar.DAY_OF_MONTH, 1);
+        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;
+                }
             }
-            long startMillis = startCal.getTimeInMillis();
-            long endMillis = endCal.getTimeInMillis();
-            return millis >= startMillis && millis <= endMillis;
+            Calendar checkCal = (Calendar) cal.clone();
+            for (Date date : rawSet) {
+                checkCal.setTime(date);
+                if (includesDate(checkCal)) {
+                    finalSet.add(date);
+                }
+            }
+            return finalSet;
         }
 
         @Override
-        public Calendar first(Calendar cal) {
-            if (includesDate(cal)) {
-                return cal;
+        public boolean includesDate(Calendar cal) {
+            for (TemporalExpression expression : this.expressionSet) {
+                if (!expression.includesDate(cal)) {
+                    return false;
+                }
             }
-            return next(cal);
+            return true;
         }
 
         @Override
         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);
+            for (TemporalExpression expression : this.expressionSet) {
+                next = expression.next(next);
+                if (next == null) {
+                    return null;
                 }
             }
-            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;
+            if (includesDate(next)) {
+                return next;
+            } else {
+                return null;
+            }
         }
 
         @Override
-        public void accept(TemporalExpressionVisitor visitor) {
-            visitor.visit(this);
-        }
-
-        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);
-        }
-
-        protected Calendar setEnd(Calendar cal) {
-            return setCalendar(cal, this.endHrs, this.endMins, this.endSecs);
+        public String toString() {
+            return super.toString() + ", size = " + this.expressionSet.size();
         }
     }
 
-    /** A temporal expression that represents a day of week range. */
-    public static class DayOfWeekRange extends TemporalExpression {
-        protected final int start;
+    /** A temporal expression that represents a minute range. */
+    public static class MinuteRange extends TemporalExpression {
         protected final int end;
+        protected final int start;
 
-        public DayOfWeekRange(int dow) {
-            this(dow, dow);
+        /**
+         * @param hour An integer in the range of 0 to 59.
+         */
+        public MinuteRange(int minute) {
+            this(minute, minute);
         }
 
         /**
-         * @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 59.
+         * @param end An integer in the range of 0 to 59.
          */
-        public DayOfWeekRange(int start, int end) {
-            if (start < Calendar.SUNDAY || start > Calendar.SATURDAY) {
+        public MinuteRange(int start, int end) {
+            if (start < 0 || start > 59) {
                 throw new IllegalArgumentException("Invalid start argument");
             }
-            if (end < Calendar.SUNDAY || end > Calendar.SATURDAY) {
+            if (end < 0 || end > 59) {
                 throw new IllegalArgumentException("Invalid end argument");
             }
-            this.sequence = 500;
-            this.subSequence = start;
             this.start = start;
             this.end = end;
+            this.sequence = SEQUENCE_MINUTE_RANGE;
+            this.subSequence = 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;
+                MinuteRange that = (MinuteRange) obj;
                 return this.start == that.start && this.end == that.end;
             } catch (ClassCastException e) {}
             return false;
         }
 
         @Override
-        public String toString() {
-            return super.toString() + ", start = " + this.start + ", end = " + this.end;
+        public Calendar first(Calendar cal) {
+            Calendar first = (Calendar) cal.clone();
+            while (!includesDate(first)) {
+                first.add(Calendar.MINUTE, 1);
+            }
+            return first;
+        }
+
+        /** Returns the ending minute of this range.
+         * @return The ending minute of this range
+         */
+        public int getEndMinute() {
+            return this.end;
+        }
+
+        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 minute of this range.
+         * @return The starting minute of this range
+         */
+        public int getStartMinute() {
+            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 minute = cal.get(Calendar.MINUTE);
+            if (minute == this.start || minute == 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.MINUTE, this.start);
+            while (compareCal.get(Calendar.MINUTE) != this.end) {
+                if (compareCal.get(Calendar.MINUTE) == minute) {
                     return true;
                 }
-                compareCal.add(Calendar.DAY_OF_MONTH, 1);
+                compareCal.add(Calendar.MINUTE, 1);
             }
             return false;
         }
 
         @Override
-        public Calendar first(Calendar cal) {
-            Calendar first = (Calendar) cal.clone();
-            while (!includesDate(first)) {
-                first.add(Calendar.DAY_OF_MONTH, 1);
-            }
-            return setStartOfDay(first);
-        }
-
-        @Override
         public Calendar next(Calendar cal) {
             Calendar next = (Calendar) cal.clone();
-            next.add(Calendar.DAY_OF_MONTH, 1);
+            next.add(Calendar.MINUTE, 1);
             while (!includesDate(next)) {
-                next.add(Calendar.DAY_OF_MONTH, 1);
-            }
-            return setStartOfDay(next);
-        }
-
-        @Override
-        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;
-        }
-
-        /** Returns the ending day of this range.
-         * @return The ending day of this range
-         */
-        public int getEndDay() {
-            return this.end;
+                next.add(Calendar.MINUTE, 1);
+            }
+            return next;
+        }
+
+        @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;
+        protected final int start;
 
         public MonthRange(int month) {
             this(month, month);
@@ -1008,7 +1061,7 @@
             if (end < Calendar.JANUARY || end > Calendar.UNDECIMBER) {
                 throw new IllegalArgumentException("Invalid end argument");
             }
-            this.sequence = 200;
+            this.sequence = SEQUENCE_MONTH_RANGE;
             this.subSequence = start;
             this.start = start;
             this.end = end;
@@ -1018,6 +1071,11 @@
         }
 
         @Override
+        public void accept(TemporalExpressionVisitor visitor) {
+            visitor.visit(this);
+        }
+
+        @Override
         public boolean equals(Object obj) {
             if (obj == this) {
                 return true;
@@ -1030,8 +1088,27 @@
         }
 
         @Override
-        public String toString() {
-            return super.toString() + ", start = " + this.start + ", end = " + this.end;
+        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);
+            }
+            return first;
+        }
+
+        /** 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
@@ -1054,16 +1131,6 @@
         }
 
         @Override
-        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);
-            }
-            return first;
-        }
-
-        @Override
         public Calendar next(Calendar cal) {
             Calendar next = (Calendar) cal.clone();
             next.set(Calendar.DAY_OF_MONTH, 1);
@@ -1075,368 +1142,320 @@
         }
 
         @Override
+        public String toString() {
+            return super.toString() + ", start = " + this.start + ", end = " + this.end;
+        }
+    }
+
+    /** This class represents a null expression. */
+    public static class Null extends TemporalExpression {
+        @Override
         public void accept(TemporalExpressionVisitor visitor) {
             visitor.visit(this);
         }
-
-        /** Returns the starting month of this range.
-         * @return The starting month of this range
-         */
-        public int getStartMonth() {
-            return this.start;
+        @Override
+        public Calendar first(Calendar cal) {
+            return null;
         }
-
-        /** Returns the ending month of this range.
-         * @return The ending month of this range
-         */
-        public int getEndMonth() {
-            return this.end;
+        @Override
+        public boolean includesDate(Calendar cal) {
+            return false;
+        }
+        @Override
+        public Calendar next(Calendar cal) {
+            return null;
         }
     }
 
-    /** A temporal expression that represents a day of month range. */
-    public static class DayOfMonthRange extends TemporalExpression {
-        protected final int start;
-        protected final int end;
-
-        public DayOfMonthRange(int dom) {
-            this(dom, dom);
-        }
+    /** 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 An integer in the range of 1 to 31
-         * @param end An integer in the range of 1 to 31
+         * @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 DayOfMonthRange(int start, int end) {
-            if (start < 1 || start > end) {
-                throw new IllegalArgumentException("Invalid start 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 (end < 1 || end > 31) {
-                throw new IllegalArgumentException("Invalid end argument");
+            if (UtilValidate.isEmpty(end)) {
+                throw new IllegalArgumentException("end argument cannot be null or empty");
             }
-            this.sequence = 300;
-            this.subSequence = start;
-            this.start = start;
-            this.end = end;
+            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 = SEQUENCE_TOD_RANGE;
+            this.subSequence = 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 {
-                DayOfMonthRange that = (DayOfMonthRange) obj;
-                return this.start == that.start && this.end == that.end;
+                TimeOfDayRange that = (TimeOfDayRange) obj;
+                return this.startStr.equals(that.startStr) && this.endStr.equals(that.endStr);
             } catch (ClassCastException e) {}
             return false;
         }
 
         @Override
-        public String toString() {
-            return super.toString() + ", start = " + this.start + ", end = " + this.end;
-        }
-
-        @Override
-        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());
-            while (!includesDate(first)) {
-                first.add(Calendar.DAY_OF_MONTH, 1);
-            }
-            return first;
-        }
-
-        @Override
-        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);
+            if (includesDate(cal)) {
+                return cal;
             }
-            return next;
+            return next(cal);
         }
 
-        @Override
-        public void accept(TemporalExpressionVisitor visitor) {
-            visitor.visit(this);
+        public int getCount() {
+            return this.count;
         }
 
-        /** Returns the starting day of this range.
-         * @return The starting day of this range
-         */
-        public int getStartDay() {
-            return this.start;
+        public int getEndHours() {
+            return this.endHrs;
         }
 
-        /** Returns the ending day of this range.
-         * @return The ending day of this range
-         */
-        public int getEndDay() {
-            return this.end;
+        public int getEndMins() {
+            return this.endMins;
         }
-    }
-
-    /** A temporal expression that represents a day in the month. */
-    public static class DayInMonth extends TemporalExpression {
-        protected final int dayOfWeek;
-        protected final int occurrence;
 
-        /**
-         * @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");
-            }
-            if (occurrence < -5 || occurrence == 0 || occurrence > 5) {
-                throw new IllegalArgumentException("Invalid occurrence argument");
-            }
-            this.sequence = 400;
-            this.subSequence = dayOfWeek;
-            this.dayOfWeek = dayOfWeek;
-            this.occurrence = occurrence;
-            if (Debug.verboseOn()) {
-                Debug.logVerbose("Created " + this, module);
-            }
+        public int getEndSecs() {
+            return this.endSecs;
         }
 
-        @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 (ClassCastException e) {}
-            return false;
+        public int getInterval() {
+            return this.interval;
         }
 
-        @Override
-        public String toString() {
-            return super.toString() + ", dayOfWeek = " + this.dayOfWeek + ", occurrence = " + this.occurrence;
+        public int getStartHours() {
+            return this.startHrs;
         }
 
-        @Override
-        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;
+        public int getStartMins() {
+            return this.startMins;
+        }
+
+        public int getStartSecs() {
+            return this.startSecs;
         }
 
         @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);
+        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;
         }
 
         @Override
         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);
+            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);
                 }
-                alignDayOfWeek(next);
             }
             return next;
         }
 
-        @Override
-        public void accept(TemporalExpressionVisitor visitor) {
-            visitor.visit(this);
+        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;
         }
 
-        /** Returns the day of week in this expression.
-         * @return The day of week in this expression
-         */
-        public int getDayOfWeek() {
-            return this.dayOfWeek;
+        protected Calendar setEnd(Calendar cal) {
+            return setCalendar(cal, this.endHrs, this.endMins, this.endSecs);
         }
 
-        /** Returns the occurrence in this expression.
-         * @return The occurrence in this expression
-         */
-        public int getOccurrence() {
-            return this.occurrence;
+        protected Calendar setStart(Calendar cal) {
+            return setCalendar(cal, this.startHrs, this.startMins, this.startSecs);
         }
 
-        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 String toString() {
+            return super.toString() + ", start = " + this.startStr + ", end = " + this.endStr
+            + ", interval = " + this.interval + ", count = " + this.count;
         }
     }
 
-    /** 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;
+    /** This class represents a mathematical union of all of its
+     * member expressions. */
+    public static class Union extends TemporalExpression {
+        protected final Set<TemporalExpression> expressionSet;
 
-        /**
-         * @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");
+        public Union(Set<TemporalExpression> expressionSet) {
+            if (expressionSet == null) {
+                throw new IllegalArgumentException("expressionSet argument cannot be null");
             }
-            if (freqCount < 1) {
-                throw new IllegalArgumentException("freqCount argument must be a positive integer");
+            this.expressionSet = expressionSet;
+            if (containsExpression(this)) {
+                throw new IllegalArgumentException("recursive expression");
             }
-            if (start != null) {
-                this.start = start;
-            } else {
-                this.start = new Date();
+            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.sequence = 100;
-            this.subSequence = 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
+        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 {
-                Frequency that = (Frequency) obj;
-                return this.start.equals(that.start) && this.freqType == that.freqType && this.freqCount == that.freqCount;
+                return this.expressionSet.equals(((Union) obj).expressionSet);
             } catch (ClassCastException e) {}
             return false;
         }
 
         @Override
-        public String toString() {
-            return super.toString() + ", start = " + this.start + ", freqType = " + this.freqType + ", freqCount = " + this.freqCount;
+        public Calendar first(Calendar cal) {
+            for (TemporalExpression expression : this.expressionSet) {
+                Calendar first = expression.first(cal);
+                if (first != null && includesDate(first)) {
+                    return first;
+                }
+            }
+            return null;
         }
 
-        @Override
-        public boolean includesDate(Calendar cal) {
-            Calendar next = first(cal);
-            return next.equals(cal);
+        /** 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 first(Calendar cal) {
-            Calendar first = prepareCal(cal);
-            while (first.before(cal)) {
-                first.add(this.freqType, this.freqCount);
+        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));
             }
-            return first;
+            Calendar checkCal = (Calendar) cal.clone();
+            for (Date date : rawSet) {
+                checkCal.setTime(date);
+                if (includesDate(checkCal)) {
+                    finalSet.add(date);
+                }
+            }
+            return finalSet;
         }
 
         @Override
-        public Calendar next(Calendar cal) {
-            Calendar next = first(cal);
-            if (next.equals(cal)) {
-                next.add(this.freqType, this.freqCount);
+        public boolean includesDate(Calendar cal) {
+            for (TemporalExpression expression : this.expressionSet) {
+                if (expression.includesDate(cal)) {
+                    return true;
+                }
             }
-            return next;
+            return false;
         }
 
         @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();
-        }
-
-        /** Returns the frequency type of this expression.
-         * @return The frequency type of this expression
-         */
-        public int getFreqType() {
-            return this.freqType;
-        }
-
-        /** Returns the frequency count of this expression.
-         * @return The frequency count of this expression
-         */
-        public int getFreqCount() {
-            return this.freqCount;
+        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;
         }
 
-        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;
-            }
-            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 skip;
+        @Override
+        public String toString() {
+            return super.toString() + ", size = " + this.expressionSet.size();
         }
     }
 }