blob: 40999a7595029b95e8117e2175adedbc882d2e91 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.lib.util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.AccessController;
/**
* Utilities for dealing with different Java spec versions.
*
* @author Abe White
* @nojavadoc
*/
public class JavaVersions {
/**
* Java version; one of 2, 3, 4, 5, 6, or 7.
*/
public static final int VERSION;
private static final Class[] EMPTY_CLASSES = new Class[0];
private static Class PARAM_TYPE = null;
private static Class ENUM_TYPE = null;
private static Class ANNO_TYPE = null;
private static Method GET_STACK = null;
private static Method SET_STACK = null;
private static Method GET_CAUSE = null;
private static Method INIT_CAUSE = null;
static {
String specVersion = (String) AccessController.doPrivileged(
J2DoPrivHelper.getPropertyAction("java.specification.version"));
if ("1.2".equals(specVersion))
VERSION = 2;
else if ("1.3".equals(specVersion))
VERSION = 3;
else if ("1.4".equals(specVersion))
VERSION = 4;
else if ("1.5".equals(specVersion))
VERSION = 5;
else if ("1.6".equals(specVersion))
VERSION = 6;
else
VERSION = 7; // maybe someday...
if (VERSION >= 5) {
try {
PARAM_TYPE = Class.forName
("java.lang.reflect.ParameterizedType");
ENUM_TYPE = Class.forName("java.lang.Enum");
ANNO_TYPE = Class.forName("java.lang.annotation.Annotation");
} catch (Throwable t) {
}
}
if (VERSION >= 4) {
try {
Class stack = Class.forName("[Ljava.lang.StackTraceElement;");
GET_STACK = Throwable.class.getMethod("getStackTrace",
(Class[]) null);
SET_STACK = Throwable.class.getMethod("setStackTrace",
new Class[]{ stack });
GET_CAUSE = Throwable.class.getMethod("getCause",
(Class[]) null);
INIT_CAUSE = Throwable.class.getMethod("initCause",
new Class[]{ Throwable.class });
} catch (Throwable t) {
}
}
}
/**
* Returns a version-specific instance of the specified class
*
* @param base the base class to check
* @return the JDK-version-specific version of the class
* @see #getVersionSpecificClass(String)
*/
public static Class getVersionSpecificClass(Class base) {
try {
return getVersionSpecificClass(base.getName());
} catch (ClassNotFoundException e) {
return base;
}
}
/**
* Obtains a subclass of the specific base class that is
* specific to the current version of Java in use. The
* heuristic for the class name to load will be that OpenJPA
* first checks for the name of the class with the current
* setting of the {@link #VERSION} field, then each number in
* decreasing order, until ending in the unqualified name.
* For example, if we are using JDK 1.5.1, and we want to load
* "org.apache.openjpa.lib.SomeClass", we will try to load the following
* classes in order and return the first one that is successfully
* found and loaded:
* <ol>
* <li>org.apache.openjpa.lib.SomeClass5</li>
* <li>org.apache.openjpa.lib.SomeClass4</li>
* <li>org.apache.openjpa.lib.SomeClass3</li>
* <li>org.apache.openjpa.lib.SomeClass2</li>
* <li>org.apache.openjpa.lib.SomeClass1</li>
* <li>org.apache.openjpa.lib.SomeClass</li>
* </ol>
*
* @param base the base name of the class to load
* @return the subclass appropriate for the current Java version
*/
public static Class getVersionSpecificClass(String base)
throws ClassNotFoundException {
for (int i = VERSION; i >= 1; i--) {
try {
return Class.forName(base + i);
} catch (Throwable e) {
// throwables might occur with bytecode that we
// cannot understand
}
}
return Class.forName(base);
}
/**
* Return true if the given type is an annotation.
*/
public static boolean isAnnotation(Class cls) {
return ANNO_TYPE != null && ANNO_TYPE.isAssignableFrom(cls);
}
/**
* Return true if the given type is an enumeration.
*/
public static boolean isEnumeration(Class cls) {
return ENUM_TYPE != null && ENUM_TYPE.isAssignableFrom(cls);
}
/**
* Collects the parameterized type declarations for a given field.
*/
public static Class[] getParameterizedTypes(Field f) {
if (f == null)
return null;
if (VERSION < 5)
return EMPTY_CLASSES;
try {
Object type = Field.class.getMethod("getGenericType",
(Class[]) null).invoke(f, (Object[]) null);
return collectParameterizedTypes(type);
} catch (Exception e) {
return EMPTY_CLASSES;
}
}
/**
* Collects the parameterized return type declarations for a given method.
*/
public static Class[] getParameterizedTypes(Method meth) {
if (meth == null)
return null;
if (VERSION < 5)
return EMPTY_CLASSES;
try {
Object type = Method.class.getMethod("getGenericReturnType",
(Class[]) null).invoke(meth, (Object[]) null);
return collectParameterizedTypes(type);
} catch (Exception e) {
return EMPTY_CLASSES;
}
}
/**
* Return all parameterized classes for the given type.
*/
private static Class[] collectParameterizedTypes(Object type)
throws Exception {
if (PARAM_TYPE == null || !PARAM_TYPE.isInstance(type))
return EMPTY_CLASSES;
Object[] args = (Object[]) PARAM_TYPE.getMethod
("getActualTypeArguments", (Class[]) null).invoke(type,
(Object[]) null);
if (args.length == 0)
return EMPTY_CLASSES;
Class[] clss = new Class[args.length];
for (int i = 0; i < args.length; i++) {
if (!(args[i] instanceof Class))
return EMPTY_CLASSES;
clss[i] = (Class) args[i];
}
return clss;
}
/**
* Transfer the stack from one throwable to another, or return
* false if it cannot be done, possibly due to an unsupported Java version.
*/
public static boolean transferStackTrace(Throwable from, Throwable to) {
if (GET_STACK == null || SET_STACK == null || from == null
|| to == null)
return false;
try {
Object stack = GET_STACK.invoke(from, (Object[]) null);
SET_STACK.invoke(to, new Object[]{ stack });
return true;
} catch (Throwable t) {
return false;
}
}
/**
* Return the cause of the given throwable.
*/
public static Throwable getCause(Throwable ex) {
if (GET_CAUSE == null || ex == null)
return null;
try {
return (Throwable) GET_CAUSE.invoke(ex, (Object[]) null);
} catch (Throwable t) {
return null;
}
}
/**
* Set the cause of the given throwable.
*/
public static Throwable initCause(Throwable ex, Throwable cause) {
if (INIT_CAUSE == null || ex == null || cause == null)
return ex;
try {
return (Throwable) INIT_CAUSE.invoke(ex, new Object[]{ cause });
} catch (Throwable t) {
return ex;
}
}
public static void main(String[] args) {
System.out.println("Java version is: " + VERSION);
}
}