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 onDelete() throws Exception {
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();
}
}
}
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;
}
}
}