001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.proxy2;
019
020import java.lang.reflect.Method;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.Map;
024
025import org.apache.commons.lang3.ArrayUtils;
026import org.apache.commons.lang3.ClassUtils;
027
028/**
029 * Provides some helpful proxy utility methods.
030 * 
031 * @since 1.0
032 */
033public final class ProxyUtils
034{
035    //******************************************************************************************************************
036    // Fields
037    //******************************************************************************************************************
038
039    public static final Object[] EMPTY_ARGUMENTS = ArrayUtils.EMPTY_OBJECT_ARRAY;
040    public static final Class<?>[] EMPTY_ARGUMENT_TYPES = ArrayUtils.EMPTY_CLASS_ARRAY;
041    private static final Map<Class<?>, Class<?>> WRAPPER_CLASS_MAP;
042    private static final Map<Class<?>, Object> NULL_VALUE_MAP;
043
044    //******************************************************************************************************************
045    // Static Methods
046    //******************************************************************************************************************
047
048    static
049    {
050        final Map<Class<?>, Class<?>> wrappers = new HashMap<Class<?>, Class<?>>();
051        wrappers.put(Integer.TYPE, Integer.class);
052        wrappers.put(Character.TYPE, Character.class);
053        wrappers.put(Boolean.TYPE, Boolean.class);
054        wrappers.put(Short.TYPE, Short.class);
055        wrappers.put(Long.TYPE, Long.class);
056        wrappers.put(Float.TYPE, Float.class);
057        wrappers.put(Double.TYPE, Double.class);
058        wrappers.put(Byte.TYPE, Byte.class);
059        WRAPPER_CLASS_MAP = Collections.unmodifiableMap(wrappers);
060
061        final Map<Class<?>, Object> nullValues = new HashMap<Class<?>, Object>();
062        nullValues.put(Integer.TYPE, Integer.valueOf(0));
063        nullValues.put(Long.TYPE, Long.valueOf(0));
064        nullValues.put(Short.TYPE, Short.valueOf((short) 0));
065        nullValues.put(Byte.TYPE, Byte.valueOf((byte) 0));
066        nullValues.put(Float.TYPE, Float.valueOf(0.0f));
067        nullValues.put(Double.TYPE, Double.valueOf(0.0));
068        nullValues.put(Character.TYPE, Character.valueOf((char) 0));
069        nullValues.put(Boolean.TYPE, Boolean.FALSE);
070        NULL_VALUE_MAP = Collections.unmodifiableMap(nullValues);
071    }
072
073    /**
074     * <p>
075     * Gets an array of {@link Class} objects representing all interfaces implemented by the given class and its
076     * superclasses.
077     * </p>
078     * <p/>
079     * <p>
080     * The order is determined by looking through each interface in turn as declared in the source file and following
081     * its hierarchy up. Then each superclass is considered in the same way. Later duplicates are ignored, so the order
082     * is maintained.
083     * </p>
084     * <p/>
085     * <b>Note</b>: Implementation of this method was "borrowed" from <a href="http://commons.apache.org/lang/">Apache
086     * Commons Lang</a> to avoid a dependency.
087     * </p>
088     * 
089     * @param cls
090     *            the class to look up, may be <code>null</code>
091     * @return an array of {@link Class} objects representing all interfaces implemented by the given class and its
092     *         superclasses or <code>null</code> if input class is null.
093     */
094    public static Class<?>[] getAllInterfaces(Class<?> cls)
095    {
096        return cls == null ? null : ClassUtils.getAllInterfaces(cls).toArray(ArrayUtils.EMPTY_CLASS_ARRAY);
097    }
098
099    /**
100     * Returns the class name as you would expect to see it in Java code.
101     * <p/>
102     * <b>Examples:</b>
103     * <ul>
104     * <li>getJavaClassName( Object[].class ) == "Object[]"</li>
105     * <li>getJavaClassName( Object[][].class ) == "Object[][]"</li>
106     * <li>getJavaClassName( Integer.TYPE ) == "int"</li>
107     * </p>
108     * 
109     * @param clazz
110     *            the class
111     * @return the class' name as you would expect to see it in Java code
112     */
113    public static String getJavaClassName(Class<?> clazz)
114    {
115        if (clazz.isArray())
116        {
117            return getJavaClassName(clazz.getComponentType()) + "[]";
118        }
119        return clazz.getName();
120    }
121
122    /**
123     * Returns the wrapper class for the given primitive type.
124     * 
125     * @param primitiveType
126     *            the primitive type
127     * @return the wrapper class
128     */
129    public static Class<?> getWrapperClass(Class<?> primitiveType)
130    {
131        return WRAPPER_CLASS_MAP.get(primitiveType);
132    }
133
134    /**
135     * Returns the proper "null value" as specified by the Java language.
136     * 
137     * @param type
138     *            the type
139     * @return the null value
140     */
141    public static <T> T nullValue(Class<T> type)
142    {
143        @SuppressWarnings("unchecked") // map only contains matching type/value entries
144        final T result = (T) NULL_VALUE_MAP.get(type);
145        return result;
146    }
147
148    /**
149     * Learn whether the specified method is/overrides {@link Object#equals(Object)}.
150     * 
151     * @param method
152     *            to compare
153     * @return <code>true</code> for a method with signature <code>equals(Object)</code>
154     */
155    public static boolean isEqualsMethod(Method method)
156    {
157        return "equals".equals(method.getName()) && method.getParameterTypes().length == 1
158                && Object.class.equals(method.getParameterTypes()[0]);
159    }
160
161    /**
162     * Learn whether the specified method is/overrides {@link Object#hashCode()}.
163     * 
164     * @param method
165     *            to compare
166     * @return true for a method with signature <code>hashCode()</code>
167     */
168    public static boolean isHashCode(Method method)
169    {
170        return "hashCode".equals(method.getName()) && method.getParameterTypes().length == 0;
171    }
172
173    /**
174     * Get a {@link ProxyFactory} that delegates to discoverable {@link ProxyFactory} service providers.
175     * 
176     * @return {@link ProxyFactory}
177     */
178    public static ProxyFactory proxyFactory()
179    {
180        return DefaultProxyFactory.INSTANCE;
181    }
182
183    private ProxyUtils()
184    {
185        // Hiding constructor in utility class!
186    }
187}