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