/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed 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.
 */

import other.PublicClass;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/*
 * Test field access through reflection.
 */
public class Main {
  public static void main(String[] args) {
    SubClass.main(null);

    try {
      GetNonexistent.main(null);
      System.err.println("Not expected to succeed");
    } catch (VerifyError fe) {
      // dalvik
      System.out.println("Got expected failure");
    } catch (NoSuchFieldError nsfe) {
      // reference
      System.out.println("Got expected failure");
    }

    try {
      Class c = Class.forName("SubClassUsingInaccessibleField");
      Object o = c.newInstance();
      c.getMethod("test").invoke(o, null);
    } catch (InvocationTargetException ite) {
      if (ite.getCause() instanceof IllegalAccessError) {
        System.out.println("Got expected failure");
      } else {
        System.out.println("Got unexpected failure " + ite.getCause());
      }
    } catch (Exception e) {
      System.out.println("Got unexpected failure " + e);
    }
  }

  /*
   * Get the field specified by "field" from "obj".
   *
   * "type" determines which "get" call is made, e.g. 'B' turns into
   * field.getByte().
   *
   * The "expectedException" must match the class of the exception thrown,
   * or be null if no exception was expected.
   *
   * On success, the boxed value retrieved is returned.
   */
  public Object getValue(Field field, Object obj, char type,
      Class expectedException) {
    Object result = null;
    try {
      switch (type) {
        case 'Z':
          result = field.getBoolean(obj);
          break;
        case 'B':
          result = field.getByte(obj);
          break;
        case 'S':
          result = field.getShort(obj);
          break;
        case 'C':
          result = field.getChar(obj);
          break;
        case 'I':
          result = field.getInt(obj);
          break;
        case 'J':
          result = field.getLong(obj);
          break;
        case 'F':
          result = field.getFloat(obj);
          break;
        case 'D':
          result = field.getDouble(obj);
          break;
        case 'L':
          result = field.get(obj);
          break;
        default:
          throw new RuntimeException("bad type '" + type + "'");
      }

      /* success; expected? */
      if (expectedException != null) {
        System.err.println("ERROR: call succeeded for field " + field +
            " with a read of type '" + type +
            "', was expecting " + expectedException);
        Thread.dumpStack();
      }
    } catch (Exception ex) {
      if (expectedException == null) {
        System.err.println("ERROR: call failed unexpectedly: "
            + ex.getClass());
        ex.printStackTrace();
      } else {
        if (!expectedException.equals(ex.getClass())) {
          System.err.println("ERROR: incorrect exception: wanted "
              + expectedException.getName() + ", got "
              + ex.getClass());
          ex.printStackTrace();
        }
      }
    }

    return result;
  }
}

/*
 * Local class with some fields.
 */
class SamePackage {
  public boolean samePackagePublicBooleanInstanceField = true;
  public byte samePackagePublicByteInstanceField = 2;
  public char samePackagePublicCharInstanceField = 3;
  public short samePackagePublicShortInstanceField = 4;
  public int samePackagePublicIntInstanceField = 5;
  public long samePackagePublicLongInstanceField = 6;
  public float samePackagePublicFloatInstanceField = 7.0f;
  public double samePackagePublicDoubleInstanceField = 8.0;
  public Object samePackagePublicObjectInstanceField = "9";

  protected boolean samePackageProtectedBooleanInstanceField = true;
  protected byte samePackageProtectedByteInstanceField = 10;
  protected char samePackageProtectedCharInstanceField = 11;
  protected short samePackageProtectedShortInstanceField = 12;
  protected int samePackageProtectedIntInstanceField = 13;
  protected long samePackageProtectedLongInstanceField = 14;
  protected float samePackageProtectedFloatInstanceField = 15.0f;
  protected double samePackageProtectedDoubleInstanceField = 16.0;
  protected Object samePackageProtectedObjectInstanceField = "17";

  private boolean samePackagePrivateBooleanInstanceField = true;
  private byte samePackagePrivateByteInstanceField = 18;
  private char samePackagePrivateCharInstanceField = 19;
  private short samePackagePrivateShortInstanceField = 20;
  private int samePackagePrivateIntInstanceField = 21;
  private long samePackagePrivateLongInstanceField = 22;
  private float samePackagePrivateFloatInstanceField = 23.0f;
  private double samePackagePrivateDoubleInstanceField = 24.0;
  private Object samePackagePrivateObjectInstanceField = "25";

