svn commit: r900217 - in /ofbiz/trunk: applications/workeffort/src/org/ofbiz/workeffort/workeffort/ framework/common/config/ framework/service/entitydef/ framework/service/src/org/ofbiz/service/calendar/ framework/webtools/webapp/webtools/tempexpr/

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

svn commit: r900217 - in /ofbiz/trunk: applications/workeffort/src/org/ofbiz/workeffort/workeffort/ framework/common/config/ framework/service/entitydef/ framework/service/src/org/ofbiz/service/calendar/ framework/webtools/webapp/webtools/tempexpr/

adrianc
Author: adrianc
Date: Sun Jan 17 20:57:19 2010
New Revision: 900217

URL: http://svn.apache.org/viewvc?rev=900217&view=rev
Log:
Added a Substitution temporal expression. This solves Martin Fowler's conundrum - how to supply a substitute date for an excluded date.

Modified:
    ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java
    ofbiz/trunk/framework/common/config/TemporalExpressionUiLabels.xml
    ofbiz/trunk/framework/service/entitydef/entitymodel.xml
    ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/RecurrenceInfo.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpression.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressionVisitor.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressionWorker.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressions.java
    ofbiz/trunk/framework/webtools/webapp/webtools/tempexpr/tempExprMaint.ftl

Modified: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java?rev=900217&r1=900216&r2=900217&view=diff
==============================================================================
--- ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java (original)
+++ ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java Sun Jan 17 20:57:19 2010
@@ -180,6 +180,11 @@
     public void visit(Null expr) {}
 
     @Override
+    public void visit(Substitution expr) {
+        // iCalendar format does not support substitutions. Do nothing for now.
+    }
+
+    @Override
     public void visit(TemporalExpressions.DateRange expr) {
         if (this.state.isExcluded) {
             throw new IllegalStateException("iCalendar does not support excluded date ranges");
@@ -276,7 +281,7 @@
         this.state.addRecur(recur);
     }
 
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({ "unchecked", "deprecation" })
     @Override
     public void visit(TimeOfDayRange expr) {
         int startHr = expr.getStartHours();

Modified: ofbiz/trunk/framework/common/config/TemporalExpressionUiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/common/config/TemporalExpressionUiLabels.xml?rev=900217&r1=900216&r2=900217&view=diff
==============================================================================
--- ofbiz/trunk/framework/common/config/TemporalExpressionUiLabels.xml (original)
+++ ofbiz/trunk/framework/common/config/TemporalExpressionUiLabels.xml Sun Jan 17 20:57:19 2010
@@ -144,6 +144,9 @@
         <value xml:lang="it">Intervallo di mese</value>
         <value xml:lang="zh">月的范围</value>
     </property>
+    <property key="TemporalExpression_SUBSTITUTION">
+        <value xml:lang="en">Substitution</value>
+    </property>
     <property key="TemporalExpression_TIME_OF_DAY_RANGE">
         <value xml:lang="en">Time Of Day Range (deprecated)</value>
         <value xml:lang="fr">Intervalle entre 2 heures d'un jour</value>

Modified: ofbiz/trunk/framework/service/entitydef/entitymodel.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/entitydef/entitymodel.xml?rev=900217&r1=900216&r2=900217&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/entitydef/entitymodel.xml (original)
+++ ofbiz/trunk/framework/service/entitydef/entitymodel.xml Sun Jan 17 20:57:19 2010
@@ -156,7 +156,11 @@
             title="Temporal Expression Association">
       <field name="fromTempExprId" type="id-ne"><description>The "parent" expression</description></field>
       <field name="toTempExprId" type="id-ne"><description>The "child" expression</description></field>
-      <field name="exprAssocType" type="id"><description>Expression association type. Applies to DIFFERENCE expression types only. Valid values are INCLUDED or EXCLUDED.</description></field>
+      <field name="exprAssocType" type="id"><description>Expression association type.
+         When applied to DIFFERENCE expression types, valid values are INCLUDED or EXCLUDED.
+         When applied to SUBSTITUTION expression types, valid values are INCLUDED, EXCLUDED, or SUBSTITUTE.
+         </description>
+      </field>
       <prim-key field="fromTempExprId"/>
       <prim-key field="toTempExprId"/>
       <relation type="one" fk-name="TEMP_EXPR_FROM" rel-entity-name="TemporalExpression" title="From">

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/RecurrenceInfo.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/RecurrenceInfo.java?rev=900217&r1=900216&r2=900217&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/RecurrenceInfo.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/RecurrenceInfo.java Sun Jan 17 20:57:19 2010
@@ -376,5 +376,9 @@
         }
         @Override
         public void accept(TemporalExpressionVisitor visitor) {}
+        @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            return false;
+        }
     }
 }

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpression.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpression.java?rev=900217&r1=900216&r2=900217&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpression.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpression.java Sun Jan 17 20:57:19 2010
@@ -50,6 +50,19 @@
      */
     public abstract boolean includesDate(Calendar cal);
 
