1 /*
2 * Metis: Java Data Framework
3 * Copyright 2012-2026. Tony Washer
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 * use this file except in compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * http://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, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17 package io.github.tonywasher.joceanus.metis.data;
18
19 import java.util.Arrays;
20
21 /**
22 * Difference enum and utility.
23 */
24 public enum MetisDataDifference {
25 /**
26 * Identical.
27 */
28 IDENTICAL,
29
30 /**
31 * Value Changed.
32 */
33 DIFFERENT,
34
35 /**
36 * Security Changed.
37 */
38 SECURITY;
39
40 /**
41 * Difference interface.
42 */
43 @FunctionalInterface
44 public interface MetisDataDiffers {
45 /**
46 * Test for difference with another object.
47 *
48 * @param pThat the other object
49 * @return the difference
50 */
51 MetisDataDifference differs(Object pThat);
52 }
53
54 /**
55 * The String name.
56 */
57 private String theName;
58
59 @Override
60 public String toString() {
61 /* If we have not yet loaded the name */
62 if (theName == null) {
63 /* Load the name */
64 theName = MetisDataResource.getKeyForDifference(this).getValue();
65 }
66
67 /* return the name */
68 return theName;
69 }
70
71 /**
72 * Is there differences?
73 *
74 * @return true/false
75 */
76 public boolean isDifferent() {
77 return !IDENTICAL.equals(this);
78 }
79
80 /**
81 * Is there no differences?
82 *
83 * @return true/false
84 */
85 public boolean isIdentical() {
86 return !isDifferent();
87 }
88
89 /**
90 * Is there value differences?
91 *
92 * @return true/false
93 */
94 public boolean isValueChanged() {
95 return DIFFERENT.equals(this);
96 }
97
98 /**
99 * Is there security differences?
100 *
101 * @return true/false
102 */
103 public boolean isSecurityChanged() {
104 return SECURITY.equals(this);
105 }
106
107 /**
108 * Combine Differences.
109 *
110 * @param pThat the difference to combine
111 * @return the combined difference
112 */
113 public MetisDataDifference combine(final MetisDataDifference pThat) {
114 switch (this) {
115 case IDENTICAL:
116 return pThat;
117 case SECURITY:
118 return (pThat == DIFFERENT)
119 ? pThat
120 : this;
121 default:
122 return this;
123 }
124 }
125
126 /**
127 * Determine whether two Generic objects differ.
128 *
129 * @param pCurr The current object
130 * @param pNew The new object
131 * @return the Difference between the objects
132 */
133 public static MetisDataDifference difference(final Object pCurr,
134 final Object pNew) {
135 /* Handle identity */
136 if (pCurr == pNew) {
137 return IDENTICAL;
138 }
139
140 /* Neither value can be null */
141 if (pCurr == null
142 || pNew == null) {
143 return DIFFERENT;
144 }
145
146 /* Handle class differences */
147 if (!pCurr.getClass().equals(pNew.getClass())) {
148 return DIFFERENT;
149 }
150
151 /* Handle differs support */
152 if (pCurr instanceof MetisDataDiffers myDiffers) {
153 return myDiffers.differs(pNew);
154 }
155
156 /* Handle Standard cases */
157 return pCurr.equals(pNew)
158 ? IDENTICAL
159 : DIFFERENT;
160 }
161
162 /**
163 * Determine whether two Generic objects are equal.
164 *
165 * @param pCurr The current object
166 * @param pNew The new object
167 * @return true/false
168 */
169 public static boolean isEqual(final Object pCurr,
170 final Object pNew) {
171 /* Handle identity */
172 if (pCurr == pNew) {
173 return true;
174 }
175
176 /* Neither value can be null */
177 if (pCurr == null
178 || pNew == null) {
179 return false;
180 }
181
182 /* Handle class differences */
183 final Class<?> myClass = pCurr.getClass();
184 if (!myClass.equals(pNew.getClass())) {
185 return false;
186 }
187
188 /* Handle arrays */
189 if (myClass.isArray()) {
190 /* Handle special cases for efficiency */
191 if (pCurr instanceof byte[] ba) {
192 return Arrays.equals(ba, (byte[]) pNew);
193 }
194 if (pCurr instanceof char[] ca) {
195 return Arrays.equals(ca, (char[]) pNew);
196 }
197
198 /* Handle generic arrays */
199 return Arrays.equals((Object[]) pCurr, (Object[]) pNew);
200 }
201
202 /* Handle Standard cases */
203 return pCurr.equals(pNew);
204 }
205
206 /**
207 * Compare two similar objects for order.
208 *
209 * @param <X> the object type
210 * @param pCurr The current object
211 * @param pNew The new object
212 * @return order
213 */
214 public static <X extends Comparable<? super X>> int compareObject(final X pCurr,
215 final X pNew) {
216 /* Handle identity */
217 if (pCurr == pNew) {
218 return 0;
219 }
220
221 /* Pass the call on, defaulting null to the end of the list */
222 return compareObject(pCurr, pNew, true);
223 }
224
225 /**
226 * Compare two similar objects for order.
227 *
228 * @param <X> the object type
229 * @param pCurr The current object
230 * @param pNew The new object
231 * @param pNullLast is Null at end of list?
232 * @return order
233 */
234 public static <X extends Comparable<? super X>> int compareObject(final X pCurr,
235 final X pNew,
236 final boolean pNullLast) {
237 /* Handle identity */
238 if (pCurr == pNew) {
239 return 0;
240 }
241
242 /* Handle positioning of nulls */
243 if (pCurr == null) {
244 return pNullLast
245 ? 1
246 : -1;
247 }
248 if (pNew == null) {
249 return pNullLast
250 ? -1
251 : 1;
252 }
253
254 /* Both non-Null, so pass the call on */
255 return pCurr.compareTo(pNew);
256 }
257 }