/
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.data;

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

import java.lang.reflect.*;
import java.util.*;

/**
 * Factory methods for new persistent objects creation
 */
public final class ObjectFactory {


    private static final Map roleDestructors = new HashMap();
    private static Method stubDestructor = null;
    private static final Object[] destructorParams = new Object[1];
    private static final Class[] destructorArgs = new Class[1];


    private static final Map roleConstructors = new HashMap();
    private static final Object[] constructorParams = new Object[2];
    private static final Class[] constructorArgs = new Class[2];


    /**
     * Destroys persistent object. Calls 'destroy' method from logic handlers if available
     * for every role destroyed.
     * @param obj - object to destroy.
     * @throws Exception
     */
    public static final void destroyObject(Role obj) throws Exception {
        //Hungry loop:
        boolean hasMoreRoles;
        do {
            hasMoreRoles = false;
//			int i = 0;
            for (Iterator it = obj.rolesIterator(); it.hasNext(); /*i++*/) {
                Role role = (Role) it.next();
                if (role.hasSubroles()) {
                    obj = role;
                    hasMoreRoles = true;
                    continue;
                }
                detachRole(role);
                it.remove();
            }
        } while (hasMoreRoles);
    }

    private static void detachRole(Role role) throws Exception {
        Class rClass = role.getClass();
        Method m = (Method) roleDestructors.get(rClass);
        if (m == null) {
            m = lookupDestructorMethod(rClass);
            roleDestructors.put(rClass, m);
        }
        destructorParams[0] = role;
        m.invoke(null, destructorParams);
    }

    private static Method lookupDestructorMethod(Class rClass) throws Exception {
        destructorArgs[0] = rClass;
        try {
            return Class.forName(getFactoryPackage() + ClassUtils.classNameWithoutPackage(rClass) + "Factory").getDeclaredMethod("destroy", destructorArgs);
        } catch (Exception e) {
            if (stubDestructor == null) {
                stubDestructor = ObjectFactory.class.getDeclaredMethod("stubDestructor", new Class[]{Role.class});
            }
        }
        return stubDestructor;
    }


    static void stubDestructor(Role role) {
        // do nothing.
    }


    private static Method lookupConstructorMethod(Class protoClass, Class rClass) {
        constructorArgs[0] = protoClass;
        constructorArgs[1] = rClass;
        final String className = getFactoryPackage() + ClassUtils.classNameWithoutPackage(rClass) + "Factory";
        try {
            return Class.forName(className).getDeclaredMethod("init", constructorArgs);
        } catch (Exception e) {
            throw new RuntimeException("Init method not found:" + className + "." + "init(" + ClassUtils.classNameWithoutPackage(protoClass) + "," + ClassUtils.classNameWithoutPackage(rClass) + ")");
        }
    }


    /**
     * Creates new object taking info from specified prototype, calls factory 'init' method
     * an every role.
     * User  (mudlib developer) must provide 'init(RolePrototype, Role)' method in factory
     * packages for every Role.
     * @return new and fully initialized object
     * @throws Exception
     */
    public static Role createObject(Prototype proto) throws Exception {
        Role obj = null;
        for (Iterator it = proto.rolesIterator(); it.hasNext();) {
            final Prototype p = (Prototype) it.next();
            final Class prototypedRoleClass = p.getPrototypedRoleClass();
            if (prototypedRoleClass == null) {
                continue;
            }
            if (obj == null) {
                obj = ObjectFactory.createRaw(prototypedRoleClass);
            } else {
                final Role role = obj.getRole(prototypedRoleClass);
                obj = role == null ? obj.addRole(prototypedRoleClass) : role;
            }

            Method m = (Method) roleConstructors.get(prototypedRoleClass);
            if (m == null) {
                m = lookupConstructorMethod(p.getClass(), prototypedRoleClass);
                roleConstructors.put(prototypedRoleClass, m);
            }
            constructorParams[0] = p;
            constructorParams[1] = obj;
            Log.debug("GlobalFactory.create.invoke:" + m + " params: " + p.getClass() + ", " + obj.getClass());
            m.invoke(null, constructorParams);
        }
        return obj;
    }

    private static String getFactoryPackage() {
        return Codebase.OBJECT_FACTORY_PACKAGE_PREFIX;
    }

    /**
     * Creates  object by specified role class. Also adds all required superroles to object
     * Does not initialize any fields.
     * WARN: very low level method, use prototypes where it's possible to create complex objects
     * @param roleClass
     * @throws Exception on Sundays
     */
    public static Role createRaw(final Class roleClass) throws Exception {
        if (roleClass == null) {
            throw new NullPointerException("role class is null");
        }
        final PainDB db = Codebase.getDB();
        DbTransaction t = new DbTransaction() {
            public Object execute(Object[] params) throws Exception {
                final Root fco = new Root(db);
                return fco._addRole(roleClass);
            }
        };
        return (Role) db.execute(t);
    }

    /**
     * codebase internal-use method
     */
    public static void __cleanCaches() {
        roleConstructors.clear();
        roleDestructors.clear();
    }

}