/*
 * JMockit Expectations
 * Copyright (c) 2006-2009 Rogério Liesenfeld
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package mockit.internal.expectations.mocking;

import java.lang.reflect.*;
import static java.lang.reflect.Modifier.*;

import static mockit.external.asm.Opcodes.*;

public abstract class FieldTypeRedefinitions extends TypeRedefinitions
{
   private static final int FIELD_ACCESS_MASK = ACC_SYNTHETIC + ACC_STATIC;
   private static final String EXCLUDED_BASE_CLASSES =
      "mockit.Expectations mockit.integration.junit4.JMockitTest " +
      "mockit.integration.junit3.JMockitTestCase";

   protected Field field;
   protected MockedType typeMetadata;
   protected boolean finalField;
   private int fieldModifiers;

   protected FieldTypeRedefinitions(Object objectWithMockFields)
   {
      super(objectWithMockFields);
   }

   protected final void redefineFieldTypes(Class<?> classWithMockFields, boolean isTestClass)
   {
      Class<?> superClass = classWithMockFields.getSuperclass();

      if (
         superClass != null && superClass != Object.class &&
         !EXCLUDED_BASE_CLASSES.contains(superClass.getName())
      ) {
         redefineFieldTypes(superClass, isTestClass);
      }

      Field[] fields = classWithMockFields.getDeclaredFields();

      for (Field candidateField : fields) {
         fieldModifiers = candidateField.getModifiers();

         if ((fieldModifiers & FIELD_ACCESS_MASK) == 0) {
            field = candidateField;
            redefineFieldType(isTestClass);
         }
      }
   }

   private void redefineFieldType(boolean fromTestClass)
   {
      typeMetadata = new MockedType(field, fromTestClass);

      if (typeMetadata.isMockField()) {
         finalField = isFinal(fieldModifiers);

         TypeRedefinition typeRedefinition = redefineTypeForMockField();
         typeMetadata.mockingCfg = typeRedefinition.mockingCfg;
         typeMetadata.mockConstructorInfo = typeRedefinition.mockConstructorInfo;
         typesRedefined++;

         registerCaptureOfNewInstances();
      }
   }

   protected abstract TypeRedefinition redefineTypeForMockField();

   @Override
   public final CaptureOfNewInstancesForFields getCaptureOfNewInstances()
   {
      return (CaptureOfNewInstancesForFields) captureOfNewInstances;
   }

   private void registerCaptureOfNewInstances()
   {
      if (typeMetadata.getMaxInstancesToCapture() <= 0) {
         return;
      }

      if (captureOfNewInstances == null) {
         captureOfNewInstances = new CaptureOfNewInstancesForFields();
      }

      getCaptureOfNewInstances().registerCaptureOfNewInstances(typeMetadata);
   }

   /**
    * Returns true iff the mock instance concrete class is not mocked in some test, ie it's a class
    * which only appears in the code under test.
    */
   public abstract boolean captureNewInstanceForApplicableMockField(Object mock);
}

@@ -20,16 +20,32 @@
 ctations
+ & Verifications
 %0D%0A * Cop
@@ -66,10 +66,10 @@
 6-20
+1
 0
-9
  Rog
@@ -1820,40 +1820,8 @@
 ld;%0D
-%0A   private int fieldModifiers;%0D
 %0A%0D%0A 
@@ -2436,24 +2436,28 @@
 %7B%0D%0A         
+int 
 fieldModifie
@@ -2624,33 +2624,76 @@
 Type(isTestClass
-)
+, fieldModifiers);%0D%0A            field = null
 ;%0D%0A         %7D%0D%0A 
@@ -2755,32 +2755,47 @@
 an fromTestClass
+, int modifiers
 )%0D%0A   %7B%0D%0A      t
@@ -2920,22 +2920,17 @@
 isFinal(
-fieldM
+m
 odifiers
@@ -3230,32 +3230,62 @@
 ces();%0D%0A      %7D%0D
+%0A%0D%0A      typeMetadata = null;%0D
 %0A   %7D%0D%0A%0D%0A   prot

Implemented clean-up of some transitory instance state.