1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.lang3.builder;
18
19 import java.util.Collections;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Objects;
23
24 import org.apache.commons.lang3.StringUtils;
25
26 /**
27 * A {@link DiffResult} contains a collection of the differences between two
28 * {@link Diffable} objects. Typically these differences are displayed using
29 * {@link #toString()} method, which returns a string describing the fields that
30 * differ between the objects.
31 *
32 * <p>
33 * Use a {@link DiffBuilder} to build a {@link DiffResult} comparing two objects.
34 * </p>
35 *
36 * @param <T> type of the left and right object.
37 * @since 3.3
38 */
39 public class DiffResult<T> implements Iterable<Diff<?>> {
40
41 /**
42 * The {@link String} returned when the objects have no differences:
43 * {@value}
44 */
45 public static final String OBJECTS_SAME_STRING = StringUtils.EMPTY;
46
47 private final List<Diff<?>> diffList;
48 private final T lhs;
49 private final T rhs;
50 private final ToStringStyle style;
51 private final String toStringFormat;
52
53 /**
54 * Creates a {@link DiffResult} containing the differences between two
55 * objects.
56 *
57 * @param lhs
58 * the left-hand side object
59 * @param rhs
60 * the right-hand side object
61 * @param diffList
62 * the list of differences, may be empty
63 * @param style
64 * the style to use for the {@link #toString()} method. May be
65 * {@code null}, in which case
66 * {@link ToStringStyle#DEFAULT_STYLE} is used
67 * @param toStringFormat
68 * Two-argument format string for {@link String#format(String, Object...)}, for example {@code "%s differs from %s"}.
69 * @throws NullPointerException if {@code lhs}, {@code rhs} or {@code diffs} are {@code null}.
70 */
71 DiffResult(final T lhs, final T rhs, final List<Diff<?>> diffList, final ToStringStyle style, final String toStringFormat) {
72 this.diffList = Objects.requireNonNull(diffList, "diffList");
73 this.lhs = Objects.requireNonNull(lhs, "lhs");
74 this.rhs = Objects.requireNonNull(rhs, "rhs");
75 this.style = Objects.requireNonNull(style, "style");
76 this.toStringFormat = Objects.requireNonNull(toStringFormat, "toStringFormat");
77 }
78
79 /**
80 * Gets an unmodifiable list of {@link Diff}s. The list may be empty if
81 * there were no differences between the objects.
82 *
83 * @return an unmodifiable list of {@link Diff}s
84 */
85 public List<Diff<?>> getDiffs() {
86 return Collections.unmodifiableList(diffList);
87 }
88
89 /**
90 * Gets the object the right object has been compared to.
91 *
92 * @return the left object of the diff
93 * @since 3.10
94 */
95 public T getLeft() {
96 return this.lhs;
97 }
98
99 /**
100 * Gets the number of differences between the two objects.
101 *
102 * @return the number of differences
103 */
104 public int getNumberOfDiffs() {
105 return diffList.size();
106 }
107
108 /**
109 * Gets the object the left object has been compared to.
110 *
111 * @return the right object of the diff
112 * @since 3.10
113 */
114 public T getRight() {
115 return this.rhs;
116 }
117
118 /**
119 * Gets the style used by the {@link #toString()} method.
120 *
121 * @return the style
122 */
123 public ToStringStyle getToStringStyle() {
124 return style;
125 }
126
127 /**
128 * Returns an iterator over the {@link Diff} objects contained in this list.
129 *
130 * @return the iterator
131 */
132 @Override
133 public Iterator<Diff<?>> iterator() {
134 return diffList.iterator();
135 }
136
137 /**
138 * Builds a {@link String} description of the differences contained within
139 * this {@link DiffResult}. A {@link ToStringBuilder} is used for each object
140 * and the style of the output is governed by the {@link ToStringStyle}
141 * passed to the constructor.
142 *
143 * <p>
144 * If there are no differences stored in this list, the method will return
145 * {@link #OBJECTS_SAME_STRING}. Otherwise, using the example given in
146 * {@link Diffable} and {@link ToStringStyle#SHORT_PREFIX_STYLE}, an output
147 * might be:
148 * </p>
149 *
150 * <pre>
151 * Person[name=John Doe,age=32] differs from Person[name=Joe Bloggs,age=26]
152 * </pre>
153 *
154 * <p>
155 * This indicates that the objects differ in name and age, but not in
156 * smoking status.
157 * </p>
158 *
159 * <p>
160 * To use a different {@link ToStringStyle} for an instance of this class,
161 * use {@link #toString(ToStringStyle)}.
162 * </p>
163 *
164 * @return a {@link String} description of the differences.
165 */
166 @Override
167 public String toString() {
168 return toString(style);
169 }
170
171 /**
172 * Builds a {@link String} description of the differences contained within
173 * this {@link DiffResult}, using the supplied {@link ToStringStyle}.
174 *
175 * @param style
176 * the {@link ToStringStyle} to use when outputting the objects
177 *
178 * @return a {@link String} description of the differences.
179 */
180 public String toString(final ToStringStyle style) {
181 if (diffList.isEmpty()) {
182 return OBJECTS_SAME_STRING;
183 }
184
185 final ToStringBuilder lhsBuilder = new ToStringBuilder(lhs, style);
186 final ToStringBuilder rhsBuilder = new ToStringBuilder(rhs, style);
187
188 diffList.forEach(diff -> {
189 lhsBuilder.append(diff.getFieldName(), diff.getLeft());
190 rhsBuilder.append(diff.getFieldName(), diff.getRight());
191 });
192
193 return String.format(toStringFormat, lhsBuilder.build(), rhsBuilder.build());
194 }
195 }