+    /** Returns true if this expression is a candidate for substitution
+     * using the expression <code>expressionToTest</code> for the date
+     * <code>cal</code>. A <code>Substitution</code> object will call this
+     * method when it needs to know if this expression could have produced
+     * the date <code>cal</code> based on the expression
+     * <code>expressionToTest</code>.
+     * @param cal A date to evaluate
+     * @param expressionToTest An expression to evaluate
+     * @return true if this expression could have produced the date
+     * <code>cal</code> using the expression <code>expressionToTest</code>
+     */
+    public abstract boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest);
+
     /** Returns a date representing the first occurrence of this expression
      * on or after a specified date. Returns <code>null</code> if there
      * is no matching date.

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressionVisitor.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressionVisitor.java?rev=900217&r1=900216&r2=900217&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressionVisitor.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressionVisitor.java Sun Jan 17 20:57:19 2010
@@ -31,6 +31,8 @@
     void visit(TemporalExpressions.MinuteRange expr);
     void visit(TemporalExpressions.MonthRange expr);
     void visit(TemporalExpressions.Null expr);
+    void visit(TemporalExpressions.Substitution expr);
+    @SuppressWarnings("deprecation")
     void visit(TemporalExpressions.TimeOfDayRange expr);
     void visit(TemporalExpressions.Union expr);
 }

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressionWorker.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressionWorker.java?rev=900217&r1=900216&r2=900217&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressionWorker.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/calendar/TemporalExpressionWorker.java Sun Jan 17 20:57:19 2010
@@ -48,10 +48,11 @@
     public final static String Intersection = "INTERSECTION";
     public final static String MinuteRange = "MINUTE_RANGE";
     public final static String MonthRange = "MONTH_RANGE";
+    public final static String Substitution = "SUBSTITUTION";
     public final static String TimeOfDayRange = "TIME_OF_DAY_RANGE";
     public final static String Union = "UNION";
     public final static String ExpressionTypeList[] = {DateRange, DayInMonth, DayOfMonthRange, DayOfWeekRange,
-        Difference, Frequency, Intersection, MonthRange, TimeOfDayRange, Union};
+        Difference, Frequency, HourRange, Intersection, MinuteRange, MonthRange, TimeOfDayRange, Substitution, Union};
 
     /** Get a <code>TemporalExpression</code> from persistent storage.
      * @param delegator
@@ -78,6 +79,7 @@
      * @return A <code>TemporalExpression</code> instance based on <code>exprValue</code>
      * @throws GenericEntityException
      */