  /* package */ boolean samePackagePackageBooleanInstanceField = true;
  /* package */ byte samePackagePackageByteInstanceField = 26;
  /* package */ char samePackagePackageCharInstanceField = 27;
  /* package */ short samePackagePackageShortInstanceField = 28;
  /* package */ int samePackagePackageIntInstanceField = 29;
  /* package */ long samePackagePackageLongInstanceField = 30;
  /* package */ float samePackagePackageFloatInstanceField = 31.0f;
  /* package */ double samePackagePackageDoubleInstanceField = 32.0;
  /* package */ Object samePackagePackageObjectInstanceField = "33";

  public static boolean samePackagePublicBooleanStaticField = true;
  public static byte samePackagePublicByteStaticField = 34;
  public static char samePackagePublicCharStaticField = 35;
  public static short samePackagePublicShortStaticField = 36;
  public static int samePackagePublicIntStaticField = 37;
  public static long samePackagePublicLongStaticField = 38;
  public static float samePackagePublicFloatStaticField = 39.0f;
  public static double samePackagePublicDoubleStaticField = 40.0;
  public static Object samePackagePublicObjectStaticField = "41";

  protected static boolean samePackageProtectedBooleanStaticField = true;
  protected static byte samePackageProtectedByteStaticField = 42;
  protected static char samePackageProtectedCharStaticField = 43;
  protected static short samePackageProtectedShortStaticField = 44;
  protected static int samePackageProtectedIntStaticField = 45;
  protected static long samePackageProtectedLongStaticField = 46;
  protected static float samePackageProtectedFloatStaticField = 47.0f;
  protected static double samePackageProtectedDoubleStaticField = 48.0;
  protected static Object samePackageProtectedObjectStaticField = "49";

  private static boolean samePackagePrivateBooleanStaticField = true;
  private static byte samePackagePrivateByteStaticField = 50;
  private static char samePackagePrivateCharStaticField = 51;
  private static short samePackagePrivateShortStaticField = 52;
  private static int samePackagePrivateIntStaticField = 53;
  private static long samePackagePrivateLongStaticField = 54;
  private static float samePackagePrivateFloatStaticField = 55.0f;
  private static double samePackagePrivateDoubleStaticField = 56.0;
  private static Object samePackagePrivateObjectStaticField = "57";

  /* package */ static boolean samePackagePackageBooleanStaticField = true;
  /* package */ static byte samePackagePackageByteStaticField = 58;
  /* package */ static char samePackagePackageCharStaticField = 59;
  /* package */ static short samePackagePackageShortStaticField = 60;
  /* package */ static int samePackagePackageIntStaticField = 61;
  /* package */ static long samePackagePackageLongStaticField = 62;
  /* package */ static float samePackagePackageFloatStaticField = 63.0f;
  /* package */ static double samePackagePackageDoubleStaticField = 64.0;
  /* package */ static Object samePackagePackageObjectStaticField = "65";

  public void samePublicMethod() { }
  protected void sameProtectedMethod() { }
  private void samePrivateMethod() { }
  /* package */ void samePackageMethod() { }
}

/*
 * This is a sub-class of other.PublicClass, which should be allowed to access
 * the various protected fields declared by other.PublicClass and its parent
 * other.ProtectedClass.
 */
class SubClass extends PublicClass {
  /*
   * Perform the various tests.
   *
   * localInst.getValue() is performed using an instance of Main as the
   * source of the reflection call.  otherInst.getValue() uses a subclass
   * of OtherPackage as the source.
   */
  public static void main(String[] args) {
    SubClass subOther = new SubClass();
    subOther.doDirectTests();
    subOther.doReflectionTests();
  }

  private static void check(boolean b) {
    if (!b) {
      throw new Error("Test failed");
    }
  }

