1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.joda.beans.test;
17
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Map;
21
22 import org.joda.beans.Bean;
23 import org.joda.beans.MetaProperty;
24
25
26
27
28 class BeanComparisonError extends AssertionError {
29
30
31 private static final long serialVersionUID = 1L;
32
33
34
35
36 private final Bean expected;
37
38
39
40 private final Bean actual;
41
42
43
44
45
46
47
48
49
50 public BeanComparisonError(final String message, final int maxErrors, final Bean expected, final Bean actual) {
51 super(buildMessage(message, maxErrors, expected, actual));
52 this.expected = expected;
53 this.actual = actual;
54 }
55
56
57
58
59
60
61
62
63
64
65
66 private static String buildMessage(final String message, final int maxErrors, final Bean expected, final Bean actual) {
67 List<String> diffs = new ArrayList<String>();
68 buildMessage(diffs, "", expected, actual);
69 StringBuilder buf = new StringBuilder();
70 buf.append(message != null ? message + ": " : "");
71 buf.append("Bean did not equal expected. Differences:");
72 int size = diffs.size();
73 if (size > maxErrors) {
74 diffs = diffs.subList(0, maxErrors);
75 }
76 for (String diff : diffs) {
77 buf.append('\n').append(diff);
78 }
79 if (size > maxErrors) {
80 buf.append("\n...and " + (size - 10) + " more differences");
81 }
82 return buf.toString();
83 }
84
85 private static void buildMessage(final List<String> diffs, final String prefix, final Object expected, final Object actual) {
86 if (expected == null && actual == null) {
87 return;
88 }
89 if (expected == null && actual != null) {
90 diffs.add(prefix + ": Expected null, but was " + buildSummary(actual, true));
91 return;
92 }
93 if (expected != null && actual == null) {
94 diffs.add(prefix + ": Was null, but expected " + buildSummary(expected, true));
95 return;
96 }
97 if (expected.getClass() != actual.getClass()) {
98 diffs.add(prefix + ": Class differs, expected " + buildSummary(expected, true) + " but was " + buildSummary(actual, true));
99 return;
100 }
101 if (expected instanceof List && actual instanceof List) {
102 List<?> expectedList = (List<?>) expected;
103 List<?> actualList = (List<?>) actual;
104 if (expectedList.size() != actualList.size()) {
105 diffs.add(prefix + ": List size differs, expected " + expectedList.size() + " but was " + actualList.size());
106 return;
107 }
108 for (int i = 0; i < expectedList.size(); i++) {
109 buildMessage(diffs, prefix + '[' + i + "]", expectedList.get(i), actualList.get(i));
110 }
111 return;
112 }
113 if (expected instanceof Map && actual instanceof Map) {
114 Map<?, ?> expectedMap = (Map<?, ?>) expected;
115 Map<?, ?> actualMap = (Map<?, ?>) actual;
116 if (expectedMap.size() != actualMap.size()) {
117 diffs.add(prefix + ": Map size differs, expected " + expectedMap.size() + " but was " + actualMap.size());
118 return;
119 }
120 if (expectedMap.keySet().equals(actualMap.keySet()) == false) {
121 diffs.add(prefix + ": Map keyset differs, expected " + buildSummary(expectedMap.keySet(), false) + " but was " + buildSummary(actualMap.keySet(), false));
122 return;
123 }
124 for (Object key : expectedMap.keySet()) {
125 buildMessage(diffs, prefix + '[' + key + "]", expectedMap.get(key), actualMap.get(key));
126 }
127 return;
128 }
129 if (expected.getClass() != actual.getClass()) {
130 diffs.add(prefix + ": Class differs, expected " + buildSummary(expected, true) + " but was " + buildSummary(actual, true));
131 return;
132 }
133 if (expected instanceof Bean) {
134 for (MetaProperty<?> prop : ((Bean) expected).metaBean().metaPropertyIterable()) {
135 buildMessage(diffs, prefix + '.' + prop.name(), prop.get((Bean) expected), prop.get((Bean) actual));
136 }
137 return;
138 }
139 if (expected.equals(actual) == false) {
140 diffs.add(prefix + ": Content differs, expected " + buildSummary(expected, true) + " but was " + buildSummary(actual, false));
141 return;
142 }
143 return;
144 }
145
146
147
148
149
150
151 private static String buildSummary(final Object obj, final boolean includeType) {
152 String type = obj.getClass().getSimpleName();
153 String toStr = obj.toString();
154 if (toStr.length() > 60) {
155 toStr = toStr.substring(0, 57) + "...";
156 }
157 return (includeType ? type + " " : "") + "<" + toStr + ">";
158 }
159
160
161
162
163
164
165 public Bean getExpected() {
166 return expected;
167 }
168
169
170
171
172
173 public Bean getActual() {
174 return actual;
175 }
176
177 }