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 }