  public void doDirectTests() {
    check(otherProtectedClassPublicBooleanInstanceField == true);
    check(otherProtectedClassPublicByteInstanceField == 2);
    check(otherProtectedClassPublicCharInstanceField == 3);
    check(otherProtectedClassPublicShortInstanceField == 4);
    check(otherProtectedClassPublicIntInstanceField == 5);
    check(otherProtectedClassPublicLongInstanceField == 6);
    check(otherProtectedClassPublicFloatInstanceField == 7.0f);
    check(otherProtectedClassPublicDoubleInstanceField == 8.0);
    check(otherProtectedClassPublicObjectInstanceField == "9");

    check(otherProtectedClassProtectedBooleanInstanceField == true);
    check(otherProtectedClassProtectedByteInstanceField == 10);
    check(otherProtectedClassProtectedCharInstanceField == 11);
    check(otherProtectedClassProtectedShortInstanceField == 12);
    check(otherProtectedClassProtectedIntInstanceField == 13);
    check(otherProtectedClassProtectedLongInstanceField == 14);
    check(otherProtectedClassProtectedFloatInstanceField == 15.0f);
    check(otherProtectedClassProtectedDoubleInstanceField == 16.0);
    check(otherProtectedClassProtectedObjectInstanceField == "17");

    // check(otherProtectedClassPrivateBooleanInstanceField == true);
    // check(otherProtectedClassPrivateByteInstanceField == 18);
    // check(otherProtectedClassPrivateCharInstanceField == 19);
    // check(otherProtectedClassPrivateShortInstanceField == 20);
    // check(otherProtectedClassPrivateIntInstanceField == 21);
    // check(otherProtectedClassPrivateLongInstanceField == 22);
    // check(otherProtectedClassPrivateFloatInstanceField == 23.0f);
    // check(otherProtectedClassPrivateDoubleInstanceField == 24.0);
    // check(otherProtectedClassPrivateObjectInstanceField == "25");

    // check(otherProtectedClassPackageBooleanInstanceField == true);
    // check(otherProtectedClassPackageByteInstanceField == 26);
    // check(otherProtectedClassPackageCharInstanceField == 27);
    // check(otherProtectedClassPackageShortInstanceField == 28);
    // check(otherProtectedClassPackageIntInstanceField == 29);
    // check(otherProtectedClassPackageLongInstanceField == 30);
    // check(otherProtectedClassPackageFloatInstanceField == 31.0f);
    // check(otherProtectedClassPackageDoubleInstanceField == 32.0);
    // check(otherProtectedClassPackageObjectInstanceField == "33");

    check(otherProtectedClassPublicBooleanStaticField == true);
    check(otherProtectedClassPublicByteStaticField == 34);
    check(otherProtectedClassPublicCharStaticField == 35);
    check(otherProtectedClassPublicShortStaticField == 36);
    check(otherProtectedClassPublicIntStaticField == 37);
    check(otherProtectedClassPublicLongStaticField == 38);
    check(otherProtectedClassPublicFloatStaticField == 39.0f);
    check(otherProtectedClassPublicDoubleStaticField == 40.0);
    check(otherProtectedClassPublicObjectStaticField == "41");

    check(otherProtectedClassProtectedBooleanStaticField == true);
    check(otherProtectedClassProtectedByteStaticField == 42);
    check(otherProtectedClassProtectedCharStaticField == 43);
    check(otherProtectedClassProtectedShortStaticField == 44);
    check(otherProtectedClassProtectedIntStaticField == 45);
    check(otherProtectedClassProtectedLongStaticField == 46);
    check(otherProtectedClassProtectedFloatStaticField == 47.0f);
    check(otherProtectedClassProtectedDoubleStaticField == 48.0);
    check(otherProtectedClassProtectedObjectStaticField == "49");

    // check(otherProtectedClassPrivateBooleanStaticField == true);
    // check(otherProtectedClassPrivateByteStaticField == 50);
    // check(otherProtectedClassPrivateCharStaticField == 51);
    // check(otherProtectedClassPrivateShortStaticField == 52);
    // check(otherProtectedClassPrivateIntStaticField == 53);
    // check(otherProtectedClassPrivateLongStaticField == 54);
    // check(otherProtectedClassPrivateFloatStaticField == 55.0f);
    // check(otherProtectedClassPrivateDoubleStaticField == 56.0);
    // check(otherProtectedClassPrivateObjectStaticField == "57");

    // check(otherProtectedClassPackageBooleanStaticField == true);
    // check(otherProtectedClassPackageByteStaticField == 58);
    // check(otherProtectedClassPackageCharStaticField == 59);
    // check(otherProtectedClassPackageShortStaticField == 60);
    // check(otherProtectedClassPackageIntStaticField == 61);
    // check(otherProtectedClassPackageLongStaticField == 62);
    // check(otherProtectedClassPackageFloatStaticField == 63.0f);
    // check(otherProtectedClassPackageDoubleStaticField == 64.0);
    // check(otherProtectedClassPackageObjectStaticField == "65");

    check(otherPublicClassPublicBooleanInstanceField == true);
    check(otherPublicClassPublicByteInstanceField == -2);
    check(otherPublicClassPublicCharInstanceField == (char)-3);
    check(otherPublicClassPublicShortInstanceField == -4);
    check(otherPublicClassPublicIntInstanceField == -5);
    check(otherPublicClassPublicLongInstanceField == -6);
    check(otherPublicClassPublicFloatInstanceField == -7.0f);
    check(otherPublicClassPublicDoubleInstanceField == -8.0);
    check(otherPublicClassPublicObjectInstanceField == "-9");

    check(otherPublicClassProtectedBooleanInstanceField == true);
    check(otherPublicClassProtectedByteInstanceField == -10);
    check(otherPublicClassProtectedCharInstanceField == (char)-11);
    check(otherPublicClassProtectedShortInstanceField == -12);
    check(otherPublicClassProtectedIntInstanceField == -13);
    check(otherPublicClassProtectedLongInstanceField == -14);
    check(otherPublicClassProtectedFloatInstanceField == -15.0f);
    check(otherPublicClassProtectedDoubleInstanceField == -16.0);
    check(otherPublicClassProtectedObjectInstanceField == "-17");

    // check(otherPublicClassPrivateBooleanInstanceField == true);
    // check(otherPublicClassPrivateByteInstanceField == -18);
    // check(otherPublicClassPrivateCharInstanceField == (char)-19);
    // check(otherPublicClassPrivateShortInstanceField == -20);
    // check(otherPublicClassPrivateIntInstanceField == -21);
    // check(otherPublicClassPrivateLongInstanceField == -22);
    // check(otherPublicClassPrivateFloatInstanceField == -23.0f);
    // check(otherPublicClassPrivateDoubleInstanceField == -24.0);
    // check(otherPublicClassPrivateObjectInstanceField == "-25");

    // check(otherPublicClassPackageBooleanInstanceField == true);
    // check(otherPublicClassPackageByteInstanceField == -26);
    // check(otherPublicClassPackageCharInstanceField == (char)-27);
    // check(otherPublicClassPackageShortInstanceField == -28);
    // check(otherPublicClassPackageIntInstanceField == -29);
    // check(otherPublicClassPackageLongInstanceField == -30);
    // check(otherPublicClassPackageFloatInstanceField == -31.0f);
    // check(otherPublicClassPackageDoubleInstanceField == -32.0);
    // check(otherPublicClassPackageObjectInstanceField == "-33");

    check(otherPublicClassPublicBooleanStaticField == true);
    check(otherPublicClassPublicByteStaticField == -34);
    check(otherPublicClassPublicCharStaticField == (char)-35);
    check(otherPublicClassPublicShortStaticField == -36);
    check(otherPublicClassPublicIntStaticField == -37);
    check(otherPublicClassPublicLongStaticField == -38);
    check(otherPublicClassPublicFloatStaticField == -39.0f);
    check(otherPublicClassPublicDoubleStaticField == -40.0);
    check(otherPublicClassPublicObjectStaticField == "-41");

    check(otherPublicClassProtectedBooleanStaticField == true);
    check(otherPublicClassProtectedByteStaticField == -42);
    check(otherPublicClassProtectedCharStaticField == (char)-43);
    check(otherPublicClassProtectedShortStaticField == -44);
    check(otherPublicClassProtectedIntStaticField == -45);
    check(otherPublicClassProtectedLongStaticField == -46);
    check(otherPublicClassProtectedFloatStaticField == -47.0f);
    check(otherPublicClassProtectedDoubleStaticField == -48.0);
    check(otherPublicClassProtectedObjectStaticField == "-49");

    // check(otherPublicClassPrivateBooleanStaticField == true);
    // check(otherPublicClassPrivateByteStaticField == -50);
    // check(otherPublicClassPrivateCharStaticField == (char)-51);
    // check(otherPublicClassPrivateShortStaticField == -52);
    // check(otherPublicClassPrivateIntStaticField == -53);
    // check(otherPublicClassPrivateLongStaticField == -54);
    // check(otherPublicClassPrivateFloatStaticField == -55.0f);
    // check(otherPublicClassPrivateDoubleStaticField == -56.0);
    // check(otherPublicClassPrivateObjectStaticField == "-57");

    // check(otherPublicClassPackageBooleanStaticField == true);
    // check(otherPublicClassPackageByteStaticField == -58);
    // check(otherPublicClassPackageCharStaticField == (char)-59);
    // check(otherPublicClassPackageShortStaticField == -60);
    // check(otherPublicClassPackageIntStaticField == -61);
    // check(otherPublicClassPackageLongStaticField == -62);
    // check(otherPublicClassPackageFloatStaticField == -63.0f);
    // check(otherPublicClassPackageDoubleStaticField == -64.0);
    // check(otherPublicClassPackageObjectStaticField == "-65");

    SamePackage s = new SamePackage();
    check(s.samePackagePublicBooleanInstanceField == true);
    check(s.samePackagePublicByteInstanceField == 2);
    check(s.samePackagePublicCharInstanceField == 3);
    check(s.samePackagePublicShortInstanceField == 4);
    check(s.samePackagePublicIntInstanceField == 5);
    check(s.samePackagePublicLongInstanceField == 6);
    check(s.samePackagePublicFloatInstanceField == 7.0f);
    check(s.samePackagePublicDoubleInstanceField == 8.0);
    check(s.samePackagePublicObjectInstanceField == "9");

    check(s.samePackageProtectedBooleanInstanceField == true);
    check(s.samePackageProtectedByteInstanceField == 10);
    check(s.samePackageProtectedCharInstanceField == 11);
    check(s.samePackageProtectedShortInstanceField == 12);
    check(s.samePackageProtectedIntInstanceField == 13);
    check(s.samePackageProtectedLongInstanceField == 14);
    check(s.samePackageProtectedFloatInstanceField == 15.0f);
    check(s.samePackageProtectedDoubleInstanceField == 16.0);
    check(s.samePackageProtectedObjectInstanceField == "17");

    // check(s.samePackagePrivateBooleanInstanceField == true);
    // check(s.samePackagePrivateByteInstanceField == 18);
    // check(s.samePackagePrivateCharInstanceField == 19);
    // check(s.samePackagePrivateShortInstanceField == 20);
    // check(s.samePackagePrivateIntInstanceField == 21);
    // check(s.samePackagePrivateLongInstanceField == 22);
    // check(s.samePackagePrivateFloatInstanceField == 23.0f);
    // check(s.samePackagePrivateDoubleInstanceField == 24.0);
    // check(s.samePackagePrivateObjectInstanceField == "25");

    check(s.samePackagePackageBooleanInstanceField == true);
    check(s.samePackagePackageByteInstanceField == 26);
    check(s.samePackagePackageCharInstanceField == 27);
    check(s.samePackagePackageShortInstanceField == 28);
    check(s.samePackagePackageIntInstanceField == 29);
    check(s.samePackagePackageLongInstanceField == 30);
    check(s.samePackagePackageFloatInstanceField == 31.0f);
    check(s.samePackagePackageDoubleInstanceField == 32.0);
    check(s.samePackagePackageObjectInstanceField == "33");

    check(SamePackage.samePackagePublicBooleanStaticField == true);
    check(SamePackage.samePackagePublicByteStaticField == 34);
    check(SamePackage.samePackagePublicCharStaticField == 35);
    check(SamePackage.samePackagePublicShortStaticField == 36);
    check(SamePackage.samePackagePublicIntStaticField == 37);
    check(SamePackage.samePackagePublicLongStaticField == 38);
    check(SamePackage.samePackagePublicFloatStaticField == 39.0f);
    check(SamePackage.samePackagePublicDoubleStaticField == 40.0);
    check(SamePackage.samePackagePublicObjectStaticField == "41");

    check(SamePackage.samePackageProtectedBooleanStaticField == true);
    check(SamePackage.samePackageProtectedByteStaticField == 42);
    check(SamePackage.samePackageProtectedCharStaticField == 43);
    check(SamePackage.samePackageProtectedShortStaticField == 44);
    check(SamePackage.samePackageProtectedIntStaticField == 45);
    check(SamePackage.samePackageProtectedLongStaticField == 46);
    check(SamePackage.samePackageProtectedFloatStaticField == 47.0f);
    check(SamePackage.samePackageProtectedDoubleStaticField == 48.0);
    check(SamePackage.samePackageProtectedObjectStaticField == "49");

    // check(SamePackage.samePackagePrivateBooleanStaticField == true);
    // check(SamePackage.samePackagePrivateByteStaticField == 50);
    // check(SamePackage.samePackagePrivateCharStaticField == 51);
    // check(SamePackage.samePackagePrivateShortStaticField == 52);
    // check(SamePackage.samePackagePrivateIntStaticField == 53);
    // check(SamePackage.samePackagePrivateLongStaticField == 54);
    // check(SamePackage.samePackagePrivateFloatStaticField == 55.0f);
    // check(SamePackage.samePackagePrivateDoubleStaticField == 56.0);
    // check(SamePackage.samePackagePrivateObjectStaticField == "57");

    check(SamePackage.samePackagePackageBooleanStaticField == true);
    check(SamePackage.samePackagePackageByteStaticField == 58);
    check(SamePackage.samePackagePackageCharStaticField == 59);
    check(SamePackage.samePackagePackageShortStaticField == 60);
    check(SamePackage.samePackagePackageIntStaticField == 61);
    check(SamePackage.samePackagePackageLongStaticField == 62);
    check(SamePackage.samePackagePackageFloatStaticField == 63.0f);
    check(SamePackage.samePackagePackageDoubleStaticField == 64.0);
    check(SamePackage.samePackagePackageObjectStaticField == "65");
  }

