/
area/
classes/net/sourceforge/pain/logic/
classes/net/sourceforge/pain/logic/event/
classes/net/sourceforge/pain/logic/fn/util/
classes/net/sourceforge/pain/network/console/
classes/net/sourceforge/pain/plugin/
classes/net/sourceforge/pain/plugin/reset/
classes/net/sourceforge/pain/plugin/shutdown/
classes/net/sourceforge/pain/plugin/social/
classest/net/sourceforge/pain/db/data/
doc/
doc/paindb/resources/
src/net/sourceforge/pain/logic/
src/net/sourceforge/pain/logic/event/
src/net/sourceforge/pain/logic/fn/util/
src/net/sourceforge/pain/network/console/
src/net/sourceforge/pain/network/console/telnet/
src/net/sourceforge/pain/plugin/
src/net/sourceforge/pain/plugin/command/
src/net/sourceforge/pain/plugin/reset/
src/net/sourceforge/pain/plugin/shutdown/
src/net/sourceforge/pain/plugin/social/
src/net/sourceforge/pain/util/
tests/
tests/net/sourceforge/pain/db/data/
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;
		}
	}


}