Role.java

/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.util.RoleConstants;

/**
 * A Role is just an aggregater of {@link Privilege}s. {@link User}s contain a number of roles
 * (Users DO NOT contain any privileges directly) Roles can be grouped by inheriting other roles. If
 * a user is given Role A that inherits from Role B, the user has all rights/abilities for both Role
 * A's privileges and for Role B's privileges.
 * 
 * @see Privilege
 */
public class Role extends BaseOpenmrsMetadata implements java.io.Serializable {
	
	public static final long serialVersionUID = 1234233L;
	
	private static final Log log = LogFactory.getLog(Role.class);
	
	// Fields
	
	private String role;
	
	private Set<Privilege> privileges;
	
	private Set<Role> inheritedRoles;
	
	private Set<Role> childRoles;
	
	// Constructors
	
	/** default constructor */
	public Role() {
	}
	
	/** constructor with id */
	public Role(String role) {
		this.role = role;
	}
	
	/** constructor with all database required properties */
	public Role(String role, String description) {
		this.role = role;
		setDescription(description);
	}
	
	/**
	 * @return Returns the privileges.
	 */
	public Set<Privilege> getPrivileges() {
		return privileges;
	}
	
	/**
	 * @param privileges The privileges to set.
	 */
	public void setPrivileges(Set<Privilege> privileges) {
		this.privileges = privileges;
	}
	
	public String getName() {
		return this.getRole();
	}
	
	/**
	 * Adds the given Privilege to the list of privileges
	 * 
	 * @param privilege Privilege to add
	 */
	public void addPrivilege(Privilege privilege) {
		if (privileges == null)
			privileges = new HashSet<Privilege>();
		if (privilege != null && !containsPrivilege(privileges, privilege.getPrivilege()))
			privileges.add(privilege);
	}
	
