Revision: 1763
          http://svn.sourceforge.net/spring-rich-c/?rev=1763&view=rev
Author:   jhoskens
Date:     2007-06-07 01:15:25 -0700 (Thu, 07 Jun 2007)

Log Message:
-----------
Fixed event forwarding of child ValidationResultsModels. Added some testcases 
to back this up. 
Delegated error checking in DefaultFormModel to ValidationResultsModel.

Modified Paths:
--------------
    
trunk/spring-richclient/support/src/main/java/org/springframework/binding/form/support/DefaultFormModel.java
    
trunk/spring-richclient/support/src/main/java/org/springframework/binding/validation/support/DefaultValidationResultsModel.java
    
trunk/spring-richclient/support/src/test/java/org/springframework/binding/validation/DefaultValidationResultsModelTests.java

Modified: 
trunk/spring-richclient/support/src/main/java/org/springframework/binding/form/support/DefaultFormModel.java
===================================================================
--- 
trunk/spring-richclient/support/src/main/java/org/springframework/binding/form/support/DefaultFormModel.java
        2007-05-31 13:07:52 UTC (rev 1762)
+++ 
trunk/spring-richclient/support/src/main/java/org/springframework/binding/form/support/DefaultFormModel.java
        2007-06-07 08:15:25 UTC (rev 1763)