  private static boolean compatibleTypes(char srcType, char dstType) {
    switch (dstType) {
      case 'Z':
      case 'C':
      case 'B':
        return srcType == dstType;
      case 'S':
        return srcType == 'B' || srcType == 'S';
      case 'I':
        return srcType == 'B' || srcType == 'C' || srcType == 'S' || srcType == 'I';
      case 'J':
        return srcType == 'B' || srcType == 'C' || srcType == 'S' || srcType == 'I' ||
        srcType == 'J';
      case 'F':
        return srcType == 'B' || srcType == 'C' || srcType == 'S' || srcType == 'I' ||
        srcType == 'J' || srcType == 'F';
      case 'D':
        return srcType == 'B' || srcType == 'C' || srcType == 'S' || srcType == 'I' ||
        srcType == 'J' || srcType == 'F' || srcType == 'D';
      case 'L':
        return true;
      default:
        throw new Error("Unexpected type char " + dstType);
    }
  }

  public void doReflectionTests() {
    String typeChars = "ZBCSIJFDL";
    String fieldNameForTypeChar[] = {
        "Boolean",
        "Byte",
        "Char",
        "Short",
        "Int",
        "Long",
        "Float",
        "Double",
        "Object"
    };

    Main localInst = new Main();
    SamePackage samePkgInst = new SamePackage();
    PublicClass otherPkgInst = new PublicClass();
    Object plainObj = new Object();

    for (int round = 0; round < 3; round++) {
      Object validInst;
      Field[] fields;
      Method[] methods;
      boolean same_package = false;
      boolean protected_class = false;
      switch (round) {
        case 0:
          validInst = new SamePackage();
          fields = SamePackage.class.getDeclaredFields();
          check(fields.length == 72);
          methods = SamePackage.class.getDeclaredMethods();
          check(methods.length == 4);
          same_package = true;
          break;
        case 1:
          validInst = new PublicClass();
          fields = PublicClass.class.getDeclaredFields();
          check(fields.length == 72);
          methods = PublicClass.class.getDeclaredMethods();
          check(methods.length == 4);
          break;
        default:
          validInst = new PublicClass();
          fields = PublicClass.class.getSuperclass().getDeclaredFields();
          check(fields.length == 72);
          methods = PublicClass.class.getSuperclass().getDeclaredMethods();
          check(methods.length == 4);
          protected_class = true;
          break;
      }
      for (Field f : fields) {
        char typeChar = '?';
        for (int i = 0; i < fieldNameForTypeChar.length; i++) {
          if (f.getName().contains(fieldNameForTypeChar[i])) {
            typeChar = typeChars.charAt(i);
            break;
          }
        }
        // Check access or lack of to field.
        Class<?> subClassAccessExceptionClass = null;
        if ((f.getName().contains("Private") ||
            (!same_package && f.getName().contains("Package")) ||
            (!same_package && f.getName().contains("Protected"))) &&
            !(protected_class && f.getName().contains("Public"))) {
          subClassAccessExceptionClass = IllegalAccessException.class;
        }
        Class<?> mainClassAccessExceptionClass = null;
        if ((f.getName().contains("Private") ||
            (!same_package && f.getName().contains("Package")) ||
            (!same_package && f.getName().contains("Protected"))) &&
            !(protected_class && f.getName().contains("Public"))) {
          mainClassAccessExceptionClass = IllegalAccessException.class;
        }

        this.getValue(f, validInst, typeChar, subClassAccessExceptionClass);
        localInst.getValue(f, validInst, typeChar, mainClassAccessExceptionClass);

        // Check things that can get beyond the IllegalAccessException.
        if (subClassAccessExceptionClass == null) {
          // Check NPE.
          Class<?> npeClass = null;
          if (!f.getName().contains("Static")) {
            npeClass = NullPointerException.class;
          }

          this.getValue(f, null, typeChar, npeClass);
          if (mainClassAccessExceptionClass == null) {
            localInst.getValue(f, null, typeChar, npeClass);
          }

          // Check access of wrong field type for valid instance.
          for (int i = 0; i < typeChars.length(); i++) {
            char otherChar = typeChars.charAt(i);
            Class<?> illArgClass = compatibleTypes(typeChar, otherChar) ?
                null : IllegalArgumentException.class;
            this.getValue(f, validInst, otherChar, illArgClass);
            if (mainClassAccessExceptionClass == null) {
              localInst.getValue(f, validInst, otherChar, illArgClass);
            }
          }

          if (!f.getName().contains("Static")) {
            // Wrong object.
            this.getValue(f, plainObj, typeChar, IllegalArgumentException.class);
            if (mainClassAccessExceptionClass == null) {
              localInst.getValue(f, plainObj, typeChar, IllegalArgumentException.class);
            }
          }
        }
      }

      for (Method m : methods) {
        Class<?> subClassAccessExceptionClass = null;
        if (m.getName().contains("Private") ||
            (!same_package && m.getName().contains("Package")) ||
            (!same_package && m.getName().contains("Protected"))) {
          subClassAccessExceptionClass = IllegalAccessException.class;
        }
        this.invoke(m, validInst, subClassAccessExceptionClass);
      }
    }
    System.out.println("good");
  }

