import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Member;
import java.lang.reflect.Constructor;

class ReflectionTest_A
	implements ReflectionTest_B
{
private int A_a;
transient public long A_b;
public void A_foo() { }
private void A_bar() { } 
public void bar() { }
};

interface ReflectionTest_B
	extends ReflectionTest_C, ReflectionTest_D
{
public static final int B_a = 1;
public static final int B_b = 2;
void bar(); 
};

interface ReflectionTest_C
{
public static final int C_a = 1;
public static final int C_b = 2;
void A_foo();
};

interface ReflectionTest_D
{
public static final int D_a = 1;
public static final int D_b = 2;
void bar();
public static final String D_c = "A constant String!";
};

class TestFailedException
	extends RuntimeException
{

private Class c;
private String message;

TestFailedException(Class c, String message)
{
	this.c = c;
	this.message = message;
}

public
String
toString()
{
	return c.getName() + ".class." + message;
}

};

public
class ReflectionTest
	extends ReflectionTest_A
{

private int a;
transient public String b;
public static String c;
private Integer d;

private
ReflectionTest(int i)
{
}

private
ReflectionTest(Integer i)
{
}

public
ReflectionTest(long l)
{
}

public
ReflectionTest(Long l)
{
}

public
ReflectionTest(String s1, String s2)
{
	b = s1;
}

public
ReflectionTest()
{
}

private
void
foo(String s)
{
}

public
void
bar()
{
}

private static
String
toString(Class[] params)
{
	StringBuffer buff = new StringBuffer();
	buff.append('[');
	for (int i = 0; params != null && i < params.length; ++i) {
		if (i != 0) {
			buff.append(", ");
		}
		buff.append(params[i].getName());
	}
	buff.append(']');
	return buff.toString();
}

private static
void
checkGetField(Class c, String fieldName, boolean mustExist)
{
	Field fld = null;
	try {
		fld = c.getField(fieldName);
	}
	catch (NoSuchFieldException e) {
		if (mustExist) {
			throw new TestFailedException(c, "getField(\"" + fieldName + "\") didn't find field.");
		}
		return;
	}
	catch (Throwable e) {
		throw new TestFailedException(c, "getField(\"" + fieldName + "\") threw: " + e);
	}
	if (!mustExist) {
		throw new TestFailedException(c, "getField(\"" + fieldName + "\") found field: " + fld);
	}
}

private static
void
checkGetFields(Class c, int expectedNum)
{
	Field[] fields = null;
	try {
		fields = c.getFields();
	}
	catch (Throwable e) {
		throw new TestFailedException(c, "getFields() threw: " + e);
	}
	if (fields.length != expectedNum) {
		throw new TestFailedException(c, "getFields() returned " + fields.length + " fields instead of " + expectedNum + ".");
	}
}

private static
void
checkGetDeclaredField(Class c, String fieldName, boolean mustExist)
{
	Field fld = null;
	try {
		fld = c.getDeclaredField(fieldName);
	}
	catch (NoSuchFieldException e) {
		if (mustExist) {
			throw new TestFailedException(c, "getDeclaredField(\"" + fieldName + "\") didn't find field.");
		}
		return;
	}
	catch (Throwable e) {
		throw new TestFailedException(c, "getDeclaredField(\"" + fieldName + "\") threw: " + e);
	}
	if (!mustExist) {
		throw new TestFailedException(c, "getDeclaredField(\"" + fieldName + "\") found field: " + fld);
	}
}

private static
void
checkGetDeclaredFields(Class c, int expectedNum)
{
	Field[] fields = null;
	try {
		fields = c.getDeclaredFields();
	}
	catch (Throwable e) {
		throw new TestFailedException(c, "getDeclaredFields() threw: " + e);
	}
	if (fields.length != expectedNum) {
		throw new TestFailedException(c, "getDeclaredFields() returned " + fields.length + " fields instead of " + expectedNum + ".");
	}
}

private static
void
checkGetMethod(Class c, String methodName, Class[] params, boolean mustExist)
{
	Method method = null;
	try {
		method = c.getMethod(methodName, params);
	}
	catch (NoSuchMethodException e) {
		if (mustExist) {
			throw new TestFailedException(c, "getMethod(\"" + methodName + "\", " + toString(params) + ") didn't find method.");
		}
		return;
	}
	catch (Throwable e) {
		throw new TestFailedException(c, "getMethod(\"" + methodName + "\", " + toString(params) + ") threw: " + e);
	}
	if (!mustExist) {
		throw new TestFailedException(c, "getMethod(\"" + methodName + "\", " + toString(params) + ") found method: " + method);
	}
}

private static
void
checkGetMethods(Class c, int expectedNum)
{
	Method[] methods = null;
	try {
		methods = c.getMethods();
	}
	catch (Throwable e) {
		throw new TestFailedException(c, "getMethods() threw: " + e);
	}
	if (methods.length != expectedNum) {
		throw new TestFailedException(c, "getMethods() returned " + methods.length + " methods instead of " + expectedNum + ".");
	}
}

private static
void
checkGetDeclaredMethod(Class c, String methodName, Class[] params, boolean mustExist)
{
	Method method = null;
	try {
		method = c.getDeclaredMethod(methodName, params);
	}
	catch (NoSuchMethodException e) {
		if (mustExist) {
			throw new TestFailedException(c, "getDeclaredMethod(\"" + methodName + "\", " + toString(params) + ") didn't find method.");
		}
		return;
	}
	catch (Throwable e) {
		throw new TestFailedException(c, "getDeclaredMethod(\"" + methodName + "\", " + toString(params) + ") threw: " + e);
	}
	if (!mustExist) {
		throw new TestFailedException(c, "getDeclaredMethod(\"" + methodName + "\", " + toString(params) + ") found method: " + method);
	}
}

private static
void
checkGetDeclaredMethods(Class c, int expectedNum)
{
	Method[] methods = null;
	try {
		methods = c.getDeclaredMethods();
	}
	catch (Throwable e) {
		throw new TestFailedException(c, "getDeclaredMethods() threw: " + e);
	}
	if (methods.length != expectedNum) {
		throw new TestFailedException(c, "getDeclaredMethods() returned " + methods.length + " methods instead of " + expectedNum + ".");
	}
}

private static
void
checkGetConstructor(Class c, Class[] params, boolean mustExist)
{
	Constructor ctor = null;
	try {
		ctor = c.getConstructor(params);
		if (ctor.getDeclaringClass() != c) {
			throw new TestFailedException(c, "getConstructor(" + toString(params) + ").getDeclaringClass() does not match class.");
		}
	}
	catch (NoSuchMethodException e) {
		if (mustExist) {
			throw new TestFailedException(c, "getConstructor(" + toString(params) + ") didn't find method.");
		}
		return;
	}
	catch (Throwable e) {
		throw new TestFailedException(c, "getConstructor(" + toString(params) + ") threw: " + e);
	}
	if (!mustExist) {
		throw new TestFailedException(c, "getConstructor(" + toString(params) + ") found method: " + ctor);
	}
}

private static
void
checkGetConstructors(Class c, int expectedNum)
{
	Constructor[] ctors = null;
	try {
		ctors = c.getConstructors();
	}
	catch (Throwable e) {
		throw new TestFailedException(c, "getConstructors() threw: " + e);
	}
	if (ctors.length != expectedNum) {
		throw new TestFailedException(c, "getConstructors() returned " + ctors.length + " constructors instead of " + expectedNum + ".");
	}
}

private static
void
checkGetDeclaredConstructor(Class c, Class[] params, boolean mustExist)
{
	Constructor ctor = null;
	try {
		ctor = c.getDeclaredConstructor(params);
		if (ctor.getDeclaringClass() != c) {
			throw new TestFailedException(c, "getDeclaredConstructor(" + toString(params) + ").getDeclaringClass() does not match class.");
		}
	}
	catch (NoSuchMethodException e) {
		if (mustExist) {
			throw new TestFailedException(c, "getDeclaredConstructor(" + toString(params) + ") didn't find method.");
		}
		return;
	}
	catch (Throwable e) {
		throw new TestFailedException(c, "getDecalredConstructor(" + toString(params) + ") threw: " + e);
	}
	if (!mustExist) {
		throw new TestFailedException(c, "getDeclaredConstructor(" + toString(params) + ") found method: " + ctor);
	}
}

private static
void
checkGetDeclaredConstructors(Class c, int expectedNum)
{
	Constructor[] ctors = null;
	try {
		ctors = c.getDeclaredConstructors();
	}
	catch (Throwable e) {
		throw new TestFailedException(c, "getDeclaredConstructors() threw: " + e);
	}
	if (ctors.length != expectedNum) {
		throw new TestFailedException(c, "getDeclaredConstructors() returned " + ctors.length + " constructors instead of " + expectedNum + ".");
	}
}

private static
void
checkGetModifiers()
  throws Exception
{
  Constructor ctor = null;
  try {
    ctor =
      ReflectionTest.class.getDeclaredConstructor(new Class[] { Long.TYPE });
  }
  catch (NoSuchMethodException e) {
    throw new TestFailedException(ReflectionTest.class,
  "getDeclaredConstructor({ long }) didn't find constructor.");
  }
  if (!ctor.toString().equals("public ReflectionTest(long)")) {
    throw new TestFailedException(ReflectionTest.class,
  "getDeclaredConstructor({ long }).toString() returned \"" + ctor.toString() + "\" instead of \"public ReflectionTest(long)\".");
  }
}

private static
void
checkGetMethodExceptionTypes()
  throws TestFailedException, RuntimeException, Exception
{
  Method method = null;
  try {
    method =
      ReflectionTest.class.getDeclaredMethod("checkGetMethodExceptionTypes",
					     null);
  }
  catch (NoSuchMethodException e) {
    throw new TestFailedException(ReflectionTest.class,
  "getDeclaredMethod(\"checkGetMethodExceptionTypes\") didn't find method.");
  }
  Class[] exceptions = method.getExceptionTypes();
  if (exceptions.length != 3) {
    throw new TestFailedException(ReflectionTest.class,
"getDeclaredMethod(\"checkGetMethodExceptionTypes\").getExceptionTypes() "+
				  "returned " + exceptions.length +
				  " exceptions instead of 3");
  }
  if (exceptions[0] != TestFailedException.class ||
      exceptions[1] != RuntimeException.class ||
      exceptions[2] != Exception.class) {
    throw new TestFailedException(ReflectionTest.class,
"getDeclaredMethod(\"checkGetMethodExceptionTypes\").getExceptionTypes() "+
				  "did not return correct exception types");
  }
}

private static
void
checkGetStaticFieldValue()
  throws TestFailedException, RuntimeException, Exception
{
  Field field = null;
  try {
    field =
      Member.class.getDeclaredField("DECLARED");
  }
  catch (NoSuchFieldException e) {
    throw new TestFailedException(Member.class,
  "getDeclaredField(\"DECLARED\") didn't find field.");
  }
  if (Member.DECLARED != field.getInt(null)) {
    throw new TestFailedException(Member.class,
  "getDeclaredField(\"DECLARED\").getInt(null) returned " + field.getInt(null) + " instead of " + Member.DECLARED);
  }
  try {
    field = ReflectionTest_D.class.getDeclaredField("D_c");
  }
  catch (Exception e) {
    throw new TestFailedException(ReflectionTest_D.class,
  "getDeclaredField(\"D_c\") didn't find field.");
  }
  if (!((String) field.get(null)).equals("A constant String!")) {
    throw new TestFailedException(ReflectionTest_D.class,
  "getDeclaredField(\"D_c\").get(null) returned \"" + field.get(null) + "\" instead of \"A constant String!\"");
  }
}

private static
void
checkConstructorInvocation()
{
	Constructor ctor = null;
	try {
		ctor = ReflectionTest.class.getConstructor(new Class[] { String.class, String.class});
		if (ctor.getDeclaringClass() != ReflectionTest.class) {
			throw new TestFailedException(ReflectionTest.class, "getConstructor(String, String).getDeclaringClass() does not match class.");
		}
	}
	catch (NoSuchMethodException e) {
		throw new TestFailedException(ReflectionTest.class, "getConstructor(String, String) didn't find method.");
	}
	catch (Throwable e) {
		throw new TestFailedException(ReflectionTest.class, "getConstructor(String, String) threw: " + e);
	}
	try {
		ReflectionTest t =
			(ReflectionTest) ctor.newInstance(new Object[] { "Hello", "World" });
		if (t == null || !t.b.equals("Hello")) {
			throw new TestFailedException(ReflectionTest.class, "getConstructor(String, String).newInstance() did not create correct object!");
		}
	}
	catch (Exception e) {
		throw new TestFailedException(ReflectionTest.class, "getConstructor(String, String).newInstance() threw: " + e);
	}
}

public static
void
main(String args[])
{
	try {
		// Checking on a class
		checkGetField(ReflectionTest.class, "a", false);
		checkGetField(ReflectionTest.class, "C_a", true);
		checkGetField(ReflectionTest.class, "A_b", true);
		checkGetFields(ReflectionTest.class, 10);

		// Checking on an interface
		checkGetField(ReflectionTest_B.class, "D_a", true);
		checkGetField(ReflectionTest_B.class, "C_a", true);
		checkGetField(ReflectionTest_B.class, "A_foo", false);
		checkGetFields(ReflectionTest_B.class, 7);

		// Checking on a class
		checkGetDeclaredField(ReflectionTest.class, "a", true);
		checkGetDeclaredField(ReflectionTest.class, "d", true);
		checkGetDeclaredField(ReflectionTest.class, "A_b", false);
		checkGetDeclaredFields(ReflectionTest.class, 16);

		// Checking on an interface
		checkGetDeclaredField(ReflectionTest_B.class, "D_a", false);
		checkGetDeclaredField(ReflectionTest_B.class, "B_a", true);
		checkGetDeclaredField(ReflectionTest_B.class, "A_foo", false);
		checkGetDeclaredFields(ReflectionTest_B.class, 2);

		// Checking on a class
		checkGetMethod(ReflectionTest.class, "<init>", null, false);
		checkGetMethod(ReflectionTest.class, "foo", new Class[] { String.class }, false);
		checkGetMethod(ReflectionTest.class, "foo", null, false);
		checkGetMethod(ReflectionTest.class, "bar", null, true);
		checkGetMethod(ReflectionTest.class, "wait", null, true);
		checkGetMethod(ReflectionTest.class, "A_foo", null, true);
		checkGetMethod(ReflectionTest.class, "A_bar", null, false);
		checkGetMethods(ReflectionTest.class, 12);

		checkGetDeclaredMethod(ReflectionTest.class, "A_foo", null, false);
		checkGetDeclaredMethod(ReflectionTest.class, "A_bar", null, false);
		checkGetDeclaredMethod(ReflectionTest.class, "foo", null, false);
		checkGetDeclaredMethod(ReflectionTest.class, "foo", new Class[] { String.class }, true);
		checkGetDeclaredMethod(ReflectionTest.class, "foo", new Class[] { Object.class }, false);
		checkGetDeclaredMethod(ReflectionTest.class, "bar", null, true);
		checkGetDeclaredMethods(ReflectionTest.class, 21);

		// Checking on an interface
		checkGetMethod(ReflectionTest_B.class, "<init>", null, false);
		checkGetMethod(ReflectionTest_B.class, "wait", null, false);
		checkGetMethod(ReflectionTest_B.class, "bar", null, true);
		checkGetMethod(ReflectionTest_B.class, "A_foo", null, true);
		checkGetMethod(ReflectionTest_B.class, "A_bar", null, false);
		checkGetMethods(ReflectionTest_B.class, 3);

		checkGetDeclaredMethod(ReflectionTest_B.class, "A_foo", null, false);
		checkGetDeclaredMethod(ReflectionTest_B.class, "A_bar", null, false);
		checkGetDeclaredMethod(ReflectionTest_B.class, "bar", null, true);
		checkGetDeclaredMethods(ReflectionTest_B.class, 1);

		// Checking on a class
		checkGetConstructor(ReflectionTest.class, null, true);
		checkGetConstructor(ReflectionTest.class, new Class[] { Integer.class }, false);
		checkGetConstructor(ReflectionTest.class, new Class[] { Integer.TYPE }, false);
		checkGetConstructor(ReflectionTest.class, new Class[] { Long.class }, true);
		checkGetConstructor(ReflectionTest.class, new Class[] { Long.TYPE }, true);
		checkGetConstructor(ReflectionTest.class, new Class[] { Double.class }, false);
		checkGetConstructor(ReflectionTest.class, new Class[] { Double.TYPE }, false);
		checkGetConstructors(ReflectionTest.class, 4);

		checkGetDeclaredConstructor(ReflectionTest.class, null, true);
		checkGetDeclaredConstructor(ReflectionTest.class, new Class[] { Integer.class }, true);
		checkGetDeclaredConstructor(ReflectionTest.class, new Class[] { Integer.TYPE }, true);
		checkGetDeclaredConstructor(ReflectionTest.class, new Class[] { Long.class }, true);
		checkGetDeclaredConstructor(ReflectionTest.class, new Class[] { Long.TYPE }, true);
		checkGetDeclaredConstructor(ReflectionTest.class, new Class[] { Double.class }, false);
		checkGetDeclaredConstructor(ReflectionTest.class, new Class[] { Double.TYPE }, false);
		checkGetDeclaredConstructors(ReflectionTest.class, 6);

		// Checking on an interface
		checkGetConstructor(ReflectionTest_B.class, null, false);
		checkGetConstructors(ReflectionTest_B.class, 0);

		checkGetDeclaredConstructor(ReflectionTest_B.class, null, false);
		checkGetDeclaredConstructors(ReflectionTest_B.class, 0);

		// Check more specific stuff
		checkGetMethodExceptionTypes();
		checkGetModifiers();
		checkGetStaticFieldValue();
		checkConstructorInvocation();
	}
	catch (Throwable e) {
		System.out.println("FAILED: " + e);
		return;
	}

	System.out.println("PASSED: All ok");
}

};