+    @SuppressWarnings("deprecation")
     public static TemporalExpression makeTemporalExpression(Delegator delegator, GenericValue exprValue) throws GenericEntityException {
         String tempExprId = exprValue.getString("tempExprId");
         String tempExprTypeId = exprValue.getString("tempExprTypeId");
@@ -99,12 +101,19 @@
             return new TemporalExpressions.Frequency(exprValue.getTimestamp("date1"), exprValue.getLong("integer1").intValue(), exprValue.getLong("integer2").intValue());
         } else if (HourRange.equals(tempExprTypeId)) {
             return new TemporalExpressions.HourRange(exprValue.getLong("integer1").intValue(), exprValue.getLong("integer2").intValue());
-        } else if (MinuteRange.equals(tempExprTypeId)) {
-            return new TemporalExpressions.MinuteRange(exprValue.getLong("integer1").intValue(), exprValue.getLong("integer2").intValue());
         } else if (Intersection.equals(tempExprTypeId)) {
             return new TemporalExpressions.Intersection(getChildExpressions(delegator, tempExprId));
+        } else if (MinuteRange.equals(tempExprTypeId)) {
+            return new TemporalExpressions.MinuteRange(exprValue.getLong("integer1").intValue(), exprValue.getLong("integer2").intValue());
         } else if (MonthRange.equals(tempExprTypeId)) {
             return new TemporalExpressions.MonthRange(exprValue.getLong("integer1").intValue(), exprValue.getLong("integer2").intValue());
+        } else if (Substitution.equals(tempExprTypeId)) {
+            GenericValue inclAssoc = EntityUtil.getFirst(delegator.findList("TemporalExpressionAssoc", EntityCondition.makeCondition(EntityCondition.makeCondition("fromTempExprId", tempExprId), EntityCondition.makeCondition("exprAssocType", "INCLUDE")), null, null, null, true));
+            GenericValue exclAssoc = EntityUtil.getFirst(delegator.findList("TemporalExpressionAssoc", EntityCondition.makeCondition(EntityCondition.makeCondition("fromTempExprId", tempExprId), EntityCondition.makeCondition("exprAssocType", "EXCLUDE")), null, null, null, true));
+            GenericValue substAssoc = EntityUtil.getFirst(delegator.findList("TemporalExpressionAssoc", EntityCondition.makeCondition(EntityCondition.makeCondition("fromTempExprId", tempExprId), EntityCondition.makeCondition("exprAssocType", "SUBSTITUTION")), null, null, null, true));
+            if (inclAssoc != null && exclAssoc != null && substAssoc != null) {
+                return new TemporalExpressions.Substitution(getTemporalExpression(delegator, inclAssoc.getString("toTempExprId")), getTemporalExpression(delegator, exclAssoc.getString("toTempExprId")), getTemporalExpression(delegator, substAssoc.getString("toTempExprId")));
+            }
         } else if (TimeOfDayRange.equals(tempExprTypeId)) {
             Debug.logWarning(TimeOfDayRange + " has been deprecated. Use " + HourRange + " and/or " + MinuteRange, module);
             int interval = Calendar.HOUR_OF_DAY;

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=900217&r1=900216&r2=900217&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 20:57:19 2010
@@ -51,6 +51,7 @@
     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_SUBSTITUTION = 9000;
     public static final int SEQUENCE_TOD_RANGE = 600;
     
 
@@ -104,6 +105,11 @@
         }
 
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            return this.range.includesDate(cal.getTime());
+        }
+
+        @Override
         public Calendar next(Calendar cal) {
             return includesDate(cal) ? cal : null;
         }
