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(); } }