content: package com.comphenix.protocol.wrappers;
  
  import java.security.PublicKey;
  
  import com.comphenix.protocol.reflect.accessors.Accessors;
  import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
  import com.comphenix.protocol.reflect.accessors.MethodAccessor;
  import com.google.common.base.Objects;
  
  /**
   * Represents a wrapper over a signed property.
   * @author Kristian
   */
  public class WrappedSignedProperty extends AbstractWrapper {
  	private static final String CLASS_NAME = "com.mojang.authlib.properties.Property";
  	private static final String UTIL_PACKAGE = "net.minecraft.util.";
  
  	private static Class<?> PROPERTY;
  	private static ConstructorAccessor CONSTRUCTOR;
  	private static MethodAccessor GET_NAME;
  	private static MethodAccessor GET_SIGNATURE;
  	private static MethodAccessor GET_VALUE;
  	private static MethodAccessor HAS_SIGNATURE;
  	private static MethodAccessor IS_SIGNATURE_VALID;
  
  	static {
  		try {
  			PROPERTY = Class.forName(CLASS_NAME);
  		} catch (ClassNotFoundException ex) {
  			try {
  				PROPERTY = Class.forName(UTIL_PACKAGE + CLASS_NAME);
  			} catch (ClassNotFoundException ex1) {
  				throw new RuntimeException("Failed to obtain Property class.", ex1);
  			}
  		}
  
  		try {
  			CONSTRUCTOR = Accessors.getConstructorAccessor(PROPERTY, String.class, String.class, String.class);
  			GET_NAME = Accessors.getMethodAccessor(PROPERTY, "getName");
  			GET_SIGNATURE = Accessors.getMethodAccessor(PROPERTY, "getSignature");
  			GET_VALUE = Accessors.getMethodAccessor(PROPERTY, "getValue");
  			HAS_SIGNATURE = Accessors.getMethodAccessor(PROPERTY, "hasSignature");
  			IS_SIGNATURE_VALID = Accessors.getMethodAccessor(PROPERTY, "isSignatureValid", PublicKey.class);
  		} catch (Throwable ex) {
  			throw new RuntimeException("Failed to obtain methods for Property.", ex);
  		}
  	}
  	
  	/**
  	 * Construct a new wrapped signed property from the given values.
  	 * @param name - the name of the property.
  	 * @param value - the value of the property.
  	 * @param signature - the BASE64-encoded signature of the value.
  	 */
  	public WrappedSignedProperty(String name, String value, String signature) {
  		this(CONSTRUCTOR.invoke(name, value, signature));
  	}
  	
  	/**
  	 * Construct a new wrapped signed property from a given handle.
  	 * @param handle - the handle.
  	 */
  	private WrappedSignedProperty(Object handle) {
  		super(PROPERTY);
  		setHandle(handle);
  	}
  	
  	/**
  	 * Construct a new signed property from a given NMS property.
  	 * @param handle - the property.
  	 * @return The wrapped signed property.
  	 */
  	public static WrappedSignedProperty fromHandle(Object handle) {
  		return new WrappedSignedProperty(handle);
  	}
  	
  	/**
  	 * Construct a new wrapped signed property from the given values.
  	 * @param name - the name of the property.
  	 * @param value - the value of the property.
  	 * @param signature - the BASE64-encoded signature of the value.
  	 * @return The signed property.
  	 */
  	public static WrappedSignedProperty fromValues(String name, String value, String signature) {
  		return new WrappedSignedProperty(name, value, signature);
  	}
  
  	/**
  	 * Retrieve the name of the underlying property, such as "textures".
  	 * @return Name of the property.
  	 */
  	public String getName() {
  		return (String) GET_NAME.invoke(handle);
  	}
  
  	/**
  	 * Retrieve the signature of the property (base64) as returned by the session server's /hasJoined.
  	 * @return The signature of the property.
  	 */
  	public String getSignature() {
  		return (String) GET_SIGNATURE.invoke(handle);
  	}
  
  	/**
  	 * Retrieve the value of the property (base64) as return by the session server's /hasJoined
  	 * @return  The value of the property.
  	 */
  	public String getValue() {
  		return (String) GET_VALUE.invoke(handle);
  	}
  
  	/**
  	 * Determine if this property has a signature.
  	 * @return TRUE if it does, FALSE otherwise.
  	 */
  	public boolean hasSignature() {
  		return (Boolean) HAS_SIGNATURE.invoke(handle);
  	}
  	
  	/**
  	 * Determine if the signature of this property is valid and signed by the corresponding private key.
  	 * @param key - the public key.
  	 * @return TRUE if it is, FALSE otherwise.
  	 */
  	public boolean isSignatureValid(PublicKey key) {
  		return (Boolean) IS_SIGNATURE_VALID.invoke(handle, key);
  	}
  
  	@Override
  	public int hashCode() {
  		return Objects.hashCode(getName(), getValue(), getSignature());
  	}
  	
  	@Override
  	public boolean equals(Object object) {
  		if (object == this) return true;
  		
  		if (object instanceof WrappedSignedProperty) {
  			WrappedSignedProperty that = (WrappedSignedProperty) object;
  			return Objects.equal(this.getName(), that.getName())
  				&& Objects.equal(this.getValue(), that.getValue())
  				&& Objects.equal(this.getSignature(), that.getSignature());
  		}
  
  		return false;
  	}
  
  	@Override
  	public String toString() {
  		return "WrappedSignedProperty[name=" + getName() + ", value=" + getValue() + ", signature=" + getSignature() + "]";
  	}
  }
  