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.expectations.argumentMatching;
6   
7   import edu.umd.cs.findbugs.annotations.NonNull;
8   import edu.umd.cs.findbugs.annotations.Nullable;
9   
10  import java.lang.reflect.Array;
11  import java.lang.reflect.Method;
12  
13  import mockit.internal.util.ObjectMethods;
14  
15  public final class ArgumentMismatch {
16      @NonNull
17      private final StringBuilder out = new StringBuilder(50);
18      @Nullable
19      private String parameterType;
20  
21      @Nullable
22      public String getParameterType() {
23          return parameterType;
24      }
25  
26      @Override
27      public String toString() {
28          return out.toString();
29      }
30  
31      @NonNull
32      public ArgumentMismatch append(char c) {
33          out.append(c);
34          return this;
35      }
36  
37      @NonNull
38      public ArgumentMismatch append(int i) {
39          out.append(i);
40          return this;
41      }
42  
43      @NonNull
44      public ArgumentMismatch append(double d) {
45          out.append(d);
46          return this;
47      }
48  
49      @NonNull
50      public ArgumentMismatch append(@Nullable CharSequence str) {
51          out.append(str);
52          return this;
53      }
54  
55      public void appendFormatted(@Nullable String parameterTypeName, @Nullable Object argumentValue,
56              @Nullable ArgumentMatcher<?> matcher) {
57          if (matcher == null) {
58              appendFormatted(argumentValue);
59          } else {
60              parameterType = parameterTypeName;
61              matcher.writeMismatchPhrase(this);
62          }
63      }
64  
65      @SuppressWarnings("OverlyComplexMethod")
66      void appendFormatted(@Nullable Object value) {
67          if (value == null) {
68              out.append("null");
69          } else if (value instanceof CharSequence) {
70              appendCharacters((CharSequence) value);
71          } else if (value instanceof Character) {
72              out.append('\'');
73              appendEscapedOrPlainCharacter('\'', (Character) value);
74              out.append('\'');
75          } else if (value instanceof Byte) {
76              out.append(value).append('b');
77          } else if (value instanceof Short) {
78              out.append(value).append('s');
79          } else if (value instanceof Long) {
80              out.append(value).append('L');
81          } else if (value instanceof Float) {
82              out.append(value).append('F');
83          } else if (value instanceof Number || value instanceof Boolean) {
84              out.append(value);
85          } else if (value.getClass().isArray()) {
86              appendArray(value);
87          } else if (value instanceof ArgumentMatcher) {
88              ((ArgumentMatcher<?>) value).writeMismatchPhrase(this);
89          } else {
90              appendArbitraryArgument(value);
91          }
92      }
93  
94      private void appendArray(@NonNull Object array) {
95          out.append('[');
96          String separator = "";
97  
98          for (int i = 0, n = Array.getLength(array); i < n; i++) {
99              Object nextValue = Array.get(array, i);
100             out.append(separator);
101             appendFormatted(nextValue);
102             separator = ", ";
103         }
104 
105         out.append(']');
106     }
107 
108     private void appendCharacters(@NonNull CharSequence characters) {
109         out.append('"');
110 
111         for (int i = 0, n = characters.length(); i < n; i++) {
112             char c = characters.charAt(i);
113             appendEscapedOrPlainCharacter('"', c);
114         }
115 
116         out.append('"');
117     }
118 
119     private void appendEscapedOrPlainCharacter(char quoteCharacter, char c) {
120         if (c == quoteCharacter) {
121             out.append('\\').append(c);
122         } else {
123             switch (c) {
124                 case '\t':
125                     out.append("\\t");
126                     break;
127                 case '\n':
128                     out.append("\\n");
129                     break;
130                 case '\r':
131                     out.append("\\r");
132                     break;
133                 default:
134                     out.append(c);
135             }
136         }
137     }
138 
139     private void appendArbitraryArgument(@NonNull Object value) {
140         Class<?> valueClass = value.getClass();
141 
142         Method toStringMethod;
143         try {
144             toStringMethod = valueClass.getMethod("toString");
145         } catch (NoSuchMethodException ignored) {
146             return;
147         }
148 
149         if (toStringMethod.getDeclaringClass() == Object.class) {
150             out.append(value);
151         } else {
152             String valueAsString = value.toString();
153 
154             if (valueAsString != null && !valueAsString.isEmpty()) {
155                 appendCharacters(valueAsString);
156             } else {
157                 out.append(ObjectMethods.objectIdentity(value));
158             }
159         }
160     }
161 
162     public void appendFormatted(@NonNull Object[] values) {
163         String separator = "";
164 
165         for (Object value : values) {
166             append(separator).appendFormatted(value);
167             separator = ", ";
168         }
169     }
170 }