package net.jonp.armi;

import java.rmi.NotBoundException;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

/**
 * A default implementation of a ClassRegistry backed by a {@link HashBiMap}.
 * This follows the same synchronization semantics as {@link HashBiMap}.
 */
public class DefaultClassRegistry
    implements ClassRegistry
{
    private final BiMap<String, Class<?>> map = HashBiMap.create();

    /**
     * Construct a new DefaultClassRegistry.
     */
    public DefaultClassRegistry()
    {
        // Nothing to do
    }

    /**
     * Clear all entries from this class registry.
     */
    public void clear()
    {
        map.clear();
    }

    /**
     * Put a name/class pair into this registry.
     * 
     * @param name The name.
     * @param clazz The class.
     * @throws IllegalArgumentException If the given mapping is already present
     *             in this registry.
     */
    public void put(final String name, final Class<?> clazz)
        throws IllegalArgumentException
    {
        map.put(name, clazz);
    }

    /**
     * Get the mapped class for a given name.
     * 
     * @param name The name.
     * @return The class, or <code>null</code> if not bound.
     */
    public Class<?> get(final String name)
    {
        return map.get(name);
    }

    /**
     * Get the name for a given mapped class.
     * 
     * @param clazz The class.
     * @return The name, or <code>null</code> if not bound.
     */
    public String get(final Class<?> clazz)
    {
        return map.inverse().get(clazz);
    }

    /**
     * Remove a mapping from this registry.
     * 
     * @param name The name to remove.
     */
    public void remove(final String name)
    {
        map.remove(name);
    }

    /**
     * Remove a mapping from this registry.
     * 
     * @param clazz The class to remove.
     */
    public void remove(final Class<?> clazz)
    {
        map.inverse().remove(clazz);
    }

    /**
     * Check whether this registry contains a mapping for the given name.
     * 
     * @param name The name.
     * @return True if the name is present in this registry; false if not.
     */
    public boolean contains(final String name)
    {
        return map.containsKey(name);
    }

    /**
     * Check whether this registry contains a mapping for the given class.
     * 
     * @param clazz The class.
     * @return True if the class is present in this registry; false if not.
     */
    public boolean contains(final Class<?> clazz)
    {
        return map.inverse().containsKey(clazz);
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.jonp.armi.ClassRegistry#lookup(java.lang.String)
     */
    @Override
    public Class<?> lookup(final String name)
        throws NotBoundException
    {
        final Class<?> clazz = map.get(name);

        if (null == clazz) {
            throw new NotBoundException(name);
        }

        return clazz;
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.jonp.armi.ClassRegistry#reverseLookup(java.lang.Class)
     */
    @Override
    public String reverseLookup(final Class<?> clazz)
        throws NotBoundException
    {
        final String name = map.inverse().get(clazz);

        if (null == name) {
            throw new NotBoundException(clazz.getName());
        }

        return name;
    }
}

@@ -218,20 +218,49 @@
 shBiMap%7D
-.%0A *
+ and%0A * the Runtime class loader.
  This fo
@@ -302,16 +302,19 @@
 ntics as
+%0A *
  %7B@link 
@@ -2632,32 +2632,33 @@
    %7D%0A%0A    /*
+*
 %0A     * 
 (non-Javadoc
@@ -2649,94 +2649,402 @@
   * 
-(non-Javadoc)%0A     * %0A     * @see net.jonp.armi.ClassRegistry#lookup(java.lang.String)
+Look up the given class name in this registry.%0A     * %0A     * @param name The name of the class to look up.%0A     * @return The class associated with that name, first checking the classes%0A     *         registered with this registry, and then the Runtime class loader.%0A     *         This allows for unregistered classes to be used, as long as they%0A     *         come from the standard library.
 %0A   
@@ -3155,22 +3155,16 @@
         
-final 
 Class%3C?%3E
@@ -3184,24 +3184,359 @@
 get(name);%0A%0A
+        if (null == clazz) %7B%0A            // It may be a standard class%0A            try %7B%0A                clazz = Class.forName(name, true, Runtime.class.getClassLoader());%0A            %7D%0A            catch (final ClassNotFoundException cnfe) %7B%0A                // Nope, not a standard class; ignore the exception%0A            %7D%0A        %7D%0A%0A
         if (
@@ -3869,22 +3869,16 @@
         
-final 
 String n
@@ -3906,24 +3906,542 @@
 et(clazz);%0A%0A
+        if (null == name) %7B%0A            // Not found? See if it is available in the standard library before%0A            // throwing an exception%0A            try %7B%0A                Class.forName(clazz.getName(), false, Runtime.class.getClassLoader());%0A%0A                // Yup, it's a standard class, so use that name%0A                name = clazz.getName();%0A            %7D%0A            catch (final ClassNotFoundException cnfe) %7B%0A                // Nope, not a standard class; ignore the exception%0A            %7D%0A        %7D%0A%0A
         if (

Update DefaultClassRegistry so it falls back onto the system classloader.