/*******************************************************************************
 * Copyright (c) 2002 - 2006 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.ibm.wala.classLoader;

import java.util.HashMap;

import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;

/**
 * Pseudo-classloader for all array classes; all other IClassLoader
 * implementations should delegate to this one for array classes only.
 */
public class ArrayClassLoader {

  private final static boolean DEBUG = false;

  /**
   * map: TypeReference -> ArrayClass
   */
  final private HashMap<TypeReference, ArrayClass> arrayClasses = HashMapFactory.make();


  /**
   * @param className
   *          name of the array class
   * @param delegator
   *          class loader to look up element type with
   */
  public IClass lookupClass(TypeName className, IClassLoader delegator, IClassHierarchy cha) throws IllegalArgumentException {
    ArrayClass arrayClass;
    if (DEBUG) {
      assert className.toString().startsWith("[");
    }
    if (delegator == null) {
      throw new IllegalArgumentException("delegator must not be null");
    }

    TypeReference type = TypeReference.findOrCreate(delegator.getReference(), className);
    TypeReference elementType = type.getArrayElementType();
    if (elementType.isPrimitiveType()) {
      TypeReference aRef = TypeReference.findOrCreateArrayOf(elementType);
      arrayClass = arrayClasses.get(aRef);
      IClassLoader primordial = getRootClassLoader(delegator);
      if (arrayClass == null) {
        arrayClasses.put(aRef, arrayClass=new ArrayClass(aRef,primordial,cha));
      }
    } else {
      arrayClass = arrayClasses.get(type);
      if (arrayClass == null) {
	// check that the element class is loadable. If not, return null.
	IClass elementCls = delegator.lookupClass(elementType.getName());
        if (elementCls == null) {
          return null;
        }
	
	TypeReference realType = TypeReference.findOrCreateArrayOf(elementCls.getReference());
	arrayClass = arrayClasses.get(realType);
	
	if (arrayClass == null) {
	  arrayClass = new ArrayClass(realType, elementCls.getClassLoader(), cha);
	}
      }
      arrayClasses.put(type, arrayClass);
    }
    return arrayClass;
  }

  private static IClassLoader getRootClassLoader(IClassLoader l) {
    while (l.getParent() != null) {
      l = l.getParent();
    }
    return l;
  }
  
  public int getNumberOfClasses() {
    return arrayClasses.size();
  }
}
