package org.jfox.mx;

import java.lang.reflect.Constructor;
import java.util.Arrays;

/**
 * 
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 *
 * Describes a constructor exposed by an MBean.  Instances of this
 * class are immutable.  Subclasses may be mutable but this is not
 * recommended.
 */
public class MxConstructorInfo extends MxFeatureInfo implements java.io.Serializable, Cloneable  {

  /* Serial version */
  static final long serialVersionUID = 4433990064191844427L;

  static final MxConstructorInfo[] NO_CONSTRUCTORS =
          new MxConstructorInfo[0];

  /** @see MxInfo#immutable */
  private final transient boolean immutable;

  /**
   * @serial The signature of the method, that is, the class names of the arguments.
   */
  private final MxParameterInfo[] signature;

  /**
   * Constructs an <CODE>MxConstructorInfo</CODE> object.
   *
   * @param description A human readable description of the operation.
   * @param constructor The <CODE>java.lang.reflect.Constructor</CODE>
   * object describing the MBean constructor.
   *
   * @exception IllegalArgumentException if one of
   * <code>constructor</code>'s parameter types is not a
   * syntactically legal Java type name.  Java reserved words are
   * not considered illegal here.  If <code>constructor</code> comes
   * from a class compiled from Java, this exception cannot happen.
   */
  public MxConstructorInfo(String description, Constructor constructor) {
    this(constructor.getName(), description,
            constructorSignature(constructor));
  }

  /**
   * Constructs an <CODE>MxConstructorInfo</CODE> object.
   *
   * @param name The name of the constructor.
   * @param signature <CODE>MxParameterInfo</CODE> objects
   * describing the parameters(arguments) of the constructor.  This
   * may be null with the same effect as a zero-length array.
   * @param description A human readable description of the constructor.
   *
   * @exception IllegalArgumentException if <code>name</code> is not
   * a valid Java identifier or a syntactically legal Java type
   * name as returned by {@link java.lang.reflect.Constructor#getName()
   * Constructor.getName()}.
   * Java reserved words are not considered illegal here.
   */
  private MxConstructorInfo(String name,
                              String description,
                              MxParameterInfo[] signature)
          throws IllegalArgumentException {
    super(name, description);

    MxInfo.mustBeValidMBeanTypeName(name);

    if (signature == null || signature.length == 0)
      signature = MxParameterInfo.NO_PARAMS;
    else
      signature = (MxParameterInfo[]) signature.clone();
    this.signature = signature;
    this.immutable =
            MxInfo.isImmutableClass(this.getClass(),
                    MxConstructorInfo.class);
  }


  /**
   * <p>Returns a shallow clone of this instance.  The clone is
   * obtained by simply calling <tt>super.clone()</tt>, thus calling
   * the default native shallow cloning mechanism implemented by
   * <tt>Object.clone()</tt>.  No deeper cloning of any internal
   * field is made.</p>
   *
   * <p>Since this class is immutable, cloning is chiefly of
   * interest to subclasses.</p>
   */
  public Object clone () {
    try {
      return  super.clone() ;
    } catch (CloneNotSupportedException e) {
      // should not happen as this class is cloneable
      return null;
    }
  }

  /**
   * <p>Returns the list of parameters for this constructor.  Each
   * parameter is described by an <CODE>MxParameterInfo</CODE>
   * object.</p>
   *
   * <p>The returned array is a shallow copy of the internal array,
   * which means that it is a copy of the internal array of
   * references to the <CODE>MxParameterInfo</CODE> objects but
   * that each referenced <CODE>MxParameterInfo</CODE> object is
   * not copied.</p>
   *
   * @return  An array of <CODE>MxParameterInfo</CODE> objects.
   */
  public MxParameterInfo[] getSignature() {
    if (signature.length == 0)
      return signature;
    else
      return (MxParameterInfo[]) signature.clone();
  }

  private MxParameterInfo[] fastGetSignature() {
    if (immutable)
      return signature;
    else
      return getSignature();
  }

  /**
   * Compare this MxConstructorInfo to another.
   *
   * @param o the object to compare to.
   *
   * @return true iff <code>o</code> is an MxConstructorInfo such
   * that its {@link #getName()}, {@link #getDescription()}, and
   * {@link #getSignature()} values are equal (not necessarily
   * identical) to those of this MxConstructorInfo.  Two
   * signature arrays are equal if their elements are pairwise
   * equal.
   */
  public boolean equals(Object o) {
    if (o == this)
      return true;
    if (!(o instanceof MxConstructorInfo))
      return false;
    MxConstructorInfo p = (MxConstructorInfo) o;
    return (p.getName().equals(getName()) &&
            p.getDescription().equals(getDescription()) &&
            Arrays.equals(p.fastGetSignature(), fastGetSignature()));
  }

  /* Unlike attributes and operations, it's quite likely we'll have
  more than one constructor with the same name and even
  description, so we include the parameter array in the hashcode.
  We don't include the description, though, because it could be
  quite long and yet the same between constructors.  */
  public int hashCode() {
    int hash = getName().hashCode();
    MxParameterInfo[] sig = fastGetSignature();
    for (int i = 0; i < sig.length; i++)
      hash ^= sig[i].hashCode();
    return hash;
  }

  private static MxParameterInfo[] constructorSignature(Constructor cn)
          throws IllegalArgumentException {
    final Class[] classes = cn.getParameterTypes();
    final MxParameterInfo[] params =
            new MxParameterInfo[classes.length];

    for (int i = 0; i < classes.length; i++) {
      final String pn = "p" + (i + 1);
      params[i] = new MxParameterInfo(pn, classes[i].getName(), "");
    }

    return params;
  }
}