001    /*
002     * Copyright 2002-2005 the original author or authors.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.richclient.form;
017    
018    import java.beans.PropertyChangeEvent;
019    import java.beans.PropertyChangeListener;
020    import java.util.Collections;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.Map;
024    
025    import org.springframework.binding.form.FormModel;
026    import org.springframework.binding.form.ValidatingFormModel;
027    import org.springframework.binding.validation.ValidationResultsModel;
028    import org.springframework.richclient.core.Guarded;
029    
030    /**
031     * Actually enables/disables registered 'guarded' objects based on the state of a
032     * {@link org.springframework.binding.form.ValidatingFormModel}.
033     * 
034     * One instance of this FormGuard supports multiple guarded objects on one formModel. Each guarded object can
035     * specify upon which state of the formModel it wants to be enabled:
036     */
037    public class FormGuard implements PropertyChangeListener {
038    
039        /** Guard-registration mask-bit indicating enabled() will be set only if the formmodel has no errors. */
040        public static final int ON_NOERRORS = 1;
041        /**
042         * Guard-registration mask-bit indicating enabled() will be set only if the formmodel has changes (is
043         * dirty).
044         */
045        public static final int ON_ISDIRTY = 1 << 1;
046        /** Guard-registration mask-bit indicating enabled() will be set only if the formmodel is enabled. */
047        public static final int ON_ENABLED = 1 << 2;
048    
049        /**
050         * Guard-registration mask-pattern indicating enabled() will be set just like is the case for the new-form
051         * command. i.e. as soon as the formmodel is enabled.
052         */
053        public static final int LIKE_NEWFORMOBJCOMMAND = ON_ENABLED;
054        /**
055         * Guard-registration mask-pattern indicating enabled() will be set just like is the case for the revert
056         * command. i.e. only if the formmodel is enabled AND uncommitted changes are present.
057         */
058        public static final int LIKE_REVERTCOMMAND = ON_ISDIRTY + LIKE_NEWFORMOBJCOMMAND;
059        /**
060         * Guard-registration mask-pattern indicating enabled() will be set just like is the case for the commit
061         * command. i.e. only if the formmodel is enabled AND uncomitted changes are presenent AND the model is
062         * valid.
063         */
064        public static final int LIKE_COMMITCOMMAND = ON_NOERRORS + LIKE_REVERTCOMMAND;
065    
066        /**
067         * Guard-registration mask-pattern indicating enabled() will be set only if the formmodel is both enabled
068         * and has no errors.
069         */
070        public static final int FORMERROR_GUARDED = ON_ENABLED + ON_NOERRORS;
071    
072        private final ValidatingFormModel formModel;
073        
074        private final Map guardedEntries = Collections.synchronizedMap(new HashMap());
075    
076        /**
077         * Creates the FormGuard monitoring the passed formModel.
078         * 
079         * @param formModel
080         *            which state-changes should be passed to registered guarded objects.
081         */
082        public FormGuard(ValidatingFormModel formModel) {
083            this.formModel = formModel;
084    
085            this.formModel.addPropertyChangeListener(FormModel.ENABLED_PROPERTY, this);
086            this.formModel.addPropertyChangeListener(ValidationResultsModel.HAS_ERRORS_PROPERTY, this);
087            this.formModel.addPropertyChangeListener(FormModel.DIRTY_PROPERTY, this);
088        }
089    
090        /**
091         * Creates the FormGuard monitoring the passed formModel, and adds the passed guarded-object.
092         * 
093         * For backwards compatibility this assumes the {@link #FORMERROR_GUARDED} mask.
094         * 
095         * @param formModel
096         *            which state-changes should be passed to registered guarded objects.
097         * @param guarded
098         *            object that will get en/dis-abled
099         */
100        public FormGuard(ValidatingFormModel formModel, Guarded guarded) {
101            this(formModel, guarded, FORMERROR_GUARDED);
102        }
103    
104        /**
105         * Creates the FormGuard monitoring the passed formModel, and adds the passed guarded-object using the
106         * specified mask.
107         * 
108         * @param formModel
109         *            which state-changes should be passed to registered guarded objects.
110         * @param guarded
111         *            object that will get en/dis-abled
112         * @param mask
113         *            specifying what formModel state should enable the guarded object.
114         */
115        public FormGuard(ValidatingFormModel formModel, Guarded guarded, int mask) {
116            this(formModel);
117            addGuarded(guarded, mask);
118        }
119    
120        private void updateAllGuarded() {
121            int formState = getFormModelState();
122    
123            Iterator guardedIter = this.guardedEntries.keySet().iterator();
124            while (guardedIter.hasNext()) {
125                Guarded guarded = (Guarded) guardedIter.next();
126                int mask = ((Integer) this.guardedEntries.get(guarded)).intValue();
127    
128                boolean b = stateMatchesMask(formState, mask);
129                guarded.setEnabled(b);
130            }
131        }
132    
133        private boolean stateMatchesMask(int formState, int mask) {
134            return ((mask & formState) == mask);
135        }
136    
137        private int getFormModelState() {
138            int formState = 0;
139            if (!formModel.getHasErrors())
140                formState += ON_NOERRORS;
141            if (formModel.isDirty())
142                formState += ON_ISDIRTY;
143            if (formModel.isEnabled())
144                formState += ON_ENABLED;
145            return formState;
146        }
147    
148        /**
149         * @see PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)  
150         */
151        public void propertyChange(PropertyChangeEvent e) {
152            updateAllGuarded();
153        }
154    
155        /**
156         * Adds a guarded object to be guarded by this FormGuard
157         * 
158         * @param newGuarded
159         *            object to be guarded
160         * @param mask
161         *            indicating which state of the formModel should enable the guarded obhject
162         */
163        public void addGuarded(Guarded newGuarded, int mask) {
164            this.guardedEntries.put(newGuarded, new Integer(mask));
165            newGuarded.setEnabled(stateMatchesMask(getFormModelState(), mask));
166        }
167    
168        /**
169         * Removes the guarded object from the management of this FormGuard.
170         * 
171         * @param toRemove
172         *            object that no longer should be managed
173         *            
174         * @return <code>false</code> if the object toRemove was not present in the list of managed guarded objects. 
175         */
176        public boolean removeGuarded(Guarded toRemove) {
177            Object mask = this.guardedEntries.remove(toRemove);
178            return mask != null;
179        }
180    }