1 /***
2 * Copyright (C) 2006 Philipp Mpalampanis
3 *
4 * License: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * Alternatively, the contents of this file may be used under the terms of
17 * either the GNU General Public License Version 2 or later (the "GPL"), or
18 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
19 * in which case the provisions of the GPL or the LGPL are applicable instead
20 * of those above. If you wish to allow use of your version of this file only
21 * under the terms of either the GPL or the LGPL, and not to allow others to
22 * use your version of this file under the terms of the MPL, indicate your
23 * decision by deleting the provisions above and replace them with the notice
24 * and other provisions required by the GPL or the LGPL. If you do not delete
25 * the provisions above, a recipient may use your version of this file under
26 * the terms of any one of the MPL, the GPL or the LGPL.
27 */
28
29 package net.sf.echobinding.binding;
30
31 import java.beans.PropertyChangeListener;
32 import java.beans.PropertyChangeSupport;
33 import java.io.Serializable;
34 import java.lang.reflect.Method;
35 import java.util.HashMap;
36 import java.util.Map.Entry;
37
38 import net.sf.echobinding.model.PresentationModel;
39 import net.sf.echobinding.validation.ValidationHandler;
40 import net.sf.echobinding.validation.ValidationReport;
41 import ognl.Ognl;
42 import ognl.OgnlContext;
43
44 import org.apache.log4j.Logger;
45
46 /***
47 *
48 */
49 public class OgnlBindingContext extends AbstractBindingContext {
50
51 private static final long serialVersionUID = -3799783525654201978L;
52
53 /***
54 * The OGNL namespace/prefix used to access the presentation model.
55 */
56 public static final String OGNL_NAMESPACE_PMOD = "pm";
57
58 @SuppressWarnings("unused")
59 private final Logger _logger = Logger.getLogger(OgnlBindingContext.class);
60
61 private Object _model;
62
63 private OgnlContext _ognlContext = new OgnlContext();
64
65 private HashMap<String, Object> _contextMap = new HashMap<String, Object>();
66
67 private ValidationHandler _validationHandler;
68
69
70 /***
71 * Creates a new OgnlBindingContext.
72 *
73 * @param bean The bean
74 *
75 */
76 public OgnlBindingContext(Object bean) {
77 this();
78 setModel(bean);
79 }
80
81 /***
82 * Creates a new OgnlBindingContext.
83 *
84 */
85 public OgnlBindingContext() {
86 super();
87 }
88
89 public BindingContext add(String id, PropertyAdapter adapter) {
90
91 if (!(adapter instanceof OgnlPropertyAdapter))
92 throw new ClassCastException("Adapter has to be of type OgnlPropertyAdapter!");
93
94 super.add(id, adapter);
95
96 return this;
97 }
98
99
100 public Object getValue(String id) throws BindingException {
101 OgnlPropertyAdapter adapter = getAdapter(id);
102
103 Object value = (_ognlContext == null) ? adapter.getValue(_model)
104 : adapter.getValue(_ognlContext, _model);
105
106 return value;
107 }
108
109
110
111
112
113
114
115
116
117 public void setValue(String id, Object value) throws BindingException {
118
119 OgnlPropertyAdapter adapter = getAdapter(id);
120
121 Object oldValue = this.getValue(id);
122
123 if (_ognlContext == null)
124 adapter.setValue( _model, value );
125 else
126 adapter.setValue( _ognlContext, _model, value );
127
128 firePropertyChange(id, oldValue, value);
129 }
130
131
132
133
134
135
136
137 @SuppressWarnings("unchecked")
138 public ValidationReport validate(String id, Object value) {
139 Object bean = getModel();
140 OgnlPropertyAdapter adapter = getAdapter(id);
141 String ognlNameSpace = adapter.getOgnlContextName();
142 if(ognlNameSpace != null && getOgnlContext() != null) {
143 bean = getOgnlContext().get( ognlNameSpace );
144 }
145
146 return adapter.validate(bean, value);
147 }
148
149
150
151
152
153
154
155 public Object getModel() {
156 return _model;
157 }
158
159
160
161
162
163
164 public OgnlBindingContext setModel(Object bean) {
165 _model = bean;
166
167 registerSource( bean );
168
169 return this;
170 }
171
172
173
174 @Override
175 public void setPresentationModel(PresentationModel presentationModel) {
176 super.setPresentationModel( presentationModel );
177 addModel( OGNL_NAMESPACE_PMOD, presentationModel );
178 }
179
180 /***
181 * Tries to add this BindingContext as a property change listener to the
182 * source bean.
183 *
184 * @param contextRoot
185 */
186 private void registerSource(Object contextRoot) {
187 if(contextRoot==null)
188 return;
189
190 if ( contextRoot instanceof PropertyChangeSupport ) {
191 PropertyChangeSupport propertyChangeSupport = (PropertyChangeSupport) contextRoot;
192 propertyChangeSupport.addPropertyChangeListener(this);
193 }
194 else {
195
196 Class contextClass = contextRoot.getClass();
197 Class[] parameterTypes = new Class[] {PropertyChangeListener.class};
198 Object[] arguments = new Object[] {this};
199 Method addPropertyChangeListenerMethod;
200 try {
201 addPropertyChangeListenerMethod = contextClass.getMethod("addPropertyChangeListener", parameterTypes);
202 addPropertyChangeListenerMethod.invoke(contextRoot, arguments);
203 } catch (Exception e) {
204
205 }
206 }
207 }
208
209 /***
210 * Adds a bean to the OgnlContext. Additional beans can be referenced with
211 * "#contextName.property" in an OGNL expression.
212 *
213 * @param contextName
214 * @param bean
215 * @return
216 */
217 public OgnlBindingContext addModel(String contextName, Object bean) {
218
219 if (_ognlContext == null)
220 _ognlContext = (OgnlContext) Ognl
221 .createDefaultContext( _model );
222
223 _ognlContext.put(contextName, bean);
224
225 _contextMap.put(contextName, bean);
226
227 return this;
228 }
229
230
231 public BindingContext createChild() {
232
233 OgnlBindingContext childContext = new OgnlBindingContext();
234
235 for (Entry<String, PropertyAdapter> entry : _adapters.entrySet()) {
236 PropertyAdapter clonedAdapter = entry.getValue().newInstance();
237 childContext.add(entry.getKey(), clonedAdapter);
238 }
239
240 childContext.setContextMap(getContextMap());
241 childContext.setParent(this);
242
243 if(getPresentationModel()!=null)
244 childContext.setPresentationModel(getPresentationModel().cloneInstance());
245
246 addChild(childContext);
247
248 childContext.setModel(getModel());
249
250 return childContext;
251 }
252
253 /***
254 * Returns the context map.
255 *
256 * @return
257 */
258 public HashMap<String, Object> getContextMap() {
259 return _contextMap;
260 }
261
262 protected OgnlContext getOgnlContext() {
263 return _ognlContext;
264 }
265
266 /***
267 * Sets the context map. The context map is a list of additional source
268 * objects which are used for binding.
269 *
270 * @param contextMap
271 */
272 public void setContextMap(HashMap<String, Object> contextMap) {
273 _contextMap = contextMap;
274 if (contextMap != null) {
275 for (Entry<String, Object> entry : contextMap.entrySet()) {
276 _ognlContext.put(entry.getKey(), entry.getValue());
277 }
278 }
279 }
280
281
282
283
284
285 public ValidationHandler getValidationHandler(String id) {
286 ValidationHandler handler = getAdapter(id).getValidationHandler();
287 return (handler==null) ? getValidationHandler() : handler;
288 }
289
290
291
292
293 public BindingContext setValidationHandler(ValidationHandler handler) {
294 _validationHandler = handler;
295 return this;
296 }
297
298
299
300
301 public ValidationHandler getValidationHandler() {
302 return _validationHandler;
303 }
304
305
306
307
308
309 public boolean isDirty(String id, Object value) throws BindingException {
310 Object oldValue = getValue( id );
311 if(oldValue==null) {
312 if(value==null)
313 return false;
314 return true;
315 }
316 return ! oldValue.equals(value);
317 }
318
319
320 /***
321 * Returns the <code>PropertyAdapter</code> for the given id. If no adapter for
322 * this id exists, a new adapter will be lazely generated using the id as
323 * binding expression. The newly generated adapter will added to he list of
324 * adapters.
325 *
326 * @param id
327 * @return
328 */
329 public OgnlPropertyAdapter getAdapter(String id) {
330 OgnlPropertyAdapter adapter = (OgnlPropertyAdapter) _adapters.get(id);
331 if (adapter == null) {
332
333 adapter = new OgnlPropertyAdapter(id);
334 this.add(id, adapter);
335 }
336 return adapter;
337 }
338
339
340 }