View Javadoc

1   /*
2    * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3    * the spring framework, http://el4j.sf.net
4    * Copyright (C) 2005 by ELCA Informatique SA, Av. de la Harpe 22-24,
5    * 1000 Lausanne, Switzerland, http://www.elca.ch
6    *
7    * EL4J is published under the GNU Lesser General Public License (LGPL)
8    * Version 2.1. See http://www.gnu.org/licenses/
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU Lesser General Public License for more details.
14   *
15   * For alternative licensing, please contact info@elca.ch
16   */
17  package ch.elca.el4j.util.objectwrapper;
18  
19  import java.util.HashMap;
20  import java.util.Map;
21  
22  import ch.elca.el4j.util.objectwrapper.impl.AbstractWrapper;
23  
24  /**
25   * Main class of the object wrapper package. The purpose of this package is to treat objects of different classes
26   * as if they implemented exactly the utility interfaces we want to use. This prevents us from having to include the
27   * interfaces in every domain class.
28   * <p>
29   * For example, Keyed is an interface indicating an object has a key. But domain classes may have keys without 
30   * implementing the keyed interface. As long as we have an algorithm to get/set keys on an object, 
31   * we can pretend it does actually implement Keyed.
32   * <p>
33   * All methods and the interfaces they create throw ObjectWrapperRTException if an object does not have the interface we
34   * want and we cannot emulate it either. As it is a RuntimeException it is not
35   * always declared; getting such an exception is under most circumstances non-recoverable anyway. 
36   *
37   * @svnLink $Revision: 4083 $;$Date: 2010-01-08 13:32:17 +0100 (Fr, 08. Jan 2010) $;$Author: jonasha $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/hibernate/src/main/java/ch/elca/el4j/util/objectwrapper/ObjectWrapper.java $
38   *
39   * @author David Bernhard (DBD)
40   */
41  public class ObjectWrapper {
42  
43  	/**
44  	 * All known wrappers, created as map Wrappable->Implementation.
45  	 */
46  	private Map<Class<? extends Wrappable>, AbstractWrapper> m_wrappables;
47  	
48  	/**
49  	 * Set up the apsects object. 
50  	 */
51  	public ObjectWrapper() {
52  		m_wrappables = new HashMap<Class<? extends Wrappable>, AbstractWrapper>();
53  	}
54  	
55  	/**
56  	 * Get the wrappables.
57  	 * @return The wrappables.
58  	 */
59  	public Map<Class<? extends Wrappable>, AbstractWrapper> getWrappables() {
60  		return m_wrappables;
61  	}
62  
63  	/**
64  	 * Setter for wrappabless.
65  	 * @param wrappables The new wrappables to set.
66  	 */
67  	public void setWrappables(Map<Class<? extends Wrappable>, AbstractWrapper> wrappables) {
68  		m_wrappables = wrappables;
69  	}
70  
71  	/**
72  	 * Register a wrappable.
73  	 * @param wrappableClass The wrappable class to register.
74  	 * @param implementation The implementation.
75  	 */
76  	public void addWrappable(Class<? extends Wrappable> wrappableClass, AbstractWrapper implementation) {
77  		m_wrappables.put(wrappableClass, implementation);
78  	}
79  	
80  	/**
81  	 * Cast an object to a wrapper.
82  	 * @param <T> The type parameter. (This eliminates a cast of the return value.)
83  	 * @param wrappable The wrappable to cast the object to.
84  	 * @param object the object.
85  	 * @return The wrapped object.
86  	 */
87  	public <T extends Wrappable> T wrap(Class<T> wrappable, Object object) {
88  		// If the object in question already implements this interface, just return it.
89  		
90  		if (wrappable.isAssignableFrom(object.getClass())) {
91  			// Safe because we've just checked it against the class object.
92  			@SuppressWarnings("unchecked")
93  			T cast = (T) object;
94  			return cast;
95  		}
96  		
97  		
98  		AbstractWrapper impl = null;
99  		for (Class<? extends Wrappable> candidate : m_wrappables.keySet()) {
100 			if (candidate == wrappable) {
101 				impl = m_wrappables.get(candidate);
102 			}
103 		}
104 		if (impl == null) {
105 			throw new IllegalArgumentException("Wrappable " + wrappable + " is not registered.");
106 		}
107 
108 		try {
109 			impl = (AbstractWrapper) impl.clone();
110 		} catch (CloneNotSupportedException e) {
111 			throw new ObjectWrapperRTException("Clone on wrappable failed.", e);
112 		}
113 		
114 		impl.setTarget(object);
115 		impl.setWrapper(this);
116 		try {
117 			impl.create();
118 		} catch (ObjectWrapperRTException ex) {
119 			// Provide a standard message and chain the exception.
120 			throw new ObjectWrapperRTException("Failed to create wrappable " + wrappable 
121 				+ " for object of class " + object.getClass(), ex);
122 		}
123 		
124 		// An exception here is an error in the set-up of the APSECTS mapping.
125 		// As long as that is ok this cast is safe.
126 		@SuppressWarnings("unchecked")
127 		T result = (T) impl;
128 		return result;
129 	}
130 	
131 	/**
132 	 * Check if a wrappable is registered. Note that even if it is not, an object might
133 	 * implement the interface already in which case wrap() will succeed on it.
134 	 * To be sure of this, use wrappablePresent(wrappableClass, objectClass).
135 	 * @param cls The wrappable class.
136 	 * @return <code>true</code> if the wrappable exists.
137 	 */
138 	public boolean wrappablePresent(Class<? extends Wrappable> cls) {
139 		return m_wrappables.containsKey(cls);
140 	}
141 	
142 	/**
143 	 * Check if a call to wrap() has a chance of creating a wrapper on an object class.
144 	 * This is the case if a) the object implements the interface or b) the wrappable is registered.
145 	 * Note that even if this returns true, the wrapper implementation may throw an expection
146 	 * in create(). This can happen if the wrapper is present but does not apply to this class.
147 	 * @param wrappableClass The wrappable class. 
148 	 * @param objectClass The object class.
149 	 * @return <code>false</code> if there is definitely no way to create the wrapper for this
150 	 * class, <code>true</code> if it can be tried (and will only fail if the implementation does). 
151 	 */
152 	public boolean wrappablePresent(Class<? extends Wrappable> wrappableClass, Class<?> objectClass) {
153 		return wrappableClass.isAssignableFrom(objectClass) || wrappablePresent(wrappableClass);
154 	}
155 }