1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.metis.field;
18
19 import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
20 import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
21 import io.github.tonywasher.joceanus.metis.data.MetisDataType;
22 import io.github.tonywasher.joceanus.metis.exc.MetisDataException;
23 import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldDef;
24 import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldSetDef;
25 import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldVersionedDef;
26 import io.github.tonywasher.joceanus.metis.field.MetisFieldVersion.MetisFieldVersionValuesCtl;
27 import io.github.tonywasher.joceanus.metis.field.MetisFieldVersion.MetisFieldVersionedItemCtl;
28 import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
29
30 import java.util.Iterator;
31
32
33
34
35 public class MetisFieldVersionValues
36 implements MetisFieldVersionValuesCtl {
37
38
39
40 private static final int DELETION_HASH = 3;
41
42
43
44
45 protected static final String ERROR_NOTVERSIONED = "Field is not versioned";
46
47
48
49
50 private static final String ERROR_VALUETYPE = "Invalid valueType";
51
52
53
54
55 private final MetisFieldVersionedItemCtl theItem;
56
57
58
59
60 private final MetisFieldSetDef theFields;
61
62
63
64
65 private final int theNumValues;
66
67
68
69
70 private final Object[] theValues;
71
72
73
74
75 private int theVersion;
76
77
78
79
80 private boolean isDeletion;
81
82
83
84
85
86
87 protected MetisFieldVersionValues(final MetisFieldVersionedItemCtl pItem) {
88
89 theItem = pItem;
90 theFields = pItem.getDataFieldSet();
91 theNumValues = theFields.getNumVersioned();
92 theValues = new Object[theNumValues];
93 }
94
95
96
97
98
99
100 public MetisFieldSetDef getFields() {
101 return theFields;
102 }
103
104
105
106
107
108
109 protected MetisFieldVersionedItemCtl getItem() {
110 return theItem;
111 }
112
113
114
115
116
117
118 public int getVersion() {
119 return theVersion;
120 }
121
122
123
124
125
126
127 public void setVersion(final int pVersion) {
128 theVersion = pVersion;
129 }
130
131
132
133
134
135
136 public boolean isDeletion() {
137 return isDeletion;
138 }
139
140
141
142
143
144
145 public void setDeletion(final boolean pDeletion) {
146 isDeletion = pDeletion;
147 }
148
149
150
151
152
153
154 public MetisFieldVersionValues cloneIt() {
155
156 final MetisFieldVersionValues mySet = new MetisFieldVersionValues(theItem);
157 mySet.copyFrom(this);
158 return mySet;
159 }
160
161
162
163
164
165
166 public void copyFrom(final MetisFieldVersionValues pPrevious) {
167
168 isDeletion = pPrevious.isDeletion();
169
170
171 int myCopyLen = pPrevious.theNumValues;
172 if (myCopyLen > theNumValues) {
173 myCopyLen = theNumValues;
174 }
175
176
177 if (myCopyLen > 0) {
178 System.arraycopy(pPrevious.theValues, 0, theValues, 0, myCopyLen);
179 }
180 }
181
182
183
184
185
186
187
188
189 public void setValue(final MetisDataFieldId pFieldId,
190 final Object pValue) throws OceanusException {
191 final MetisFieldDef myField = theFields.getField(pFieldId);
192 setValue(myField, pValue);
193 }
194
195
196
197
198
199
200
201
202 public void setValue(final MetisFieldDef pField,
203 final Object pValue) throws OceanusException {
204
205 if (!(pField instanceof MetisFieldVersionedDef)) {
206 throw new IllegalArgumentException(ERROR_NOTVERSIONED);
207 }
208
209
210 checkValueType(pField, pValue);
211
212
213 theValues[((MetisFieldVersionedDef) pField).getIndex()] = pValue;
214 }
215
216
217
218
219
220
221
222 public void setUncheckedValue(final MetisDataFieldId pFieldId,
223 final Object pValue) {
224 final MetisFieldDef myField = theFields.getField(pFieldId);
225 setUncheckedValue(myField, pValue);
226 }
227
228
229
230
231
232
233
234 public void setUncheckedValue(final MetisFieldDef pField,
235 final Object pValue) {
236
237 if (!(pField instanceof MetisFieldVersionedDef)) {
238 throw new IllegalArgumentException(ERROR_NOTVERSIONED);
239 }
240
241
242 theValues[((MetisFieldVersionedDef) pField).getIndex()] = pValue;
243 }
244
245
246
247
248
249
250
251 public Object getValue(final MetisDataFieldId pFieldId) {
252 final MetisFieldDef myField = theFields.getField(pFieldId);
253 return getValue(myField);
254 }
255
256
257
258
259
260
261
262 public Object getValue(final MetisFieldDef pField) {
263
264 if (!(pField instanceof MetisFieldVersionedDef)) {
265 throw new IllegalArgumentException(ERROR_NOTVERSIONED);
266 }
267
268
269 return theValues[((MetisFieldVersionedDef) pField).getIndex()];
270 }
271
272
273
274
275
276
277
278
279
280 public <X> X getValue(final MetisDataFieldId pFieldId,
281 final Class<X> pClazz) {
282 final MetisFieldDef myField = theFields.getField(pFieldId);
283 return getValue(myField, pClazz);
284 }
285
286
287
288
289
290
291
292
293
294 public <X> X getValue(final MetisFieldDef pField,
295 final Class<X> pClazz) {
296
297 final Object myValue = getValue(pField);
298
299
300 return pClazz.cast(myValue);
301 }
302
303 @Override
304 public boolean equals(final Object pThat) {
305
306 if (this == pThat) {
307 return true;
308 }
309 if (pThat == null) {
310 return false;
311 }
312
313
314 if (pThat.getClass() != this.getClass()) {
315 return false;
316 }
317
318
319 final MetisFieldVersionValues mySet = (MetisFieldVersionValues) pThat;
320
321
322 if (isDeletion != mySet.isDeletion
323 || theNumValues != mySet.theNumValues) {
324 return false;
325 }
326
327
328 final Iterator<MetisFieldDef> myIterator = theFields.fieldIterator();
329 while (myIterator.hasNext()) {
330
331 final MetisFieldDef myField = myIterator.next();
332 if (!(myField instanceof MetisFieldVersionedDef myVersioned)
333 || !myVersioned.isEquality()) {
334 continue;
335 }
336
337
338 final int iIndex = myVersioned.getIndex();
339 if (MetisDataDifference.difference(theValues[iIndex], mySet.theValues[iIndex]).isDifferent()) {
340 return false;
341 }
342 }
343
344
345 return true;
346 }
347
348 @Override
349 public int hashCode() {
350
351 int iHashCode = isDeletion
352 ? DELETION_HASH
353 : 1;
354
355
356 final Iterator<MetisFieldDef> myIterator = theFields.fieldIterator();
357 while (myIterator.hasNext()) {
358
359 final MetisFieldDef myField = myIterator.next();
360 if (!(myField instanceof MetisFieldVersionedDef myVersioned)
361 || !myVersioned.isEquality()) {
362 continue;
363 }
364
365
366 iHashCode *= MetisFieldSet.HASH_PRIME;
367
368
369 final int iIndex = myVersioned.getIndex();
370 final Object o = theValues[iIndex];
371 if (o != null) {
372 iHashCode += o.hashCode();
373 }
374 }
375
376
377 return iHashCode;
378 }
379
380
381
382
383
384
385
386 public MetisDataDifference differs(final MetisFieldVersionValues pOriginal) {
387 boolean isSecureDiff = false;
388
389
390 if (isDeletion != pOriginal.isDeletion
391 || theNumValues != pOriginal.theNumValues) {
392 return MetisDataDifference.DIFFERENT;
393 }
394
395
396 final Iterator<MetisFieldDef> myIterator = theFields.fieldIterator();
397 while (myIterator.hasNext()) {
398
399 final MetisFieldDef myField = myIterator.next();
400 if (!(myField instanceof MetisFieldVersionedDef myVersioned)
401 || !myVersioned.isEquality()) {
402 continue;
403 }
404
405
406 final int iIndex = myVersioned.getIndex();
407 final MetisDataDifference myDiff = MetisDataDifference.difference(theValues[iIndex], pOriginal.theValues[iIndex]);
408 if (myDiff == MetisDataDifference.DIFFERENT) {
409 return myDiff;
410 }
411 if (myDiff == MetisDataDifference.SECURITY) {
412 isSecureDiff = true;
413 }
414 }
415
416
417 return isSecureDiff
418 ? MetisDataDifference.SECURITY
419 : MetisDataDifference.IDENTICAL;
420 }
421
422
423
424
425
426
427
428
429 public MetisDataDifference fieldChanged(final MetisDataFieldId pFieldId,
430 final MetisFieldVersionValues pOriginal) {
431 final MetisFieldDef myField = theFields.getField(pFieldId);
432 return fieldChanged(myField, pOriginal);
433 }
434
435
436
437
438
439
440
441
442 public MetisDataDifference fieldChanged(final MetisFieldDef pField,
443 final MetisFieldVersionValues pOriginal) {
444
445 if (!(pField instanceof MetisFieldVersionedDef myVersioned)
446 || !myVersioned.isEquality()) {
447 return MetisDataDifference.IDENTICAL;
448 }
449
450
451 final int iIndex = myVersioned.getIndex();
452 return MetisDataDifference.difference(theValues[iIndex], pOriginal.theValues[iIndex]);
453 }
454
455
456
457
458
459
460
461
462 protected void checkValueType(final MetisFieldDef pField,
463 final Object pValue) throws OceanusException {
464
465 if (pValue == null
466 || pValue instanceof String) {
467 return;
468 }
469
470
471 final MetisDataType myDataType = pField.getDataType();
472 if (MetisDataType.LINK.equals(myDataType)
473 && pValue instanceof Integer) {
474 return;
475 }
476
477
478 if (MetisDataType.LINKPAIR.equals(myDataType)
479 && pValue instanceof Long) {
480 return;
481 }
482
483
484 final Class<?> myClass = myDataType.getDataTypeClass();
485 final boolean bAllowed = myClass == null || myClass.isInstance(pValue);
486
487
488 if (!bAllowed) {
489 throw new MetisDataException(ERROR_VALUETYPE);
490 }
491 }
492 }