1 /*
   2  * Copyright (c) 2016, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package member;
  24 
  25 import java.lang.reflect.AccessibleObject;
  26 import java.util.Arrays;
  27 import java.util.EnumSet;
  28 import java.util.Iterator;
  29 import java.util.function.BiFunction;
  30 import java.util.function.Function;
  31 
  32 import static member.MemberFactory.Kind.CONSTRUCTOR;
  33 import static member.MemberFactory.Kind.FIELD;
  34 import static member.MemberFactory.Kind.METHOD;
  35 
  36 /**
  37  * Enumeration of:
  38  * <p>
  39  * {private, package, protected, public} x {instance, static} x {field, method}
  40  * <p>
  41  * and:
  42  * <p>
  43  * {private, package, protected, public} x {constructor},
  44  * <p>
  45  * with each element acting as a factory of AccessibleObject(s)
  46  * declared by given declaringClass(es).
  47  */
  48 public enum MemberFactory implements Function<Class<?>, AccessibleObject> {
  49     // instance fields
  50     PRIVATE_INSTANCE_FIELD(FIELD, "privateInstance"),
  51     PACKAGE_INSTANCE_FIELD(FIELD, "packageInstance"),
  52     PROTECTED_INSTANCE_FIELD(FIELD, "protectedInstance"),
  53     PUBLIC_INSTANCE_FIELD(FIELD, "publicInstance"),
  54     // instance methods
  55     PRIVATE_INSTANCE_METHOD(METHOD, "privateInstance"),
  56     PACKAGE_INSTANCE_METHOD(METHOD, "packageInstance"),
  57     PROTECTED_INSTANCE_METHOD(METHOD, "protectedInstance"),
  58     PUBLIC_INSTANCE_METHOD(METHOD, "publicInstance"),
  59     // static fields
  60     PRIVATE_STATIC_FIELD(FIELD, "privateStatic"),
  61     PACKAGE_STATIC_FIELD(FIELD, "packageStatic"),
  62     PROTECTED_STATIC_FIELD(FIELD, "protectedStatic"),
  63     PUBLIC_STATIC_FIELD(FIELD, "publicStatic"),
  64     // static methods
  65     PRIVATE_STATIC_METHOD(METHOD, "privateStatic"),
  66     PACKAGE_STATIC_METHOD(METHOD, "packageStatic"),
  67     PROTECTED_STATIC_METHOD(METHOD, "protectedStatic"),
  68     PUBLIC_STATIC_METHOD(METHOD, "publicStatic"),
  69     // constructors
  70     PRIVATE_CONSTRUCTOR(CONSTRUCTOR, null, Void.class, Void.class, Void.class),
  71     PACKAGE_CONSTRUCTOR(CONSTRUCTOR, null, Void.class, Void.class),
  72     PROTECTED_CONSTRUCTOR(CONSTRUCTOR, null, Void.class),
  73     PUBLIC_CONSTRUCTOR(CONSTRUCTOR, null),;
  74 
  75     final Kind kind;
  76     final String name;
  77     final Class<?>[] parameterTypes;
  78 
  79     MemberFactory(Kind kind, String name, Class<?>... parameterTypes) {
  80         this.kind = kind;
  81         this.name = name;
  82         this.parameterTypes = parameterTypes;
  83     }
  84 
  85     @Override
  86     public AccessibleObject apply(Class<?> declaringClass) {
  87         return kind.apply(declaringClass, this);
  88     }
  89 
  90     public static EnumSet<MemberFactory> asSet(MemberFactory... members) {
  91         return members.length == 0 ? EnumSet.noneOf(MemberFactory.class)
  92                                    : EnumSet.copyOf(Arrays.asList(members));
  93     }
  94 
  95     /**
  96      * @param members the set of MemberFactory(s) to convert to set of
  97      *                MemberFactory.Group(s).
  98      * @return a set of groups that cover all elements of the members set if
  99      * such set of groups exists or null if it doesn't.
 100      */
 101     public static EnumSet<Group> membersToGroupsOrNull(EnumSet<MemberFactory> members) {
 102         EnumSet<MemberFactory> mSet = members.clone();
 103         EnumSet<Group> gSet = EnumSet.allOf(Group.class);
 104         Iterator<Group> gIter = gSet.iterator();
 105         while (gIter.hasNext()) {
 106             Group g = gIter.next();
 107             if (mSet.containsAll(g.members)) {
 108                 mSet.removeAll(g.members);
 109             } else {
 110                 gIter.remove();
 111             }
 112         }
 113         return mSet.isEmpty() ? gSet : null;
 114     }
 115 
 116     /**
 117      * @param groups the set of MemberFactory.Group(s) to convert to set of
 118      *               MemberFactory(s).
 119      * @return a set of members as a union of members of all groups.
 120      */
 121     public static EnumSet<MemberFactory> groupsToMembers(EnumSet<Group> groups) {
 122         EnumSet<MemberFactory> mSet = EnumSet.noneOf(MemberFactory.class);
 123         for (Group g : groups) {
 124             mSet.addAll(g.members);
 125         }
 126         return mSet;
 127     }
 128 
 129     enum Kind implements BiFunction<Class<?>, MemberFactory, AccessibleObject> {
 130         FIELD {
 131             @Override
 132             public AccessibleObject apply(Class<?> declaringClass, MemberFactory factory) {
 133                 assert factory.kind == this;
 134                 try {
 135                     return declaringClass.getDeclaredField(factory.name);
 136                 } catch (NoSuchFieldException e) {
 137                     // a fault in test - fail fast
 138                     throw new RuntimeException(e.getMessage());
 139                 }
 140             }
 141         },
 142         METHOD {
 143             @Override
 144             public AccessibleObject apply(Class<?> declaringClass, MemberFactory factory) {
 145                 assert factory.kind == this;
 146                 try {
 147                     return declaringClass.getDeclaredMethod(factory.name, factory.parameterTypes);
 148                 } catch (NoSuchMethodException e) {
 149                     // a fault in test - fail fast
 150                     throw new RuntimeException(e.getMessage());
 151                 }
 152             }
 153         },
 154         CONSTRUCTOR {
 155             @Override
 156             public AccessibleObject apply(Class<?> declaringClass, MemberFactory factory) {
 157                 assert factory.kind == this;
 158                 try {
 159                     return declaringClass.getDeclaredConstructor(factory.parameterTypes);
 160                 } catch (NoSuchMethodException e) {
 161                     // a fault in test - fail fast
 162                     throw new RuntimeException(e.getMessage());
 163                 }
 164             }
 165         }
 166     }
 167 
 168     /**
 169      * We know fields and methods share the same access control rules.
 170      * We therefore define groups of MemberFactory(s) for members that should
 171      * exhibit same access restrictions in order to allow specifying groups
 172      * instead of individual members in the test cases, making them less verbose.
 173      */
 174     public enum Group {
 175         // instance field and method pairs
 176         PRIVATE_INSTANCE_F_M(PRIVATE_INSTANCE_FIELD, PRIVATE_INSTANCE_METHOD),
 177         PACKAGE_INSTANCE_F_M(PACKAGE_INSTANCE_FIELD, PACKAGE_INSTANCE_METHOD),
 178         PROTECTED_INSTANCE_F_M(PROTECTED_INSTANCE_FIELD, PROTECTED_INSTANCE_METHOD),
 179         PUBLIC_INSTANCE_F_M(PUBLIC_INSTANCE_FIELD, PUBLIC_INSTANCE_METHOD),
 180         // static field and method pairs
 181         PRIVATE_STATIC_F_M(PRIVATE_STATIC_FIELD, PRIVATE_STATIC_METHOD),
 182         PACKAGE_STATIC_F_M(PACKAGE_STATIC_FIELD, PACKAGE_STATIC_METHOD),
 183         PROTECTED_STATIC_F_M(PROTECTED_STATIC_FIELD, PROTECTED_STATIC_METHOD),
 184         PUBLIC_STATIC_F_M(PUBLIC_STATIC_FIELD, PUBLIC_STATIC_METHOD),
 185         // constructor singles
 186         PRIVATE_C(PRIVATE_CONSTRUCTOR),
 187         PACKAGE_C(PACKAGE_CONSTRUCTOR),
 188         PROTECTED_C(PROTECTED_CONSTRUCTOR),
 189         PUBLIC_C(PUBLIC_CONSTRUCTOR);
 190 
 191         final EnumSet<MemberFactory> members;
 192 
 193         Group(MemberFactory... members) {
 194             this.members = EnumSet.copyOf(Arrays.asList(members));
 195         }
 196 
 197         public static EnumSet<Group> asSet(Group... groups) {
 198             return groups.length == 0 ? EnumSet.noneOf(Group.class)
 199                                       : EnumSet.copyOf(Arrays.asList(groups));
 200         }
 201     }
 202 }