001 /*
002 * (c) 2003-2005, 2009, 2010 ThoughtWorks Ltd
003 * All rights reserved.
004 *
005 * The software in this package is published under the terms of the BSD
006 * style license a copy of which has been included with this distribution in
007 * the LICENSE.txt file.
008 *
009 * Created on 24-Mar-2004
010 */
011 package com.thoughtworks.proxy.toys.nullobject;
012
013 import java.lang.reflect.Array;
014 import java.util.Collection;
015 import java.util.Collections;
016 import java.util.List;
017 import java.util.Map;
018 import java.util.Set;
019 import java.util.SortedMap;
020 import java.util.SortedSet;
021 import java.util.TreeMap;
022 import java.util.TreeSet;
023
024 import com.thoughtworks.proxy.ProxyFactory;
025 import com.thoughtworks.proxy.factory.StandardProxyFactory;
026
027
028 /**
029 * Toy factory to create proxies acting as Null Objects.
030 *
031 * @author Dan North
032 * @author Aslak Hellesøy
033 * @author Juan Li
034 * @author Jörg Schaible
035 * @see com.thoughtworks.proxy.toys.nullobject
036 * @since 0.1
037 */
038 public class Null<T> {
039
040 /**
041 * The Null {@link Object}.
042 * @since 0.1
043 */
044 public static final Object NULL_OBJECT = new Object();
045
046 /**
047 * Immutable Null Object implementation of {@link SortedMap}.
048 * @since 0.1
049 */
050 public static final SortedMap<Object, Object> NULL_SORTED_MAP = new TreeMap<Object, Object>() {
051 private static final long serialVersionUID = -4388170961744587609L;
052
053 @Override
054 public Object put(Object key, Object value) {
055 throw new UnsupportedOperationException();
056 }
057
058 @Override
059 public void clear() {
060 // nothing to do
061 }
062
063 @Override
064 public Object remove(Object key) {
065 return null;
066 }
067
068 @Override
069 public Set<Object> keySet() {
070 return Collections.emptySet();
071 }
072
073 @Override
074 public Collection<Object> values() {
075 return Collections.emptyList();
076 }
077
078 @Override
079 public Set<Map.Entry<Object, Object>> entrySet() {
080 return Collections.emptySet();
081 }
082 };
083
084 /**
085 * Immutable Null Object implementation of {@link SortedSet}.
086 * @since 0.1
087 */
088 public static final SortedSet<Object> NULL_SORTED_SET = new TreeSet<Object>() {
089 private static final long serialVersionUID = 809722154285517876L;
090
091 @Override
092 public boolean add(Object o) {
093 throw new UnsupportedOperationException();
094 }
095
096 @Override
097 public void clear() {
098 // nothing to do
099 }
100
101 @Override
102 public boolean remove(Object o) {
103 return false;
104 }
105
106 @Override
107 public boolean removeAll(Collection<?> c) {
108 return false;
109 }
110
111 @Override
112 public boolean retainAll(Collection<?> c) {
113 return false;
114 }
115 };
116 private Class<T> type;
117
118 private Null(Class<T> type) {
119 this.type = type;
120 }
121
122
123 /**
124 * Creates a factory for proxy instances that is nullable.
125 *
126 * @param type the type implemented by the proxy
127 * @return the factory
128 * @since 1.0
129 */
130 public static <T> NullBuild<T> proxy(Class<T> type) {
131 return new NullBuild<T>(new Null<T>(type));
132 }
133
134 public static class NullBuild<T> {
135
136 private final Null<T> nullObject;
137
138 private NullBuild(Null<T> nullObject) {
139 this.nullObject = nullObject;
140 }
141
142 /**
143 * Generate a Null Object proxy for a specific type using the{@link StandardProxyFactory}.
144 * <p>
145 * Note that the method will only return a proxy if it cannot handle the type itself or <code>null</code> if the
146 * type cannot be proxied.
147 * </p>
148 *
149 * @return object, proxy or <code>null</code>
150 * @see com.thoughtworks.proxy.toys.nullobject
151 * @since 1.0
152 */
153 public T build() {
154 return nullObject.build(new StandardProxyFactory());
155 }
156
157 /**
158 * Generate a Null Object proxy for a specific type using a special {@link ProxyFactory}.
159 * <p>
160 * Note that the method will only return a proxy if it cannot handle the type itself or <code>null</code> if the
161 * type cannot be proxied.
162 * </p>
163 *
164 * @param factory the {@link ProxyFactory} in use
165 * @return object, proxy or <code>null</code>
166 * @see com.thoughtworks.proxy.toys.nullobject
167 */
168 public T build(ProxyFactory factory) {
169 return nullObject.build(factory);
170 }
171
172 }
173
174 private T build(ProxyFactory proxyFactory) {
175 final Object result;
176
177 // Primitives
178 if (boolean.class.equals(type) || Boolean.class.equals(type)) {
179 result = Boolean.FALSE;
180 } else if (byte.class.equals(type) || Byte.class.equals(type)) {
181 result = (byte) 0;
182 } else if (char.class.equals(type) || Character.class.equals(type)) {
183 result = (char) 0;
184 } else if (int.class.equals(type) || Integer.class.equals(type)) {
185 result = 0;
186 } else if (long.class.equals(type) || Long.class.equals(type)) {
187 result = (long) 0;
188 } else if (float.class.equals(type) || Float.class.equals(type)) {
189 result = 0.0f;
190 } else if (double.class.equals(type) || Double.class.equals(type)) {
191 result = 0.0;
192 }
193
194 // String
195 else if (String.class.equals(type)) {
196 result = "";
197 }
198
199 // Object
200 else if (Object.class.equals(type)) {
201 result = NULL_OBJECT;
202 }
203
204 // Arrays
205 else if (type.isArray()) {
206 result = Array.newInstance(type.getComponentType(), 0);
207 }
208
209 // Collections
210 else if (Set.class == type) {
211 result = Collections.EMPTY_SET;
212 } else if (Map.class == type) {
213 result = Collections.EMPTY_MAP;
214 } else if (List.class == type) {
215 result = Collections.EMPTY_LIST;
216 } else if (SortedSet.class == type) {
217 result = NULL_SORTED_SET;
218 } else if (SortedMap.class == type) {
219 result = NULL_SORTED_MAP;
220 } else if (proxyFactory.canProxy(type)) {
221 result = proxyFactory.createProxy(new NullInvoker(type, proxyFactory), type);
222 } else {
223 result = null;
224 }
225 @SuppressWarnings("unchecked")
226 T typedResult = (T) result;
227 return typedResult;
228 }
229
230
231 /**
232 * Determine whether an object was created by {@link Null#proxy(Class)}.
233 *
234 * @param object the object to examine
235 * @return <code>true</code> if the object is a Null proxy.
236 * @since 0.1
237 */
238 public static boolean isNullObject(final Object object) {
239 return isNullObject(object, new StandardProxyFactory());
240 }
241
242 /**
243 * Determine whether an object was created by {@link Null#proxy(Class)} using a special ProxyFactory with the builder.
244 *
245 * @param object the object to examine
246 * @param proxyFactory the {@link ProxyFactory} to use
247 * @return <code>true</code> if the object is a Null proxy.
248 * @since 0.1
249 */
250 public static boolean isNullObject(final Object object, final ProxyFactory proxyFactory) {
251 return isStandardNullObject(object) || isNullProxyObject(object, proxyFactory);
252 }
253
254 private static boolean isStandardNullObject(Object object) {
255 return object == Collections.EMPTY_LIST
256 || object == Collections.EMPTY_SET
257 || object == Collections.EMPTY_MAP
258 || object == NULL_SORTED_SET
259 || object == NULL_SORTED_MAP
260 || object == NULL_OBJECT;
261 }
262
263 private static boolean isNullProxyObject(final Object object, final ProxyFactory proxyFactory) {
264 return proxyFactory.isProxyClass(object.getClass()) && proxyFactory.getInvoker(object) instanceof NullInvoker;
265 }
266 }