/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package pluginbase.permission; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; /** * Use this to create new Perm objects. * <p/> * This class will function without any initialization but should generally be registered with a Minecraft server * implementation specific implementation of itself via {@link #registerPermissionFactory(Class)}. * This allows the PermFactory to create properly implemented permissions. * @param <FACTORY_IMPL> Curiously Recurring Generic Pattern. Specify the implementation of this class * as the generic type. * @param <PERM_IMPL> Curiously Recurring Generic Pattern. Specify the implementation of the Perm class * as the generic type. */ public abstract class PermFactory<FACTORY_IMPL extends PermFactory, PERM_IMPL extends Perm> { private static Constructor<? extends PermFactory> factory; /** Represents the information for the plugin utilizing this PermFactory. */ protected static boolean hasFactory() { return factory != null; } private static final Map<String, String> PERM_NAME_MAP = new HashMap<String, String>(); /** * Registers a given base name to the given class. * <p/> * This affects what name is used as the top level namespace when using {@link #usePluginName()} * * @param pluginClass Your plugin class. * @param permissionName The top level namespace for your plugin's permissions. */ public static void registerPermissionName(final Class pluginClass, final String permissionName) { PERM_NAME_MAP.put(pluginClass.getName(), permissionName); } static String getPermissionName(final Class pluginClass) { return PERM_NAME_MAP.get(pluginClass.getName()); } /** * Creates a builder object for creating new {@link Perm}s. * * @param pluginClass The Class for the Plugin declaring this permission. This is used for setting top level * permissions and a base permission name. * @param permName The name of the permission, generally without top level namespaces. * @return A new PermFactory object used for building a new {@link Perm}. */ public static PermFactory newPerm(final Class pluginClass, final String permName) { if (factory == null) { throw new IllegalStateException("Must register a PermFactory class!"); } if (!PERM_NAME_MAP.containsKey(pluginClass.getName())) { throw new IllegalArgumentException(pluginClass + " does not have a registered permission name!"); } Perm.ensureParentPermsConfigured(pluginClass); return newUncheckedPerm(pluginClass, permName); } static PermFactory newUncheckedPerm(final Class pluginClass, final String permName) { if (factory != null) { final boolean access = factory.isAccessible(); try { if (!access) { factory.setAccessible(true); } return factory.newInstance(pluginClass, permName); } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { e.printStackTrace(); } finally { if (!access) { factory.setAccessible(false); } } } return new BasicPermFactory(pluginClass, permName); } private static class BasicPermFactory extends PermFactory { BasicPermFactory(Class pluginClass, String permName) { super(pluginClass, permName); } @Override public Perm build() { return new BasicPerm(pluginClass, this.name, this.description, this.children, this.permissionDefault, this.parents, this.baseName, this.specificOnly); } private static class BasicPerm extends Perm { BasicPerm(Class declaringPluginClass, String name, String description, Map<String, Boolean> children, PermDefault permDefault, Map<String, Boolean> parents, boolean baseName, boolean specificOnly) { super(declaringPluginClass, name, description, children, permDefault, parents, baseName, specificOnly); } @Override protected void verify(final String name) { } } } /** * Registers an implementation specific PermissionFactory. * <p/> * <b>Call this before initializing any Perm objects!</b> * <p/> * The given PermissionFactory class must have a constructor that accepts only a Class object and String object, * respectively. * * @param clazz The implementation specific PermissionFactory class to use. */ public static void registerPermissionFactory(final Class<? extends PermFactory> clazz) { try { factory = clazz.getDeclaredConstructor(Class.class, String.class); Perm.init(); } catch (NoSuchMethodException e) { throw new IllegalArgumentException("PermFactory must have constructor accepting single string!"); } } /** * Instructs PermFactory to create basic permissions that require no special handling by the implementation. */ public static void useBasicPermissionFactory() { try { factory = BasicPermFactory.class.getDeclaredConstructor(Class.class, String.class); Perm.init(); } catch (NoSuchMethodException e) { throw new IllegalArgumentException("PermFactory must have constructor accepting single string!"); } } protected final Class pluginClass; /** The permission's name. */ protected final String name; /** The permission's description. */ protected String description = ""; /** The permission's children. */ protected Map<String, Boolean> children = new HashMap<String, Boolean>(); /** The permission's default. */ protected PermDefault permissionDefault = PermDefault.OP; /** The permission's parents. */ protected Map<String, Boolean> parents = new HashMap<String, Boolean>(); /** Whether or not to use the plugin name as a top level namespace. */ protected boolean baseName = false; /** Whether or not this permissions is allowed to be used without a specific node added. */ protected boolean specificOnly = false; protected PermFactory(final Class pluginClass, final String permName) { if (pluginClass == null) { throw new IllegalArgumentException("pluginClass may not be null!"); } if (permName == null) { throw new IllegalArgumentException("permName may not be null!"); } this.pluginClass = pluginClass; this.name = permName; } /** * Sets the description for the permission. * * @param description The description. * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL desc(final String description) { this.description = description; return (FACTORY_IMPL) this; } /** * Adds a child permission with a default value of true. * * @param perm The child permission to add. * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL child(final Perm perm) { return (FACTORY_IMPL) child(perm.getName()); } /** * Adds a child permission with a default value of true. * * @param name The child permission to add. * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL child(final String name) { return child(name, true); } /** * Adds a child permission with a specified default value. * * @param perm The child permission to add. * @param state The default value for the child. * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL child(final Perm perm, final boolean state) { return child(perm.getName(), state); } /** * Adds a child permission with a specified default value. * * @param name The child permission to add. * @param state The default value for the child. * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL child(final String name, final boolean state) { children.put(name, state); return (FACTORY_IMPL) this; } /** * Adds a parent permission that will grant this permission by default. * * @param perm The parent permission to add. * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL parent(final Perm perm) { return parent(perm.getName()); } /** * Adds a parent permission that will grant this permission by default. * * @param name The parent permission to add. * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL parent(final String name) { return parent(name.toLowerCase(), true); } /** * Adds a parent permission that will grant the specified default for this permission. * * @param perm The parent permission to add. * @param state The default state for this permission when given the parent. * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL parent(final Perm perm, final boolean state) { return parent(perm.getName(), state); } /** * Adds a parent permission that will grant the specified default for this permission. * * @param name The parent permission to add. * @param state The default state for this permission when given the parent. * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL parent(final String name, final boolean state) { parents.put(name.toLowerCase(), state); return (FACTORY_IMPL) this; } /** * Adds this permission as a child to {@link Perm#getAllPerm(Class)}. * * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL addToAll() { return parent(Perm.getAllPerm(pluginClass)); } /** * Adds this permission as a child to {@link Perm#getCommandPerm(Class)}. * * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL commandPermission() { return parent(Perm.getCommandPerm(pluginClass)); } /** * Sets the default for this permission. * <p/> * By default {@link PermDefault#OP} is used. * * @param permissionDefault The default for this permission. * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL def(final PermDefault permissionDefault) { this.permissionDefault = permissionDefault; return (FACTORY_IMPL) this; } /** * Calling this will cause the permission to be prefixed with a top level name space provided by the class object * passed into the {@link #newPerm(Class, String)} method. * <p/> * The name is based on the name registered through {@link #registerPermissionName(Class, String)} or the * static method 'String getPermissionName()' defined in the plugin class. If neither of those methods result * in a name, the simple class name will be used. * * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL usePluginName() { baseName = true; return (FACTORY_IMPL) this; } /** * Indicates that the permissions is only to be used with the specific node method * {@link Perm#hasPermission(Permissible, String)}. * <p/> * This means that the name of the permission defined will not be registered in the server implementation and that * it only serves as a placeholder. * * @return this PermFactory for method chaining. */ @SuppressWarnings("unchecked") public FACTORY_IMPL specificOnly() { specificOnly = true; return (FACTORY_IMPL) this; } /** * Creates the Perm object. * <p/> * Call this after using all the other methods in this class that you require. Afterwards, this PermFactory object * may be discarded. * * @return a new Perm based on all the supplied values of this PermFactory. */ @SuppressWarnings("unchecked") public abstract PERM_IMPL build(); }