	private boolean containsPrivilege(Collection<Privilege> privileges, String privilegeName) {
		for (Privilege privilege : privileges) {
			if (privilege.getPrivilege().equals(privilegeName)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Removes the given Privilege from the list of privileges
	 * 
	 * @param privilege Privilege to remove
	 */
	public void removePrivilege(Privilege privilege) {
		if (privileges != null)
			privileges.remove(privilege);
	}
	
	/**
	 * @return Returns the role.
	 */
	public String getRole() {
		return role;
	}
	
	/**
	 * @param role The role to set.
	 */
	public void setRole(String role) {
		this.role = role;
	}
	
	/**
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return this.role;
	}
	
	/**
	 * Looks for the given <code>privilegeName</code> privilege name in this roles privileges. This
	 * method does not recurse through the inherited roles
	 * 
	 * @param privilegeName String name of a privilege
	 * @return true/false whether this role has the given privilege
	 * @should return false if not found
	 * @should return true if found
	 * @should not fail given null parameter
	 * @should return true for any privilegeName if super user
	 */
	public boolean hasPrivilege(String privilegeName) {
		
		if (RoleConstants.SUPERUSER.equals(this.role))
			return true;
		
		if (privileges != null) {
			for (Privilege p : privileges) {
				if (p.getPrivilege().equals(privilegeName))
					return true;
			}
		}
		
		return false;
	}
	
	/**
	 * @return Returns the inheritedRoles.
	 */
	public Set<Role> getInheritedRoles() {
		if (inheritedRoles == null)
			inheritedRoles = new HashSet<Role>();
		return inheritedRoles;
	}
	
	/**
	 * @param inheritedRoles The inheritedRoles to set.
	 */
	public void setInheritedRoles(Set<Role> inheritedRoles) {
		this.inheritedRoles = inheritedRoles;
	}
	
	/**
	 * Convenience method to test whether or not this role extends/ inherits from any other roles
	 * 
	 * @return true/false whether this role inherits from other roles
	 */
	public boolean inheritsRoles() {
		return (getInheritedRoles() != null && getInheritedRoles().size() > 0);
	}
	
	/**
	 * Recursive (if need be) method to return all parent roles of this role
	 * 
	 * @should only return parent roles
	 * @return Return this role's parents
	 */
	public Set<Role> getAllParentRoles() {
		Set<Role> parents = new HashSet<Role>();
		if (inheritsRoles()) {
			parents.addAll(this.recurseOverParents(parents));
		}
		return parents;
	}
	
	/**
	 * Returns the full set of roles be looping over inherited roles. Duplicate roles are dropped.
	 * 
	 * @param total Roles already looped over
	 * @return Set<Role> Current and inherited roles
	 */
	public Set<Role> recurseOverParents(final Set<Role> total) {
		if (!this.inheritsRoles())
			return total;
		
		Set<Role> allRoles = new HashSet<Role>(); // total roles (parents + children)
		Set<Role> myRoles = new HashSet<Role>(); // new roles
		allRoles.addAll(total);
		
		myRoles.addAll(this.getInheritedRoles());
		myRoles.removeAll(total);
		myRoles.remove(this); // prevent an obvious looping problem
		allRoles.addAll(myRoles);
		
		for (Role r : myRoles) {
			if (r.inheritsRoles())
				allRoles.addAll(r.recurseOverParents(allRoles));
		}
		
		if (log.isDebugEnabled())
			log.debug("Total roles: " + allRoles);
		
		return allRoles;
	}
	
	/**
	 * @since 1.5
	 * @see org.openmrs.OpenmrsObject#getId()
	 */
	public Integer getId() {
		throw new UnsupportedOperationException();
	}
	
	/**
	 * @since 1.5
	 * @see org.openmrs.OpenmrsObject#setId(java.lang.Integer)
	 */
	public void setId(Integer id) {
		throw new UnsupportedOperationException();
	}
	
	/**
	 * @since 1.9
	 * @return immediate children
	 */
	public Set<Role> getChildRoles() {
		if (childRoles == null) {
			childRoles = new HashSet<Role>();
		}
		return childRoles;
	}
	
	/**
	 * @since 1.9
	 * @param childRoles the immediate children to set
	 */
	public void setChildRoles(Set<Role> childRoles) {
		this.childRoles = childRoles;
	}
	
	/**
	 * Convenience method to test whether or not this role is a parent of another role
	 * 
	 * @return true/false whether this role is a parent of another role
	 * @since 1.9
	 */
	public boolean hasChildRoles() {
		return (getChildRoles() != null && getChildRoles().size() > 0);
	}
	
	/**
	 * Recursive (if need be) method to return all child roles of this role
	 * 
	 * @should only return child roles
	 * @return this role's children
	 * @since 1.9
	 */
	public Set<Role> getAllChildRoles() {
		Set<Role> children = new HashSet<Role>();
		if (hasChildRoles()) {
			children.addAll(this.recurseOverChildren(children));
		}
		return children;
	}
	
	/**
	 * Returns the full set of child roles be looping over children. Duplicate roles are dropped.
	 * 
	 * @param total Roles already looped over
	 * @return Set<Role> Current and child roles
	 * @since 1.9
	 */
	public Set<Role> recurseOverChildren(final Set<Role> total) {
		if (!this.hasChildRoles()) {
			return total;
		}
		
		Set<Role> allRoles = new HashSet<Role>(); // total roles (parents + children)
		Set<Role> myRoles = new HashSet<Role>(); // new roles
		allRoles.addAll(total);
		
		myRoles.addAll(this.getChildRoles());
		myRoles.removeAll(total);
		myRoles.remove(this); // prevent an obvious looping problem
		allRoles.addAll(myRoles);
		
		for (Role r : myRoles) {
			if (r.hasChildRoles()) {
				allRoles.addAll(r.recurseOverChildren(allRoles));
			}
		}
		
		if (log.isDebugEnabled()) {
			log.debug("Total roles: " + allRoles);
		}
		
		return allRoles;
	}
}