package org.eclipse.skalli.services.permit;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
/**
* Sorted set-like collection of {@link Permit permits}.
*
* This collection behaves similar to a {@link SortedSet}, i.e. it sorts entries
* according to {@link Permit#compareTo(Permit)} and it contains no duplicate entries,
* but its {@link #add(Permit)} and {@link #addAll(Collection)} methods do not
* fulfill the usual contract for a set: A {@link Permit permit} that equals an
* already existing permit <b>replaces</b> the existing permit. In contrast, ordinary
* sets simply ignore the new entry and keep the existing.
* <p>
* Note, all methods of this set use {@link Permit#equals(Object)} for comparing arguments
* with set entries. Two permits are equal when they have the same
* {@link Permit#getAction() action} and {@link Permit#getPath() path}
* regardless of their {@link Permit#getLevel() levels}.
*/
public class PermitSet implements Collection<Permit>, Iterable<Permit> {
private TreeMap<Permit,Permit> permits = new TreeMap<Permit,Permit>();
/**
* Creates an empty permit collection.
*/
public PermitSet() {
}
/**
* Creates a permit set and adds the given permits to it.
*
* @param c a collection of permits.
*/
public PermitSet(Collection<? extends Permit> c) {
addAll(c);
}
/**
* Creates a permit set and adds the given permits to it.
*
* @param permits a list of permits.
*/
public PermitSet(Permit... permits) {
if (permits != null) {
for (Permit permit: permits) {
add(permit);
}
}
}
/**
* Adds the given permit to this set replacing an already present permit
* with the same {@link Permit#getAction() action} and {@link Permit#getPath() path}.
*
* @param permit the permit to add. Note, this method accepts <code>null</code> arguments,
* but simply ignores them.
*
* @return <code>true</code> if the given permit actually replaced a previously
* added permit, or is an entirely new permit.
*/
@Override
public boolean add(Permit permit) {
if (permit == null) {
return false;
}
Permit oldPermit = permits.remove(permit);
permits.put(permit, permit);
return oldPermit == null || !isSame(oldPermit, permit);
}
private boolean isSame(Permit oldPermit, Permit permit) {
return oldPermit.getLevel() == permit.getLevel()
&& oldPermit.getPath().equals(permit.getPath())
&& oldPermit.getAction().equals(permit.getAction());
}
/**
* Adds the given permits to this set replacing already present permits
* with same {@link Permit#getAction() action} and {@link Permit#getPath() path}
* attributes.
*
* @param c the permits to add to this set. Note, this method accepts <code>null</code>
* arguments, but simply ignores them.
*
* @return <tt>true</tt> if permits actually have been added to this set.
*/
@Override
public boolean addAll(Collection<? extends Permit> c) {
if (c == null || c.isEmpty()) {
return false;
}
boolean modified = false;
for (Permit permit: c) {
if (add(permit)) {
modified = true;
}
}
return modified;
}
/**
* Adds the given permits to this set replacing already
* present permits with same {@link Permit#getAction() action} and
* {@link Permit#getPath() path} attributes.
*
* @param permitSet the permits to add to this set. Note, this method
* accepts <code>null</code> arguments, but simply ignores them.
*
* @return <tt>true</tt> if permits actually have been added to this set.
*/
public boolean addAll(PermitSet permitSet) {
if (permitSet == null || permitSet.isEmpty()) {
return false;
}
return addAll(permitSet.permits());
}
@Override
public int size() {
return permits.size();
}
@Override
public boolean isEmpty() {
return permits.isEmpty();
}
@Override
public Iterator<Permit> iterator() {
return permits.keySet().iterator();
}
/**
* Returns the permits stored in this set as unmodifiable set of permits.
*/
public Set<Permit> permits() {
return Collections.unmodifiableSet(permits.keySet());
}
@Override
public Object[] toArray() {
return permits.keySet().toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return permits.keySet().toArray(a);
}
/**
* Removes a permit with the same {@link Permit#getAction() action} and
* {@link Permit#getPath() path} as the given permit from this set.
*
* @param o the permit to remove. Note, this method accepts <code>null</code> arguments,
* but simply ignores them.
*
* @return <code>true</code> if any permit actually has been removed
* from this set.
*/
@Override
public boolean remove(Object o) {
boolean modified = false;
if (o instanceof Permit) {
Permit permit = (Permit)o;
modified = permits.remove(permit) != null;
}
return modified;
}
/**
* Removes all permits with same {@link Permit#getAction() actions} and
* {@link Permit#getPath() paths} as the given permits from this set.
*
* @param c the collection of permits to remove. Note, this method accepts
* <code>null</code> arguments, but simply ignores them.
*
* @return <code>true</code> if any permit actually has been removed
* from this set.
*/
@Override
public boolean removeAll(Collection<?> c) {
if (c == null || c.isEmpty()) {
return false;
}
boolean modified = false;
for (Object o : c) {
modified |= remove(o);
}
return modified;
}
/**
* Checks if this set contains a permit with the same {@link Permit#getAction() action}
* and {@link Permit#getPath() path} as the given permit.
*
* @param o the permit to check.
*
* @return <code>true</code> if this set contains a matching permit.
*/
@Override
public boolean contains(Object o) {
return o != null ? permits.containsKey(o) : false;
}
/**
* Checks if all permits in the given collection are contained in this set
* by comparing corresponding {@link Permit#getAction() actions} and {@link Permit#getPath() paths}.
*
* @param c a collection of permits.
*
* @return <code>true</code> if all permits of the given collection are contained
* in this set.
*/
@Override
public boolean containsAll(Collection<?> c) {
return c != null ? permits.keySet().containsAll(c) : false;
}
/**
* This method always throws {@link UnsupportedOperationException}.
*/
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
permits.clear();
}
}