1 /*
2 Copyright 2002-2006 Martin van den Bemt
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of 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,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16 package org.xulux.dataprovider.bean;
17
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.xulux.api.dataprovider.IConverter;
26 import org.xulux.api.dataprovider.IDataProvider;
27 import org.xulux.api.dataprovider.IMapping;
28 import org.xulux.utils.ClassLoaderUtils;
29
30 /**
31 * The BeanDataProvider is the main entry point for the dataprovider.
32 * It is the datasource
33 *
34 * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a>
35 * @version $Id: BeanDataProvider.java,v 1.1 2005/12/18 12:58:23 mvdb Exp $
36 */
37 public final class BeanDataProvider implements IDataProvider {
38
39 /**
40 * The map containing all the mappings
41 */
42 private HashMap mappings;
43
44 /**
45 * the baseclass of all the mappings
46 */
47 private Class baseClass;
48 /**
49 * A mappingcache is used temporarily to add
50 * classes that have been discovered, this way
51 * we can test for infinite loop troubles
52 */
53 private ArrayList mappingCache;
54 /**
55 * the depth of the mappings.
56 */
57 private int mappingDepth = 0;
58
59 /**
60 * A map containg the converters
61 */
62 private HashMap converters;
63
64 /**
65 * Constructor for BeanDataProvider.
66 */
67 public BeanDataProvider() {
68 }
69
70 /**
71 * @param name the name of the mapping
72 * @return the mapping
73 */
74 public IMapping getMapping(String name) {
75 IMapping mapping = null;
76 if (mappings != null) {
77 mapping = (BeanMapping) mappings.get(name);
78 }
79 if (mapping == null) {
80 mapping = getMapping(ClassLoaderUtils.getClass(name));
81 }
82 return mapping;
83 }
84
85 /**
86 * Returns a clone of the original mapping
87 * map, so alteration doesn't effect the
88 * registry..
89 *
90 * @return the mappings in a Map
91 */
92 public Map getMappings() {
93 if (mappings == null) {
94 return new HashMap();
95 }
96 return (HashMap) mappings.clone();
97 }
98
99 /**
100 * @see org.xulux.dataprovider.IDataProvider#addMapping(org.xulux.dataprovider.IMapping)
101 */
102 public void addMapping(IMapping mapping) {
103 if (mappings == null) {
104 mappings = new HashMap();
105 }
106 if (mappingCache == null) {
107 mappingCache = new ArrayList();
108 }
109 BeanMapping beanMapping = (BeanMapping) mapping;
110 beanMapping.setDataProvider(this);
111 mappingCache.add(beanMapping.getBean());
112 mappingDepth++;
113 beanMapping.discover();
114 mappings.put(beanMapping.getName(), beanMapping);
115 mappingDepth--;
116 if (mappingDepth == 0) {
117 mappingCache = null;
118 }
119 }
120
121 /**
122 * @param clazz the class
123 * @return the mapping of the specified clazz
124 */
125 public IMapping getMapping(Class clazz) {
126 if (clazz == null) {
127 return null;
128 }
129 return getMapping(clazz, false);
130 }
131
132 /**
133 * Convenience method. This calls the
134 * getMapping(Class)
135 *
136 * @param object the object to get the mapping for
137 * @return the beanmapping that is connected
138 * to the class of the specified instance
139 * or null when the object is null.
140 */
141 public IMapping getMapping(Object object) {
142 if (object == null) {
143 return null;
144 }
145 if (object instanceof String) {
146 return getMapping((String) object);
147 } else if (object instanceof Class) {
148 return getMapping((Class) object);
149 }
150 return getMapping(object.getClass());
151 }
152
153 /**
154 * @param clazz the class to get the mapping for
155 * @param newMapping if true it creates the mapping and adds
156 * it to the dictionary, if the name is not
157 * yet known.
158 * @return the beanmapping or null when no mapping could be created
159 */
160 public BeanMapping getMapping(Class clazz, boolean newMapping) {
161 String name = null;
162 if (newMapping) {
163 name = getPossibleMappingName(clazz);
164 } else {
165 name = getPlainBeanName(clazz);
166 }
167 return getMapping(clazz, name);
168 }
169 /**
170 * Tries to get a mapping based on the specified bean
171 * @param clazz the class
172 * @param preferredName the name to use for the mapping if it needs to be created
173 * @return the beanmapping found
174 */
175 public BeanMapping getMapping(Class clazz, String preferredName) {
176 // if (getBaseClass() == null) {
177 // if (log.isDebugEnabled()) {
178 // log.debug("Base class is not set. Xulux will possibly not be able to disover data beans correctly");
179 // }
180 // }
181 BeanMapping mapping = (BeanMapping) getMapping(preferredName);
182 if (mapping == null) {
183 mapping = new BeanMapping(preferredName);
184 mapping.setBean(clazz);
185 mapping.setDiscovery(true);
186 addMapping(mapping);
187 }
188 return mapping;
189 }
190
191 /**
192 * Creates a mapping from the specified with the specified name
193 * Do not use, since it doesn't do much...
194 * @param bean the bean to create a mapping for
195 * @param name the name of the mapping
196 * @return the newly creating mapping.
197 */
198 private BeanMapping createMapping(Object bean, String name) {
199 // first
200 BeanMapping mapping = new BeanMapping(name);
201 return mapping;
202 }
203
204 /**
205 * @param clazz the class
206 * @return the plaing bean name for the specified class
207 */
208 public String getPlainBeanName(Class clazz) {
209 int pLength = clazz.getPackage().getName().length();
210 String mapName = clazz.getName().substring(pLength + 1);
211 return mapName;
212 }
213 /**
214 * Tries to find a possible name for the mapping
215 * @param clazz the class to investigate
216 * @return the possible mapping name
217 */
218 public String getPossibleMappingName(Class clazz) {
219 String mapName = getPlainBeanName(clazz);
220 int i = 1;
221 if (getMapping(mapName) != null) {
222 while (true) {
223 if (getMapping(mapName + i) == null) {
224 mapName += i;
225 break;
226 }
227 i++;
228 }
229 }
230 return mapName;
231 }
232
233 /**
234 * @see org.xulux.dataprovider.IDictionary#initialize(java.lang.Object)
235 */
236 public void initialize(Object object) {
237 if (object instanceof InputStream) {
238 initialize((InputStream) object);
239 } else {
240 System.out.println("Cannot initialize dictionary, object is not an InputStream");
241 }
242 }
243
244 /**
245 * Initializes the dictionary from a stream
246 * The stream will be closed.
247 * @param stream - a stream with the dictionary.xml
248 * @todo move init like stuff to API / CORE..
249 */
250 public void initialize(InputStream stream) {
251 if (stream == null) {
252 return;
253 }
254 // new DictionaryHandler().read(stream);
255 try {
256 stream.close();
257 } catch (IOException e) {
258 e.printStackTrace();
259 }
260 }
261 /**
262 * Returns the baseClass.
263 * @return Class
264 */
265 public Class getBaseClass() {
266 return baseClass;
267 }
268
269 /**
270 * Sets the baseClass.
271 * @param baseClass The baseClass to set
272 */
273 public void setBaseClass(Class baseClass) {
274 this.baseClass = baseClass;
275 }
276
277 /**
278 * Clears all the mappings currently available
279 */
280 public void clearMappings() {
281 if (mappings != null) {
282 mappings.clear();
283 }
284 }
285
286 /**
287 * Reset all dictionary settings..
288 *
289 */
290 public void reset() {
291 clearMappings();
292 if (converters != null) {
293 converters.clear();
294 }
295 }
296
297 /**
298 * Checks to see if this class is currently being
299 * discovered. This is a nice way to prevent
300 * infinite loops.
301 *
302 * @param clazz the class to look for
303 * @return boolean if the class is already in the cache
304 */
305 public boolean isInCache(Class clazz) {
306 if (mappingCache == null) {
307 return false;
308 }
309 return mappingCache.indexOf(clazz) != -1;
310 }
311
312 /**
313 *
314 * @return a clone of the current cache.
315 */
316 public List getCache() {
317 if (mappingCache != null) {
318 return (List) mappingCache.clone();
319 }
320 return null;
321 }
322
323 /**
324 * @param clazz - the class of the converter.
325 */
326 public void addConverter(Class clazz) {
327 if (converters == null) {
328 converters = new HashMap();
329 }
330 Object object = ClassLoaderUtils.getObjectFromClass(clazz);
331 if (object instanceof IConverter) {
332 IConverter c = (IConverter) object;
333 converters.put(c.getType(), c);
334 }
335 // else {
336 // if (log.isWarnEnabled()) {
337 // String clazzName = "null";
338 // if (clazz != null) {
339 // clazzName = clazz.getName();
340 // }
341 // log.warn(clazzName + " class does not implement IConverter, not registering object");
342 // }
343 }
344 // }
345
346 /**
347 * Convenience method. see addConverter(Class) for more
348 * details.
349 *
350 * @param clazz the clazz to add the converter for
351 */
352 public void addConverter(String clazz) {
353 if (clazz == null) {
354 return;
355 }
356 Class clz = ClassLoaderUtils.getClass(clazz);
357 if (clz != null) {
358 addConverter(clz);
359 }
360 // } else {
361 // }
362 // if (log.isWarnEnabled()) {
363 // log.warn(clz + " does not exists or could not be loaded");
364 // }
365 }
366
367 /**
368 *
369 * @param object the object to get the convert for
370 * @return the converter for the object specified or
371 * null when no converter is present
372 */
373 public IConverter getConverter(Object object) {
374 if (object != null) {
375 return getConverter(object.getClass());
376 }
377 return null;
378 }
379 /**
380 *
381 * @return all registered converters
382 */
383 public Map getConverters() {
384 return converters;
385 }
386 /**
387 *
388 * @param clazz the class to get the convert for
389 * @return the coverter for the clazz specified or null
390 * when no converter is present
391 */
392 public IConverter getConverter(Class clazz) {
393 if (clazz != null && converters != null) {
394 return (IConverter) converters.get(clazz);
395 }
396 return null;
397 }
398
399 /**
400 * @see org.xulux.api.dataprovider.IDataProvider#initialize()
401 */
402 public void initialize() {
403 }
404
405 /**
406 * @see org.xulux.api.dataprovider.IDataProvider#setValue(java.lang.Object, java.lang.String, java.lang.Object,
407 * java.lang.Object)
408 */
409 public Object setValue(Object mapping, String field, Object object, Object value) {
410 IMapping iMapping = getMapping(mapping);
411 if (iMapping != null) {
412 return iMapping.setValue(field, object, value);
413 }
414 return null;
415 }
416
417 /**
418 * @see org.xulux.api.dataprovider.IDataProvider#getValue(java.lang.Object, java.lang.String, java.lang.Object)
419 */
420 public Object getValue(Object mapping, String field, Object object) {
421 return null;
422 }
423
424 /**
425 * @see org.xulux.api.dataprovider.IDataProvider#needsPartValue()
426 */
427 public boolean needsPartValue() {
428 return false;
429 }
430
431 /**
432 * @see org.xulux.api.dataprovider.IDataProvider#setProperty(java.lang.String, java.lang.String)
433 */
434 public void setProperty(String name, String value) {
435 if (name == null) {
436 return;
437 }
438 if (name.equalsIgnoreCase("baseclass")) {
439 setBaseClass(ClassLoaderUtils.getClass(value));
440 }
441 }
442
443 /**
444 * @see org.xulux.api.dataprovider.IDataProvider#createMapping(java.lang.Object)
445 */
446 public IMapping createMapping(Object object) {
447 if (object == null) {
448 return null;
449 }
450 if (object instanceof String) {
451 BeanMapping mapping = new BeanMapping((String) object);
452 mapping.setDataProvider(this);
453 return mapping;
454 }
455 return null;
456 }
457
458 }