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: package org.apache.openjpa.lib.util;
020:
021: import java.lang.reflect.Field;
022: import java.lang.reflect.Method;
023: import java.security.AccessController;
024:
025: /**
026: * Utilities for dealing with different Java spec versions.
027: *
028: * @author Abe White
029: * @nojavadoc
030: */
031: public class JavaVersions {
032:
033: /**
034: * Java version; one of 2, 3, 4, 5, 6, or 7.
035: */
036: public static final int VERSION;
037:
038: private static final Class[] EMPTY_CLASSES = new Class[0];
039:
040: private static Class PARAM_TYPE = null;
041: private static Class ENUM_TYPE = null;
042: private static Class ANNO_TYPE = null;
043: private static Method GET_STACK = null;
044: private static Method SET_STACK = null;
045: private static Method GET_CAUSE = null;
046: private static Method INIT_CAUSE = null;
047:
048: static {
049: String specVersion = (String) AccessController
050: .doPrivileged(J2DoPrivHelper
051: .getPropertyAction("java.specification.version"));
052: if ("1.2".equals(specVersion))
053: VERSION = 2;
054: else if ("1.3".equals(specVersion))
055: VERSION = 3;
056: else if ("1.4".equals(specVersion))
057: VERSION = 4;
058: else if ("1.5".equals(specVersion))
059: VERSION = 5;
060: else if ("1.6".equals(specVersion))
061: VERSION = 6;
062: else
063: VERSION = 7; // maybe someday...
064:
065: if (VERSION >= 5) {
066: try {
067: PARAM_TYPE = Class
068: .forName("java.lang.reflect.ParameterizedType");
069: ENUM_TYPE = Class.forName("java.lang.Enum");
070: ANNO_TYPE = Class
071: .forName("java.lang.annotation.Annotation");
072: } catch (Throwable t) {
073: }
074: }
075:
076: if (VERSION >= 4) {
077: try {
078: Class stack = Class
079: .forName("[Ljava.lang.StackTraceElement;");
080: GET_STACK = Throwable.class.getMethod("getStackTrace",
081: (Class[]) null);
082: SET_STACK = Throwable.class.getMethod("setStackTrace",
083: new Class[] { stack });
084: GET_CAUSE = Throwable.class.getMethod("getCause",
085: (Class[]) null);
086: INIT_CAUSE = Throwable.class.getMethod("initCause",
087: new Class[] { Throwable.class });
088: } catch (Throwable t) {
089: }
090: }
091: }
092:
093: /**
094: * Returns a version-specific instance of the specified class
095: *
096: * @param base the base class to check
097: * @return the JDK-version-specific version of the class
098: * @see #getVersionSpecificClass(String)
099: */
100: public static Class getVersionSpecificClass(Class base) {
101: try {
102: return getVersionSpecificClass(base.getName());
103: } catch (ClassNotFoundException e) {
104: return base;
105: }
106: }
107:
108: /**
109: * Obtains a subclass of the specific base class that is
110: * specific to the current version of Java in use. The
111: * heuristic for the class name to load will be that OpenJPA
112: * first checks for the name of the class with the current
113: * setting of the {@link #VERSION} field, then each number in
114: * decreasing order, until ending in the unqualified name.
115: * For example, if we are using JDK 1.5.1, and we want to load
116: * "org.apache.openjpa.lib.SomeClass", we will try to load the following
117: * classes in order and return the first one that is successfully
118: * found and loaded:
119: * <ol>
120: * <li>org.apache.openjpa.lib.SomeClass5</li>
121: * <li>org.apache.openjpa.lib.SomeClass4</li>
122: * <li>org.apache.openjpa.lib.SomeClass3</li>
123: * <li>org.apache.openjpa.lib.SomeClass2</li>
124: * <li>org.apache.openjpa.lib.SomeClass1</li>
125: * <li>org.apache.openjpa.lib.SomeClass</li>
126: * </ol>
127: *
128: * @param base the base name of the class to load
129: * @return the subclass appropriate for the current Java version
130: */
131: public static Class getVersionSpecificClass(String base)
132: throws ClassNotFoundException {
133: for (int i = VERSION; i >= 1; i--) {
134: try {
135: return Class.forName(base + i);
136: } catch (Throwable e) {
137: // throwables might occur with bytecode that we
138: // cannot understand
139: }
140: }
141: return Class.forName(base);
142: }
143:
144: /**
145: * Return true if the given type is an annotation.
146: */
147: public static boolean isAnnotation(Class cls) {
148: return ANNO_TYPE != null && ANNO_TYPE.isAssignableFrom(cls);
149: }
150:
151: /**
152: * Return true if the given type is an enumeration.
153: */
154: public static boolean isEnumeration(Class cls) {
155: return ENUM_TYPE != null && ENUM_TYPE.isAssignableFrom(cls);
156: }
157:
158: /**
159: * Collects the parameterized type declarations for a given field.
160: */
161: public static Class[] getParameterizedTypes(Field f) {
162: if (f == null)
163: return null;
164: if (VERSION < 5)
165: return EMPTY_CLASSES;
166:
167: try {
168: Object type = Field.class.getMethod("getGenericType",
169: (Class[]) null).invoke(f, (Object[]) null);
170: return collectParameterizedTypes(type);
171: } catch (Exception e) {
172: return EMPTY_CLASSES;
173: }
174: }
175:
176: /**
177: * Collects the parameterized return type declarations for a given method.
178: */
179: public static Class[] getParameterizedTypes(Method meth) {
180: if (meth == null)
181: return null;
182: if (VERSION < 5)
183: return EMPTY_CLASSES;
184:
185: try {
186: Object type = Method.class.getMethod(
187: "getGenericReturnType", (Class[]) null).invoke(
188: meth, (Object[]) null);
189: return collectParameterizedTypes(type);
190: } catch (Exception e) {
191: return EMPTY_CLASSES;
192: }
193: }
194:
195: /**
196: * Return all parameterized classes for the given type.
197: */
198: private static Class[] collectParameterizedTypes(Object type)
199: throws Exception {
200: if (PARAM_TYPE == null || !PARAM_TYPE.isInstance(type))
201: return EMPTY_CLASSES;
202:
203: Object[] args = (Object[]) PARAM_TYPE.getMethod(
204: "getActualTypeArguments", (Class[]) null).invoke(type,
205: (Object[]) null);
206: if (args.length == 0)
207: return EMPTY_CLASSES;
208:
209: Class[] clss = new Class[args.length];
210: for (int i = 0; i < args.length; i++) {
211: if (!(args[i] instanceof Class))
212: return EMPTY_CLASSES;
213: clss[i] = (Class) args[i];
214: }
215: return clss;
216: }
217:
218: /**
219: * Transfer the stack from one throwable to another, or return
220: * false if it cannot be done, possibly due to an unsupported Java version.
221: */
222: public static boolean transferStackTrace(Throwable from,
223: Throwable to) {
224: if (GET_STACK == null || SET_STACK == null || from == null
225: || to == null)
226: return false;
227:
228: try {
229: Object stack = GET_STACK.invoke(from, (Object[]) null);
230: SET_STACK.invoke(to, new Object[] { stack });
231: return true;
232: } catch (Throwable t) {
233: return false;
234: }
235: }
236:
237: /**
238: * Return the cause of the given throwable.
239: */
240: public static Throwable getCause(Throwable ex) {
241: if (GET_CAUSE == null || ex == null)
242: return null;
243:
244: try {
245: return (Throwable) GET_CAUSE.invoke(ex, (Object[]) null);
246: } catch (Throwable t) {
247: return null;
248: }
249: }
250:
251: /**
252: * Set the cause of the given throwable.
253: */
254: public static Throwable initCause(Throwable ex, Throwable cause) {
255: if (INIT_CAUSE == null || ex == null || cause == null)
256: return ex;
257:
258: try {
259: return (Throwable) INIT_CAUSE.invoke(ex,
260: new Object[] { cause });
261: } catch (Throwable t) {
262: return ex;
263: }
264: }
265:
266: public static void main(String[] args) {
267: System.out.println("Java version is: " + VERSION);
268: }
269: }
|