1 /*
2 * Scope: a generic MVC framework.
3 * Copyright (c) 2000-2002, The Scope team
4 * All rights reserved.
5 *
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * Neither the name "Scope" nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 *
36 * $Id: BasicModel.java,v 1.11 2002/09/12 10:51:03 ludovicc Exp $
37 */
38 package org.scopemvc.model.basic;
39
40
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43 import org.scopemvc.core.ModelChangeEvent;
44 import org.scopemvc.core.ModelChangeEventSource;
45 import org.scopemvc.core.ModelChangeListener;
46 import org.scopemvc.core.ModelChangeTypes;
47 import org.scopemvc.core.PropertyManager;
48 import org.scopemvc.core.Selector;
49 import org.scopemvc.util.Debug;
50
51 /***
52 * <P>
53 *
54 * A simple implementation of ModelChangeEventSource for use as a base class. An
55 * alternative (JavaBeans style) is to use ModelChangeEventSupport as a delegate
56 * in all models. </P> <P>
57 *
58 * To implement a BasicModel:
59 * <OL>
60 * <LI> extend <CODE>BasicModel</CODE>, </LI>
61 * <LI> Notify interested listeners of changes in Model state at the end of
62 * <CODE>set</CODE> methods by: <CODE>fireModelChange(...)</CODE> </LI>
63 * <LI> Register as a ModelChangeListener with submodels in the appropriate
64 * set methods to ensure event propagation up the tree of models. For example:
65 * <PRE>
66 * public final static Selector NAME = Selector.fromString("name");
67 * public final static Selector ADDRESS = Selector.fromString("address");
68 *
69 * public void setAddress(AddressModel inAddress) throws ModelException {
70 * if (inAddress == address) {
71 * return;
72 * }
73 * <FONT COLOR="red">unlistenOldSubmodel(ADDRESS);</FONT> address =
74 * inAddress; <FONT COLOR="red">listenNewSubmodel(ADDRESS);</FONT>
75 * fireModelChange(VALUE_CHANGE, ADDRESS); } </PRE> <br>
76 * This ensures that any change in the Address's state is received by this
77 * parent which will then fire its own ModelChangeEvent to its listeners. See
78 * {@link org.scopemvc.core.ModelChangeEventSource} for more discussion. Note
79 * the use of the Selector constants: this is for convenience (and
80 * performance). </LI>
81 * <LI> Use Scope's collection models instead of the regular Java collections:
82 * Scope collections propagate changes to submodels as above and are compliant
83 * ModelChangeEventSources. These collections must be listened to in the same
84 * way as other submodel properties. Note that Scope collections are just thin
85 * wrappers on native Java collections. </LI>
86 * </OL>
87 * </P>
88 *
89 * @author <A HREF="mailto:smeyfroi@users.sourceforge.net">Steve Meyfroidt</A>
90 * @created 05 August 2002
91 * @version $Revision: 1.11 $ $Date: 2002/09/12 10:51:03 $
92 */
93 public abstract class BasicModel implements ModelChangeEventSource, ModelChangeTypes {
94
95 private static final Log LOG = LogFactory.getLog(BasicModel.class);
96 private ModelChangeEventSupport mceSupport = new ModelChangeEventSupport(this);
97 private PropertyManager manager = PropertyManager.getInstance(this);
98
99
100 /***
101 * Adds a listener for ModelChangeEvent
102 *
103 * @param inListener The listener to be added
104 */
105 public final void addModelChangeListener(ModelChangeListener inListener) {
106 mceSupport.addModelChangeListener(inListener);
107 }
108
109
110 /***
111 * Removes a listener for ModelChangeEvent
112 *
113 * @param inListener The listener to be removed
114 */
115 public final void removeModelChangeListener(ModelChangeListener inListener) {
116 mceSupport.removeModelChangeListener(inListener);
117 }
118
119
120 /***
121 * Fire a ModelChangeEvent to all listeners
122 *
123 * @param inChangeType The type of the change. One of the {@link
124 * org.scopemvc.core.ModelChangeTypes} values
125 * @param inSelector The Selector for the property of the model affected by
126 * the change
127 */
128 public final void fireModelChange(int inChangeType, Selector inSelector) {
129 mceSupport.fireModelChange(inChangeType, inSelector);
130 }
131
132
133 /***
134 * <P>
135 *
136 * Handle changes to children ModelChangeEventSources by firing a change
137 * event from <CODE>this</CODE> (and propagating the original Selector). If
138 * the event's source is no longer a child property of this parent then
139 * there is no need to continue propagating the event. </P>
140 *
141 * @param inEvent the event received from a child ModelChangeEventSource.
142 */
143 public void modelChanged(ModelChangeEvent inEvent) {
144 if (Debug.ON) {
145 Debug.assertTrue(inEvent != null);
146 }
147 if (LOG.isDebugEnabled()) {
148 LOG.debug("modelChanged: " + inEvent);
149 }
150
151 // Construct a Selector that includes the 'next' selector we received.
152 Selector selector = manager.getSelectorFor(this, inEvent.getModel());
153
154 // Is the model unrecognised? If so then the model
155 // has changed since this change so
156 // don't need to propagate it any further.
157 if (selector == null) {
158 return;
159 }
160
161 if (inEvent.getSelector() != null) {
162 selector.chain(inEvent.getSelector().deepClone());
163 }
164
165 fireModelChange(inEvent.getType(), selector);
166 }
167
168
169 // ------------------- Model activation and inactivation -------------------
170
171 /***
172 * <P>
173 *
174 * Control whether this BasicModel broadcasts ModelChangeEvents. Make sure
175 * nested calls are properly matched to fully re-activate a BasicModel that
176 * was deactivated. </P> <P>
177 *
178 * Subclasses may override this to propagate the activation state to child
179 * BasicModel properties if necessary. </P>
180 *
181 * @param inActive true to activate this model, false to desactivate it
182 */
183 public void makeActive(boolean inActive) {
184 if (inActive) {
185 mceSupport.activateModel();
186 } else {
187 mceSupport.deactivateModel();
188 }
189 }
190
191
192 /***
193 * Convenience for BasicModel implementors: call this at the start of
194 * setters for submodel properties (ie properties of type
195 * ModelChangeEventSource) to deregister this as a ModelChangeListener to
196 * the current submodel (about to be set to another Model).
197 *
198 * @param inSelector The Selector for the submodel
199 */
200 protected final void unlistenOldSubmodel(Selector inSelector) {
201 try {
202 Object o = manager.get(this, inSelector);
203 if (o instanceof ModelChangeEventSource) {
204 ((ModelChangeEventSource) o).removeModelChangeListener(this);
205 }
206 } catch (Exception e) {
207 LOG.warn("unlistenOldSubmodel: selector " + inSelector, e);
208 }
209 }
210
211
212 /***
213 * Convenience for BasicModel implementors: call this at the end of setters
214 * for submodel properties (ie properties of type ModelChangeEventSource) to
215 * register this as a ModelChangeListener to the current submodel for event
216 * propagation.
217 *
218 * @param inSelector The Selector for the submodel
219 */
220 protected final void listenNewSubmodel(Selector inSelector) {
221 try {
222 Object o = manager.get(this, inSelector);
223 if (o instanceof ModelChangeEventSource) {
224 ((ModelChangeEventSource) o).addModelChangeListener(this);
225 }
226 } catch (Exception e) {
227 LOG.warn("listenNewSubmodel: selector " + inSelector, e);
228 }
229 }
230 }
This page was automatically generated by Maven