/* 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package java.lang.reflect;

import java.lang.annotation.Annotation;

import org.jikesrvm.classloader.RVMClass;
import org.jikesrvm.classloader.RVMMember;
import org.jikesrvm.classloader.RVMMethod;
import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.runtime.RuntimeEntrypoints;
import org.jikesrvm.runtime.Reflection;

/**
 * This class must be implemented by the VM vendor. This class models a
 * constructor. Information about the constructor can be accessed, and the
 * constructor can be invoked dynamically.
 * 
 */
public final class Constructor<T> extends AccessibleObject implements GenericDeclaration, Member {
  /**
	* Data being wrapped by the Constructor API
	*/
  private final RVMMethod vmConstructor;

  /**
	* Constructor
	*/
  Constructor(RVMMethod vmConstructor){
	 this.vmConstructor = vmConstructor;
  }

  /**
	* Prevent this class from being instantiated
	*/
  private Constructor(){
	 //do nothing
	 this.vmConstructor = null;
  }

  public TypeVariable<Constructor<T>>[] getTypeParameters() {
	 throw new Error("TODO");
  }

  /**
	* <p>
	* Returns the String representation of the constructor's declaration,
	* including the type parameters.
	* </p>
	* 
	* @return An instance of String.
	* @since 1.5
	*/
  public String toGenericString() {
	 throw new Error("TODO");
  }

  /**
	* <p>
	* Gets the parameter types as an array of {@link Type} instances, in
	* declaration order. If the constructor has no parameters, then an empty
	* array is returned.
	* </p>
	* 
	* @return An array of {@link Type} instances.
	* @throws GenericSignatureFormatError if the generic method signature is
	*         invalid.
	* @throws TypeNotPresentException if the component type points to a missing
	*         type.
	* @throws MalformedParameterizedTypeException if the component type points
	*         to a type that can't be instantiated for some reason.
	* @since 1.5
	*/
  public Type[] getGenericParameterTypes() {
	 throw new Error("TODO");
  }

  /**
	* <p>
	* Gets the exception types as an array of {@link Type} instances. If the
	* constructor has no declared exceptions, then an empty array is returned.
	* </p>
	* 
	* @return An array of {@link Type} instances.
	* @throws GenericSignatureFormatError if the generic method signature is
	*         invalid.
	* @throws TypeNotPresentException if the component type points to a missing
	*         type.
	* @throws MalformedParameterizedTypeException if the component type points
	*         to a type that can't be instantiated for some reason.
	* @since 1.5
	*/
  public Type[] getGenericExceptionTypes() {
	 throw new Error("TODO");
  }

  /**
	* <p>
	* Gets an array of arrays that represent the annotations of the formal
	* parameters of this constructor. If there are no parameters on this
	* constructor, then an empty array is returned. If there are no annotations
	* set, then and array of empty arrays is returned.
	* </p>
	* 
	* @return An array of arrays of {@link Annotation} instances.
	* @since 1.5
	*/
  public Annotation[][] getParameterAnnotations() {
	 return super.getParameterAnnotations();
  }

  /**
	* <p>
	* Indicates whether or not this constructor takes a variable number
	* argument.
	* </p>
	* 
	* @return A value of <code>true</code> if a vararg is declare, otherwise
	*         <code>false</code>.
	* @since 1.5
	*/
  public boolean isVarArgs() {
	 return super.isVarArgs();
  }

  public boolean isSynthetic() {
	 return super.isSynthetic();
  }

  /**
	* Compares the specified object to this Constructor and answer if they are
	* equal. The object must be an instance of Constructor with the same
	* defining class and parameter types.
	* 
	* @param object the object to compare
	* @return true if the specified object is equal to this Constructor, false
	*         otherwise
	* @see #hashCode
	*/
  @Override
    public boolean equals(Object object) {
	 if (object instanceof Constructor) {
		Constructor that = (Constructor)object;
		return this.vmConstructor == that.vmConstructor;
	 } else {
		return false;
	 }
  }

  /**
	* Return the {@link Class} associated with the class that defined this
	* constructor.
	* 
	* @return the declaring class
	*/
  public Class<T> getDeclaringClass() {
	 return (Class<T>)vmConstructor.getDeclaringClass().getClassForType();
  }

  /**
	* Return an array of the {@link Class} objects associated with the
	* exceptions declared to be thrown by this constructor. If the constructor
	* was not declared to throw any exceptions, the array returned will be
	* empty.
	* 
	* @return the declared exception classes
	*/
  public Class<?>[] getExceptionTypes() {
	 return super.getExceptionTypes();
  }

  /**
	* Return the modifiers for the modeled constructor. The Modifier class
	* should be used to decode the result.
	* 
	* @return the modifiers
	* @see java.lang.reflect.Modifier
	*/
  public int getModifiers() {
	 return super.getModifiers();
  }

