View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
4    */
5   package mockit.internal.faking;
6   
7   import edu.umd.cs.findbugs.annotations.NonNull;
8   import edu.umd.cs.findbugs.annotations.Nullable;
9   
10  import java.util.ArrayList;
11  import java.util.IdentityHashMap;
12  import java.util.Iterator;
13  import java.util.List;
14  import java.util.Map;
15  import java.util.Map.Entry;
16  import java.util.regex.Pattern;
17  
18  import mockit.internal.util.ClassLoad;
19  
20  import org.checkerframework.checker.index.qual.NonNegative;
21  
22  /**
23   * Holds state associated with fake class containing {@linkplain mockit.Mock annotated fakes}.
24   */
25  public final class FakeStates {
26      private static final Pattern SPACE = Pattern.compile(" ");
27  
28      /**
29       * For each fake instance and each <code>@Mock</code> method containing the <code>Invocation</code> parameter, a
30       * runtime state will be kept here.
31       */
32      @NonNull
33      private final Map<Object, List<FakeState>> fakesToFakeStates;
34      @NonNull
35      private final Map<Object, List<FakeState>> startupFakesToFakeStates;
36  
37      public FakeStates() {
38          startupFakesToFakeStates = new IdentityHashMap<>(2);
39          fakesToFakeStates = new IdentityHashMap<>(8);
40      }
41  
42      void addStartupFakeAndItsFakeStates(@NonNull Object fake, @NonNull List<FakeState> fakeStates) {
43          startupFakesToFakeStates.put(fake, fakeStates);
44      }
45  
46      void addFakeAndItsFakeStates(@NonNull Object fake, @NonNull List<FakeState> fakeStates) {
47          fakesToFakeStates.put(fake, fakeStates);
48      }
49  
50      public void copyFakeStates(@NonNull Object previousFake, @NonNull Object newFake) {
51          List<FakeState> fakeStates = fakesToFakeStates.get(previousFake);
52  
53          if (fakeStates != null) {
54              List<FakeState> copiedFakeStates = new ArrayList<>(fakeStates.size());
55  
56              for (FakeState fakeState : fakeStates) {
57                  copiedFakeStates.add(new FakeState(fakeState));
58              }
59  
60              fakesToFakeStates.put(newFake, copiedFakeStates);
61          }
62      }
63  
64      public void removeClassState(@NonNull Class<?> redefinedClass,
65              @Nullable String internalNameForOneOrMoreFakeClasses) {
66          removeFakeStates(redefinedClass);
67  
68          if (internalNameForOneOrMoreFakeClasses != null) {
69              if (internalNameForOneOrMoreFakeClasses.indexOf(' ') < 0) {
70                  removeFakeStates(internalNameForOneOrMoreFakeClasses);
71              } else {
72                  String[] fakeClassesInternalNames = SPACE.split(internalNameForOneOrMoreFakeClasses);
73  
74                  for (String fakeClassInternalName : fakeClassesInternalNames) {
75                      removeFakeStates(fakeClassInternalName);
76                  }
77              }
78          }
79      }
80  
81      private void removeFakeStates(@NonNull Class<?> redefinedClass) {
82          Iterator<List<FakeState>> itr = fakesToFakeStates.values().iterator();
83  
84          while (itr.hasNext()) {
85              List<FakeState> fakeStates = itr.next();
86              FakeState fakeState = fakeStates.get(0);
87  
88              if (fakeState.getRealClass() == redefinedClass) {
89                  fakeStates.clear();
90                  itr.remove();
91              }
92          }
93      }
94  
95      private void removeFakeStates(@NonNull String fakeClassInternalName) {
96          Class<?> fakeClass = ClassLoad.loadClass(fakeClassInternalName.replace('/', '.'));
97          Iterator<Entry<Object, List<FakeState>>> itr = fakesToFakeStates.entrySet().iterator();
98  
99          while (itr.hasNext()) {
100             Entry<Object, List<FakeState>> fakeAndFakeStates = itr.next();
101             Object fake = fakeAndFakeStates.getKey();
102 
103             if (fake.getClass() == fakeClass) {
104                 itr.remove();
105             }
106         }
107     }
108 
109     public boolean updateFakeState(@NonNull Object fake, @NonNegative int fakeStateIndex) {
110         FakeState fakeState = getFakeState(fake, fakeStateIndex);
111         return fakeState.update();
112     }
113 
114     @NonNull
115     FakeState getFakeState(@NonNull Object fake, @NonNegative int fakeStateIndex) {
116         List<FakeState> fakeStates = startupFakesToFakeStates.get(fake);
117 
118         if (fakeStates == null) {
119             fakeStates = fakesToFakeStates.get(fake);
120         }
121 
122         FakeState fakeState = fakeStates.get(fakeStateIndex);
123         assert fakeState != null;
124         return fakeState;
125     }
126 }