  /*
   * [this is a clone of Main.getValue() -- the class issuing the
   * reflection call is significant]
   */
  public Object getValue(Field field, Object obj, char type,
      Class expectedException) {
    Object result = null;
    try {
      switch (type) {
        case 'Z':
          result = field.getBoolean(obj);
          break;
        case 'B':
          result = field.getByte(obj);
          break;
        case 'S':
          result = field.getShort(obj);
          break;
        case 'C':
          result = field.getChar(obj);
          break;
        case 'I':
          result = field.getInt(obj);
          break;
        case 'J':
          result = field.getLong(obj);
          break;
        case 'F':
          result = field.getFloat(obj);
          break;
        case 'D':
          result = field.getDouble(obj);
          break;
        case 'L':
          result = field.get(obj);
          break;
        default:
          throw new RuntimeException("bad type '" + type + "'");
      }

      /* success; expected? */
      if (expectedException != null) {
        System.err.println("ERROR: call succeeded for field " + field +
            " with a read of type '" + type +
            "', was expecting " + expectedException);
        Thread.dumpStack();
      }
    } catch (Exception ex) {
      if (expectedException == null) {
        System.err.println("ERROR: call failed unexpectedly: "
            + ex.getClass());
        ex.printStackTrace();
      } else {
        if (!expectedException.equals(ex.getClass())) {
          System.err.println("ERROR: incorrect exception: wanted "
              + expectedException.getName() + ", got "
              + ex.getClass());
          ex.printStackTrace();
        }
      }
    }

    return result;
  }

  public Object invoke(Method method, Object obj, Class expectedException) {
    Object result = null;
    try {
      result = method.invoke(obj);
      /* success; expected? */
      if (expectedException != null) {
        System.err.println("ERROR: call succeeded for method " + method + "', was expecting " +
                           expectedException);
        Thread.dumpStack();
      }
    } catch (Exception ex) {
      if (expectedException == null) {
        System.err.println("ERROR: call failed unexpectedly: " + ex.getClass());
        ex.printStackTrace();
      } else {
        if (!expectedException.equals(ex.getClass())) {
          System.err.println("ERROR: incorrect exception: wanted " + expectedException.getName() +
                             ", got " + ex.getClass());
          ex.printStackTrace();
        }
      }
    }
    return result;
  }
}
