View Javadoc
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 }