@@ -216,6 +222,19 @@
         }
 
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            Calendar checkCal = (Calendar) cal.clone();
+            checkCal.add(Calendar.DAY_OF_MONTH, -1);
+            while (!includesDate(checkCal)) {
+                if (expressionToTest.includesDate(checkCal)) {
+                    return true;
+                }
+                checkCal.add(Calendar.DAY_OF_MONTH, -1);
+            }
+            return false;
+        }
+
+        @Override
         public Calendar next(Calendar cal) {
             int month = cal.get(Calendar.MONTH);
             Calendar next = setStartOfDay(alignDayOfWeek((Calendar) cal.clone()));
@@ -311,6 +330,19 @@
         }
 
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            Calendar checkCal = (Calendar) cal.clone();
+            checkCal.add(Calendar.DAY_OF_MONTH, -1);
+            while (!includesDate(checkCal)) {
+                if (expressionToTest.includesDate(checkCal)) {
+                    return true;
+                }
+                checkCal.add(Calendar.DAY_OF_MONTH, -1);
+            }
+            return false;
+        }
+
+        @Override
         public Calendar next(Calendar cal) {
             Calendar next = setStartOfDay((Calendar) cal.clone());
             next.add(Calendar.DAY_OF_MONTH, 1);
@@ -417,6 +449,19 @@
         }
 
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            Calendar checkCal = (Calendar) cal.clone();
+            checkCal.add(Calendar.DAY_OF_MONTH, -1);
+            while (!includesDate(checkCal)) {
+                if (expressionToTest.includesDate(checkCal)) {
+                    return true;
+                }
+                checkCal.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);
@@ -520,6 +565,11 @@
         }
 
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            return this.included.isSubstitutionCandidate(cal, expressionToTest) && !this.excluded.isSubstitutionCandidate(cal, expressionToTest);
+        }
+
+        @Override
         public Calendar next(Calendar cal) {
             Calendar next = this.included.next(cal);
             while (next != null && this.excluded.includesDate(next)) {
@@ -624,6 +674,19 @@
         }
 
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            Calendar checkCal = (Calendar) cal.clone();
+            checkCal.add(this.freqType, -this.freqCount);
+            while (!includesDate(checkCal)) {
+                if (expressionToTest.includesDate(checkCal)) {
+                    return true;
+                }
+                checkCal.add(this.freqType, -this.freqCount);
+            }
+            return false;
+        }
+
+        @Override
         public Calendar next(Calendar cal) {
             Calendar next = first(cal);
             if (next.equals(cal)) {
@@ -774,6 +837,19 @@
         }
 
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            Calendar checkCal = (Calendar) cal.clone();
+            checkCal.add(Calendar.HOUR_OF_DAY, -1);
+            while (!includesDate(checkCal)) {
+                if (expressionToTest.includesDate(checkCal)) {
+                    return true;
+                }
+                checkCal.add(Calendar.HOUR_OF_DAY, -1);
+            }
+            return false;
+        }
+
+        @Override
         public Calendar next(Calendar cal) {
             Calendar next = (Calendar) cal.clone();
             next.add(Calendar.HOUR_OF_DAY, 1);
@@ -898,6 +974,16 @@
         }
 
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            for (TemporalExpression expression : this.expressionSet) {
+                if (!expression.isSubstitutionCandidate(cal, expressionToTest)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
         public Calendar next(Calendar cal) {
             Calendar next = (Calendar) cal.clone();
             for (TemporalExpression expression : this.expressionSet) {
@@ -1024,6 +1110,19 @@
         }
 
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            Calendar checkCal = (Calendar) cal.clone();
+            checkCal.add(Calendar.MINUTE, -1);
+            while (!includesDate(checkCal)) {
+                if (expressionToTest.includesDate(checkCal)) {
+                    return true;
+                }
+                checkCal.add(Calendar.MINUTE, -1);
+            }
+            return false;
+        }
+
+        @Override
         public Calendar next(Calendar cal) {
             Calendar next = (Calendar) cal.clone();
             next.add(Calendar.MINUTE, 1);
@@ -1131,6 +1230,19 @@
         }
 
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            Calendar checkCal = (Calendar) cal.clone();
+            checkCal.add(Calendar.MONTH, -1);
+            while (!includesDate(checkCal)) {
+                if (expressionToTest.includesDate(checkCal)) {
+                    return true;
+                }
+                checkCal.add(Calendar.MONTH, -1);
+            }
+            return false;
+        }
+
+        @Override
         public Calendar next(Calendar cal) {
             Calendar next = (Calendar) cal.clone();
             next.set(Calendar.DAY_OF_MONTH, 1);
@@ -1162,11 +1274,145 @@
             return false;
         }
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            return false;
+        }
+        @Override
         public Calendar next(Calendar cal) {
             return null;
         }
     }
 
