package net.sourceforge.pain.data; import net.sourceforge.pain.db.*; import net.sourceforge.pain.util.*; import java.util.*; import java.lang.reflect.*; /** * todo: doc */ public final class Root extends DbObject implements LogicalObject { /** persistent schema: */ protected static final int ROLES = 0; protected static final int NFIELDS = 1; private static final Class[] roleParameterTypes = new Class[]{PainDB.class}; private final Object[] initargs = new Object[1]; /** used by db during startup*/ public Root() { } /** used to create new object*/ Root(PainDB db) { super(db); } public boolean is(Class typeClass) { return getRole(typeClass) != null; } /** There will many different types written by different coders but type creation is very specific operation - its create persistent image and identity in db in creation time (in factory) and any exception in subclass factory can lead to mem-leak in db so we need to have control on type creation in only one place to avoid bugs. Here this place! */ public final Role addRole(final Class roleClass) throws Exception { if (roleClass == null) { throw new NullPointerException("role class is null"); } final PainDB db = getDB(); DbTransaction t = new DbTransaction() { public Object execute(Object[] params) throws Exception { return _addRole(roleClass); // will check if role already exists } }; return (Role) db.execute(t); } public Role getRole(Class roleClass) { return (Role) getIntKeyMap(ROLES).get(roleClassToRoleId(roleClass)); } /** package internal use only if role already exists this method do nothing and returns it */ Role _addRole(Class roleClass) throws Exception { Log.debug("Root: _addRole:" + roleClass); int roleId = roleClassToRoleId(roleClass); Role role; if (roleId != -1 && (role = (Role) getIntKeyMap(ROLES).get(roleId)) != null) { // this item was already added with different hierarchy return role; } initargs[0] = getDB(); try { role = (Role) roleClass.getDeclaredConstructor(roleParameterTypes).newInstance(initargs); } catch (InvocationTargetException e) { Log.error(e); throw (Exception) e.getCause(); } role.init(this); Class superRoles[] = role.getSuperroles(); if (superRoles.length > 0) { for (int i = 0; i < superRoles.length; i++) { Role superRole = _addRole(superRoles[i]); superRole.incNSubroles(); } } if (roleId == -1) { roleId = role.getRoleClassId(); } // Log.debug("Adding Role:"+roleClass+" roleId:"+roleId); getIntKeyMap(ROLES).put(roleId, role); return role; } public void removeRole(Class roleClass) throws Exception { Role role = (Role) getIntKeyMap(ROLES).get(roleClassToRoleId(roleClass)); removeRole(role); } void removeRole(Role role) { if (role != null) { _removeRole(role); } } private int roleClassToRoleId(Class roleClass) { if (roleClass == null) { throw new NullPointerException("Role class is null!"); } DbClass c = getDB().getDbClass(roleClass); if (c == null) { //no object with this class was created in db so we do not have it's schema in db //todo: something more exciting here return -1; } return ((DbOid) c.getOid()).getIndexId(); // here we use knoweledge abouf paindb internals, (about indexIds), for out package internal needs } private void _removeRole(Role role) { if (role.hasSubroles()) { throw new RuntimeException("cant remove type:" + role.getClass().getName() + "!, Active subtypes found!"); } Class[] superRoles = role.getSuperroles(); for (int i = 0; i < superRoles.length; i++) { Role superRole = getRole(superRoles[i]); superRole.decNSubroles(); } role._nullRoot(); role._delete(); if (getIntKeyMap(ROLES).isEmpty()) { this.delete(); } } protected final void _delete() { Map roles = getIntKeyMap(ROLES); if (!roles.isEmpty()) { for (Iterator it = getIntKeyMap(ROLES).values().iterator(); it.hasNext();) { Role role = (Role) it.next(); it.remove(); role._delete(); } } super.delete(); } public final void delete() { _delete(); } public final DbClassSchema provideSchema() { byte types[] = new byte[NFIELDS]; String names[] = new String[NFIELDS]; types[ROLES] = DbType.INT_KEY_MAP; names[ROLES] = "types"; return new DbClassSchema(types, names); } Collection getRoles() { return getIntKeyMap(ROLES).values(); } public boolean sameObjectAs(LogicalObject obj) { if (obj == this) { return true; } if (obj.getClass() == Root.class) { return false; } return this == ((Role) obj).getRoot(); } public Iterator rolesIterator() { return new RolesIterator(); } private final class RolesIterator implements Iterator { private final Iterator it; Role r; RolesIterator() { it = getRoles().iterator(); } public void remove() { it.remove(); try { _removeRole(r); } catch (Exception e) { throw new RuntimeException(e); } } public boolean hasNext() { if (isDeleted()) { return false; } return it.hasNext(); } public Object next() { r = (Role) it.next(); return r; } } }