/* * Copyright 2009 Alin Dreghiciu. * * Licensed 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.ops4j.pax.swissbox.converter; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import org.osgi.service.blueprint.container.ReifiedType; import org.ops4j.pax.swissbox.converter.internal.Primitives; import org.ops4j.pax.swissbox.converter.loader.Loader; /** * JAVADOC * * NOTICE: This class contains code originally developed by "Apache Geronimo Project", OSGi Blueprint Implementation. * * @author <a href="mailto:dev@geronimo.apache.org">Apache Geronimo Project</a> * @author Alin Dreghiciu (adreghiciu@gmail.com) */ public class GenericType extends ReifiedType { private static final GenericType[] EMPTY = new GenericType[0]; private final GenericType[] parameters; public GenericType( final Type type ) { this( getConcreteClass( type ), parametersOf( type ) ); } public GenericType( final Class clazz, final GenericType... parameters ) { super( clazz ); this.parameters = parameters; } @Override public ReifiedType getActualTypeArgument( final int index ) { if( parameters.length == 0 ) { return super.getActualTypeArgument( index ); } return parameters[ index ]; } @Override public int size() { return parameters.length; } @Override public String toString() { final Class clazz = getRawClass(); if( clazz.isArray() ) { if( parameters.length > 0 ) { return parameters[ 0 ].toString() + "[]"; } else { return clazz.getComponentType().getName() + "[]"; } } if( parameters.length > 0 ) { final StringBuilder sb = new StringBuilder(); sb.append( clazz.getName() ); sb.append( "<" ); for( int i = 0; i < parameters.length; i++ ) { if( i > 0 ) { sb.append( "," ); } sb.append( parameters[ i ].toString() ); } sb.append( ">" ); return sb.toString(); } return clazz.getName(); } public static GenericType parse( final String type, final Loader loader ) throws ClassNotFoundException, IllegalArgumentException { assert type != null : "Type must be specified (cannot be null)"; assert loader != null : "Loader must be specified (cannot be null)"; final String localType = type.trim(); // Check if this is an array if( localType.endsWith( "[]" ) ) { final GenericType parsedType = parse( localType.substring( 0, localType.length() - 2 ), loader ); return new GenericType( Array.newInstance( parsedType.getRawClass(), 0 ).getClass(), parsedType ); } // Check if this is a generic int genericIndex = localType.indexOf( '<' ); if( genericIndex > 0 ) { if( !localType.endsWith( ">" ) ) { throw new IllegalArgumentException( "Can not load type: " + localType ); } final GenericType baseType = parse( localType.substring( 0, genericIndex ), loader ); final String[] params = localType.substring( genericIndex + 1, localType.length() - 1 ).split( "," ); GenericType[] types = new GenericType[params.length]; for( int i = 0; i < params.length; i++ ) { types[ i ] = parse( params[ i ], loader ); } return new GenericType( baseType.getRawClass(), types ); } // Primitive if( Primitives.isPrimitive( localType ) ) { return new GenericType( Primitives.primitive( localType ) ); } return new GenericType( loader.loadClass( localType ) ); } public boolean equals( final Object object ) { if( !( object instanceof GenericType ) ) { return false; } GenericType other = (GenericType) object; if( getRawClass() != other.getRawClass() ) { return false; } if( parameters == null ) { return ( other.parameters == null ); } else { if( other.parameters == null ) { return false; } if( parameters.length != other.parameters.length ) { return false; } for( int i = 0; i < parameters.length; i++ ) { if( !parameters[ i ].equals( other.parameters[ i ] ) ) { return false; } } return true; } } private static GenericType[] parametersOf( final Type type ) { if( type instanceof Class ) { Class clazz = (Class) type; if( clazz.isArray() ) { GenericType t = new GenericType( clazz.getComponentType() ); if( t.size() > 0 ) { return new GenericType[]{ t }; } else { return EMPTY; } } else { return EMPTY; } } if( type instanceof ParameterizedType ) { ParameterizedType pt = (ParameterizedType) type; Type[] parameters = pt.getActualTypeArguments(); GenericType[] gts = new GenericType[parameters.length]; for( int i = 0; i < gts.length; i++ ) { gts[ i ] = new GenericType( parameters[ i ] ); } return gts; } if( type instanceof GenericArrayType ) { return new GenericType[]{ new GenericType( ( (GenericArrayType) type ).getGenericComponentType() ) }; } if( type instanceof WildcardType ) { return EMPTY; } if( type instanceof TypeVariable ) { return EMPTY; } throw new RuntimeException( "Unknown type " + type ); } private static Class<?> getConcreteClass( final Type type ) { final Type collapsed = collapse( type ); if( collapsed instanceof Class ) { return (Class<?>) collapsed; } if( collapsed instanceof ParameterizedType ) { return getConcreteClass( collapse( ( (ParameterizedType) collapsed ).getRawType() ) ); } throw new RuntimeException( "Unknown type " + type ); } private static Type collapse( final Type type ) { if( type instanceof Class || type instanceof ParameterizedType ) { return type; } else if( type instanceof TypeVariable ) { return collapse( ( (TypeVariable<?>) type ).getBounds()[ 0 ] ); } else if( type instanceof GenericArrayType ) { Type arrayType = collapse( ( (GenericArrayType) type ).getGenericComponentType() ); while( arrayType instanceof ParameterizedType ) { arrayType = collapse( ( (ParameterizedType) arrayType ).getRawType() ); } return Array.newInstance( (Class<?>) arrayType, 0 ).getClass(); } else if( type instanceof WildcardType ) { WildcardType wildcardType = (WildcardType) type; if( wildcardType.getLowerBounds().length == 0 ) { return collapse( wildcardType.getUpperBounds()[ 0 ] ); } else { return collapse( wildcardType.getLowerBounds()[ 0 ] ); } } throw new RuntimeException( "Unknown type " + type ); } public static GenericType genericType( final Type type ) { return new GenericType( type ); } public static GenericType genericType( final Class clazz, final GenericType... parameters ) { return new GenericType( clazz, parameters ); } }