@@ -178,19 +178,7 @@
     }
 
     public boolean getHasErrors() {
-        if (validationResultsModel.getHasErrors())
-            return true;
-        
-        FormModel[] children = getChildren();
-        for (int i = 0; i < children.length;++i)
-        {
-            if (children[i] instanceof ValidatingFormModel)
-            {
-                if (((ValidatingFormModel)children[i]).getHasErrors())
-                    return true;
-            }
-        }
-        return false;
+        return validationResultsModel.getHasErrors();
     }
     
     protected void hasErrorsUpdated() {

Modified: 
trunk/spring-richclient/support/src/main/java/org/springframework/binding/validation/support/DefaultValidationResultsModel.java
===================================================================
--- 
trunk/spring-richclient/support/src/main/java/org/springframework/binding/validation/support/DefaultValidationResultsModel.java
     2007-05-31 13:07:52 UTC (rev 1762)
+++ 
trunk/spring-richclient/support/src/main/java/org/springframework/binding/validation/support/DefaultValidationResultsModel.java
     2007-06-07 08:15:25 UTC (rev 1763)
@@ -35,11 +35,61 @@
 import org.springframework.util.ObjectUtils;
 
 /**
- * Default implementation of ValidationResultsModel
+ * Default implementation of [EMAIL PROTECTED] ValidationResultsModel}. 
Several events are
+ * fired when validationResults are set and can be tracked by registering the
+ * appropriate listener.
  * 
+ * <p>
+ * You can register listeners on:
+ * </p>
+ * <ul>
+ * <li>Changes of the ValidationResults in general. ([EMAIL PROTECTED] 
#addValidationListener(ValidationListener)})</li>
+ * <li>Changes of validationResults concerning a specific property of the
+ * FormModel. ([EMAIL PROTECTED] #addValidationListener(String, 
ValidationListener)})</li>
+ * <li>Specific events concerning errors, warnings and info. ([EMAIL 
PROTECTED] #addPropertyChangeListener(String, PropertyChangeListener) with one 
of: 
+ * ValidationResultsModel#HAS_ERRORS_PROPERTY,
+ * ValidationResultsModel#HAS_INFO_PROPERTY or
+ * ValidationResultsModel#HAS_WARNINGS_PROPERTY)</li>
+ * </ul>
+ * 
+ * <p>
+ * A child-parent relation can be used to bundle events and results. A listener
+ * set on a parent will receive events originating from the child and when
+ * polling for messages, childMessages will be available as well. This makes it
+ * possible to efficiently couple formModels and their validation aspect and
+ * provides a means to bundle validation reporting. When eg using a
+ * [EMAIL PROTECTED] 
org.springframework.richclient.form.ValidationResultsReporter}, you have the 
opportunity to bundle
+ * results from various unrelated formModels to report to one end point.</p>
+ * 
+ * <p>Example:</p>
+ * <pre>
+ * DefaultFormModel formModelA = ...
+ * DefaultFormModel formModelChildOfA = ...
+ * formModelA.addChild(formModelChildOfA);
+ * 
+ * DefaultFormModel formModelB = ...
+ * 
+ * \\ At this stage, the ValidationResultsModel of formModelChildOfA will 
route results &amp; 
+ * \\ events to the ValidationResultsModel of formModelA
+ * 
+ * DefaultValidationResultsModel container = new 
DefaultValidationResultsModel();
+ * container.add(formModelA.getValidationResults());
+ * container.add(formModelB.getValidationResults());
+ * 
+ * new SimpleValidationResultsReporter(container, messagable);
+ * 
+ * \\ the reporter will now receive events &amp; results of all formModels and 
can show messages of each of them
+ * 
+ * </pre>
+ * 
+ * @see 
org.springframework.binding.form.support.DefaultFormModel#addChild(org.springframework.binding.form.HierarchicalFormModel)
+ * @see org.springframework.richclient.form.SimpleValidationResultsReporter
+ * 
  * @author Oliver Hutchison
+ * @author Jan Hoskens
  */
-public class DefaultValidationResultsModel implements ValidationResultsModel, 
ValidationListener {
+public class DefaultValidationResultsModel implements ValidationResultsModel, 
ValidationListener,
+               PropertyChangeListener {
 
        private final EventListenerListHelper validationListeners = new 
EventListenerListHelper(ValidationListener.class);
 
@@ -57,16 +107,36 @@
                }
        };
 
+       /** Delegate or reference to this. */
        private final ValidationResultsModel delegateFor;
 
+       /** All children connected to this [EMAIL PROTECTED] 
DefaultValidationResultsModel}. */
        private List children = new ArrayList();
 
+       /** The actual results for this instance only. */
        private ValidationResults validationResults = 
EmptyValidationResults.INSTANCE;
 
+       /** Error bookkeeping. */
+       private boolean hasErrors = false;
+
+       /** Warning bookkeeping. */
+       private boolean hasWarnings = false;
+
+       /** Info bookkeeping. */
+       private boolean hasInfo = false;
+
+       /**
+        * Constructor without delegate. (Delegating for 'this').
+        */
        public DefaultValidationResultsModel() {
                delegateFor = this;
        }
 
+       /**
+        * Constructor with delegate.
+        * 
+        * @param delegateFor delegate object.
+        */
        public DefaultValidationResultsModel(ValidationResultsModel 
delegateFor) {
                this.delegateFor = delegateFor;
        }
@@ -78,6 +148,7 @@
                if (oldValidationResults.getMessageCount() == 0 && 
validationResults.getMessageCount() == 0) {
                        return;
                }
+               fireChangedEvents();
                for (Iterator i = 
propertyValidationListeners.keySet().iterator(); i.hasNext();) {
                        String propertyName = (String) i.next();
                        if (oldValidationResults.getMessageCount(propertyName) 
> 0
@@ -85,7 +156,6 @@
                                fireValidationResultsChanged(propertyName);
                        }
                }
-               fireChangedEvents(oldValidationResults);
        }
 
        // TODO: test
@@ -95,8 +165,8 @@
                        List newMessages = new 
ArrayList(oldValidationResults.getMessages());
                        newMessages.add(validationMessage);
                        validationResults = new 
DefaultValidationResults(newMessages);
+                       fireChangedEvents();
                        
fireValidationResultsChanged(validationMessage.getProperty());
-                       fireChangedEvents(oldValidationResults);
                }
        }
 
@@ -107,8 +177,8 @@
                        List newMessages = new 
