/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.nio.file.attribute;
import java.util.*;
/**
* An entry in an access control list (ACL).
*
*
The ACL entry represented by this class is based on the ACL model
* specified in RFC 3530:
* Network File System (NFS) version 4 Protocol. Each entry has four
* components as follows:
*
*
* The {@link #type() type} component determines if the entry
* grants or denies access.
*
* The {@link #principal() principal} component, sometimes called the
* "who" component, is a {@link UserPrincipal} corresponding to the identity
* that the entry grants or denies access
*
*
* The {@link #permissions permissions} component is a set of
* {@link AclEntryPermission permissions}
*
*
* The {@link #flags flags} component is a set of {@link AclEntryFlag
* flags} to indicate how entries are inherited and propagated
*
*
* ACL entries are created using an associated {@link Builder} object by
* invoking its {@link Builder#build build} method.
*
*
ACL entries are immutable and are safe for use by multiple concurrent
* threads.
*
* @since 1.7
*/
public final class AclEntry {
private final AclEntryType type;
private final UserPrincipal who;
private final Set perms;
private final Set flags;
// cached hash code
private volatile int hash;
// private constructor
private AclEntry(AclEntryType type,
UserPrincipal who,
Set perms,
Set flags)
{
this.type = type;
this.who = who;
this.perms = perms;
this.flags = flags;
}
/**
* A builder of {@link AclEntry} objects.
*
* A {@code Builder} object is obtained by invoking one of the {@link
* AclEntry#newBuilder newBuilder} methods defined by the {@code AclEntry}
* class.
*
*
Builder objects are mutable and are not safe for use by multiple
* concurrent threads without appropriate synchronization.
*
* @since 1.7
*/
public static final class Builder {
private AclEntryType type;
private UserPrincipal who;
private Set perms;
private Set flags;
private Builder(AclEntryType type,
UserPrincipal who,
Set perms,
Set flags)
{
assert perms != null && flags != null;
this.type = type;
this.who = who;
this.perms = perms;
this.flags = flags;
}
/**
* Constructs an {@link AclEntry} from the components of this builder.
* The type and who components are required to have been set in order
* to construct an {@code AclEntry}.
*
* @return a new ACL entry
*
* @throws IllegalStateException
* if the type or who component have not been set
*/
public AclEntry build() {
if (type == null)
throw new IllegalStateException("Missing type component");
if (who == null)
throw new IllegalStateException("Missing who component");
return new AclEntry(type, who, perms, flags);
}
/**
* Sets the type component of this builder.
*
* @return this builder
*/
public Builder setType(AclEntryType type) {
if (type == null)
throw new NullPointerException();
this.type = type;
return this;
}
/**
* Sets the principal component of this builder.
*
* @return this builder
*/
public Builder setPrincipal(UserPrincipal who) {
if (who == null)
throw new NullPointerException();
this.who = who;
return this;
}
// check set only contains elements of the given type
private static void checkSet(Set> set, Class> type) {
for (Object e: set) {
if (e == null)
throw new NullPointerException();
type.cast(e);
}
}
/**
* Sets the permissions component of this builder. On return, the
* permissions component of this builder is a copy of the given set.
*
* @return this builder
*
* @throws ClassCastException
* if the set contains elements that are not of type {@code
* AclEntryPermission}
*/
public Builder setPermissions(Set perms) {
// copy and check for erroneous elements
perms = EnumSet.copyOf(perms);
checkSet(perms, AclEntryPermission.class);
this.perms = perms;
return this;
}
/**
* Sets the permissions component of this builder. On return, the
* permissions component of this builder is a copy of the permissions in
* the given array.
*
* @return this builder
*/
public Builder setPermissions(AclEntryPermission... perms) {
Set set = EnumSet.noneOf(AclEntryPermission.class);
// copy and check for null elements
for (AclEntryPermission p: perms) {
if (p == null)
throw new NullPointerException();
set.add(p);
}
this.perms = set;
return this;
}
/**
* Sets the flags component of this builder. On return, the flags
* component of this builder is a copy of the given set.
*
* @return this builder
*
* @throws ClassCastException
* if the set contains elements that are not of type {@code
* AclEntryFlag}
*/
public Builder setFlags(Set flags) {
// copy and check for erroneous elements
flags = EnumSet.copyOf(flags);
checkSet(flags, AclEntryFlag.class);
this.flags = flags;
return this;
}
/**
* Sets the flags component of this builder. On return, the flags
* component of this builder is a copy of the flags in the given
* array.
*
* @return this builder
*/
public Builder setFlags(AclEntryFlag... flags) {
Set set = EnumSet.noneOf(AclEntryFlag.class);
// copy and check for null elements
for (AclEntryFlag f: flags) {
if (f == null)
throw new NullPointerException();
set.add(f);
}
this.flags = set;
return this;
}
}
/**
* Constructs a new builder. The initial value of the type and who
* components is {@code null}. The initial value of the permissions and
* flags components is the empty set.
*
* @return a new builder
*/
public static Builder newBuilder() {
Set perms = Collections.emptySet();
Set flags = Collections.emptySet();
return new Builder(null, null, perms, flags);
}
/**
* Constructs a new builder with the components of an existing ACL entry.
*
* @param entry
* an ACL entry
*
* @return a new builder
*/
public static Builder newBuilder(AclEntry entry) {
return new Builder(entry.type, entry.who, entry.perms, entry.flags);
}
/**
* Returns the ACL entry type.
*/
public AclEntryType type() {
return type;
}
/**
* Returns the principal component.
*/
public UserPrincipal principal() {
return who;
}
/**
* Returns a copy of the permissions component.
*
* The returned set is a modifiable copy of the permissions.
*/
public Set permissions() {
return new HashSet(perms);
}
/**
* Returns a copy of the flags component.
*
* The returned set is a modifiable copy of the flags.
*/
public Set flags() {
return new HashSet(flags);
}
/**
* Compares the specified object with this ACL entry for equality.
*
* If the given object is not an {@code AclEntry} then this method
* immediately returns {@code false}.
*
*
For two ACL entries to be considered equals requires that they are
* both the same type, their who components are equal, their permissions
* components are equal, and their flags components are equal.
*
*
This method satisfies the general contract of the {@link
* java.lang.Object#equals(Object) Object.equals} method.
*
* @param ob the object to which this object is to be compared
*
* @return {@code true} if, and only if, the given object is an AclEntry that
* is identical to this AclEntry
*/
@Override
public boolean equals(Object ob) {
if (ob == this)
return true;
if (ob == null || !(ob instanceof AclEntry))
return false;
AclEntry other = (AclEntry)ob;
if (this.type != other.type)
return false;
if (!this.who.equals(other.who))
return false;
if (!this.perms.equals(other.perms))
return false;
if (!this.flags.equals(other.flags))
return false;
return true;
}
private static int hash(int h, Object o) {
return h * 127 + o.hashCode();
}
/**
* Returns the hash-code value for this ACL entry.
*
* This method satisfies the general contract of the {@link
* Object#hashCode} method.
*/
@Override
public int hashCode() {
// return cached hash if available
if (hash != 0)
return hash;
int h = type.hashCode();
h = hash(h, who);
h = hash(h, perms);
h = hash(h, flags);
hash = h;
return hash;
}
/**
* Returns the string representation of this ACL entry.
*
* @return the string representation of this entry
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
// who
sb.append(who.getName());
sb.append(':');
// permissions
for (AclEntryPermission perm: perms) {
sb.append(perm.name());
sb.append('/');
}
sb.setLength(sb.length()-1); // drop final slash
sb.append(':');
// flags
if (!flags.isEmpty()) {
for (AclEntryFlag flag: flags) {
sb.append(flag.name());
sb.append('/');
}
sb.setLength(sb.length()-1); // drop final slash
sb.append(':');
}
// type
sb.append(type.name());
return sb.toString();
}
}