  /**
	* Return the name of the modeled constructor. This is the name of the
	* declaring class.
	* 
	* @return the name
	*/
  public String getName() {
	 return getDeclaringClass().getName();
  }

  /**
	* Return an array of the {@link Class} objects associated with the
	* parameter types of this constructor. If the constructor was declared with
	* no parameters, the array returned will be empty.
	* 
	* @return the parameter types
	*/
  public Class<?>[] getParameterTypes() {
	 return VMCommonLibrarySupport.typesToClasses(vmConstructor.getParameterTypes());
  }

  /**
	* Answers an integer hash code for the receiver. Objects which are equal
	* answer the same value for this method. The hash code for a Constructor is
	* the hash code of the declaring class' name.
	* 
	* @return the receiver's hash
	* @see #equals
	*/
  @Override
    public int hashCode() {
	 return getName().hashCode();
  }

  /**
	* Return a new instance of the declaring class, initialized by dynamically
	* invoking the modeled constructor. This reproduces the effect of
	* <code>new declaringClass(arg1, arg2, ... , argN)</code> This method
	* performs the following:
	* <ul>
	* <li>A new instance of the declaring class is created. If the declaring
	* class cannot be instantiated (i.e. abstract class, an interface, an array
	* type, or a base type) then an InstantiationException is thrown.</li>
	* <li>If this Constructor object is enforcing access control (see
	* AccessibleObject) and the modeled constructor is not accessible from the
	* current context, an IllegalAccessException is thrown.</li>
	* <li>If the number of arguments passed and the number of parameters do
	* not match, an IllegalArgumentException is thrown.</li>
	* <li>For each argument passed:
	* <ul>
	* <li>If the corresponding parameter type is a base type, the argument is
	* unwrapped. If the unwrapping fails, an IllegalArgumentException is
	* thrown.</li>
	* <li>If the resulting argument cannot be converted to the parameter type
	* via a widening conversion, an IllegalArgumentException is thrown.</li>
	* </ul>
	* <li>The modeled constructor is then invoked. If an exception is thrown
	* during the invocation, it is caught and wrapped in an
	* InvocationTargetException. This exception is then thrown. If the
	* invocation completes normally, the newly initialized object is returned.
	* </ul>
	* 
	* @param args the arguments to the constructor
	* @return the new, initialized, object
	* @exception java.lang.InstantiationException if the class cannot be
	*            instantiated
	* @exception java.lang.IllegalAccessException if the modeled constructor
	*            is not accessible
	* @exception java.lang.IllegalArgumentException if an incorrect number of
	*            arguments are passed, or an argument could not be converted by
	*            a widening conversion
	* @exception java.lang.reflect.InvocationTargetException if an exception
	*            was thrown by the invoked constructor
	* @see java.lang.reflect.AccessibleObject
	*/
  public T newInstance(Object... args) throws InstantiationException, IllegalAccessException,
															 IllegalArgumentException, InvocationTargetException {
	 return (T)VMCommonLibrarySupport.construct(vmConstructor, this, args, RVMClass.getClassFromStackFrame(1));
  }

  /**
	* Answers a string containing a concise, human-readable description of the
	* receiver. The format of the string is modifiers (if any) declaring class
	* name '(' parameter types, separated by ',' ')' If the constructor throws
	* exceptions, ' throws ' exception types, separated by ',' For example:
	* <code>public String(byte[],String) throws UnsupportedEncodingException</code>
	* 
	* @return a printable representation for the receiver
	*/
  @Override
  public String toString() {
	 StringBuilder sb = new StringBuilder(80);
	 // append modifiers if any
	 int modifier = getModifiers();
	 if (modifier != 0) {
		// VARARGS incorrectly recognized
		final int MASK = ~Modifier.VARARGS;  
		sb.append(Modifier.toString(modifier & MASK)).append(' ');            
	 }
	 // append constructor name
	 appendArrayType(sb, getDeclaringClass());
	 // append parameters
	 sb.append('(');
	 appendArrayType(sb, getParameterTypes());
	 sb.append(')');
	 // append exeptions if any
	 Class[] exn = getExceptionTypes();
	 if (exn.length > 0) {
		sb.append(" throws ");
		appendSimpleType(sb, exn);
	 }
	 return sb.toString();
  }

  /* ---- Non-API methods ---- */

  /**
   * Set the accessibilty to the value of flag performing a check on the constructor.
   */
  @Override
  void setAccessible0(boolean flag) {
	 if (flag && getDeclaringClass() == Class.class) {
		throw new SecurityException("Can not make the java.lang.Class class constructor accessible");
	 }
	 super.setAccessible0(flag);
  }

  /**
   * Get the VM member implementation. Package protected to stop outside use.
   */
  @Override
  RVMMember getVMMember() {
	 return vmConstructor;
  }

  /**
   * Get the VM method implementation, invalid for fields. Package protected to
   * stop outside use.
   */
  @Override
  RVMMethod getVMMethod() {
	 return vmConstructor;
  }
}
