1 /*
   2  * Copyright (c) 2007, 2009, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.nio.file.attribute;
  27 
  28 import java.util.*;
  29 
  30 /**
  31  * An entry in an access control list (ACL).
  32  *
  33  * <p> The ACL entry represented by this class is based on the ACL model
  34  * specified in <a href="http://www.ietf.org/rfc/rfc3530.txt"><i>RFC&nbsp;3530:
  35  * Network File System (NFS) version 4 Protocol</i></a>. Each entry has four
  36  * components as follows:
  37  *
  38  * <ol>
  39  *    <li><p> The {@link #type() type} component determines if the entry
  40  *    grants or denies access. </p></li>
  41  *
  42  *    <li><p> The {@link #principal() principal} component, sometimes called the
  43  *    "who" component, is a {@link UserPrincipal} corresponding to the identity
  44  *    that the entry grants or denies access
  45  *    </p></li>
  46  *
  47  *    <li><p> The {@link #permissions permissions} component is a set of
  48  *    {@link AclEntryPermission permissions}
  49  *    </p></li>
  50  *
  51  *    <li><p> The {@link #flags flags} component is a set of {@link AclEntryFlag
  52  *    flags} to indicate how entries are inherited and propagated </p></li>
  53  * </ol>
  54  *
  55  * <p> ACL entries are created using an associated {@link Builder} object by
  56  * invoking its {@link Builder#build build} method.
  57  *
  58  * <p> ACL entries are immutable and are safe for use by multiple concurrent
  59  * threads.
  60  *
  61  * @since 1.7
  62  */
  63 
  64 public final class AclEntry {
  65 
  66     private final AclEntryType type;
  67     private final UserPrincipal who;
  68     private final Set<AclEntryPermission> perms;
  69     private final Set<AclEntryFlag> flags;
  70 
  71     // cached hash code
  72     private volatile int hash;
  73 
  74     // private constructor
  75     private AclEntry(AclEntryType type,
  76                      UserPrincipal who,
  77                      Set<AclEntryPermission> perms,
  78                      Set<AclEntryFlag> flags)
  79     {
  80         this.type = type;
  81         this.who = who;
  82         this.perms = perms;
  83         this.flags = flags;
  84     }
  85 
  86     /**
  87      * A builder of {@link AclEntry} objects.
  88      *
  89      * <p> A {@code Builder} object is obtained by invoking one of the {@link
  90      * AclEntry#newBuilder newBuilder} methods defined by the {@code AclEntry}
  91      * class.
  92      *
  93      * <p> Builder objects are mutable and are not safe for use by multiple
  94      * concurrent threads without appropriate synchronization.
  95      *
  96      * @since 1.7
  97      */
  98     public static final class Builder {
  99         private AclEntryType type;
 100         private UserPrincipal who;
 101         private Set<AclEntryPermission> perms;
 102         private Set<AclEntryFlag> flags;
 103 
 104         private Builder(AclEntryType type,
 105                         UserPrincipal who,
 106                         Set<AclEntryPermission> perms,
 107                         Set<AclEntryFlag> flags)
 108         {
 109             assert perms != null && flags != null;
 110             this.type = type;
 111             this.who = who;
 112             this.perms = perms;
 113             this.flags = flags;
 114         }
 115 
 116         /**
 117          * Constructs an {@link AclEntry} from the components of this builder.
 118          * The type and who components are required to have been set in order
 119          * to construct an {@code AclEntry}.
 120          *
 121          * @return  a new ACL entry
 122          *
 123          * @throws  IllegalStateException
 124          *          if the type or who component have not been set
 125          */
 126         public AclEntry build() {
 127             if (type == null)
 128                 throw new IllegalStateException("Missing type component");
 129             if (who == null)
 130                 throw new IllegalStateException("Missing who component");
 131             return new AclEntry(type, who, perms, flags);
 132         }
 133 
 134         /**
 135          * Sets the type component of this builder.
 136          *
 137          * @return  this builder
 138          */
 139         public Builder setType(AclEntryType type) {
 140             if (type == null)
 141                 throw new NullPointerException();
 142             this.type = type;
 143             return this;
 144         }
 145 
 146         /**
 147          * Sets the principal component of this builder.
 148          *
 149          * @return  this builder
 150          */
 151         public Builder setPrincipal(UserPrincipal who) {
 152             if (who == null)
 153                 throw new NullPointerException();
 154             this.who = who;
 155             return this;
 156         }
 157 
 158         // check set only contains elements of the given type
 159         private static void checkSet(Set<?> set, Class<?> type) {
 160             for (Object e: set) {
 161                 if (e == null)
 162                     throw new NullPointerException();
 163                 type.cast(e);
 164             }
 165         }
 166 
 167         /**
 168          * Sets the permissions component of this builder. On return, the
 169          * permissions component of this builder is a copy of the given set.
 170          *
 171          * @return  this builder
 172          *
 173          * @throws  ClassCastException
 174          *          if the set contains elements that are not of type {@code
 175          *          AclEntryPermission}
 176          */
 177         public Builder setPermissions(Set<AclEntryPermission> perms) {
 178             // copy and check for erroneous elements
 179             perms = EnumSet.copyOf(perms);
 180             checkSet(perms, AclEntryPermission.class);
 181             this.perms = perms;
 182             return this;
 183         }
 184 
 185         /**
 186          * Sets the permissions component of this builder. On return, the
 187          * permissions component of this builder is a copy of the permissions in
 188          * the given array.
 189          *
 190          * @return  this builder
 191          */
 192         public Builder setPermissions(AclEntryPermission... perms) {
 193             Set<AclEntryPermission> set = EnumSet.noneOf(AclEntryPermission.class);
 194             // copy and check for null elements
 195             for (AclEntryPermission p: perms) {
 196                 if (p == null)
 197                     throw new NullPointerException();
 198                 set.add(p);
 199             }
 200             this.perms = set;
 201             return this;
 202         }
 203 
 204         /**
 205          * Sets the flags component of this builder. On return, the flags
 206          * component of this builder is a copy of the given set.
 207          *
 208          * @return  this builder
 209          *
 210          * @throws  ClassCastException
 211          *          if the set contains elements that are not of type {@code
 212          *          AclEntryFlag}
 213          */
 214         public Builder setFlags(Set<AclEntryFlag> flags) {
 215             // copy and check for erroneous elements
 216             flags = EnumSet.copyOf(flags);
 217             checkSet(flags, AclEntryFlag.class);
 218             this.flags = flags;
 219             return this;
 220         }
 221 
 222         /**
 223          * Sets the flags component of this builder. On return, the flags
 224          * component of this builder is a copy of the flags in the given
 225          * array.
 226          *
 227          * @return  this builder
 228          */
 229         public Builder setFlags(AclEntryFlag... flags) {
 230             Set<AclEntryFlag> set = EnumSet.noneOf(AclEntryFlag.class);
 231             // copy and check for null elements
 232             for (AclEntryFlag f: flags) {
 233                 if (f == null)
 234                     throw new NullPointerException();
 235                 set.add(f);
 236             }
 237             this.flags = set;
 238             return this;
 239         }
 240     }
 241 
 242     /**
 243      * Constructs a new builder. The initial value of the type and who
 244      * components is {@code null}. The initial value of the permissions and
 245      * flags components is the empty set.
 246      *
 247      * @return  a new builder
 248      */
 249     public static Builder newBuilder() {
 250         Set<AclEntryPermission> perms = Collections.emptySet();
 251         Set<AclEntryFlag> flags = Collections.emptySet();
 252         return new Builder(null, null, perms, flags);
 253     }
 254 
 255     /**
 256      * Constructs a new builder with the components of an existing ACL entry.
 257      *
 258      * @param   entry
 259      *          an ACL entry
 260      *
 261      * @return  a new builder
 262      */
 263     public static Builder newBuilder(AclEntry entry) {
 264         return new Builder(entry.type, entry.who, entry.perms, entry.flags);
 265     }
 266 
 267     /**
 268      * Returns the ACL entry type.
 269      */
 270     public AclEntryType type() {
 271         return type;
 272     }
 273 
 274     /**
 275      * Returns the principal component.
 276      */
 277     public UserPrincipal principal() {
 278         return who;
 279     }
 280 
 281     /**
 282      * Returns a copy of the permissions component.
 283      *
 284      * <p> The returned set is a modifiable copy of the permissions.
 285      */
 286     public Set<AclEntryPermission> permissions() {
 287         return new HashSet<AclEntryPermission>(perms);
 288     }
 289 
 290     /**
 291      * Returns a copy of the flags component.
 292      *
 293      * <p> The returned set is a modifiable copy of the flags.
 294      */
 295     public Set<AclEntryFlag> flags() {
 296         return new HashSet<AclEntryFlag>(flags);
 297     }
 298 
 299     /**
 300      * Compares the specified object with this ACL entry for equality.
 301      *
 302      * <p> If the given object is not an {@code AclEntry} then this method
 303      * immediately returns {@code false}.
 304      *
 305      * <p> For two ACL entries to be considered equals requires that they are
 306      * both the same type, their who components are equal, their permissions
 307      * components are equal, and their flags components are equal.
 308      *
 309      * <p> This method satisfies the general contract of the {@link
 310      * java.lang.Object#equals(Object) Object.equals} method. </p>
 311      *
 312      * @param   ob   the object to which this object is to be compared
 313      *
 314      * @return  {@code true} if, and only if, the given object is an AclEntry that
 315      *          is identical to this AclEntry
 316      */
 317     @Override
 318     public boolean equals(Object ob) {
 319         if (ob == this)
 320             return true;
 321         if (ob == null || !(ob instanceof AclEntry))
 322             return false;
 323         AclEntry other = (AclEntry)ob;
 324         if (this.type != other.type)
 325             return false;
 326         if (!this.who.equals(other.who))
 327             return false;
 328         if (!this.perms.equals(other.perms))
 329             return false;
 330         if (!this.flags.equals(other.flags))
 331             return false;
 332         return true;
 333     }
 334 
 335     private static int hash(int h, Object o) {
 336         return h * 127 + o.hashCode();
 337     }
 338 
 339     /**
 340      * Returns the hash-code value for this ACL entry.
 341      *
 342      * <p> This method satisfies the general contract of the {@link
 343      * Object#hashCode} method.
 344      */
 345     @Override
 346     public int hashCode() {
 347         // return cached hash if available
 348         if (hash != 0)
 349             return hash;
 350         int h = type.hashCode();
 351         h = hash(h, who);
 352         h = hash(h, perms);
 353         h = hash(h, flags);
 354         hash = h;
 355         return hash;
 356     }
 357 
 358     /**
 359      * Returns the string representation of this ACL entry.
 360      *
 361      * @return  the string representation of this entry
 362      */
 363     @Override
 364     public String toString() {
 365         StringBuilder sb = new StringBuilder();
 366 
 367         // who
 368         sb.append(who.getName());
 369         sb.append(':');
 370 
 371         // permissions
 372         for (AclEntryPermission perm: perms) {
 373             sb.append(perm.name());
 374             sb.append('/');
 375         }
 376         sb.setLength(sb.length()-1); // drop final slash
 377         sb.append(':');
 378 
 379         // flags
 380         if (!flags.isEmpty()) {
 381             for (AclEntryFlag flag: flags) {
 382                 sb.append(flag.name());
 383                 sb.append('/');
 384             }
 385             sb.setLength(sb.length()-1);  // drop final slash
 386             sb.append(':');
 387         }
 388 
 389         // type
 390         sb.append(type.name());
 391         return sb.toString();
 392     }
 393 }