package edu.mit.streamjit.impl.compiler;

import static com.google.common.base.Preconditions.*;
import com.google.common.base.Strings;
import edu.mit.streamjit.impl.compiler.types.TypeFactory;
import java.lang.reflect.Array;
import java.util.List;

/**
 * Module is the top-level IR node for a single compilation, analogous to a
 * translation unit in other compilers.
 * @author Jeffrey Bosboom <jeffreybosboom@gmail.com>
 * @since 3/6/2013
 */
public final class Module {
	private final TypeFactory typeFactory = new TypeFactory(this);
	private KlassList klasses = new KlassList(this);
	public Module() {
	}

	public List<Klass> klasses() {
		return klasses;
	}

	public TypeFactory types() {
		return typeFactory;
	}

	/**
	 * Gets the Klass with the given name, or null if this Module doesn't
	 * contain a Klass with the given name.
	 * @param name the name of the Klass to get
	 * @return the Klass with the given name, or null
	 */
	public Klass getKlass(String name) {
		checkNotNull(name);
		for (Klass klass : klasses())
			if (klass.getName().equals(name))
				return klass;
		return null;
	}

	/**
	 * Gets the Klass representing the given Class object, creating and adding
	 * it to this module if necessary.
	 * @param klass the class to get a Klass for
	 * @return a Klass representing the given Class
	 */
	public Klass getKlass(Class<?> klass) {
		Klass klassByName = getKlass(klass.getName());
		if (klassByName != null)
			return klassByName;
		return new Klass(klass, this);
	}

	public Klass getArrayKlass(Klass componentType, int dimensions) {
		checkNotNull(componentType);
		checkArgument(dimensions >= 1);
		if (componentType.getBackingClass() != null)
			return getKlass(Array.newInstance(componentType.getBackingClass(), new int[dimensions]).getClass());
		StringBuilder nameBuilder = new StringBuilder(Strings.repeat("[", dimensions));
		//Always a reference type; if not already an array, add L and ;.
		nameBuilder.append(componentType.isArray() ? componentType.getName() : "L" + componentType.getName() + ";");
		String name = nameBuilder.toString();
		Klass alreadyExists = getKlass(name);
		if (alreadyExists != null)
			return alreadyExists;
		return new Klass(componentType, dimensions, this);
	}

	/**
	 * Ensures we don't end up with two classes with the same name in the list.
	 */
	private static class KlassList extends ParentedList<Module, Klass> {
		private KlassList(Module parent) {
			super(parent, Klass.class);
		}
		@Override
		protected void elementAdding(Klass t) {
			for (Klass klass : this)
				checkArgument(!klass.getName().equals(t.getName()), "adding duplicate %s", t.getName());
			super.elementAdding(t);
		}
		@Override
		protected void elementRemoving(Klass t) {
			//Removing an immutable Klass makes no sense as any replacement
			//would have no effect (because the module-based ClassLoader prefers
			//classes loaded by the parent) and could break other mutable
			//Klasses if they depended on the changed definitions.
			checkArgument(t.isMutable(), "removing immutable Klass %s", t.getName());
			super.elementRemoving(t);
		}
	}
}

@@ -235,20 +235,68 @@
 va.util.
-List
+HashMap;%0Aimport java.util.List;%0Aimport java.util.Map
 ;%0A%0A/**%0A 
@@ -629,16 +629,74 @@
 (this);%0A
+%09private Map%3CString, Klass%3E klassesMap = new HashMap%3C%3E();%0A
 %09public 
@@ -1104,106 +1104,35 @@
 ;%0A%09%09
-for (Klass klass : klasses())%0A%09%09%09if (klass.getName().equals(name))%0A%09%09%09%09return klass;%0A%09%09return null
+return klassesMap.get(name)
 ;%0A%09%7D
@@ -2357,23 +2357,16 @@
 private 
-static 
 class Kl
@@ -2541,37 +2541,8 @@
 ) %7B%0A
-%09%09%09for (Klass klass : this)%0A%09
 %09%09%09c
@@ -2564,25 +2564,25 @@
 lass
-.getName().equals
+esMap.containsKey
 (t.g
@@ -2664,32 +2664,150 @@
 %09%09%7D%0A%09%09@Override%0A
+%09%09protected void elementAdded(Klass t) %7B%0A%09%09%09super.elementAdded(t);%0A%09%09%09klassesMap.put(t.getName(), t);%0A%09%09%7D%0A%09%09@Override%0A
 %09%09protected void

use a map to speed up getKlass(String)