package com.laytonsmith.PureUtilities.MSP; import java.util.HashMap; import java.util.Map; /** * A capability list is a list of known supported * actions by a remote. The remote is free to update * this list at any time, and capabilities can have * various states. * */ public class CapabilityList { private Map<Capability, CapabilityValue> caps = new HashMap<Capability, CapabilityValue>(); private Connection connection; /** * Creates a new CapabilityList object. * @param connection The connection to the server. This will be used * to automatically determine server capabilites when required. */ public CapabilityList(Connection connection){ this.connection = connection; } /** * Clears out this capability list. */ public void clear(){ caps.clear(); } public void setCapability(Capability capability, CapabilityValue value){ if(value.serverReturnable()){ caps.put(capability, value); } else { throw new RuntimeException("An error occured during runtime, the server returned an invalid capability: " + value); } } /** * Returns true if the server supports this capability. If the capability is * dynamic or unknown, it is looked up from the server. * @param capability * @return */ public CapabilityValue hasCapability(Capability capability){ CapabilityValue value = caps.get(capability); if(value == null){ //If the capability is unknown, we need to look it up. setCapability(capability, connection.getCapability(capability)); return hasCapability(capability); } else if(value == CapabilityValue.DYNAMIC){ //Do a one time lookup return connection.getCapability(capability); } return value; } /** * A capability is intended to be an enum, but since the valid * values may vary from version to version, an interface is defined instead, * which various enum values should implement. All capabilities are required * to return a namespace as well, which, in combination with the name, should * uniquely identify this capability. Generally, the namespace should return the * fully qualified class name. */ public interface Capability{ /** * The namespace of the capability. * @return */ String namespace(); /** * The name of the capability. * @return */ String name(); } public static enum CapabilityValue{ /** * This capability is always supported by this server, and * the client should never request if this is * supported or not. (It still may fail, but that is unexpected). * This is generally reserved for core operations, but * may be also used for other requests. If the request fails, it * will not automatically switch the value of this capability, however, * the server may still actively change the capability value. */ ALWAYS_SUPPORTED(true), /** * This capability is never supported by this server, and * the client should never request if this is supported * or not. (It still may succeed, but that is unexpected). * This is generally reserved for the "default" case, where a * server doesn't know about a capability at all. If the client * still attempts the operation, and it succeeds, it will not * automatically switch the value of this capability, however, * the server may still actively change the capability value. */ ALWAYS_UNSUPPORTED(true), /** * This capability is supported, but given certain runtime conditions, * it may change (for instance, lack of permissions). * If the client attempts the operation and it doesn't * succeed, the value will automatically change to UNSUPPORTED, and the * server may actively change this. */ SUPPORTED(true), /** * This capability is unsupported, but given certain runtime conditions, * it may change (for instance, elevation of permissions). * If the client forces the operation and it does succeed, * the value will change automatically to SUPPORTED, and the server may * actively change this. */ UNSUPPORTED(true), /** * This capability is requested from the server each time it is used, and * that new value is temporarily used to determine support or not. */ DYNAMIC(true), /** * This is the "default" value, that is, if a client does not know if a server * supports this capabilty, it works like {@see #DYNAMIC}, but the server's response * will be cached. */ UNKNOWN(false); private final boolean serverReturnable; private CapabilityValue(boolean serverReturnable){ this.serverReturnable = serverReturnable; } /** * Returns true if the server can return this value during a request * for capabilities. * @return */ public boolean serverReturnable(){ return serverReturnable; } } }