/*
* Copyright (c) 2009, SQL Power Group Inc.
*
* This file is part of Wabit.
*
* Wabit is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Wabit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ca.sqlpower.enterprise.client;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.security.GrantedAuthority;
import ca.sqlpower.object.AbstractSPObject;
import ca.sqlpower.object.SPObject;
import ca.sqlpower.object.annotation.Accessor;
import ca.sqlpower.object.annotation.Constructor;
import ca.sqlpower.object.annotation.ConstructorParameter;
import ca.sqlpower.object.annotation.NonProperty;
import ca.sqlpower.object.annotation.Transient;
public class Group extends AbstractSPObject implements GrantedAuthority {
/**
* Defines an absolute ordering of the child types of this class.
*
* IMPORTANT!: When changing this, ensure you maintain the order specified by {@link #getChildren()}
*/
public static List<Class<? extends SPObject>> allowedChildTypes =
Collections.unmodifiableList(new ArrayList<Class<? extends SPObject>>(
Arrays.asList(GroupMember.class, Grant.class)));
/**
* FIXME This enum defines the {@link SPObject} child classes a
* {@link Group} takes as well as the ordinal order of these child classes
* such that the class going before does not depend on the class that goes
* after.
*/
public enum SPObjectOrder {
GROUP_MEMBER(GroupMember.class),
GRANT(Grant.class);
/**
* @see #getSuperChildClass()
*/
private final Class<? extends SPObject> superChildClass;
/**
* @see #getChildClasses()
*/
private final Set<Class<? extends SPObject>> classes;
/**
* Creates a new {@link SPObjectOrder},
*
* @param superChildClass
* The highest {@link SPObject} class that the
* {@link SPObject#childPositionOffset(Class)} method looks
* at to determine the index.
* @param classes
* The list of child {@link SPObject} class varargs which
* share the same ordering in the list of children. These
* classes must be extending/implementing
* {@link #superChildClass}.
*/
private SPObjectOrder(Class<? extends SPObject> superChildClass, Class<? extends SPObject>... classes) {
this.superChildClass = superChildClass;
this.classes = new HashSet<Class<? extends SPObject>>(Arrays.asList(classes));
}
/**
* Returns the highest {@link SPObject} class that the
* {@link SPObject#childPositionOffset(Class)} method looks at to
* determine the index.
*/
public Class<? extends SPObject> getSuperChildClass() {
return superChildClass;
}
/**
* Returns the {@link Set} of {@link SPObject} classes that share the
* same ordering in the list of children. These classes must either
* extend/implement from the same class type given by
* {@link SPObjectOrder#getSuperChildClass()}.
*/
public Set<Class<? extends SPObject>> getChildClasses() {
return Collections.unmodifiableSet(classes);
}
public static SPObjectOrder getOrderBySimpleClassName(String name) {
for (SPObjectOrder order : values()) {
if (order.getSuperChildClass().getSimpleName().equals(name)) {
return order;
} else {
for (Class<? extends SPObject> childClass : order.getChildClasses()) {
if (childClass.getSimpleName().equals(name)) {
return order;
}
}
}
}
throw new IllegalArgumentException("The " + SPObject.class.getSimpleName() +
" class \"" + name + "\" does not exist or is not a child type " +
"of " + Group.class.getSimpleName() + ".");
}
}
private final List<Grant> grants = new ArrayList<Grant>();
private final List<GroupMember> members = new ArrayList<GroupMember>();
@Constructor
public Group(@ConstructorParameter(propertyName = "name") String name) {
setName(name);
}
@Override
protected boolean removeChildImpl(SPObject child) {
if (child instanceof Grant) {
return removeGrant((Grant)child);
} else if (child instanceof GroupMember) {
return removeMember((GroupMember)child);
} else {
return false;
}
}
@NonProperty
public List<SPObject> getChildren() {
List<SPObject> children = new ArrayList<SPObject>();
children.addAll(this.members);
children.addAll(this.grants);
return children;
}
@NonProperty
@SuppressWarnings("unchecked")
@Override
public <T extends SPObject> List<T> getChildren(Class<T> type) {
List<T> children = new ArrayList<T>();
if (type.isAssignableFrom(Grant.class)) {
children.addAll((List<? extends T>) grants);
}
if (type.isAssignableFrom(GroupMember.class)) {
children.addAll((List<? extends T>) members);
}
return children;
}
@NonProperty
public List<SPObject> getDependencies() {
return Collections.emptyList();
}
public void removeDependency(SPObject dependency) {
// no-op
}
public void addGrant(Grant grant) {
addGrant(grant, grants.size());
}
public void addGrant(Grant grant, int index) {
this.grants.add(index, grant);
grant.setParent(this);
fireChildAdded(Grant.class, grant, index);
}
public boolean removeGrant(Grant grant) {
boolean wasRemoved = false;
if (this.grants.contains(grant)) {
int index = this.grants.indexOf(grant);
wasRemoved = this.grants.remove(grant);
fireChildRemoved(Grant.class, grant, index);
grant.setParent(null);
}
return wasRemoved;
}
public void addMember(GroupMember member, int index) {
this.members.add(index, member);
member.setParent(this);
fireChildAdded(GroupMember.class, member, index);
}
public void addMember(GroupMember member) {
addMember(member, members.size());
}
public void addUser(User user) {
addMember(new GroupMember(user));
}
public boolean removeMember(GroupMember member) {
boolean wasRemoved = false;
if (this.members.contains(member)) {
int index = this.members.indexOf(member);
wasRemoved = this.members.remove(member);
fireChildRemoved(GroupMember.class, member, index);
member.setParent(null);
}
return wasRemoved;
}
public boolean removeUser(User user) {
for (GroupMember member : members) {
if (member.getUser().getUUID().equals(user.getUUID())) {
return removeMember(member);
}
}
return false;
}
@Transient @Accessor
public String getAuthority() {
return super.getName();
}
public int compareTo(Object o) {
assert o instanceof GrantedAuthority;
return ((GrantedAuthority)o).getAuthority().compareTo(this.getAuthority());
}
@Override
public String toString() {
return super.getName();
}
@Override
protected void addChildImpl(SPObject child, int index) {
if (child instanceof GroupMember) {
addMember((GroupMember) child, index);
} else if (child instanceof Grant) {
addGrant((Grant) child, index);
} else {
throw new IllegalArgumentException("Group does not accept this child: " + child);
}
}
@NonProperty
public List<Class<? extends SPObject>> getAllowedChildTypes() {
List<Class<? extends SPObject>> childTypes = new ArrayList<Class<? extends SPObject>>();
childTypes.add(GroupMember.class);
childTypes.add(Grant.class);
return childTypes;
}
/**
* Marking this class as not serializable. The {@link GrantedAuthority}
* interface extends from {@link Serializable}, which makes this class also
* {@link Serializable}. However, our object model does not use
* serialization.
*
* Followed the article written by Sun at
* http://java.sun.com/developer/technicalArticles/ALT/serialization/
*
* @param ois
* @throws ClassNotFoundException
* @throws IOException
*/
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
throw new NotSerializableException();
}
/**
* Marking this class as not serializable. The {@link GrantedAuthority}
* interface extends from {@link Serializable}, which makes this class also
* {@link Serializable}. However, our object model does not use
* serialization.
*
* Followed the article written by Sun at
* http://java.sun.com/developer/technicalArticles/ALT/serialization/
*
* @param ois
* @throws IOException
*/
private void writeObject(ObjectOutputStream ois) throws IOException {
throw new NotSerializableException();
}
}