1 /*
   2  * Copyright (c) 2007, 2013, 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          * @param   type  the component type
 138          * @return  this builder
 139          */
 140         public Builder setType(AclEntryType type) {
 141             if (type == null)
 142                 throw new NullPointerException();
 143             this.type = type;
 144             return this;
 145         }
 146 
 147         /**
 148          * Sets the principal component of this builder.
 149          *
 150          * @param   who  the principal component
 151          * @return  this builder
 152          */
 153         public Builder setPrincipal(UserPrincipal who) {
 154             if (who == null)
 155                 throw new NullPointerException();
 156             this.who = who;
 157             return this;
 158         }
 159 
 160         // check set only contains elements of the given type
 161         private static void checkSet(Set<?> set, Class<?> type) {
 162             for (Object e: set) {
 163                 if (e == null)
 164                     throw new NullPointerException();
 165                 type.cast(e);
 166             }
 167         }
 168 
 169         /**
 170          * Sets the permissions component of this builder. On return, the
 171          * permissions component of this builder is a copy of the given set.
 172          *
 173          * @param   perms  the permissions component
 174          * @return  this builder
 175          *
 176          * @throws  ClassCastException
 177          *          if the set contains elements that are not of type {@code
 178          *          AclEntryPermission}
 179          */
 180         public Builder setPermissions(Set<AclEntryPermission> perms) {
 181             if (perms.isEmpty()) {
 182                 // EnumSet.copyOf does not allow empty set
 183                 perms = Collections.emptySet();
 184             } else {
 185                 // copy and check for erroneous elements
 186                 perms = EnumSet.copyOf(perms);
 187                 checkSet(perms, AclEntryPermission.class);
 188             }
 189 
 190             this.perms = perms;
 191             return this;
 192         }
 193 
 194         /**
 195          * Sets the permissions component of this builder. On return, the
 196          * permissions component of this builder is a copy of the permissions in
 197          * the given array.
 198          *
 199          * @param   perms  the permissions component
 200          * @return  this builder
 201          */
 202         public Builder setPermissions(AclEntryPermission... perms) {
 203             Set<AclEntryPermission> set = EnumSet.noneOf(AclEntryPermission.class);
 204             // copy and check for null elements
 205             for (AclEntryPermission p: perms) {
 206                 if (p == null)
 207                     throw new NullPointerException();
 208                 set.add(p);
 209             }
 210             this.perms = set;
 211             return this;
 212         }
 213 
 214         /**
 215          * Sets the flags component of this builder. On return, the flags
 216          * component of this builder is a copy of the given set.
 217          *
 218          * @param   flags  the flags component
 219          * @return  this builder
 220          *
 221          * @throws  ClassCastException
 222          *          if the set contains elements that are not of type {@code
 223          *          AclEntryFlag}
 224          */
 225         public Builder setFlags(Set<AclEntryFlag> flags) {
 226             if (flags.isEmpty()) {
 227                 // EnumSet.copyOf does not allow empty set
 228                 flags = Collections.emptySet();
 229             } else {
 230                 // copy and check for erroneous elements
 231                 flags = EnumSet.copyOf(flags);
 232                 checkSet(flags, AclEntryFlag.class);
 233             }
 234 
 235             this.flags = flags;
 236             return this;
 237         }
 238 
 239         /**
 240          * Sets the flags component of this builder. On return, the flags
 241          * component of this builder is a copy of the flags in the given
 242          * array.
 243          *
 244          * @param   flags  the flags component
 245          * @return  this builder
 246          */
 247         public Builder setFlags(AclEntryFlag... flags) {
 248             Set<AclEntryFlag> set = EnumSet.noneOf(AclEntryFlag.class);
 249             // copy and check for null elements
 250             for (AclEntryFlag f: flags) {
 251                 if (f == null)
 252                     throw new NullPointerException();
 253                 set.add(f);
 254             }
 255             this.flags = set;
 256             return this;
 257         }
 258     }
 259 
 260     /**
 261      * Constructs a new builder. The initial value of the type and who
 262      * components is {@code null}. The initial value of the permissions and
 263      * flags components is the empty set.
 264      *
 265      * @return  a new builder
 266      */
 267     public static Builder newBuilder() {
 268         Set<AclEntryPermission> perms = Collections.emptySet();
 269         Set<AclEntryFlag> flags = Collections.emptySet();
 270         return new Builder(null, null, perms, flags);
 271     }
 272 
 273     /**
 274      * Constructs a new builder with the components of an existing ACL entry.
 275      *
 276      * @param   entry  an ACL entry
 277      * @return  a new builder
 278      */
 279     public static Builder newBuilder(AclEntry entry) {
 280         return new Builder(entry.type, entry.who, entry.perms, entry.flags);
 281     }
 282 
 283     /**
 284      * Returns the ACL entry type.
 285      *
 286      * @return the ACL entry type
 287      */
 288     public AclEntryType type() {
 289         return type;
 290     }
 291 
 292     /**
 293      * Returns the principal component.
 294      *
 295      * @return the principal component
 296      */
 297     public UserPrincipal principal() {
 298         return who;
 299     }
 300 
 301     /**
 302      * Returns a copy of the permissions component.
 303      *
 304      * <p> The returned set is a modifiable copy of the permissions.
 305      *
 306      * @return the permissions component
 307      */
 308     public Set<AclEntryPermission> permissions() {
 309         return new HashSet<AclEntryPermission>(perms);
 310     }
 311 
 312     /**
 313      * Returns a copy of the flags component.
 314      *
 315      * <p> The returned set is a modifiable copy of the flags.
 316      *
 317      * @return the flags component
 318      */
 319     public Set<AclEntryFlag> flags() {
 320         return new HashSet<AclEntryFlag>(flags);
 321     }
 322 
 323     /**
 324      * Compares the specified object with this ACL entry for equality.
 325      *
 326      * <p> If the given object is not an {@code AclEntry} then this method
 327      * immediately returns {@code false}.
 328      *
 329      * <p> For two ACL entries to be considered equals requires that they are
 330      * both the same type, their who components are equal, their permissions
 331      * components are equal, and their flags components are equal.
 332      *
 333      * <p> This method satisfies the general contract of the {@link
 334      * java.lang.Object#equals(Object) Object.equals} method. </p>
 335      *
 336      * @param   ob   the object to which this object is to be compared
 337      *
 338      * @return  {@code true} if, and only if, the given object is an AclEntry that
 339      *          is identical to this AclEntry
 340      */
 341     @Override
 342     public boolean equals(Object ob) {
 343         if (ob == this)
 344             return true;
 345         if (ob == null || !(ob instanceof AclEntry))
 346             return false;
 347         AclEntry other = (AclEntry)ob;
 348         if (this.type != other.type)
 349             return false;
 350         if (!this.who.equals(other.who))
 351             return false;
 352         if (!this.perms.equals(other.perms))
 353             return false;
 354         if (!this.flags.equals(other.flags))
 355             return false;
 356         return true;
 357     }
 358 
 359     private static int hash(int h, Object o) {
 360         return h * 127 + o.hashCode();
 361     }
 362 
 363     /**
 364      * Returns the hash-code value for this ACL entry.
 365      *
 366      * <p> This method satisfies the general contract of the {@link
 367      * Object#hashCode} method.
 368      */
 369     @Override
 370     public int hashCode() {
 371         // return cached hash if available
 372         if (hash != 0)
 373             return hash;
 374         int h = type.hashCode();
 375         h = hash(h, who);
 376         h = hash(h, perms);
 377         h = hash(h, flags);
 378         hash = h;
 379         return hash;
 380     }
 381 
 382     /**
 383      * Returns the string representation of this ACL entry.
 384      *
 385      * @return  the string representation of this entry
 386      */
 387     @Override
 388     public String toString() {
 389         StringBuilder sb = new StringBuilder();
 390 
 391         // who
 392         sb.append(who.getName());
 393         sb.append(':');
 394 
 395         // permissions
 396         for (AclEntryPermission perm: perms) {
 397             sb.append(perm.name());
 398             sb.append('/');
 399         }
 400         sb.setLength(sb.length()-1); // drop final slash
 401         sb.append(':');
 402 
 403         // flags
 404         if (!flags.isEmpty()) {
 405             for (AclEntryFlag flag: flags) {
 406                 sb.append(flag.name());
 407                 sb.append('/');
 408             }
 409             sb.setLength(sb.length()-1);  // drop final slash
 410             sb.append(':');
 411         }
 412 
 413         // type
 414         sb.append(type.name());
 415         return sb.toString();
 416     }
 417 }