001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019
020package org.apache.isis.core.commons.lang;
021
022import java.io.IOException;
023import java.io.InputStream;
024import java.lang.reflect.Constructor;
025import java.lang.reflect.InvocationTargetException;
026import java.lang.reflect.Method;
027import java.lang.reflect.Modifier;
028import java.net.URL;
029import java.nio.charset.Charset;
030import java.util.HashMap;
031import java.util.Map;
032import java.util.Properties;
033
034import com.google.common.io.InputSupplier;
035import com.google.common.io.Resources;
036
037import org.apache.isis.core.commons.exceptions.IsisException;
038import org.apache.isis.core.metamodel.methodutils.MethodFinderUtils;
039
040public final class ClassExtensions {
041
042
043
044
045    // //////////////////////////////////////
046
047
048    private ClassExtensions() {
049    }
050
051    public static Object newInstance(final Class<?> extendee, final Class<?> constructorParamType, final Object constructorArg) {
052        return ClassExtensions.newInstance(extendee, new Class[] { constructorParamType }, new Object[] { constructorArg });
053    }
054
055    /**
056     * Tries to instantiate using a constructor accepting the supplied
057     * arguments; if no such constructor then falls back to trying the no-arg
058     * constructor.
059     */
060    public static Object newInstance(final Class<?> extendee, final Class<?>[] constructorParamTypes, final Object[] constructorArgs) {
061        try {
062            Constructor<?> constructor;
063            try {
064                constructor = extendee.getConstructor(constructorParamTypes);
065                return constructor.newInstance(constructorArgs);
066            } catch (final NoSuchMethodException ex) {
067                try {
068                    constructor = extendee.getConstructor();
069                    return constructor.newInstance();
070                } catch (final NoSuchMethodException e) {
071                    throw new IsisException(e);
072                }
073            }
074        } catch (final SecurityException ex) {
075            throw new IsisException(ex);
076        } catch (final IllegalArgumentException e) {
077            throw new IsisException(e);
078        } catch (final InstantiationException e) {
079            throw new IsisException(e);
080        } catch (final IllegalAccessException e) {
081            throw new IsisException(e);
082        } catch (final InvocationTargetException e) {
083            throw new IsisException(e);
084        }
085    }
086
087    public static String getSuperclass(final Class<?> extendee) {
088        final Class<?> superType = extendee.getSuperclass();
089    
090        if (superType == null) {
091            return null;
092        }
093        return superType.getName();
094    }
095
096    public static boolean isAbstract(final Class<?> extendee) {
097        return Modifier.isAbstract(extendee.getModifiers());
098    }
099
100    public static boolean isFinal(final Class<?> extendee) {
101        return Modifier.isFinal(extendee.getModifiers());
102    }
103
104    public static boolean isPublic(final Class<?> extendee) {
105        return Modifier.isPublic(extendee.getModifiers());
106    }
107
108    public static boolean isJavaClass(final Class<?> extendee) {
109        final String className = extendee.getName();
110        return className.startsWith(ClassUtil.JAVA_CLASS_PREFIX) || 
111               extendee.getName().startsWith("sun.");
112    }
113
114    static Class<?> implementingClassOrNull(final Class<?> extendee, final Class<?> requiredClass, final Class<?> constructorParamType) {
115        if (extendee == null) {
116            return null;
117        }
118        if (!requiredClass.isAssignableFrom(extendee)) {
119            return null;
120        }
121        try {
122            extendee.getConstructor(new Class[] { constructorParamType });
123        } catch (final NoSuchMethodException ex) {
124            try {
125                extendee.getConstructor(new Class[] {});
126            } catch (final NoSuchMethodException e) {
127                return null;
128            }
129        } catch (final SecurityException e) {
130            return null;
131        }
132        final int modifiers = extendee.getModifiers();
133        if (!Modifier.isPublic(modifiers)) {
134            return null;
135        }
136        return extendee;
137    }
138
139    public static Method getMethod(final Class<?> clazz, final String methodName, final Class<?>... parameterClass) throws NoSuchMethodException {
140        return clazz.getMethod(methodName, parameterClass);
141    }
142
143    public static Method getMethodElseNull(final Class<?> clazz, final String methodName, final Class<?>... parameterClass) {
144        try {
145            return clazz.getMethod(methodName, parameterClass);
146        } catch (final NoSuchMethodException e) {
147            return null;
148        }
149    }
150
151    public static Method findMethodElseNull(final Class<?> clazz, final String[] candidateMethodNames, final Class<?>... parameterClass) {
152        for (final String candidateMethodName : candidateMethodNames) {
153            final Method method = getMethodElseNull(clazz, candidateMethodName, parameterClass);
154            if (method != null) {
155                return method;
156            }
157        }
158        return null;
159    }
160
161    public static Properties resourceProperties(final Class<?> extendee, final String suffix) {
162        try {
163            final URL url = Resources.getResource(extendee, extendee.getSimpleName()+suffix);
164            final InputSupplier<InputStream> inputSupplier = com.google.common.io.Resources.newInputStreamSupplier(url);
165            final Properties properties = new Properties();
166            properties.load(inputSupplier.getInput());
167            return properties;
168        } catch (Exception e) {
169            return null;
170        }
171    }
172
173    public static String resourceContent(final Class<?> cls, final String suffix) throws IOException {
174        final URL url = Resources.getResource(cls, cls.getSimpleName()+suffix);
175        return Resources.toString(url, Charset.defaultCharset());
176    }
177
178    public static Class<?> asWrapped(final Class<?> primitiveClassExtendee) {
179        return ClassUtil.wrapperClasses.get(primitiveClassExtendee);
180    }
181
182    public static Class<? extends Object> asWrappedIfNecessary(final Class<?> cls) {
183        return cls.isPrimitive() ? asWrapped(cls) : cls;
184    }
185
186    public static Object toDefault(final Class<?> extendee) {
187        if(!extendee.isPrimitive()) {
188            return null;
189        }
190        return ClassUtil.defaultByPrimitiveClass.get(extendee);
191    }
192
193    /**
194     * Returns the corresponding 'null' value for the primitives, or just
195     * <tt>null</tt> if the class represents a non-primitive type.
196     */
197    public static Object getNullOrDefault(final Class<?> type) {
198        return ClassUtil.defaultByPrimitiveType.get(type);
199    }
200
201    public static boolean isCompatibleAsReturnType(final Class<?> returnTypeExtendee, final boolean canBeVoid, final Class<?> type) {
202        if (returnTypeExtendee == null) {
203            return true;
204        }
205        if (canBeVoid && (type == void.class)) {
206            return true;
207        }
208    
209        if (type.isPrimitive()) {
210            return returnTypeExtendee.isAssignableFrom(ClassUtil.wrapperClasses.get(type));
211        }
212    
213        return (returnTypeExtendee.isAssignableFrom(type));
214    }
215
216}