View Javadoc

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 	 * (non-Javadoc)
113 	 * 
114 	 * @see echobinding.BindingContext#setValue(java.lang.String,
115 	 *      java.lang.Object)
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 	 * (non-Javadoc)
133 	 * 
134 	 * @see echobinding.BindingContext#isValid(java.lang.String,
135 	 *      java.lang.Object)
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 	 * (non-Javadoc)
152 	 * 
153 	 * @see echobinding.BindingContext#getModel()
154 	 */
155 	public Object getModel() {
156 		return _model;
157 	}
158 
159 	/*
160 	 * (non-Javadoc)
161 	 * 
162 	 * @see echobinding.BindingContext#setModel(java.lang.Object)
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 			// check if class provides the addPropertyListener method
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 				// ignore
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 	/* (non-Javadoc)
283 	 * @see echobinding.binding.BindingContext#getValidationhandler(java.lang.String)
284 	 */
285 	public ValidationHandler getValidationHandler(String id) {
286 		ValidationHandler handler = getAdapter(id).getValidationHandler();
287 		return (handler==null) ? getValidationHandler() : handler;
288 	}
289 
290 	/* (non-Javadoc)
291 	 * @see echobinding.binding.BindingContext#setValidationHandler(echobinding.validation.ValidationHandler)
292 	 */
293 	public BindingContext setValidationHandler(ValidationHandler handler) {
294 		_validationHandler = handler;
295 		return this;
296 	}
297 
298 	/* (non-Javadoc)
299 	 * @see echobinding.binding.BindingContext#getValidationHandler()
300 	 */
301 	public ValidationHandler getValidationHandler() {
302 		return _validationHandler;
303 	}
304 
305 	
306 	/* (non-Javadoc)
307 	 * @see echobinding.binding.BindingContext#isDirty(java.lang.String, java.lang.Object)
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 			// create a new adapter
333 			adapter = new OgnlPropertyAdapter(id);
334 			this.add(id, adapter);
335 		}
336 		return adapter;
337 	}
338 	
339 
340 }