/*
 * Copyright (C) 2004 TiongHiang Lee
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not,  write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Email: thlee@onemindsoft.org
 */

package org.onemind.commons.java.lang.reflect;

import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.onemind.commons.java.datastructure.LookupCache;
/**
 * A class lookup cache can lookup non-fully-qualified name classes for a given set 
 * of packages and will cache the lookup for later use. For example, <br>
 * 
 * <pre>
 *      ClassLookupCache cache = new ClassLookupCache();
 *      cache.addPackage("*"); //default package
 *      cache.addPackage("java.io.*");
 *  
 *      Class c = cache.getClass("File"); //c = java.io.File
 *      c = cache.getClass("File1"); //c = null
 * </pre>
 * 
 * NOTE: 
 * 1. The cache is static for all instances of the lookup cache. 
 * 2. The packages is instance specific 
 * 3. It will cache only positive and negative response of fully qualified name thus 
 * lookup of non-fully-qualified has some performance hit, but for the sake of correctness
 * 
 * 
 * @author TiongHiang Lee (thlee@onemindsoft.org)
 * @version $Id: ClassLookupCache.java,v 1.6 2006/10/29 17:02:38 thlee Exp $ $Name:  $
 */
public class ClassLookupCache extends LookupCache
{

    /** the packages * */
    private final HashSet _packages = new LinkedHashSet();

    /** the logger * */
    private static final Logger _logger = Logger.getLogger(ClassLookupCache.class.getName());

    /**
     * {@inheritDoc}
     */
    public ClassLookupCache()
    {
    }

    /**
     * Add a new package.
     * @param packageName the package name
     */
    public void addImport(String importName)
    {
        if (importName==null){
            throw new IllegalArgumentException("Package name must not be null");
        }
        _packages.add(importName);
        clearNegCache();
    }

    /**
     * Get the class given by the fully-qualified or non-fully qualified java class name
     * @param className the class name
     * @return the class or null
     */
    public Class getClass(String className)
    {
        if (className.indexOf('.') == -1)
        { //not fully qualified name
            Iterator it = _packages.iterator();
            Class c = null;
            while (it.hasNext())
            {
                String importName = (String) it.next();
                String fullName = null;
                int idx = importName.indexOf("*");
                if (idx==-1){ //importName is a class name
                    if (importName.endsWith("." + className) || importName.equals(className)){
                        fullName = importName;
                    } else { //don't bother
                        continue;
                    }
                } else {
                    fullName = importName.substring(0, idx) + className;
                }
                if (_logger.isLoggable(Level.FINEST))
                {
                    _logger.finest("Looking up class " + fullName);
                }
                c = (Class) lookup(fullName);
                if (c != null)
                {
                    return c;
                }
            }
            return null;
        } else
        {
            return (Class) lookup(className);
        }
    }

    /**
     * Produce the class given the key {@inheritDoc}
     */
    public Object produce(Object key)
    {
        String className = (String) key;
        Class c = null;
        //first trial
        try
        {
            c = Class.forName(className);
            if (_logger.isLoggable(Level.FINEST))
            {
                _logger.finest("Lookup class " + key + " successful");
                //otherwise the ClassNotFoundException must have been throwned
            }
        } catch (Exception e)
        {
            if (_logger.isLoggable(Level.FINEST))
            {
                _logger.finest("Lookup class " + key + " failed");
            }
        }
        return c;
    }

    /**
     * Get all the import packages in this lookup cache.
     * @return the packages
     */
    public Set getPackages()
    {
        return Collections.unmodifiableSet(_packages);
    }

    /**
     * {@inheritDoc}
     */
    protected void clearNegCache()
    {
        super.clearNegCache();
    }

    /**
     * {@inheritDoc}
     */
    protected boolean isInCache(Object o)
    {
        return super.isInCache(o);
    }

    /** 
     * {@inheritDoc}
     */
    protected boolean isInNegCache(Object o)
    {
        return super.isInNegCache(o);
    }

    /** 
     * {@inheritDoc}
     */
    protected void setDoNegativeCache(boolean b)
    {
        super.setDoNegativeCache(b);
    }
}