+    /** An expression that provides a substitution for an excluded temporal expression. */
+    public static class Substitution extends TemporalExpression {
+        protected final TemporalExpression excluded;
+        protected final TemporalExpression included;
+        protected final TemporalExpression substitute;
+
+        public Substitution(TemporalExpression included, TemporalExpression excluded, TemporalExpression substitute) {
+            if (included == null) {
+                throw new IllegalArgumentException("included argument cannot be null");
+            }
+            if (excluded == null) {
+                throw new IllegalArgumentException("excluded argument cannot be null");
+            }
+            if (substitute == null) {
+                throw new IllegalArgumentException("substitute argument cannot be null");
+            }
+            this.included = included;
+            this.excluded = excluded;
+            this.substitute = substitute;
+            if (containsExpression(this)) {
+                throw new IllegalArgumentException("recursive expression");
+            }
+            if (this.compareTo(included) > 0) {
+                this.sequence = included.sequence;
+                this.subSequence = included.subSequence;
+            }
+            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;
+            }
+            try {
+                Substitution that = (Substitution) obj;
+                return this.included.equals(that.included) && this.excluded.equals(that.excluded) && this.substitute.equals(that.substitute);
+            } catch (ClassCastException e) {}
+            return false;
+        }
+
+        @Override
+        public Calendar first(Calendar cal) {
+            Calendar first = this.included.first(cal);
+            if (first != null && this.excluded.includesDate(first)) {
+                first = this.substitute.first(first);
+            }
+            return first;
+        }
+
+        /** Returns the excluded expression.
+         * @return The excluded <code>TemporalExpression</code>
+         */
+        public TemporalExpression getExcluded() {
+            return this.excluded;
+        }
+
+        /** Returns the included expression.
+         * @return The included <code>TemporalExpression</code>
+         */
+        public TemporalExpression getIncluded() {
+            return this.included;
+        }
+
+        @Override
+        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)) {
+                    Calendar substCal = this.excluded.first(checkCal);
+                    if (substCal != null) {
+                        finalSet.add(substCal.getTime());
+                    }
+                } else {
+                    finalSet.add(checkCal.getTime());
+                }
+            }
+            return finalSet;
+        }
+
+        /** Returns the substitute expression.
+         * @return The substitute <code>TemporalExpression</code>
+         */
+        public TemporalExpression getSubstitute() {
+            return this.substitute;
+        }
+
+        @Override
+        public boolean includesDate(Calendar cal) {
+            if (this.included.includesDate(cal)) {
+                return true;
+            }
+            return this.substitute.isSubstitutionCandidate(cal, this.excluded);
+        }
+
+        @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            return this.substitute.isSubstitutionCandidate(cal, expressionToTest);
+        }
+
+        @Override
+        public Calendar next(Calendar cal) {
+            Calendar next = this.included.next(cal);
+            if (next != null && this.excluded.includesDate(next)) {
+                next = this.substitute.next(next);
+            }
+            return next;
+        }
+
+        @Override
+        public String toString() {
+            return super.toString() + ", included = " + this.included + ", excluded = " + this.excluded + ", substitute = " + this.substitute;
+        }
+    }
+
     /** A temporal expression that represents a time of day range.
      * @deprecated
      */
@@ -1305,6 +1551,19 @@
         }
 
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            Calendar checkCal = (Calendar) cal.clone();
+            checkCal.add(this.interval, -this.count);
+            while (!includesDate(checkCal)) {
+                if (expressionToTest.includesDate(checkCal)) {
+                    return true;
+                }
+                checkCal.add(this.interval, -this.count);
+            }
+            return false;
+        }
+
+        @Override
         public Calendar next(Calendar cal) {
             Calendar next = (Calendar) cal.clone();
             next.add(this.interval, this.count);
@@ -1440,6 +1699,16 @@
         }
 
         @Override
