Project: blueprint-namespaces
/**
 * 
 * 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.aries.blueprint.container; 
 
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 java.security.AccessController; 
import java.security.PrivilegedActionException; 
import java.security.PrivilegedExceptionAction; 
import java.util.HashMap; 
import java.util.Map; 
 
import org.apache.aries.blueprint.ExtendedBlueprintContainer; 
import org.apache.aries.blueprint.di.ExecutionContext; 
import org.osgi.framework.Bundle; 
import org.osgi.service.blueprint.container.ReifiedType; 
 
/**
 * XXXX: Currently, in case of arrays getActualTypeArgument(0) returns something similar to what 
 * Class.getComponentType() does for arrays.  I don't think this is quite right since getActualTypeArgument() 
 * should return the given parameterized type not the component type. Need to check this behavior with the spec. 
 */
 
public class GenericType extends ReifiedType { 
 
 private static final GenericType[] EMPTY = new GenericType[0]; 
 
    private static final Map<String, Class> primitiveClasses = new HashMap<String, Class>(); 
 
    static { 
        primitiveClasses.put("int"int.class); 
        primitiveClasses.put("short"short.class); 
        primitiveClasses.put("long"long.class); 
        primitiveClasses.put("byte"byte.class); 
        primitiveClasses.put("char"char.class); 
        primitiveClasses.put("float"float.class); 
        primitiveClasses.put("double"double.class); 
        primitiveClasses.put("boolean"boolean.class); 
    } 
 
    private GenericType[] parameters; 
 
 public GenericType(Type type) { 
  this(getConcreteClass(type), parametersOf(type)); 
 
 
    public GenericType(Class clazz, GenericType... parameters) { 
        super(clazz); 
        this.parameters = parameters; 
    } 
 
    public static GenericType parse(String rawType, final Object loader) throws ClassNotFoundException, IllegalArgumentException { 
        final String type = rawType.trim(); 
        // Check if this is an array 
        if (type.endsWith("[]")) { 
            GenericType t = parse(type.substring(0, type.length() - 2), loader); 
            return new GenericType(Array.newInstance(t.getRawClass(), 0).getClass(), t); 
        } 
        // Check if this is a generic 
        int genericIndex = type.indexOf('<'); 
        if (genericIndex > 0) { 
            if (!type.endsWith(">")) { 
                throw new IllegalArgumentException("Can not load type: " + type); 
            } 
            GenericType base = parse(type.substring(0, genericIndex), loader); 
            String[] params = type.substring(genericIndex + 1, type.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(base.getRawClass(), types); 
        } 
        // Primitive 
        if (primitiveClasses.containsKey(type)) { 
            return new GenericType(primitiveClasses.get(type)); 
        } 
        // Class 
        if (loader instanceof ClassLoader) { 
            return new GenericType(((ClassLoader) loader).loadClass(type)); 
        } else if (loader instanceof Bundle) { 
            try { 
              return AccessController.doPrivileged(new PrivilegedExceptionAction<GenericType>() { 
                public GenericType run() throws ClassNotFoundException { 
                  return new GenericType(((Bundle) loader).loadClass(type)); 
                } 
              }); 
            } catch (PrivilegedActionException pae) { 
              Exception e = pae.getException(); 
              if (e instanceof ClassNotFoundException)  
                throw (ClassNotFoundException) e; 
              else 
                throw (RuntimeException) e; 
            } 
        } else if (loader instanceof ExecutionContext) { 
            return new GenericType(((ExecutionContext) loader).loadClass(type)); 
        } else if (loader instanceof ExtendedBlueprintContainer) { 
            return new GenericType(((ExtendedBlueprintContainer) loader).loadClass(type)); 
        } else { 
            throw new IllegalArgumentException("Unsupported loader: " + loader); 
        } 
    } 
 
    @Override 
    public ReifiedType getActualTypeArgument(int i) { 
        if (parameters.length == 0) { 
            return super.getActualTypeArgument(i); 
        } 
        return parameters[i]; 
    } 
 
    @Override 
    public int size() { 
        return parameters.length; 
    } 
 
    @Override 
    public String toString() { 
        Class cl = getRawClass(); 
        if (cl.isArray()) { 
            if (parameters.length > 0) { 
                return parameters[0].toString() + "[]"
            } else { 
                return cl.getComponentType().getName() + "[]"
            } 
        } 
        if (parameters.length > 0) { 
            StringBuilder sb = new StringBuilder(); 
            sb.append(cl.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 cl.getName(); 
    } 
 
    public boolean equals(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
        } 
    } 
     
    static GenericType[] parametersOf(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 IllegalStateException(); 
 
 
 static Class<?> getConcreteClass(Type type) { 
  Type ntype = collapse(type); 
  if ( ntype instanceof Class ) 
   return (Class<?>) ntype; 
 
  if ( ntype instanceof ParameterizedType ) 
   return getConcreteClass(collapse(((ParameterizedType)ntype).getRawType())); 
 
  throw new RuntimeException("Unknown type " + type ); 
 
 
 static Type collapse(Type target) { 
  if (target instanceof Class || target instanceof ParameterizedType ) { 
   return target; 
  else if (target instanceof TypeVariable) { 
   return collapse(((TypeVariable<?>) target).getBounds()[0]); 
  else if (target instanceof GenericArrayType) { 
   Type t = collapse(((GenericArrayType) target) 
     .getGenericComponentType()); 
   while ( t instanceof ParameterizedType ) 
    t = collapse(((ParameterizedType)t).getRawType()); 
   return Array.newInstance((Class<?>)t, 0).getClass(); 
  else if (target instanceof WildcardType) { 
   WildcardType wct = (WildcardType) target; 
   if (wct.getLowerBounds().length == 0
    return collapse(wct.getUpperBounds()[0]); 
   else 
    return collapse(wct.getLowerBounds()[0]); 
  
  throw new RuntimeException("Huh? " + target); 
 
 
}