View Javadoc

1   /*
2    * Copyright 2004-2005, 2008 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    * 
8    * http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package net.sf.morph.context.contexts;
17  
18  import java.util.Arrays;
19  import java.util.Collection;
20  import java.util.Iterator;
21  import java.util.Locale;
22  import java.util.Map;
23  import java.util.Set;
24  
25  import net.sf.composite.util.ObjectUtils;
26  import net.sf.morph.Defaults;
27  import net.sf.morph.context.Context;
28  import net.sf.morph.context.ContextException;
29  import net.sf.morph.context.DecoratedContext;
30  import net.sf.morph.context.support.ContextMapBridge;
31  import net.sf.morph.lang.Language;
32  import net.sf.morph.transform.Converter;
33  import net.sf.morph.util.ContainerUtils;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  
38  /**
39   * <p>
40   * Convenient base class for Contexts. Validates arguments and takes care of
41   * logging and exception handling. Also implements the {@link java.util.Map}
42   * interface.
43   * </p>
44   * 
45   * <p>
46   * As a Map, this class only supports non-null Strings for keys (i.e. - calling
47   * <code>Map.put(new Object(), new Object())</code> will throw a
48   * {@link net.sf.morph.context.ContextException}).
49   * </p>
50   * 
51   * @author Matt Sgarlata
52   * @since Nov 19, 2004
53   */
54  public abstract class BaseContext implements Context, Map, DecoratedContext {
55  
56  	private static final ContextMapBridge DEFAULT_CONTEXT_MAP_BRIDGE = new ContextMapBridge();
57  
58  	private transient Log log = LogFactory.getLog(getClass());
59  
60  	private ContextMapBridge contextMapBridge;
61  	private Converter converter;
62  	private Language language;
63  
64  	/**
65  	 * Create a new BaseContext.
66  	 */
67  	public BaseContext() {
68  		super();
69  		setContextMapBridge(DEFAULT_CONTEXT_MAP_BRIDGE);
70  		setConverter(Defaults.createConverter());
71  		setLanguage(Defaults.createLanguage());
72  	}
73  
74  	/**
75  	 * Implement getPropertyNames()
76  	 * @return String[] of property names
77  	 * @throws Exception if errors occur
78  	 */
79  	protected abstract String[] getPropertyNamesImpl() throws Exception;
80  
81  	/**
82  	 * Implement <code>get(propertyName)</code>.
83  	 * @param propertyName to get
84  	 * @return Object value
85  	 * @throws Exception in case of errors
86  	 */
87  	protected abstract Object getImpl(String propertyName)
88  		throws Exception;
89  
90  	/**
91  	 * Implement <code>set(propertyName, propertyValue)</code>.
92  	 * @param propertyName to set
93  	 * @param propertyValue to set
94  	 * @throws Exception in case of errors
95  	 */
96  	protected abstract void setImpl(String propertyName, Object propertyValue)
97  		throws Exception;
98  
99  	/**
100 	 * {@inheritDoc}
101 	 */
102 	public final String[] getPropertyNames() throws ContextException {
103 		try {
104 			// retrieve the property names
105 			String[] cachedPropertyNames = getPropertyNamesImpl();
106 			Set propertyNames = ContainerUtils.createOrderedSet();
107 			if (!ObjectUtils.isEmpty(cachedPropertyNames)) {
108 				propertyNames.addAll(Arrays.asList(cachedPropertyNames));
109 			}
110 //			// include the propertyNames property
111 //			int size = cachedPropertyNames == null ? 0 : cachedPropertyNames.length;
112 //			Set propertyNames = new ContainerUtils.createOrderedSet(size+1);
113 //			propertyNames.add(PROPERTY_NAMES_PROPERTY);
114 //			if (!ObjectUtils.isEmpty(cachedPropertyNames)) {
115 //				propertyNames.addAll(Arrays.asList(cachedPropertyNames));
116 //			}
117 			// loop through and remove properties that aren't valid according to
118 			// the language
119 			Iterator iterator = propertyNames.iterator();
120 			while (iterator.hasNext()) {
121 				String propertyName = (String) iterator.next();
122 				if (!getLanguage().isProperty(propertyName)) {
123 					iterator.remove();
124 				}
125 			}
126 			return (String[]) propertyNames.toArray(new String[propertyNames.size()]);
127 		}
128 		catch (ContextException e) {
129 			throw e;
130 		}
131 		catch (Exception e) {
132 			throw new ContextException("Unable to retrieve property names", e);
133 		}
134 	}
135 
136 	/**
137 	 * {@inheritDoc}
138 	 */
139 	public final Object get(String expression) throws ContextException {
140 		if (getLanguage().isProperty(expression)) {
141 			// make sure a property name is specified
142 			if (ObjectUtils.isEmpty(expression)) {
143 				throw new ContextException("You must specify a propertyName to retrieve");
144 			}
145 			// make sure the propertyName is a valid property
146 			String[] propertyNames = getPropertyNames();		
147 			if (!(ContainerUtils.contains(propertyNames, expression))) {
148 				return null;
149 			}
150 			try {
151 //				Object returnVal = getImpl(expression);
152 //				// exposes propertyNames as a property of the Map, if desired
153 //				if (returnVal == null &&
154 //					PROPERTY_NAMES_PROPERTY.equals(expression)) {
155 //					return getPropertyNames();
156 //				}
157 				return getImpl(expression);
158 			}
159 			catch (ContextException e) {
160 				throw e;
161 			}
162 			catch (Exception e) {
163 				throw new ContextException("Could not retrieve property '" + expression + "' from this context", e);
164 			}
165 		}
166 		return getLanguage().get(this, expression);
167 	}
168 
169 	/**
170 	 * {@inheritDoc}
171 	 */
172 	public final void set(String expression, Object value) throws ContextException {
173 		if (getLanguage().isProperty(expression)) {
174 			// make sure a property name is specified
175 			if (ObjectUtils.isEmpty(expression)) {
176 				throw new ContextException("You must specify an expression to set");
177 			}
178 //			// make sure the propertyName is a valid property
179 //			String[] propertyNames = getPropertyNames();		
180 //			if (!(MorphUtils.contains(propertyNames, expression))) {
181 //				return null;
182 //			}
183 			try {
184 				setImpl(expression, value);
185 			}
186 			catch (ContextException e) {
187 				throw e;
188 			}
189 			catch (Exception e) {
190 				throw new ContextException("Could not set property '" + expression + "' in context " + ObjectUtils.getObjectDescription(this), e);
191 			}
192 		}
193 		else {
194 			getLanguage().set(this, expression, value);
195 		}
196 	}
197 
198 	/**
199 	 * {@inheritDoc}
200 	 */
201 	public final Object get(String expression, Class destinationClass)
202 			throws ContextException {
203 		return get(expression, destinationClass, Locale.getDefault());
204 	}
205 
206 	/**
207 	 * {@inheritDoc}
208 	 */
209 	public final Object get(String expression, Class destinationClass, Locale locale)
210 			throws ContextException {
211 		try {
212 			Object object = get(expression);
213 			return getConverter().convert(destinationClass, object, locale);
214 		}
215 		catch (Exception e) {
216 			throw new ContextException("Unable to retrieve value for expression '" + expression + "' as destination " + ObjectUtils.getObjectDescription(destinationClass), e);
217 		}
218 	}
219 
220 	/**
221 	 * {@inheritDoc}
222 	 */
223 	public final Object get(String expression, Locale locale, Class destinationClass)
224 			throws ContextException {
225 		return get(expression, destinationClass, locale);
226 	}
227 
228 	/**
229 	 * {@inheritDoc}
230 	 */
231 	public final void set(String expression, Object value, Locale locale)
232 			throws ContextException {
233 		if (ObjectUtils.isEmpty(expression)) {
234 			throw new ContextException("You must specify a propertyName to set");
235 		}
236 
237 		try {
238 			setImpl(expression, value);
239 		}
240 		catch (ContextException e) {
241 			throw e;
242 		}
243 		catch (Exception e) {
244 			throw new ContextException("Could not set '" + expression + "' to " + ObjectUtils.getObjectDescription(value), e);
245 		}
246 	}
247 
248 	/**
249 	 * {@inheritDoc}
250 	 */
251 	public void clear() {
252 		getContextMapBridge().clear(this);
253 	}
254 
255 	/**
256 	 * The implementation of this method has O(n) time complexity.
257 	 * @see java.util.Map#containsKey(java.lang.Object)
258 	 */
259 	public boolean containsKey(Object key) {
260 		return getContextMapBridge().containsKey(this, key);
261 	}
262 
263 	/**
264 	 * {@inheritDoc}
265 	 */
266 	public boolean containsValue(Object value) {
267 		return getContextMapBridge().containsValue(this, value);
268 	}
269 
270 	/**
271 	 * {@inheritDoc}
272 	 */
273 	public Set entrySet() {
274 		return getContextMapBridge().entrySet(this);
275 	}
276 
277 	/**
278 	 * {@inheritDoc}
279 	 */
280 	public Object get(Object key) {
281 		return getContextMapBridge().get(this, key);
282 	}
283 
284 	/**
285 	 * {@inheritDoc}
286 	 */
287 	public boolean isEmpty() {
288 		return getContextMapBridge().isEmpty(this);
289 	}
290 
291 	/**
292 	 * {@inheritDoc}
293 	 */
294 	public Set keySet() {
295 		return getContextMapBridge().keySet(this);
296 	}
297 
298 	/**
299 	 * {@inheritDoc}
300 	 */
301 	public Object put(Object key, Object value) {
302 		return getContextMapBridge().put(this, key, value);
303 	}
304 
305 	/**
306 	 * {@inheritDoc}
307 	 */
308 	public void putAll(Map t) {
309 		getContextMapBridge().putAll(this, t);
310 	}
311 
312 	/**
313 	 * {@inheritDoc}
314 	 */
315 	public Object remove(Object key) {
316 		return getContextMapBridge().remove(this, key);
317 	}
318 
319 	/**
320 	 * {@inheritDoc}
321 	 */
322 	public int size() {
323 		return getContextMapBridge().size(this);
324 	}
325 
326 	/**
327 	 * {@inheritDoc}
328 	 */
329 	public Collection values() {
330 		return getContextMapBridge().values(this);
331 	}
332 
333 	/**
334 	 * Get the converter.
335 	 * @return Returns the converter.
336 	 */
337 	public Converter getConverter() {
338 		if (converter == null) {
339 			setConverter(Defaults.createConverter());
340 		}
341 		return converter;
342 	}
343 
344 	/**
345 	 * Set the converter.
346 	 * @param converter The converter to set.
347 	 */
348 	public void setConverter(Converter converter) {
349 		this.converter = converter;
350 	}
351 
352 	/**
353 	 * Get the Language.
354 	 * @return Returns the language.
355 	 */
356 	public Language getLanguage() {
357 		if (language == null) {
358 			setLanguage(Defaults.createLanguage());
359 		}
360 		return language;
361 	}
362 
363 	/**
364 	 * Set the Language.
365 	 * @param language The language to set.
366 	 */
367 	public void setLanguage(Language language) {
368 		this.language = language;
369 	}
370 
371 	/**
372 	 * Set the ContextMapBridge.
373 	 * @param contextMapBridge to set
374 	 */
375 	public void setContextMapBridge(ContextMapBridge contextMapBridge) {
376 		this.contextMapBridge = contextMapBridge;
377 	}
378 
379 	/**
380 	 * Get the ContextMapBridge. 
381 	 * @return ContextMapBridge
382 	 */
383 	public ContextMapBridge getContextMapBridge() {
384 		return contextMapBridge == null ? DEFAULT_CONTEXT_MAP_BRIDGE : contextMapBridge;
385 	}
386 
387 	/**
388 	 * Get the log.
389  	 * @return Log
390 	 */
391 	protected Log getLog() {
392 		return log;
393 	}
394 
395 	/**
396 	 * Set the Log.
397 	 * @param log to set
398 	 */
399 	protected void setLog(Log log) {
400 		this.log = log;
401 	}
402 
403 // this causes an infinite loop
404 //	public String toString() {
405 //		return (new HashMap(this)).toString();
406 //	}
407 
408 }