+        public boolean isSubstitutionCandidate(Calendar cal, TemporalExpression expressionToTest) {
+            for (TemporalExpression expression : this.expressionSet) {
+                if (expression.isSubstitutionCandidate(cal, expressionToTest)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
         public Calendar next(Calendar cal) {
             Calendar result = null;
             for (TemporalExpression expression : this.expressionSet) {

Modified: ofbiz/trunk/framework/webtools/webapp/webtools/tempexpr/tempExprMaint.ftl
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/webtools/webapp/webtools/tempexpr/tempExprMaint.ftl?rev=900217&r1=900216&r2=900217&view=diff
==============================================================================
--- ofbiz/trunk/framework/webtools/webapp/webtools/tempexpr/tempExprMaint.ftl (original)
+++ ofbiz/trunk/framework/webtools/webapp/webtools/tempexpr/tempExprMaint.ftl Sun Jan 17 20:57:19 2010
@@ -29,7 +29,7 @@
       <td class="label">${uiLabelMap.TemporalExpressionType}</td>
       <td>${uiLabelMap.get("TemporalExpression_" + temporalExpression.tempExprTypeId)}</td>
     </tr>
-    <#if !"INTERSECTION.UNION.DIFFERENCE"?contains(temporalExpression.tempExprTypeId)>
+    <#if !"INTERSECTION.UNION.DIFFERENCE.SUBSTITUTION"?contains(temporalExpression.tempExprTypeId)>
       <form name="updateExpression" method="post" action="<@ofbizUrl>updateTemporalExpression</@ofbizUrl>">
         <input type="hidden" name="tempExprId" value="${temporalExpression.tempExprId}"/>
         <input type="hidden" name="tempExprTypeId" value="${temporalExpression.tempExprTypeId}"/>
@@ -52,7 +52,7 @@
       <@MinuteRange fromMinute=temporalExpression.integer1 toMinute=temporalExpression.integer2/>
     <#elseif temporalExpression.tempExprTypeId == "TIME_OF_DAY_RANGE">
       <@TimeOfDayRange fromTime=temporalExpression.string1 toTime=temporalExpression.string2 freqType=temporalExpression.integer1 freqValue=temporalExpression.integer2/>
-    <#elseif "INTERSECTION.UNION.DIFFERENCE"?contains(temporalExpression.tempExprTypeId)>
+    <#elseif "INTERSECTION.UNION.DIFFERENCE.SUBSTITUTION"?contains(temporalExpression.tempExprTypeId)>
       <#assign candidateIdList = Static["org.ofbiz.service.calendar.ExpressionUiHelper"].getCandidateIncludeIds(delegator, temporalExpression.tempExprId)/>
       <#if "INTERSECTION.UNION"?contains(temporalExpression.tempExprTypeId)>
         <tr>
@@ -60,13 +60,15 @@
           <td><@CreateExprAssocForm formName="includeExpression"/></td>
         </tr>
       <#else>
-        <#assign hasInclude = false hasExclude = false/>
+        <#assign hasInclude = false hasExclude = false hasSubstitution = false/>
         <#if childExpressionList?has_content>
           <#list childExpressionList as childExpression>
             <#if childExpression.exprAssocType == "INCLUDE">
               <#assign hasInclude = true/>
             <#elseif childExpression.exprAssocType == "EXCLUDE">
               <#assign hasExclude = true/>
+            <#elseif childExpression.exprAssocType == "SUBSTITUTION">
+              <#assign hasSubstitution = true/>
             </#if>
           </#list>
         </#if>
@@ -82,9 +84,15 @@
             <td><@CreateExprAssocForm formName="excludeExpression" exprAssocType="EXCLUDE"/></td>
           </tr>
         </#if>
+        <#if !hasSubstitution>
+          <tr>
+            <td class="label">${uiLabelMap.TemporalExpression_SUBSTITUTION}</td>
+            <td><@CreateExprAssocForm formName="substitutionExpression" exprAssocType="SUBSTITUTION"/></td>
+          </tr>
+        </#if>
       </#if>
     </#if>
-    <#if !"INTERSECTION.UNION.DIFFERENCE"?contains(temporalExpression.tempExprTypeId)>
+    <#if !"INTERSECTION.UNION.DIFFERENCE.SUBSTITUTION"?contains(temporalExpression.tempExprTypeId)>
         <tr>
           <td>&nbsp;</td>
           <td><input type="submit" name="submitBtn" value="${uiLabelMap.CommonSave}"/></td>
@@ -117,6 +125,8 @@
   <@CreateForm "UNION"/>
   <hr/>
   <@CreateForm "DIFFERENCE"/>
+  <hr/>
+  <@CreateForm "SUBSTITUTION"/>
 </#if>
 
 <#macro CreateForm expressionTypeId="" formContents=NullMacro>