ArrayList(oldValidationResults.getMessages());
                        newMessages.remove(validationMessage);
                        validationResults = new 
DefaultValidationResults(newMessages);
+                       fireChangedEvents();
                        
fireValidationResultsChanged(validationMessage.getProperty());
-                       fireChangedEvents(oldValidationResults);
                }
        }
 
@@ -122,12 +192,12 @@
                }
                newMessages.add(replacementMessage);
                validationResults = new DefaultValidationResults(newMessages);
+               fireChangedEvents();
                if (containsMessageToReplace
                                && 
!ObjectUtils.nullSafeEquals(messageToReplace.getProperty(), 
replacementMessage.getProperty())) {
                        
fireValidationResultsChanged(messageToReplace.getProperty());
                }
                fireValidationResultsChanged(replacementMessage.getProperty());
-               fireChangedEvents(oldValidationResults);
        }
 
        public void clearAllValidationResults() {
@@ -135,42 +205,95 @@
        }
 
        /**
-        * Check children too.
+        * @return <code>true</code> if this instance of one of its children has
+        * errors contained in their results.
         */
        public boolean getHasErrors() {
-               if (validationResults.getHasErrors())
-                       return true;
-               Iterator childIter = children.iterator();
-               while (childIter.hasNext()) {
-                       ValidationResultsModel childModel = 
(ValidationResultsModel) childIter.next();
-                       if (childModel.getHasErrors())
-                               return true;
+               return hasErrors;
+       }
+
+       /**
+        * Revaluate the hasErrors property and fire an event if things have
+        * changed.
+        */
+       private void updateErrors() {
+               boolean oldErrors = hasErrors;
+               hasErrors = false;
+               if (validationResults.getHasErrors()) {
+                       hasErrors = true;
                }
-               return false;
+               else {
+                       Iterator childIter = children.iterator();
+                       while (childIter.hasNext()) {
+                               ValidationResultsModel childModel = 
(ValidationResultsModel) childIter.next();
+                               if (childModel.getHasErrors()) {
+                                       hasErrors = true;
+                                       break;
+                               }
+                       }
+               }
+               firePropertyChange(HAS_ERRORS_PROPERTY, oldErrors, hasErrors);
        }
 
+       /**
+        * @return <code>true</code> if this instance of one of its children has
+        * info contained in their results.
+        */
        public boolean getHasInfo() {
-               if (validationResults.getHasInfo())
-                       return true;
-               Iterator childIter = children.iterator();
-               while (childIter.hasNext()) {
-                       ValidationResultsModel childModel = 
(ValidationResultsModel) childIter.next();
-                       if (childModel.getHasInfo())
-                               return true;
+               return hasInfo;
+       }
+
+       /**
+        * Revaluate the hasInfo property and fire an event if things have 
changed.
+        */
+       private void updateInfo() {
+               boolean oldInfo = hasInfo;
+               hasInfo = false;
+               if (validationResults.getHasInfo()) {
+                       hasInfo = true;
                }
-               return false;
+               else {
+                       Iterator childIter = children.iterator();
+                       while (childIter.hasNext()) {
+                               ValidationResultsModel childModel = 
(ValidationResultsModel) childIter.next();
+                               if (childModel.getHasInfo()) {
+                                       hasInfo = true;
+                                       break;
+                               }
+                       }
+               }
+               firePropertyChange(HAS_INFO_PROPERTY, oldInfo, hasInfo);
        }
 
+       /**
+        * @return <code>true</code> if this instance of one of its children has
+        * warnings contained in their results.
+        */
        public boolean getHasWarnings() {
-               if (validationResults.getHasWarnings())
-                       return true;
-               Iterator childIter = children.iterator();
-               while (childIter.hasNext()) {
-                       ValidationResultsModel childModel = 
(ValidationResultsModel) childIter.next();
-                       if (childModel.getHasWarnings())
-                               return true;
+               return hasWarnings;
+       }
+
+       /**
+        * Revaluate the hasWarnings property and fire an event if things have
+        * changed.
+        */
+       private void updateWarnings() {
+               boolean oldWarnings = hasWarnings;
+               hasWarnings = false;
+               if (validationResults.getHasWarnings()) {
+                       hasWarnings = true;
                }
-               return false;
+               else {
+                       Iterator childIter = children.iterator();
+                       while (childIter.hasNext()) {
+                               ValidationResultsModel childModel = 
(ValidationResultsModel) childIter.next();
+                               if (childModel.getHasWarnings()) {
+                                       hasWarnings = true;
+                                       break;
+                               }
+                       }
+               }
+               firePropertyChange(HAS_WARNINGS_PROPERTY, oldWarnings, 
hasWarnings);
        }
 
        public int getMessageCount() {
@@ -268,11 +391,11 @@
                getPropertyChangeListeners(propertyName).remove(listener);
        }
 
-       protected void fireChangedEvents(ValidationResults 
oldValidationResults) {
+       protected void fireChangedEvents() {
+               updateErrors();
+               updateWarnings();
+               updateInfo();
                fireValidationResultsChanged();
-               firePropertyChange(HAS_ERRORS_PROPERTY, 
oldValidationResults.getHasErrors(), getHasErrors());
-               firePropertyChange(HAS_WARNINGS_PROPERTY, 
oldValidationResults.getHasWarnings(), getHasWarnings());
-               firePropertyChange(HAS_INFO_PROPERTY, 
oldValidationResults.getHasInfo(), getHasInfo());
        }
 
        protected void fireValidationResultsChanged() {
@@ -309,33 +432,58 @@
        }
 
        /**
-        * Add a validationResultsModel as a child to this one. If it already 
has
-        * messages, fire events.
+        * Add a validationResultsModel as a child to this one. Attach 
listeners and
+        * if it already has messages, fire events.
         * 
         * @param validationResultsModel
         */
        public void add(ValidationResultsModel validationResultsModel) {
-               children.add(validationResultsModel);
-               validationResultsModel.addValidationListener(this);
-               if (validationResultsModel.getMessageCount() > 0)
-                       fireValidationResultsChanged();
+               if (children.add(validationResultsModel)) {
+                       validationResultsModel.addValidationListener(this);
+                       
validationResultsModel.addPropertyChangeListener(HAS_ERRORS_PROPERTY, this);
+                       
validationResultsModel.addPropertyChangeListener(HAS_WARNINGS_PROPERTY, this);
+                       
validationResultsModel.addPropertyChangeListener(HAS_INFO_PROPERTY, this);
+                       if ((validationResultsModel.getMessageCount() > 0))
+                               fireChangedEvents();
+               }
        }
 
        /**
-        * Remove the given validationResultsModel from the list of children. 
If it
-        * had messages, fire events.
+        * Remove the given validationResultsModel from the list of children. 
Remove
+        * listeners and if it had messages, fire events.
         * 
         * @param validationResultsModel
         */
        public void remove(ValidationResultsModel validationResultsModel) {
                if (children.remove(validationResultsModel)) {
                        validationResultsModel.removeValidationListener(this);
+                       
validationResultsModel.removePropertyChangeListener(HAS_ERRORS_PROPERTY, this);
+                       
validationResultsModel.removePropertyChangeListener(HAS_WARNINGS_PROPERTY, 
this);
+                       
validationResultsModel.removePropertyChangeListener(HAS_INFO_PROPERTY, this);
                        if (validationResultsModel.getMessageCount() > 0)
-                               fireValidationResultsChanged();
+                               fireChangedEvents();
                }
        }
 
+       /**
+        * [EMAIL PROTECTED] DefaultValidationResultsModel} registers itself as 
a
+        * validationListener on it's children to forward the event.
+        */
        public void validationResultsChanged(ValidationResults results) {
                fireValidationResultsChanged();
        }
+
+       /**
+        * Forwarding of known property events coming from child models. Each 
event
+        * triggers a specific evaluation of the parent property, which will 
trigger
+        * events as needed.
+        */
+       public void propertyChange(PropertyChangeEvent evt) {
+               if (evt.getPropertyName() == HAS_ERRORS_PROPERTY)
+                       updateErrors();
+               else if (evt.getPropertyName() == HAS_WARNINGS_PROPERTY)
+                       updateWarnings();
+               else if (evt.getPropertyName() == HAS_INFO_PROPERTY)
+                       updateInfo();
+       }
 }
\ No newline at end of file

Modified: 
trunk/spring-richclient/support/src/test/java/org/springframework/binding/validation/DefaultValidationResultsModelTests.java
===================================================================
--- 
trunk/spring-richclient/support/src/test/java/org/springframework/binding/validation/DefaultValidationResultsModelTests.java
        2007-05-31 13:07:52 UTC (rev 1762)
+++ 
trunk/spring-richclient/support/src/test/java/org/springframework/binding/validation/DefaultValidationResultsModelTests.java
        2007-06-07 08:15:25 UTC (rev 1763)
@@ -143,6 +143,9 @@
         assertEquals(3, nullListener.eventCount());
     }
     
+    /**
+     * Simply check if [EMAIL PROTECTED] DefaultValidationResultsModel} counts 
its messages correctly.
+     */
     public void testMessageCount() {
        DefaultValidationResultsModel resultsModel = new 
DefaultValidationResultsModel();
        resultsModel.addMessage(new DefaultValidationMessage("property1", 
Severity.ERROR, "message1"));
@@ -152,29 +155,63 @@
        assertEquals("Number of messages registered for property1 should be 2", 
2, resultsModel.getMessageCount("property1"));
        assertEquals("Number of messages flagged as INFO should be 1", 1, 
resultsModel.getMessageCount(Severity.INFO));
     }
-    
-    public void testAddAndRemoveChild() {
-       DefaultValidationResultsModel parentModel = new 
DefaultValidationResultsModel();
+
+    /**
+     * Check if adding a child triggers the parent to fire appropriate events.
+     */
+    public void testAddChildEvents() {
        DefaultValidationResultsModel childModel = new 
DefaultValidationResultsModel();
-       int events = 0;
        
-       TestValidationListener validationListener = new 
TestValidationListener();
-       parentModel.addValidationListener(validationListener);
-       assertEquals("Init: no events yet", events, 
validationListener.eventCount());
-
-       parentModel.addMessage(new DefaultValidationMessage("parentProperty1", 
Severity.ERROR, "parentMessage1"));
-       assertEquals("ParentModel added ErrorMessage.", ++events, 
validationListener.eventCount());
+       childModel.addMessage(new DefaultValidationMessage("childProperty1", 
Severity.ERROR, "childErrorMessage1"));
+       childModel.addMessage(new DefaultValidationMessage("childProperty1", 
Severity.WARNING, "childWarningMessage1"));
+       childModel.addMessage(new DefaultValidationMessage("childProperty1", 
Severity.INFO, "childInfoMessage1"));
+       vrm.add(childModel);
+       assertEquals("ParentModel adds child with Error.", 1, 
listener.eventCount());
+       assertEquals("ChildModel has ErrorMessage.", 1, 
errorsListener.eventCount());
+       assertEquals("ChildModel has WarningMessage.", 1, 
warnListener.eventCount());
+       assertEquals("ChildModel has InfoMessage.", 1, 
infoListener.eventCount());
+       assertEquals("ChildModel has ErrorMessage.", Boolean.TRUE, 
errorsListener.lastEvent().getNewValue());
+       assertEquals("ChildModel has WarningMessage.", Boolean.TRUE, 
warnListener.lastEvent().getNewValue());
+       assertEquals("ChildModel has InfoMessage.", Boolean.TRUE, 
infoListener.lastEvent().getNewValue());
+    }
        
-       childModel.addMessage(new DefaultValidationMessage("childProperty1", 
Severity.ERROR, "childMessage1"));
-       parentModel.add(childModel);
-       assertEquals("ParentModel adds child with Error.", ++events, 
validationListener.eventCount());
+    /**
+     * Check if adding a child triggers the parent to fire appropriate events.
+     */
+    public void testChildEvents() {
+       DefaultValidationResultsModel childModel = new 
DefaultValidationResultsModel();
        
-       childModel.addMessage(new DefaultValidationMessage("childProperty2", 
Severity.ERROR, "childMessage2"));
-       assertEquals("Child added errorMessage.", ++events, 
validationListener.eventCount());
+       vrm.add(childModel);
+       childModel.addMessage(new DefaultValidationMessage("childProperty1", 
Severity.ERROR, "childErrorMessage1"));
+       childModel.addMessage(new DefaultValidationMessage("childProperty1", 
Severity.WARNING, "childWarningMessage1"));
+       childModel.addMessage(new DefaultValidationMessage("childProperty1", 
Severity.INFO, "childInfoMessage1"));
+       assertEquals("Child added errorMessage, warningMessage and 
InfoMessage.", 3, listener.eventCount());
+       assertEquals("ChildModel added ErrorMessage.", 1, 
errorsListener.eventCount());
+       assertEquals("ChildModel added WarningMessage.", 1, 
warnListener.eventCount());
+       assertEquals("ChildModel added InfoMessage.", 1, 
infoListener.eventCount());
+       assertEquals("ChildModel added ErrorMessage.", Boolean.TRUE, 
errorsListener.lastEvent().getNewValue());
+       assertEquals("ChildModel added WarningMessage.", Boolean.TRUE, 
warnListener.lastEvent().getNewValue());
+       assertEquals("ChildModel added InfoMessage.", Boolean.TRUE, 
infoListener.lastEvent().getNewValue());
+    }
+    
+    /**
+     * Check if adding a child triggers the parent to fire appropriate events.
+     */
+    public void testRemoveChildEvents() {
+       DefaultValidationResultsModel childModel = new 
DefaultValidationResultsModel();
        
-       parentModel.remove(childModel);
-       assertEquals("Child removed, revalidate.", ++events, 
validationListener.eventCount());
-       
+       childModel.addMessage(new DefaultValidationMessage("childProperty1", 
Severity.ERROR, "childErrorMessage1"));
+       childModel.addMessage(new DefaultValidationMessage("childProperty1", 
Severity.WARNING, "childWarningMessage1"));
+       childModel.addMessage(new DefaultValidationMessage("childProperty1", 
Severity.INFO, "childInfoMessage1"));
+       vrm.add(childModel);
+       vrm.remove(childModel);
+       assertEquals("Child removed, revalidate.", 2, listener.eventCount());   
        
+       assertEquals("Child removed, revalidate ErrorMessages.", 2, 
errorsListener.eventCount());
+       assertEquals("Child removed, revalidate WarningMessages.", 2, 
warnListener.eventCount());
+       assertEquals("Child removed, revalidate InfoMessages.", 2, 
infoListener.eventCount());
+       assertEquals("Child removed, revalidate ErrorMessages.", Boolean.FALSE, 
errorsListener.lastEvent().getNewValue());
+       assertEquals("Child removed, revalidate WarningMessages.", 
Boolean.FALSE, warnListener.lastEvent().getNewValue());
+       assertEquals("Child removed, revalidate InfoMessages.", Boolean.FALSE, 
infoListener.lastEvent().getNewValue());
     }
 
     private ValidationResults getResults(String field, Severity severity) {


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
spring-rich-c-cvs mailing list
spring-rich-c-cvs@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/spring-rich-c-cvs

Reply via email to