/
codebase/src/net/sourceforge/pain/admin/console/command/
codebase/src/net/sourceforge/pain/data/role/
codebase/src/net/sourceforge/pain/network/console/telnet/
codebase/src/net/sourceforge/pain/network/guitool/
codebase/src/net/sourceforge/pain/plugin/
codebase/src/net/sourceforge/pain/util/
db/src/net/sourceforge/pain/util/
gui/
gui/lib/
gui/src/net/sourceforge/pain/tools/guitool/dbbrowse/
gui/src/net/sourceforge/pain/tools/guitool/dialog/
gui/src/net/sourceforge/pain/tools/guitool/menu/
gui/src/net/sourceforge/pain/tools/guitool/resources/
gui/src/net/sourceforge/pain/tools/guitool/resources/images/
gui/src/net/sourceforge/pain/tools/guitool/resources/images/explorer/
mudlibs/tinylib/
mudlibs/tinylib/area/
mudlibs/tinylib/etc/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/data/affect/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/data/prototype/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/data/trigger/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/affect/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/event/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/event/deploy/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/event/guitool/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/event/guitool/event/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/fn/util/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/trigger/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/trigger/impl/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/plugin/command/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/plugin/reset/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/plugin/shutdown/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/plugin/social/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/util/
tests/
tests/src/
tests/src/net/sourceforge/pain/db/data/
package net.sourceforge.pain.logic;


import net.sourceforge.pain.*;
import net.sourceforge.pain.data.*;
import net.sourceforge.pain.util.*;

import java.io.*;
import java.util.*;

/**
 * Class responsible for logic loading
 */
public class LogicLoader {

    private String EVENTS_PACKAGE;
    private String classesDir;
    private String logicPackages[] = new String[0];
    private int counter = 0;//for debug use


    /**
     * classloaders by classname
     */
    private LogicClassLoader loader = new LogicClassLoader();
    private Set reloadListeners = new HashSet();

    public LogicLoader(String cd) {
        File dir = new File(cd);
        if (!dir.isDirectory()) {
            throw new IllegalArgumentException("not a directory:" + dir.getAbsolutePath());
        }
        classesDir = cd;
    }

    /**
     * Knowledge of default event package allows to instantiate events by name
     * without indication of package prefix
     *
     * @param eventPackagePrefix - package name with dot on it's end. (example: net.sourceforge.pain.tinylib.logic.)
     */
    public void setEventsPackagePrefix(String eventPackagePrefix) {
        assert EVENTS_PACKAGE == null;
        if (!eventPackagePrefix.endsWith(".")) {
            throw new IllegalArgumentException("events package prefix should ends with '.' ");
        }
        EVENTS_PACKAGE = eventPackagePrefix;
        _addLogicPackage(EVENTS_PACKAGE);
    }


    /**
     * @param packagePrefix - package name with dot on it's end. (example: net.sourceforge.pain.tinylib.logic.)
     */
    public void addLogicPackage(String packagePrefix) {
        if (packagePrefix.endsWith(".")) {
            throw new IllegalArgumentException("logic package prefix should ends with '.' ");
        }
        _addLogicPackage(packagePrefix);
    }

    public synchronized Class provideClass(String className) throws ClassNotFoundException {
        Class c = loader._findLoadedClass(className);
        if (c != null) {
            Log.debug("LL:findLoadedClass works!:" + className);
            return c;
        }
        // net.sourceforge.pain.logic.Event is not reloadable system class!
        if (!isInLogicPackages(className)) {
            if (Codebase.getPluginManager().isInPluginPackages(className)) {
                Log.debug("LL:Asking plugin from LOGIC LOADER:" + className);
                return Codebase.getPluginManager().loadClassByPluginClassloader(null, className);
            }
            try {
                c = Class.forName(className);
            } catch (LinkageError error) {
                if (error instanceof NoClassDefFoundError && error.getMessage().indexOf("wrong name:") > 0) {
                    // case insensitive filesystem -> we will hide this errors
                } else {
                    // this will not stop server but will panic!
                    Log.error("PANIC!!!", error);
                }
                throw new ClassNotFoundException("Error during systen class loading!", error);
            }
            Log.debug("findSystemClass works!:" + className);
            return c;
        }

        Log.debug("LL:asking logic class:" + className);
        c = loader.defineClassFromFile(className);
        return c;
    }

    private boolean isInLogicPackages(String fullClassName) {
        for (int i = 0; i < logicPackages.length; i++) {
            if (fullClassName.startsWith(logicPackages[i])) {
                return true;
            }
        }
        return false;
    }


    public synchronized void reload() {
        try {
            Codebase.processEvent("LogicReloadingEvent", null);
        } catch (Exception e) {
            Log.error(e);
        }
        for (Iterator iterator = reloadListeners.iterator(); iterator.hasNext();) {
            LogicReloadListener logicReloadListener = (LogicReloadListener) iterator.next();
            try {
                logicReloadListener.onLogicReloading();
            } catch (Exception e) {
                Log.error(e);
            }
        }
        ObjectFactory.__cleanCaches();
        TriggersLogicFactory.__cleanCaches();
        loader = new LogicClassLoader();
        System.gc();
    }

    public void addLogicReloadListener(LogicReloadListener l) {
        reloadListeners.add(l);
    }

    public void removeReloadLogicListener(LogicReloadListener l) {
        reloadListeners.remove(l);
    }


    public ClassLoader getActiveClassLoader() {
        return loader;
    }

    public Class provideEventClass(String eventClassSuffix) throws ClassNotFoundException {
        if (EVENTS_PACKAGE != null) {
            return provideClass(EVENTS_PACKAGE + eventClassSuffix);
        }
        throw new IllegalStateException("event package was not initialized!");
    }

    private synchronized void _addLogicPackage(String packageStr) {
        for (int i = logicPackages.length; --i >= 0;) {
            if (logicPackages[i].equals(packageStr)) {
                return;
            }
        }
        String[] tmp = new String[logicPackages.length + 1];
        System.arraycopy(logicPackages, 0, tmp, 0, logicPackages.length);
        tmp[logicPackages.length] = packageStr;
        logicPackages = tmp;
    }


    class LogicClassLoader extends ClassLoader {
        int number = counter;

        protected LogicClassLoader() {
        }

        public synchronized Class loadClass(String className) throws ClassNotFoundException {
            Log.debug("LOGIC LOADER[" + number + "]: ask load:" + className);
            return provideClass(className);
        }

        protected Class defineClassFromFile(String className) throws ClassNotFoundException {
            try {
                byte[] data = IOUtils.getFileData(classesDir + "/" + className.replace('.', '/') + ".class");
                return super.defineClass(className, data, 0, data.length);
            } catch (IOException e) {
                Log.warn("Can't load class file! " + e.getMessage());
                throw new ClassNotFoundException("Class File Not Found:" + classesDir + "/" + className + ".class");
            }
        }

        protected Class _findLoadedClass(String className) {
            return super.findLoadedClass(className);
        }

        protected void finalize() {
            Log.debug("LOGIC LOADER[" + number + "]:finalizing");
        }
    }
}