001/*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005 *
006 * The contents of this file are subject to the terms of either the GNU
007 * General Public License Version 2 only ("GPL") or the Common Development
008 * and Distribution License("CDDL") (collectively, the "License").  You
009 * may not use this file except in compliance with the License. You can obtain
010 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
012 * language governing permissions and limitations under the License.
013 *
014 * When distributing the software, include this License Header Notice in each
015 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016 * Sun designates this particular file as subject to the "Classpath" exception
017 * as provided by Sun in the GPL Version 2 section of the License file that
018 * accompanied this code.  If applicable, add the following below the License
019 * Header, with the fields enclosed by brackets [] replaced by your own
020 * identifying information: "Portions Copyrighted [year]
021 * [name of copyright owner]"
022 *
023 * Contributor(s):
024 *
025 * If you wish your version of this file to be governed by only the CDDL or
026 * only the GPL Version 2, indicate your decision by adding "[Contributor]
027 * elects to include this software in this distribution under the [CDDL or GPL
028 * Version 2] license."  If you don't indicate a single choice of license, a
029 * recipient has the option to distribute your version of this file under
030 * either the CDDL, the GPL Version 2 or to extend the choice of license to
031 * its licensees as provided above.  However, if you add GPL Version 2 code
032 * and therefore, elected the GPL Version 2 license, then the option applies
033 * only if the new code is made subject to such option by the copyright
034 * holder.
035 */
036package org.kohsuke.stapler.export;
037
038import java.lang.reflect.Type;
039import java.lang.reflect.ParameterizedType;
040import java.lang.reflect.GenericArrayType;
041import java.lang.reflect.WildcardType;
042import java.lang.reflect.TypeVariable;
043import java.lang.reflect.Array;
044import java.lang.reflect.GenericDeclaration;
045import java.lang.reflect.MalformedParameterizedTypeException;
046import java.util.Arrays;
047
048/**
049 * Type arithmetic code. Taken from the JAXB RI.
050 *
051 * @author Kohsuke Kawaguchi
052 */
053public class TypeUtil {
054    abstract static class TypeVisitor<T,P> {
055        public final T visit( Type t, P param ) {
056            assert t!=null;
057
058            if (t instanceof Class)
059                return onClass((Class)t,param);
060            if (t instanceof ParameterizedType)
061                return onParameterizedType( (ParameterizedType)t,param);
062            if(t instanceof GenericArrayType)
063                return onGenericArray((GenericArrayType)t,param);
064            if(t instanceof WildcardType)
065                return onWildcard((WildcardType)t,param);
066            if(t instanceof TypeVariable)
067                return onVariable((TypeVariable)t,param);
068
069            // covered all the cases
070            assert false;
071            throw new IllegalArgumentException();
072        }
073
074        protected abstract T onClass(Class c, P param);
075        protected abstract T onParameterizedType(ParameterizedType p, P param);
076        protected abstract T onGenericArray(GenericArrayType g, P param);
077        protected abstract T onVariable(TypeVariable v, P param);
078        protected abstract T onWildcard(WildcardType w, P param);
079    }
080
081    /**
082     * Implements the logic for {@link #erasure(Type)}.
083     */
084    private static final TypeVisitor<Class,Void> eraser = new TypeVisitor<Class,Void>() {
085        public Class onClass(Class c,Void _) {
086            return c;
087        }
088
089        public Class onParameterizedType(ParameterizedType p,Void _) {
090            // TODO: why getRawType returns Type? not Class?
091            return visit(p.getRawType(),null);
092        }
093
094        public Class onGenericArray(GenericArrayType g,Void _) {
095            return Array.newInstance(
096                visit(g.getGenericComponentType(),null),
097                0 ).getClass();
098        }
099
100        public Class onVariable(TypeVariable v,Void _) {
101            return visit(v.getBounds()[0],null);
102        }
103
104        public Class onWildcard(WildcardType w,Void _) {
105            return visit(w.getUpperBounds()[0],null);
106        }
107    };
108
109    /**
110     * Returns the runtime representation of the given type.
111     *
112     * This corresponds to the notion of the erasure in JSR-14.
113     */
114    public static <T> Class<T> erasure(Type t) {
115        return eraser.visit(t,null);
116    }
117
118    private static final TypeVisitor<Type,Class> baseClassFinder = new TypeVisitor<Type,Class>() {
119        public Type onClass(Class c, Class sup) {
120            // t is a raw type
121            if(sup==c)
122                return sup;
123
124            Type r;
125
126            Type sc = c.getGenericSuperclass();
127            if(sc!=null) {
128                r = visit(sc,sup);
129                if(r!=null)     return r;
130            }
131
132            for( Type i : c.getGenericInterfaces() ) {
133                r = visit(i,sup);
134                if(r!=null)  return r;
135            }
136
137            return null;
138        }
139
140        public Type onParameterizedType(ParameterizedType p, Class sup) {
141            Class raw = (Class) p.getRawType();
142            if(raw==sup) {
143                // p is of the form sup<...>
144                return p;
145            } else {
146                // recursively visit super class/interfaces
147                Type r = raw.getGenericSuperclass();
148                if(r!=null)
149                    r = visit(bind(r,raw,p),sup);
150                if(r!=null)
151                    return r;
152                for( Type i : raw.getGenericInterfaces() ) {
153                    r = visit(bind(i,raw,p),sup);
154                    if(r!=null)  return r;
155                }
156                return null;
157            }
158        }
159
160        public Type onGenericArray(GenericArrayType g, Class sup) {
161            // not clear what I should do here
162            return null;
163        }
164
165        public Type onVariable(TypeVariable v, Class sup) {
166            return visit(v.getBounds()[0],sup);
167        }
168
169        public Type onWildcard(WildcardType w, Class sup) {
170            // not clear what I should do here
171            return null;
172        }
173
174        /**
175         * Replaces the type variables in {@code t} by its actual arguments.
176         *
177         * @param decl
178         *      provides a list of type variables. See {@link GenericDeclaration#getTypeParameters()}
179         * @param args
180         *      actual arguments. See {@link ParameterizedType#getActualTypeArguments()}
181         */
182        private Type bind( Type t, GenericDeclaration decl, ParameterizedType args ) {
183            return binder.visit(t,new BinderArg(decl,args.getActualTypeArguments()));
184        }
185    };
186
187    private static final TypeVisitor<Type,BinderArg> binder = new TypeVisitor<Type,BinderArg>() {
188        public Type onClass(Class c, BinderArg args) {
189            return c;
190        }
191
192        public Type onParameterizedType(ParameterizedType p, BinderArg args) {
193            Type[] params = p.getActualTypeArguments();
194
195            boolean different = false;
196            for( int i=0; i<params.length; i++ ) {
197                Type t = params[i];
198                params[i] = visit(t,args);
199                different |= t!=params[i];
200            }
201
202            Type newOwner = p.getOwnerType();
203            if(newOwner!=null)
204                newOwner = visit(newOwner,args);
205            different |= p.getOwnerType()!=newOwner;
206
207            if(!different)  return p;
208
209            return new ParameterizedTypeImpl( (Class<?>)p.getRawType(), params, newOwner );
210        }
211
212        public Type onGenericArray(GenericArrayType g, BinderArg types) {
213            Type c = visit(g.getGenericComponentType(),types);
214            if(c==g.getGenericComponentType())  return g;
215
216            return new GenericArrayTypeImpl(c);
217        }
218
219        public Type onVariable(TypeVariable v, BinderArg types) {
220            return types.replace(v);
221        }
222
223        public Type onWildcard(WildcardType w, BinderArg types) {
224            // TODO: this is probably still incorrect
225            // bind( "? extends T" ) with T= "? extends Foo" should be "? extends Foo",
226            // not "? extends (? extends Foo)"
227            Type[] lb = w.getLowerBounds();
228            Type[] ub = w.getUpperBounds();
229            boolean diff = false;
230
231            for( int i=0; i<lb.length; i++ ) {
232                Type t = lb[i];
233                lb[i] = visit(t,types);
234                diff |= (t!=lb[i]);
235            }
236
237            for( int i=0; i<ub.length; i++ ) {
238                Type t = ub[i];
239                ub[i] = visit(t,types);
240                diff |= (t!=ub[i]);
241            }
242
243            if(!diff)       return w;
244
245            return new WildcardTypeImpl(lb,ub);
246        }
247    };
248
249    private static class BinderArg {
250        final TypeVariable[] params;
251        final Type[] args;
252
253        BinderArg(TypeVariable[] params, Type[] args) {
254            this.params = params;
255            this.args = args;
256            assert params.length==args.length;
257        }
258
259        public BinderArg( GenericDeclaration decl, Type[] args ) {
260            this(decl.getTypeParameters(),args);
261        }
262
263        Type replace( TypeVariable v ) {
264            for(int i=0; i<params.length; i++)
265                if(params[i]==v)
266                    return args[i];
267            return v;   // this is a free variable
268        }
269    }
270
271    /**
272     * Gets the parameterization of the given base type.
273     *
274     * <p>
275     * For example, given the following
276     * <pre>{@code
277     * interface Foo<T> extends List<List<T>> {}
278     * interface Bar extends Foo<String> {}
279     * }</pre>
280     * This method works like this:
281     * <pre>{@code
282     * getBaseClass( Bar, List ) = List<List<String>
283     * getBaseClass( Bar, Foo  ) = Foo<String>
284     * getBaseClass( Foo<? extends Number>, Collection ) = Collection<List<? extends Number>>
285     * getBaseClass( ArrayList<? extends BigInteger>, List ) = List<? extends BigInteger>
286     * }</pre>
287     *
288     * @param type
289     *      The type that derives from {@code baseType}
290     * @param baseType
291     *      The class whose parameterization we are interested in.
292     * @return
293     *      The use of {@code baseType} in {@code type}.
294     *      or null if the type is not assignable to the base type.
295     */
296    public static Type getBaseClass(Type type, Class baseType) {
297        return baseClassFinder.visit(type,baseType);
298    }
299
300    static class ParameterizedTypeImpl implements ParameterizedType {
301        private Type[] actualTypeArguments;
302        private Class<?> rawType;
303        private Type ownerType;
304
305        ParameterizedTypeImpl(Class<?> rawType,
306                                      Type[] actualTypeArguments,
307                                      Type ownerType) {
308            this.actualTypeArguments = actualTypeArguments;
309            this.rawType = rawType;
310            if (ownerType != null) {
311                this.ownerType = ownerType;
312            } else {
313                this.ownerType = rawType.getDeclaringClass();
314            }
315            validateConstructorArguments();
316        }
317
318        private void validateConstructorArguments() {
319            TypeVariable/*<?>*/[] formals = rawType.getTypeParameters();
320            // check correct arity of actual type args
321            if (formals.length != actualTypeArguments.length) {
322                throw new MalformedParameterizedTypeException();
323            }
324            for (int i = 0; i < actualTypeArguments.length; i++) {
325                // check actuals against formals' bounds
326            }
327
328        }
329
330        public Type[] getActualTypeArguments() {
331            return actualTypeArguments.clone();
332        }
333
334        public Class<?> getRawType() {
335            return rawType;
336        }
337
338        public Type getOwnerType() {
339            return ownerType;
340        }
341
342        /*
343         * From the JavaDoc for java.lang.reflect.ParameterizedType
344         * "Instances of classes that implement this interface must
345         * implement an equals() method that equates any two instances
346         * that share the same generic type declaration and have equal
347         * type parameters."
348         */
349        @Override
350        public boolean equals(Object o) {
351            if (o instanceof ParameterizedType) {
352                // Check that information is equivalent
353                ParameterizedType that = (ParameterizedType) o;
354
355                if (this == that)
356                    return true;
357
358                Type thatOwner = that.getOwnerType();
359                Type thatRawType = that.getRawType();
360
361                if (false) { // Debugging
362                    boolean ownerEquality = (ownerType == null ?
363                            thatOwner == null :
364                            ownerType.equals(thatOwner));
365                    boolean rawEquality = (rawType == null ?
366                            thatRawType == null :
367                            rawType.equals(thatRawType));
368
369                    boolean typeArgEquality = Arrays.equals(actualTypeArguments, // avoid clone
370                            that.getActualTypeArguments());
371                    for (Type t : actualTypeArguments) {
372                        System.out.printf("\t\t%s%s%n", t, t.getClass());
373                    }
374
375                    System.out.printf("\towner %s\traw %s\ttypeArg %s%n",
376                            ownerEquality, rawEquality, typeArgEquality);
377                    return ownerEquality && rawEquality && typeArgEquality;
378                }
379
380
381                return
382                        (ownerType == null ?
383                        thatOwner == null :
384                        ownerType.equals(thatOwner)) &&
385                        (rawType == null ?
386                        thatRawType == null :
387                        rawType.equals(thatRawType)) &&
388                        Arrays.equals(actualTypeArguments, // avoid clone
389                                that.getActualTypeArguments());
390            } else
391                return false;
392        }
393
394        @Override
395        public int hashCode() {
396            return  Arrays.hashCode(actualTypeArguments) ^
397                    (ownerType == null ? 0 : ownerType.hashCode()) ^
398                    (rawType == null ? 0 : rawType.hashCode());
399        }
400
401        public String toString() {
402            StringBuilder sb = new StringBuilder();
403
404            if (ownerType != null) {
405                if (ownerType instanceof Class)
406                    sb.append(((Class) ownerType).getName());
407                else
408                    sb.append(ownerType.toString());
409
410                sb.append(".");
411
412                if (ownerType instanceof ParameterizedTypeImpl) {
413                    // Find simple name of nested type by removing the
414                    // shared prefix with owner.
415                    sb.append(rawType.getName().replace(((ParameterizedTypeImpl) ownerType).rawType.getName() + "$",
416                            ""));
417                } else
418                    sb.append(rawType.getName());
419            } else
420                sb.append(rawType.getName());
421
422            if (actualTypeArguments != null &&
423                    actualTypeArguments.length > 0) {
424                sb.append("<");
425                boolean first = true;
426                for (Type t : actualTypeArguments) {
427                    if (!first)
428                        sb.append(", ");
429                    if (t instanceof Class)
430                        sb.append(((Class) t).getName());
431                    else
432                        sb.append(t.toString());
433                    first = false;
434                }
435                sb.append(">");
436            }
437
438            return sb.toString();
439        }
440    }
441
442    static final class GenericArrayTypeImpl implements GenericArrayType {
443        private Type genericComponentType;
444
445        GenericArrayTypeImpl(Type ct) {
446            assert ct!=null;
447            genericComponentType = ct;
448        }
449
450        /**
451         * Returns  a <tt>Type</tt> object representing the component type
452         * of this array.
453         *
454         * @return a <tt>Type</tt> object representing the component type
455         *         of this array
456         * @since 1.5
457         */
458        public Type getGenericComponentType() {
459            return genericComponentType; // return cached component type
460        }
461
462        public String toString() {
463            Type componentType = getGenericComponentType();
464            StringBuilder sb = new StringBuilder();
465
466            if (componentType instanceof Class)
467                sb.append(((Class) componentType).getName());
468            else
469                sb.append(componentType.toString());
470            sb.append("[]");
471            return sb.toString();
472        }
473
474        @Override
475        public boolean equals(Object o) {
476            if (o instanceof GenericArrayType) {
477                GenericArrayType that = (GenericArrayType) o;
478
479                Type thatComponentType = that.getGenericComponentType();
480                return genericComponentType.equals(thatComponentType);
481            } else
482                return false;
483        }
484
485        @Override
486        public int hashCode() {
487            return genericComponentType.hashCode();
488        }
489    }
490
491    static final class WildcardTypeImpl implements WildcardType {
492
493        private final Type[] ub;
494        private final Type[] lb;
495
496        public WildcardTypeImpl(Type[] ub, Type[] lb) {
497            this.ub = ub;
498            this.lb = lb;
499        }
500
501        public Type[] getUpperBounds() {
502            return ub;
503        }
504
505        public Type[] getLowerBounds() {
506            return lb;
507        }
508
509        public int hashCode() {
510            return Arrays.hashCode(lb) ^ Arrays.hashCode(ub);
511        }
512
513        public boolean equals(Object obj) {
514            if (obj instanceof WildcardType) {
515                WildcardType that = (WildcardType) obj;
516                return Arrays.equals(that.getLowerBounds(),lb)
517                    && Arrays.equals(that.getUpperBounds(),ub);
518            }
519            return false;
520        }
521    }
522
523    public static Type getTypeArgument(Type type, int i) {
524        if (type instanceof ParameterizedType) {
525            ParameterizedType p = (ParameterizedType) type;
526            return fix(p.getActualTypeArguments()[i]);
527        } else
528            throw new IllegalArgumentException();
529    }
530
531    /**
532     * JDK 5.0 has a bug of creating {@link GenericArrayType} where it shouldn't.
533     * fix that manually to work around the problem.
534     *
535     * See bug 6202725.
536     */
537    private static Type fix(Type t) {
538        if(!(t instanceof GenericArrayType))
539            return t;
540
541        GenericArrayType gat = (GenericArrayType) t;
542        if(gat.getGenericComponentType() instanceof Class) {
543            Class c = (Class) gat.getGenericComponentType();
544            return Array.newInstance(c,0).getClass();
545        }
546
547        return t;
548    }
549}