1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.healthmarketscience.jackcess.impl;
18
19 import java.io.ByteArrayOutputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.ObjectOutputStream;
23 import java.io.ObjectStreamException;
24 import java.io.Reader;
25 import java.io.Serializable;
26 import java.math.BigDecimal;
27 import java.math.BigInteger;
28 import java.nio.ByteBuffer;
29 import java.nio.ByteOrder;
30 import java.nio.CharBuffer;
31 import java.nio.charset.Charset;
32 import java.time.DateTimeException;
33 import java.time.Duration;
34 import java.time.Instant;
35 import java.time.LocalDate;
36 import java.time.LocalDateTime;
37 import java.time.LocalTime;
38 import java.time.ZoneId;
39 import java.time.ZonedDateTime;
40 import java.time.temporal.ChronoUnit;
41 import java.time.temporal.TemporalAccessor;
42 import java.time.temporal.TemporalQueries;
43 import java.util.Calendar;
44 import java.util.Collection;
45 import java.util.Date;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.TimeZone;
49 import java.util.UUID;
50 import java.util.regex.Matcher;
51 import java.util.regex.Pattern;
52
53 import com.healthmarketscience.jackcess.Column;
54 import com.healthmarketscience.jackcess.ColumnBuilder;
55 import com.healthmarketscience.jackcess.DataType;
56 import com.healthmarketscience.jackcess.DateTimeType;
57 import com.healthmarketscience.jackcess.InvalidValueException;
58 import com.healthmarketscience.jackcess.PropertyMap;
59 import com.healthmarketscience.jackcess.Table;
60 import com.healthmarketscience.jackcess.complex.ComplexColumnInfo;
61 import com.healthmarketscience.jackcess.complex.ComplexValue;
62 import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
63 import com.healthmarketscience.jackcess.expr.Identifier;
64 import com.healthmarketscience.jackcess.impl.complex.ComplexValueForeignKeyImpl;
65 import com.healthmarketscience.jackcess.impl.expr.NumberFormatter;
66 import com.healthmarketscience.jackcess.util.ColumnValidator;
67 import com.healthmarketscience.jackcess.util.SimpleColumnValidator;
68 import org.apache.commons.lang3.builder.ToStringBuilder;
69 import org.apache.commons.logging.Log;
70 import org.apache.commons.logging.LogFactory;
71
72
73
74
75
76
77 public class ColumnImpl implements Column, Comparable<ColumnImpl>, DateTimeContext
78 {
79
80 protected static final Log LOG = LogFactory.getLog(ColumnImpl.class);
81
82
83
84
85
86
87
88
89 public static final Object RETURN_ROW_ID = "<RETURN_ROW_ID>";
90
91
92
93
94 private static final long MILLISECONDS_PER_DAY = (24L * 60L * 60L * 1000L);
95 private static final long SECONDS_PER_DAY = (24L * 60L * 60L);
96 private static final long NANOS_PER_SECOND = 1_000_000_000L;
97 private static final long NANOS_PER_MILLI = 1_000_000L;
98 private static final long MILLIS_PER_SECOND = 1000L;
99
100
101
102
103
104
105
106 static final long MILLIS_BETWEEN_EPOCH_AND_1900 =
107 25569L * MILLISECONDS_PER_DAY;
108
109 public static final LocalDate BASE_LD = LocalDate.of(1899, 12, 30);
110 public static final LocalTime BASE_LT = LocalTime.of(0, 0);
111 public static final LocalDateTime BASE_LDT = LocalDateTime.of(BASE_LD, BASE_LT);
112
113 private static final LocalDate BASE_EXT_LD = LocalDate.of(1, 1, 1);
114 private static final LocalTime BASE_EXT_LT = LocalTime.of(0, 0);
115 private static final LocalDateTime BASE_EXT_LDT =
116 LocalDateTime.of(BASE_EXT_LD, BASE_EXT_LT);
117 private static final byte[] EXT_LDT_TRAILER = {':', '7', 0x00};
118
119 private static final DateTimeFactory DEF_DATE_TIME_FACTORY =
120 new DefaultDateTimeFactory();
121
122 static final DateTimeFactory LDT_DATE_TIME_FACTORY =
123 new LDTDateTimeFactory();
124
125
126
127
128
129 public static final byte FIXED_LEN_FLAG_MASK = (byte)0x01;
130
131
132
133
134
135 public static final byte AUTO_NUMBER_FLAG_MASK = (byte)0x04;
136
137
138
139
140
141 public static final byte AUTO_NUMBER_GUID_FLAG_MASK = (byte)0x40;
142
143
144
145
146
147 public static final byte HYPERLINK_FLAG_MASK = (byte)0x80;
148
149
150
151
152
153 public static final byte UPDATABLE_FLAG_MASK = (byte)0x02;
154
155
156
157
158 protected static final byte COMPRESSED_UNICODE_EXT_FLAG_MASK = (byte)0x01;
159 private static final byte CALCULATED_EXT_FLAG_MASK = (byte)0xC0;
160
161 static final byte NUMERIC_NEGATIVE_BYTE = (byte)0x80;
162
163
164 private static final short GENERAL_SORT_ORDER_VALUE = 1033;
165
166
167
168
169
170 public static final SortOrder GENERAL_97_SORT_ORDER =
171 new SortOrder(GENERAL_SORT_ORDER_VALUE, (short)-1);
172
173
174
175
176
177 public static final SortOrder GENERAL_LEGACY_SORT_ORDER =
178 new SortOrder(GENERAL_SORT_ORDER_VALUE, (short)0);
179
180
181
182
183
184 public static final SortOrder GENERAL_SORT_ORDER =
185 new SortOrder(GENERAL_SORT_ORDER_VALUE, (short)1);
186
187
188
189 private static final Pattern GUID_PATTERN = Pattern.compile("\\s*[{]?([\\p{XDigit}]{8})-([\\p{XDigit}]{4})-([\\p{XDigit}]{4})-([\\p{XDigit}]{4})-([\\p{XDigit}]{12})[}]?\\s*");
190
191
192 private static final byte[] TEXT_COMPRESSION_HEADER =
193 { (byte)0xFF, (byte)0XFE };
194 private static final char MIN_COMPRESS_CHAR = 1;
195 private static final char MAX_COMPRESS_CHAR = 0xFF;
196
197
198 static final int INVALID_AUTO_NUMBER = 0;
199
200 static final int INVALID_LENGTH = -1;
201
202
203
204 private final TableImpl _table;
205
206 private final boolean _variableLength;
207
208 private final boolean _autoNumber;
209
210 private final boolean _calculated;
211
212 private final DataType _type;
213
214 private final short _columnLength;
215
216 private final short _columnNumber;
217
218 private int _columnIndex;
219
220 private final int _displayIndex;
221
222 private final String _name;
223
224 private final int _fixedDataOffset;
225
226 private final int _varLenTableIndex;
227
228 private final AutoNumberGenerator _autoNumberGenerator;
229
230 private PropertyMap _props;
231
232 private ColumnValidator _validator = SimpleColumnValidator.INSTANCE;
233
234 private ColDefaultValueEvalContext _defValue;
235
236 private int _lengthInUnits = INVALID_LENGTH;
237
238
239
240
241 protected ColumnImpl(TableImpl table, String name, DataType type,
242 int colNumber, int fixedOffset, int varLenIndex) {
243 _table = table;
244 _name = name;
245 _type = type;
246
247 if(!_type.isVariableLength()) {
248 _columnLength = (short)type.getFixedSize();
249 } else {
250 _columnLength = (short)type.getMaxSize();
251 }
252 _variableLength = type.isVariableLength();
253 _autoNumber = false;
254 _calculated = false;
255 _autoNumberGenerator = null;
256 _columnNumber = (short)colNumber;
257 _columnIndex = colNumber;
258 _displayIndex = colNumber;
259 _fixedDataOffset = fixedOffset;
260 _varLenTableIndex = varLenIndex;
261 }
262
263
264
265
266
267 ColumnImpl(InitArgs args)
268 {
269 _table = args.table;
270 _name = args.name;
271 _displayIndex = args.displayIndex;
272 _type = args.type;
273
274 _columnNumber = args.buffer.getShort(
275 args.offset + getFormat().OFFSET_COLUMN_NUMBER);
276 _columnLength = args.buffer.getShort(
277 args.offset + getFormat().OFFSET_COLUMN_LENGTH);
278
279 _variableLength = ((args.flags & FIXED_LEN_FLAG_MASK) == 0);
280 _autoNumber = ((args.flags &
281 (AUTO_NUMBER_FLAG_MASK | AUTO_NUMBER_GUID_FLAG_MASK)) != 0);
282 _calculated = ((args.extFlags & CALCULATED_EXT_FLAG_MASK) != 0);
283
284 _autoNumberGenerator = createAutoNumberGenerator();
285
286 _varLenTableIndex = args.buffer.getShort(
287 args.offset + getFormat().OFFSET_COLUMN_VARIABLE_TABLE_INDEX);
288 _fixedDataOffset = args.buffer.getShort(
289 args.offset + getFormat().OFFSET_COLUMN_FIXED_DATA_OFFSET);
290 }
291
292
293
294
295
296
297
298
299
300 public static ColumnImpl create(TableImpl table, ByteBuffer buffer,
301 int offset, String name, int displayIndex)
302 throws IOException
303 {
304 InitArgs args = new InitArgs(table, buffer, offset, name, displayIndex);
305
306 boolean calculated = ((args.extFlags & CALCULATED_EXT_FLAG_MASK) != 0);
307 byte colType = args.colType;
308 if(calculated) {
309
310 PropertyMap colProps = table.getPropertyMaps().get(name);
311 Byte resultType = (Byte)colProps.getValue(PropertyMap.RESULT_TYPE_PROP);
312 if(resultType != null) {
313 colType = resultType;
314 }
315 }
316
317 try {
318 args.type = DataType.fromByte(colType);
319 } catch(IOException e) {
320 LOG.warn(withErrorContext("Unsupported column type " + colType,
321 table.getDatabase(), table.getName(), name));
322 boolean variableLength = ((args.flags & FIXED_LEN_FLAG_MASK) == 0);
323 args.type = (variableLength ? DataType.UNSUPPORTED_VARLEN :
324 DataType.UNSUPPORTED_FIXEDLEN);
325 return new UnsupportedColumnImpl(args);
326 }
327
328 if(calculated) {
329 return CalculatedColumnUtil.create(args);
330 }
331
332 switch(args.type) {
333 case TEXT:
334 return new TextColumnImpl(args);
335 case MEMO:
336 return new MemoColumnImpl(args);
337 case COMPLEX_TYPE:
338 return new ComplexColumnImpl(args);
339 default:
340
341 }
342
343 if(args.type.getHasScalePrecision()) {
344 return new NumericColumnImpl(args);
345 }
346 if(args.type.isLongValue()) {
347 return new LongValueColumnImpl(args);
348 }
349
350 return new ColumnImpl(args);
351 }
352
353
354
355
356 void setUsageMaps(UsageMap./../../com/healthmarketscience/jackcess/impl/UsageMap.html#UsageMap">UsageMap ownedPages, UsageMap freeSpacePages) {
357
358 }
359
360 void collectUsageMapPages(Collection<Integer> pages) {
361
362 }
363
364
365
366
367 void postTableLoadInit() throws IOException {
368
369 }
370
371 @Override
372 public TableImpl getTable() {
373 return _table;
374 }
375
376 @Override
377 public DatabaseImpl getDatabase() {
378 return getTable().getDatabase();
379 }
380
381
382
383
384 public JetFormat getFormat() {
385 return getDatabase().getFormat();
386 }
387
388
389
390
391 public PageChannel getPageChannel() {
392 return getDatabase().getPageChannel();
393 }
394
395 @Override
396 public String getName() {
397 return _name;
398 }
399
400 @Override
401 public boolean isVariableLength() {
402 return _variableLength;
403 }
404
405 @Override
406 public boolean isAutoNumber() {
407 return _autoNumber;
408 }
409
410
411
412
413 public short getColumnNumber() {
414 return _columnNumber;
415 }
416
417 @Override
418 public int getColumnIndex() {
419 return _columnIndex;
420 }
421
422
423
424
425 public void setColumnIndex(int newColumnIndex) {
426 _columnIndex = newColumnIndex;
427 }
428
429
430
431
432 public int getDisplayIndex() {
433 return _displayIndex;
434 }
435
436 @Override
437 public DataType getType() {
438 return _type;
439 }
440
441 @Override
442 public int getSQLType() throws IOException {
443 return _type.getSQLType();
444 }
445
446 @Override
447 public boolean isCompressedUnicode() {
448 return false;
449 }
450
451 @Override
452 public byte getPrecision() {
453 return (byte)getType().getDefaultPrecision();
454 }
455
456 @Override
457 public byte getScale() {
458 return (byte)getType().getDefaultScale();
459 }
460
461
462
463
464 public SortOrder getTextSortOrder() {
465 return null;
466 }
467
468
469
470
471 public short getTextCodePage() {
472 return 0;
473 }
474
475 @Override
476 public short getLength() {
477 return _columnLength;
478 }
479
480 @Override
481 public final short getLengthInUnits() {
482 if(_lengthInUnits == INVALID_LENGTH) {
483 _lengthInUnits = calcLengthInUnits();
484 }
485 return (short)_lengthInUnits;
486 }
487
488 protected int calcLengthInUnits() {
489 return getType().toUnitSize(getLength(), getFormat());
490 }
491
492 @Override
493 public boolean isCalculated() {
494 return _calculated;
495 }
496
497
498
499
500 public int getVarLenTableIndex() {
501 return _varLenTableIndex;
502 }
503
504
505
506
507 public int getFixedDataOffset() {
508 return _fixedDataOffset;
509 }
510
511 protected int getFixedDataSize() {
512 return _type.getFixedSize(_columnLength);
513 }
514
515 protected Charset getCharset() {
516 return getDatabase().getCharset();
517 }
518
519 @Override
520 public TimeZone getTimeZone() {
521 return getDatabase().getTimeZone();
522 }
523
524 @Override
525 public ZoneId getZoneId() {
526 return getDatabase().getZoneId();
527 }
528
529 @Override
530 public DateTimeFactory getDateTimeFactory() {
531 return getDatabase().getDateTimeFactory();
532 }
533
534 @Override
535 public boolean isAppendOnly() {
536 return (getVersionHistoryColumn() != null);
537 }
538
539 @Override
540 public ColumnImpl getVersionHistoryColumn() {
541 return null;
542 }
543
544
545
546
547
548 public int getOwnedPageCount() {
549 return 0;
550 }
551
552
553
554
555 public void setVersionHistoryColumn(ColumnImpl versionHistoryCol) {
556 throw new UnsupportedOperationException();
557 }
558
559 @Override
560 public boolean isHyperlink() {
561 return false;
562 }
563
564 @Override
565 public ComplexColumnInfo<? extends ComplexValue> getComplexInfo() {
566 return null;
567 }
568
569 void initColumnValidator() throws IOException {
570
571 if(getDatabase().isReadOnly()) {
572
573 return;
574 }
575
576
577 setColumnValidator(null);
578
579
580 reloadPropertiesValidators();
581 }
582
583 void reloadPropertiesValidators() throws IOException {
584
585 if(isAutoNumber()) {
586
587 return;
588 }
589
590 if(isCalculated()) {
591
592 CalcColEvalContext calcCol = null;
593
594 if(getDatabase().isEvaluateExpressions()) {
595
596
597 PropertyMap props = getProperties();
598 String calcExpr = (String)props.getValue(PropertyMap.EXPRESSION_PROP);
599 calcCol = new CalcColEvalContext(this).setExpr(calcExpr);
600 }
601
602 setCalcColEvalContext(calcCol);
603
604
605 return;
606 }
607
608
609
610 _validator = getColumnValidator();
611 _defValue = null;
612
613 PropertyMap props = getProperties();
614
615
616 boolean required = (Boolean)props.getValue(PropertyMap.REQUIRED_PROP,
617 Boolean.FALSE);
618 if(required) {
619 _validator = new RequiredColValidator(_validator);
620 }
621
622
623
624 boolean allowZeroLen =
625 !getType().isTextual() ||
626 (Boolean)props.getValue(PropertyMap.ALLOW_ZERO_LEN_PROP,
627 Boolean.TRUE);
628 if(!allowZeroLen) {
629 _validator = new NoZeroLenColValidator(_validator);
630 }
631
632
633 if(!getDatabase().isEvaluateExpressions()) {
634 return;
635 }
636
637 String exprStr = PropertyMaps.getTrimmedStringProperty(
638 props, PropertyMap.VALIDATION_RULE_PROP);
639
640 if(exprStr != null) {
641 String helpStr = PropertyMaps.getTrimmedStringProperty(
642 props, PropertyMap.VALIDATION_TEXT_PROP);
643
644 _validator = new ColValidatorEvalContext(this)
645 .setExpr(exprStr, helpStr)
646 .toColumnValidator(_validator);
647 }
648
649 String defValueStr = PropertyMaps.getTrimmedStringProperty(
650 props, PropertyMap.DEFAULT_VALUE_PROP);
651 if(defValueStr != null) {
652 _defValue = new ColDefaultValueEvalContext(this)
653 .setExpr(defValueStr);
654 }
655 }
656
657 void propertiesUpdated() throws IOException {
658 reloadPropertiesValidators();
659 }
660
661 @Override
662 public ColumnValidator getColumnValidator() {
663
664 return ((_validator instanceof InternalColumnValidator) ?
665 ((InternalColumnValidator)_validator).getExternal() : _validator);
666 }
667
668 @Override
669 public void setColumnValidator(ColumnValidator newValidator) {
670
671 if(isAutoNumber()) {
672
673
674 if(newValidator != null) {
675 throw new IllegalArgumentException(withErrorContext(
676 "Cannot set ColumnValidator for autonumber columns"));
677 }
678
679 return;
680 }
681
682 if(newValidator == null) {
683 newValidator = getDatabase().getColumnValidatorFactory()
684 .createValidator(this);
685 if(newValidator == null) {
686 newValidator = SimpleColumnValidator.INSTANCE;
687 }
688 }
689
690
691 if(_validator instanceof InternalColumnValidator) {
692 ((InternalColumnValidator)_validator).setExternal(newValidator);
693 } else {
694 _validator = newValidator;
695 }
696 }
697
698 byte getOriginalDataType() {
699 return _type.getValue();
700 }
701
702 private AutoNumberGenerator createAutoNumberGenerator() {
703 if(!_autoNumber || (_type == null)) {
704 return null;
705 }
706
707 switch(_type) {
708 case LONG:
709 return new LongAutoNumberGenerator();
710 case GUID:
711 return new GuidAutoNumberGenerator();
712 case COMPLEX_TYPE:
713 return new ComplexTypeAutoNumberGenerator();
714 default:
715 LOG.warn(withErrorContext("Unknown auto number column type " + _type));
716 return new UnsupportedAutoNumberGenerator(_type);
717 }
718 }
719
720
721
722
723
724
725 public AutoNumberGenerator getAutoNumberGenerator() {
726 return _autoNumberGenerator;
727 }
728
729 @Override
730 public PropertyMap getProperties() throws IOException {
731 if(_props == null) {
732 _props = getTable().getPropertyMaps().get(getName());
733 }
734 return _props;
735 }
736
737 @Override
738 public Object setRowValue(Object[] rowArray, Object value) {
739 rowArray[_columnIndex] = value;
740 return value;
741 }
742
743 @Override
744 public Object setRowValue(Map<String,Object> rowMap, Object value) {
745 rowMap.put(_name, value);
746 return value;
747 }
748
749 @Override
750 public Object getRowValue(Object[] rowArray) {
751 return rowArray[_columnIndex];
752 }
753
754 @Override
755 public Object getRowValue(Map<String,?> rowMap) {
756 return rowMap.get(_name);
757 }
758
759 public boolean storeInNullMask() {
760 return (getType() == DataType.BOOLEAN);
761 }
762
763 public boolean writeToNullMask(Object value) {
764 return toBooleanValue(value);
765 }
766
767 public Object readFromNullMask(boolean isNull) {
768 return Boolean.valueOf(!isNull);
769 }
770
771
772
773
774
775
776
777 public Object read(byte[] data) throws IOException {
778 return read(data, PageChannel.DEFAULT_BYTE_ORDER);
779 }
780
781
782
783
784
785
786
787
788 public Object read(byte[] data, ByteOrder order) throws IOException {
789 ByteBuffer buffer = ByteBuffer.wrap(data).order(order);
790
791 switch(getType()) {
792 case BOOLEAN:
793 throw new IOException(withErrorContext("Tried to read a boolean from data instead of null mask."));
794 case BYTE:
795 return Byte.valueOf(buffer.get());
796 case INT:
797 return Short.valueOf(buffer.getShort());
798 case LONG:
799 return Integer.valueOf(buffer.getInt());
800 case DOUBLE:
801 return Double.valueOf(buffer.getDouble());
802 case FLOAT:
803 return Float.valueOf(buffer.getFloat());
804 case SHORT_DATE_TIME:
805 return readDateValue(buffer);
806 case BINARY:
807 return data;
808 case TEXT:
809 return decodeTextValue(data);
810 case MONEY:
811 return readCurrencyValue(buffer);
812 case NUMERIC:
813 return readNumericValue(buffer);
814 case GUID:
815 return readGUIDValue(buffer, order);
816 case EXT_DATE_TIME:
817 return readExtendedDateValue(buffer);
818 case UNKNOWN_0D:
819 case UNKNOWN_11:
820
821 return data;
822 case COMPLEX_TYPE:
823 return new ComplexValueForeignKeyImpl(this, buffer.getInt());
824 case BIG_INT:
825 return Long.valueOf(buffer.getLong());
826 default:
827 throw new IOException(withErrorContext("Unrecognized data type: " + _type));
828 }
829 }
830
831
832
833
834
835
836
837
838 private BigDecimal readCurrencyValue(ByteBuffer buffer)
839 throws IOException
840 {
841 if(buffer.remaining() != 8) {
842 throw new IOException(withErrorContext("Invalid money value"));
843 }
844
845 return new BigDecimal(BigInteger.valueOf(buffer.getLong(0)), 4);
846 }
847
848
849
850
851 private void writeCurrencyValue(ByteBuffer buffer, Object value)
852 throws IOException
853 {
854 Object inValue = value;
855 try {
856 BigDecimal decVal = toBigDecimal(value);
857 inValue = decVal;
858
859
860
861 decVal = decVal.setScale(4);
862
863
864
865 buffer.putLong(decVal.movePointRight(4).longValueExact());
866 } catch(ArithmeticException e) {
867 throw new IOException(
868 withErrorContext("Currency value '" + inValue + "' out of range"), e);
869 }
870 }
871
872
873
874
875 private BigDecimal readNumericValue(ByteBuffer buffer)
876 {
877 boolean negate = (buffer.get() != 0);
878
879 byte[] tmpArr = ByteUtil.getBytes(buffer, 16);
880
881 if(buffer.order() != ByteOrder.BIG_ENDIAN) {
882 fixNumericByteOrder(tmpArr);
883 }
884
885 return toBigDecimal(tmpArr, negate, getScale());
886 }
887
888 static BigDecimal toBigDecimal(byte[] bytes, boolean negate, int scale)
889 {
890 if((bytes[0] & 0x80) != 0) {
891
892
893
894 bytes = ByteUtil.copyOf(bytes, 0, bytes.length + 1, 1);
895 }
896 BigInteger intVal = new BigInteger(bytes);
897 if(negate) {
898 intVal = intVal.negate();
899 }
900 return new BigDecimal(intVal, scale);
901 }
902
903
904
905
906 private void writeNumericValue(ByteBuffer buffer, Object value)
907 throws IOException
908 {
909 Object inValue = value;
910 try {
911 BigDecimal decVal = toBigDecimal(value);
912 inValue = decVal;
913
914 int signum = decVal.signum();
915 if(signum < 0) {
916 decVal = decVal.negate();
917 }
918
919
920 buffer.put((signum < 0) ? NUMERIC_NEGATIVE_BYTE : 0);
921
922
923
924 decVal = decVal.setScale(getScale());
925
926
927 if(decVal.precision() > getPrecision()) {
928 throw new InvalidValueException(withErrorContext(
929 "Numeric value is too big for specified precision "
930 + getPrecision() + ": " + decVal));
931 }
932
933
934 byte[] intValBytes = toUnscaledByteArray(
935 decVal, getType().getFixedSize() - 1);
936 if(buffer.order() != ByteOrder.BIG_ENDIAN) {
937 fixNumericByteOrder(intValBytes);
938 }
939 buffer.put(intValBytes);
940 } catch(ArithmeticException e) {
941 throw new IOException(
942 withErrorContext("Numeric value '" + inValue + "' out of range"), e);
943 }
944 }
945
946 byte[] toUnscaledByteArray(BigDecimal decVal, int maxByteLen)
947 throws IOException
948 {
949
950 byte[] intValBytes = decVal.unscaledValue().toByteArray();
951 if(intValBytes.length > maxByteLen) {
952 if((intValBytes[0] == 0) && ((intValBytes.length - 1) == maxByteLen)) {
953
954
955
956 intValBytes = ByteUtil.copyOf(intValBytes, 1, maxByteLen);
957 } else {
958 throw new InvalidValueException(withErrorContext(
959 "Too many bytes for valid BigInteger?"));
960 }
961 } else if(intValBytes.length < maxByteLen) {
962 intValBytes = ByteUtil.copyOf(intValBytes, 0, maxByteLen,
963 (maxByteLen - intValBytes.length));
964 }
965 return intValBytes;
966 }
967
968
969
970
971 private Object readDateValue(ByteBuffer buffer) {
972 long dateBits = buffer.getLong();
973 return getDateTimeFactory().fromDateBits(this, dateBits);
974 }
975
976
977
978
979 private static Object readExtendedDateValue(ByteBuffer buffer) {
980
981 long numDays = readExtDateLong(buffer, 19);
982 buffer.get();
983 long seconds = readExtDateLong(buffer, 12);
984
985 long nanos = readExtDateLong(buffer, 7) * 100L;
986 ByteUtil.forward(buffer, EXT_LDT_TRAILER.length);
987
988 return BASE_EXT_LDT
989 .plusDays(numDays)
990 .plusSeconds(seconds)
991 .plusNanos(nanos);
992 }
993
994
995
996
997 private static long readExtDateLong(ByteBuffer buffer, int numChars) {
998 long val = 0L;
999 for(int i = 0; i < numChars; ++i) {
1000 char digit = (char)buffer.get();
1001 long inc = digit - '0';
1002 val = (val * 10L) + inc;
1003 }
1004 return val;
1005 }
1006
1007
1008
1009
1010
1011 public long fromDateDouble(double value) {
1012 return fromDateDouble(value, getTimeZone());
1013 }
1014
1015 private static long fromDateDouble(double value, TimeZone tz) {
1016 long localTime = fromLocalDateDouble(value);
1017 return localTime - getFromLocalTimeZoneOffset(localTime, tz);
1018 }
1019
1020 static long fromLocalDateDouble(double value) {
1021 long datePart = ((long)value) * MILLISECONDS_PER_DAY;
1022
1023
1024
1025
1026
1027
1028 long timePart = Math.round((Math.abs(value) % 1.0d) *
1029 MILLISECONDS_PER_DAY);
1030
1031 long time = datePart + timePart;
1032 return time - MILLIS_BETWEEN_EPOCH_AND_1900;
1033 }
1034
1035 public static LocalDateTime ldtFromLocalDateDouble(double value) {
1036 Duration dateTimeOffset = durationFromLocalDateDouble(value);
1037 return BASE_LDT.plus(dateTimeOffset);
1038 }
1039
1040 private static Duration durationFromLocalDateDouble(double value) {
1041 long dateSeconds = ((long)value) * SECONDS_PER_DAY;
1042
1043
1044
1045
1046
1047
1048
1049 double secondsDouble = (Math.abs(value) % 1.0d) * SECONDS_PER_DAY;
1050 long timeSeconds = (long)secondsDouble;
1051 long timeMillis = (long)(roundToMillis(secondsDouble % 1.0d) *
1052 MILLIS_PER_SECOND);
1053
1054 return Duration.ofSeconds(dateSeconds + timeSeconds,
1055 timeMillis * NANOS_PER_MILLI);
1056 }
1057
1058
1059
1060
1061 private void writeDateValue(ByteBuffer buffer, Object value)
1062 throws InvalidValueException
1063 {
1064 if(value == null) {
1065 buffer.putDouble(0d);
1066 } else if(value instanceof DateExt) {
1067
1068
1069 buffer.putLong(((DateExt)value).getDateBits());
1070 } else {
1071 buffer.putDouble(toDateDouble(value));
1072 }
1073 }
1074
1075
1076
1077
1078 private void writeExtendedDateValue(ByteBuffer buffer, Object value)
1079 {
1080 LocalDateTime ldt = BASE_EXT_LDT;
1081 if(value != null) {
1082 ldt = toLocalDateTime(value, this);
1083 }
1084
1085 LocalDate ld = ldt.toLocalDate();
1086 LocalTime lt = ldt.toLocalTime();
1087
1088 long numDays = BASE_EXT_LD.until(ld, ChronoUnit.DAYS);
1089 long numSeconds = BASE_EXT_LT.until(lt, ChronoUnit.SECONDS);
1090 long nanos = lt.getNano();
1091
1092
1093 writeExtDateLong(buffer, numDays, 19);
1094 buffer.put((byte)':');
1095 writeExtDateLong(buffer, numSeconds, 12);
1096
1097 writeExtDateLong(buffer, (nanos / 100L), 7);
1098
1099 buffer.put(EXT_LDT_TRAILER);
1100 }
1101
1102
1103
1104
1105
1106 private static void writeExtDateLong(
1107 ByteBuffer buffer, long val, int numChars) {
1108
1109 int end = buffer.position();
1110 int start = end + numChars - 1;
1111 for(int i = start; i >= end; --i) {
1112 char digit = (char)('0' + (char)(val % 10L));
1113 buffer.put(i, (byte)digit);
1114 val /= 10L;
1115 }
1116 ByteUtil.forward(buffer, numChars);
1117 }
1118
1119
1120
1121
1122
1123
1124 public double toDateDouble(Object value)
1125 throws InvalidValueException
1126 {
1127 try {
1128 return toDateDouble(value, this);
1129 } catch(IllegalArgumentException iae) {
1130 throw new InvalidValueException(withErrorContext(iae.getMessage()), iae);
1131 }
1132 }
1133
1134
1135
1136
1137
1138
1139 private static double toDateDouble(Object value, DateTimeContext dtc) {
1140 return dtc.getDateTimeFactory().toDateDouble(value, dtc);
1141 }
1142
1143 static LocalDateTime toLocalDateTime(
1144 Object value, DateTimeContext dtc) {
1145 if(value instanceof TemporalAccessor) {
1146 return temporalToLocalDateTime((TemporalAccessor)value, dtc);
1147 }
1148 Instant inst = Instant.ofEpochMilli(toDateLong(value));
1149 return LocalDateTime.ofInstant(inst, dtc.getZoneId());
1150 }
1151
1152 private static LocalDateTime temporalToLocalDateTime(
1153 TemporalAccessor value, DateTimeContext dtc) {
1154
1155
1156 if(value instanceof LocalDateTime) {
1157 return (LocalDateTime)value;
1158 }
1159 if(value instanceof ZonedDateTime) {
1160
1161 return ((ZonedDateTime)value).withZoneSameInstant(
1162 dtc.getZoneId()).toLocalDateTime();
1163 }
1164 if(value instanceof Instant) {
1165 return LocalDateTime.ofInstant((Instant)value, dtc.getZoneId());
1166 }
1167 if(value instanceof LocalDate) {
1168 return ((LocalDate)value).atTime(BASE_LT);
1169 }
1170 if(value instanceof LocalTime) {
1171 return ((LocalTime)value).atDate(BASE_LD);
1172 }
1173
1174
1175 try {
1176
1177 LocalDate ld = value.query(TemporalQueries.localDate());
1178 if(ld == null) {
1179 ld = BASE_LD;
1180 }
1181 LocalTime lt = value.query(TemporalQueries.localTime());
1182 if(lt == null) {
1183 lt = BASE_LT;
1184 }
1185 ZoneId zone = value.query(TemporalQueries.zone());
1186 if(zone != null) {
1187
1188
1189 ZoneId zoneId = dtc.getZoneId();
1190 if(!zoneId.equals(zone)) {
1191 return ZonedDateTime.of(ld, lt, zone).withZoneSameInstant(zoneId)
1192 .toLocalDateTime();
1193 }
1194 }
1195
1196 return LocalDateTime.of(ld, lt);
1197
1198 } catch(DateTimeException | ArithmeticException e) {
1199 throw new IllegalArgumentException(
1200 "Unsupported temporal type " + value.getClass(), e);
1201 }
1202 }
1203
1204 private static Instant toInstant(TemporalAccessor value, DateTimeContext dtc) {
1205 if(value instanceof ZonedDateTime) {
1206 return ((ZonedDateTime)value).toInstant();
1207 }
1208 if(value instanceof Instant) {
1209 return (Instant)value;
1210 }
1211 return temporalToLocalDateTime(value, dtc).atZone(dtc.getZoneId())
1212 .toInstant();
1213 }
1214
1215 static double toLocalDateDouble(long time) {
1216 time += MILLIS_BETWEEN_EPOCH_AND_1900;
1217
1218 if(time < 0L) {
1219
1220 long timePart = -time % MILLISECONDS_PER_DAY;
1221 if(timePart > 0) {
1222 time -= (2 * (MILLISECONDS_PER_DAY - timePart));
1223 }
1224 }
1225
1226 return time / (double)MILLISECONDS_PER_DAY;
1227 }
1228
1229 public static double toDateDouble(LocalDateTime ldt) {
1230 Duration dateTimeOffset = Duration.between(BASE_LDT, ldt);
1231 return toLocalDateDouble(dateTimeOffset);
1232 }
1233
1234 private static double toLocalDateDouble(Duration time) {
1235 long dateTimeSeconds = time.getSeconds();
1236 long timeSeconds = dateTimeSeconds % SECONDS_PER_DAY;
1237 if(timeSeconds < 0) {
1238 timeSeconds += SECONDS_PER_DAY;
1239 }
1240 long dateSeconds = dateTimeSeconds - timeSeconds;
1241 long timeNanos = time.getNano();
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260 double timeDouble = ((roundToMillis((double)timeNanos / NANOS_PER_SECOND) +
1261 timeSeconds) / SECONDS_PER_DAY);
1262
1263 double dateDouble = ((double)dateSeconds / SECONDS_PER_DAY);
1264
1265 if(dateSeconds < 0) {
1266 timeDouble = -timeDouble;
1267 }
1268
1269 return dateDouble + timeDouble;
1270 }
1271
1272
1273
1274
1275
1276 private static double roundToMillis(double dbl) {
1277 return ((dbl == 0d) ? dbl :
1278 new BigDecimal(dbl).setScale(3, NumberFormatter.ROUND_MODE)
1279 .doubleValue());
1280 }
1281
1282
1283
1284
1285 private static long toDateLong(Object value) {
1286 return ((value instanceof Date) ?
1287 ((Date)value).getTime() :
1288 ((value instanceof Calendar) ?
1289 ((Calendar)value).getTimeInMillis() :
1290 ((Number)value).longValue()));
1291 }
1292
1293
1294
1295
1296
1297 private static long getToLocalTimeZoneOffset(long time, TimeZone tz) {
1298 return tz.getOffset(time);
1299 }
1300
1301
1302
1303
1304
1305 private static long getFromLocalTimeZoneOffset(long time, TimeZone tz) {
1306
1307
1308
1309 return tz.getOffset(time - tz.getRawOffset());
1310 }
1311
1312
1313
1314
1315 private static String readGUIDValue(ByteBuffer buffer, ByteOrder order)
1316 {
1317 if(order != ByteOrder.BIG_ENDIAN) {
1318 byte[] tmpArr = ByteUtil.getBytes(buffer, 16);
1319
1320
1321
1322 ByteUtil.swap4Bytes(tmpArr, 0);
1323 ByteUtil.swap2Bytes(tmpArr, 4);
1324 ByteUtil.swap2Bytes(tmpArr, 6);
1325 buffer = ByteBuffer.wrap(tmpArr);
1326 }
1327
1328 StringBuilder sb = new StringBuilder(22);
1329 sb.append("{");
1330 sb.append(ByteUtil.toHexString(buffer, 0, 4,
1331 false));
1332 sb.append("-");
1333 sb.append(ByteUtil.toHexString(buffer, 4, 2,
1334 false));
1335 sb.append("-");
1336 sb.append(ByteUtil.toHexString(buffer, 6, 2,
1337 false));
1338 sb.append("-");
1339 sb.append(ByteUtil.toHexString(buffer, 8, 2,
1340 false));
1341 sb.append("-");
1342 sb.append(ByteUtil.toHexString(buffer, 10, 6,
1343 false));
1344 sb.append("}");
1345 return (sb.toString());
1346 }
1347
1348
1349
1350
1351 private void writeGUIDValue(ByteBuffer buffer, Object value)
1352 throws IOException
1353 {
1354 Matcher m = GUID_PATTERN.matcher(toCharSequence(value));
1355 if(!m.matches()) {
1356 throw new InvalidValueException(
1357 withErrorContext("Invalid GUID: " + value));
1358 }
1359
1360 ByteBuffer origBuffer = null;
1361 byte[] tmpBuf = null;
1362 if(buffer.order() != ByteOrder.BIG_ENDIAN) {
1363
1364 origBuffer = buffer;
1365 tmpBuf = new byte[16];
1366 buffer = ByteBuffer.wrap(tmpBuf);
1367 }
1368
1369 ByteUtil.writeHexString(buffer, m.group(1));
1370 ByteUtil.writeHexString(buffer, m.group(2));
1371 ByteUtil.writeHexString(buffer, m.group(3));
1372 ByteUtil.writeHexString(buffer, m.group(4));
1373 ByteUtil.writeHexString(buffer, m.group(5));
1374
1375 if(tmpBuf != null) {
1376
1377
1378 ByteUtil.swap4Bytes(tmpBuf, 0);
1379 ByteUtil.swap2Bytes(tmpBuf, 4);
1380 ByteUtil.swap2Bytes(tmpBuf, 6);
1381 origBuffer.put(tmpBuf);
1382 }
1383 }
1384
1385
1386
1387
1388 static boolean isGUIDValue(Object value) throws IOException {
1389 return GUID_PATTERN.matcher(toCharSequence(value)).matches();
1390 }
1391
1392
1393
1394
1395 public Object generateDefaultValue() throws IOException {
1396 return ((_defValue != null) ? _defValue.eval() : null);
1397 }
1398
1399
1400
1401
1402
1403 public Object validate(Object obj) throws IOException {
1404 return _validator.validate(this, obj);
1405 }
1406
1407
1408
1409
1410 protected CalcColEvalContext getCalculationContext() {
1411 throw new UnsupportedOperationException();
1412 }
1413
1414 protected void setCalcColEvalContext(CalcColEvalContext calcCol) {
1415 throw new UnsupportedOperationException();
1416 }
1417
1418
1419
1420
1421
1422
1423
1424
1425 public ByteBuffer write(Object obj, int remainingRowLength)
1426 throws IOException
1427 {
1428 return write(obj, remainingRowLength, PageChannel.DEFAULT_BYTE_ORDER);
1429 }
1430
1431
1432
1433
1434
1435
1436
1437
1438 public ByteBuffer write(Object obj, int remainingRowLength, ByteOrder order)
1439 throws IOException
1440 {
1441 if(isRawData(obj)) {
1442
1443 return ByteBuffer.wrap(((RawData)obj).getBytes());
1444 }
1445
1446 return writeRealData(obj, remainingRowLength, order);
1447 }
1448
1449 protected ByteBuffer writeRealData(Object obj, int remainingRowLength,
1450 ByteOrder order)
1451 throws IOException
1452 {
1453 if(!isVariableLength() || !getType().isVariableLength()) {
1454 return writeFixedLengthField(obj, order);
1455 }
1456
1457
1458 switch(getType()) {
1459 case NUMERIC:
1460
1461 ByteBuffer buffer = PageChannel.createBuffer(
1462 getType().getFixedSize(), order);
1463 writeNumericValue(buffer, obj);
1464 buffer.flip();
1465 return buffer;
1466
1467 case TEXT:
1468 return encodeTextValue(
1469 obj, 0, getLengthInUnits(), false).order(order);
1470
1471 case BINARY:
1472 case UNKNOWN_0D:
1473 case UNSUPPORTED_VARLEN:
1474
1475 break;
1476 default:
1477 throw new RuntimeException(withErrorContext(
1478 "unexpected inline var length type: " + getType()));
1479 }
1480
1481 return ByteBuffer.wrap(toByteArray(obj)).order(order);
1482 }
1483
1484
1485
1486
1487
1488
1489
1490
1491 protected ByteBuffer writeFixedLengthField(Object obj, ByteOrder order)
1492 throws IOException
1493 {
1494 int size = getFixedDataSize();
1495
1496 ByteBuffer buffer = writeFixedLengthField(
1497 obj, PageChannel.createBuffer(size, order));
1498 buffer.flip();
1499 return buffer;
1500 }
1501
1502 protected ByteBuffer writeFixedLengthField(Object obj, ByteBuffer buffer)
1503 throws IOException
1504 {
1505
1506
1507 obj = booleanToInteger(obj);
1508
1509 switch(getType()) {
1510 case BOOLEAN:
1511
1512 break;
1513 case BYTE:
1514 buffer.put(toNumber(obj).byteValue());
1515 break;
1516 case INT:
1517 buffer.putShort(toNumber(obj).shortValue());
1518 break;
1519 case LONG:
1520 buffer.putInt(toNumber(obj).intValue());
1521 break;
1522 case MONEY:
1523 writeCurrencyValue(buffer, obj);
1524 break;
1525 case FLOAT:
1526 buffer.putFloat(toNumber(obj).floatValue());
1527 break;
1528 case DOUBLE:
1529 buffer.putDouble(toNumber(obj).doubleValue());
1530 break;
1531 case SHORT_DATE_TIME:
1532 writeDateValue(buffer, obj);
1533 break;
1534 case TEXT:
1535
1536
1537 int numChars = getLengthInUnits();
1538
1539 buffer.put(encodeTextValue(obj, numChars, numChars, true));
1540 break;
1541 case GUID:
1542 writeGUIDValue(buffer, obj);
1543 break;
1544 case NUMERIC:
1545
1546
1547 writeNumericValue(buffer, obj);
1548 break;
1549 case BINARY:
1550 case UNKNOWN_0D:
1551 case UNKNOWN_11:
1552 case COMPLEX_TYPE:
1553 buffer.putInt(toNumber(obj).intValue());
1554 break;
1555 case BIG_INT:
1556 buffer.putLong(toNumber(obj).longValue());
1557 break;
1558 case EXT_DATE_TIME:
1559 writeExtendedDateValue(buffer, obj);
1560 break;
1561 case UNSUPPORTED_FIXEDLEN:
1562 byte[] bytes = toByteArray(obj);
1563 if(bytes.length != getLength()) {
1564 throw new InvalidValueException(withErrorContext(
1565 "Invalid fixed size binary data, size "
1566 + getLength() + ", got " + bytes.length));
1567 }
1568 buffer.put(bytes);
1569 break;
1570 default:
1571 throw new IOException(withErrorContext(
1572 "Unsupported data type: " + getType()));
1573 }
1574 return buffer;
1575 }
1576
1577
1578
1579
1580 String decodeTextValue(byte[] data)
1581 {
1582
1583
1584 boolean isCompressed = ((data.length > 1) &&
1585 (data[0] == TEXT_COMPRESSION_HEADER[0]) &&
1586 (data[1] == TEXT_COMPRESSION_HEADER[1]));
1587
1588 if(isCompressed) {
1589
1590
1591
1592
1593 StringBuilder textBuf = new StringBuilder(data.length);
1594
1595 int dataStart = TEXT_COMPRESSION_HEADER.length;
1596 int dataEnd = dataStart;
1597 boolean inCompressedMode = true;
1598 while(dataEnd < data.length) {
1599 if(data[dataEnd] == (byte)0x00) {
1600
1601
1602 decodeTextSegment(data, dataStart, dataEnd, inCompressedMode,
1603 textBuf);
1604 inCompressedMode = !inCompressedMode;
1605 ++dataEnd;
1606 dataStart = dataEnd;
1607
1608 } else {
1609 ++dataEnd;
1610 }
1611 }
1612
1613 decodeTextSegment(data, dataStart, dataEnd, inCompressedMode, textBuf);
1614
1615 return textBuf.toString();
1616
1617 }
1618
1619 return decodeUncompressedText(data, getCharset());
1620 }
1621
1622
1623
1624
1625
1626 private void decodeTextSegment(byte[] data, int dataStart, int dataEnd,
1627 boolean inCompressedMode,
1628 StringBuilder textBuf)
1629 {
1630 if(dataEnd <= dataStart) {
1631
1632 return;
1633 }
1634 int dataLength = dataEnd - dataStart;
1635
1636 if(inCompressedMode) {
1637 byte[] tmpData = new byte[dataLength * 2];
1638 int tmpIdx = 0;
1639 for(int i = dataStart; i < dataEnd; ++i) {
1640 tmpData[tmpIdx] = data[i];
1641 tmpIdx += 2;
1642 }
1643 data = tmpData;
1644 dataStart = 0;
1645 dataLength = data.length;
1646 }
1647
1648 textBuf.append(decodeUncompressedText(data, dataStart, dataLength,
1649 getCharset()));
1650 }
1651
1652
1653
1654
1655
1656 private static CharBuffer decodeUncompressedText(
1657 byte[] textBytes, int startPos, int length, Charset charset)
1658 {
1659 return charset.decode(ByteBuffer.wrap(textBytes, startPos, length));
1660 }
1661
1662
1663
1664
1665 ByteBuffer encodeTextValue(Object obj, int minChars, int maxChars,
1666 boolean forceUncompressed)
1667 throws IOException
1668 {
1669 CharSequence text = toCharSequence(obj);
1670 if((text.length() > maxChars) || (text.length() < minChars)) {
1671 throw new InvalidValueException(withErrorContext(
1672 "Text is wrong length for " + getType() +
1673 " column, max " + maxChars
1674 + ", min " + minChars + ", got " + text.length()));
1675 }
1676
1677
1678 if(!forceUncompressed && isCompressedUnicode() &&
1679 (text.length() <= getFormat().MAX_COMPRESSED_UNICODE_SIZE) &&
1680 isUnicodeCompressible(text)) {
1681
1682 byte[] encodedChars = new byte[TEXT_COMPRESSION_HEADER.length +
1683 text.length()];
1684 encodedChars[0] = TEXT_COMPRESSION_HEADER[0];
1685 encodedChars[1] = TEXT_COMPRESSION_HEADER[1];
1686 for(int i = 0; i < text.length(); ++i) {
1687 encodedChars[i + TEXT_COMPRESSION_HEADER.length] =
1688 (byte)text.charAt(i);
1689 }
1690 return ByteBuffer.wrap(encodedChars);
1691 }
1692
1693 return encodeUncompressedText(text, getCharset());
1694 }
1695
1696
1697
1698
1699
1700 private static boolean isUnicodeCompressible(CharSequence text) {
1701
1702
1703 if(text.length() <= TEXT_COMPRESSION_HEADER.length) {
1704 return false;
1705 }
1706
1707 for(int i = 0; i < text.length(); ++i) {
1708 char c = text.charAt(i);
1709 if((c < MIN_COMPRESS_CHAR) || (c > MAX_COMPRESS_CHAR)) {
1710 return false;
1711 }
1712 }
1713 return true;
1714 }
1715
1716
1717
1718
1719 private static byte getColumnBitFlags(ColumnBuilder col) {
1720 byte flags = UPDATABLE_FLAG_MASK;
1721 if(!col.isVariableLength()) {
1722 flags |= FIXED_LEN_FLAG_MASK;
1723 }
1724 if(col.isAutoNumber()) {
1725 byte autoNumFlags = 0;
1726 switch(col.getType()) {
1727 case LONG:
1728 case COMPLEX_TYPE:
1729 autoNumFlags = AUTO_NUMBER_FLAG_MASK;
1730 break;
1731 case GUID:
1732 autoNumFlags = AUTO_NUMBER_GUID_FLAG_MASK;
1733 break;
1734 default:
1735
1736 }
1737 flags |= autoNumFlags;
1738 }
1739 if(col.isHyperlink()) {
1740 flags |= HYPERLINK_FLAG_MASK;
1741 }
1742 return flags;
1743 }
1744
1745 @Override
1746 public String toString() {
1747 ToStringBuilder sb = CustomToStringStyle.builder(this)
1748 .append("name", "(" + _table.getName() + ") " + _name);
1749 byte typeValue = getOriginalDataType();
1750 sb.append("type", "0x" + Integer.toHexString(typeValue) +
1751 " (" + _type + ")")
1752 .append("number", _columnNumber)
1753 .append("length", _columnLength)
1754 .append("variableLength", _variableLength);
1755 if(_calculated) {
1756 sb.append("calculated", _calculated)
1757 .append("expression",
1758 CustomToStringStyle.ignoreNull(getCalculationContext()));
1759 }
1760 if(_type.isTextual()) {
1761 sb.append("compressedUnicode", isCompressedUnicode())
1762 .append("textSortOrder", getTextSortOrder());
1763 if(getTextCodePage() > 0) {
1764 sb.append("textCodePage", getTextCodePage());
1765 }
1766 if(isAppendOnly()) {
1767 sb.append("appendOnly", isAppendOnly());
1768 }
1769 if(isHyperlink()) {
1770 sb.append("hyperlink", isHyperlink());
1771 }
1772 }
1773 if(_type.getHasScalePrecision()) {
1774 sb.append("precision", getPrecision())
1775 .append("scale", getScale());
1776 }
1777 if(_autoNumber) {
1778 sb.append("lastAutoNumber", _autoNumberGenerator.getLast());
1779 }
1780 sb.append("complexInfo", CustomToStringStyle.ignoreNull(getComplexInfo()))
1781 .append("validator", CustomToStringStyle.ignoreNull(
1782 ((_validator != SimpleColumnValidator.INSTANCE) ?
1783 _validator : null)))
1784 .append("defaultValue", CustomToStringStyle.ignoreNull(_defValue));
1785 return sb.toString();
1786 }
1787
1788
1789
1790
1791
1792
1793
1794 public static String decodeUncompressedText(byte[] textBytes,
1795 Charset charset)
1796 {
1797 return decodeUncompressedText(textBytes, 0, textBytes.length, charset)
1798 .toString();
1799 }
1800
1801
1802
1803
1804
1805
1806
1807 public static ByteBuffer encodeUncompressedText(CharSequence text,
1808 Charset charset)
1809 {
1810 CharBuffer cb = ((text instanceof CharBuffer) ?
1811 (CharBuffer)text : CharBuffer.wrap(text));
1812 return charset.encode(cb);
1813 }
1814
1815
1816
1817
1818
1819
1820 @Override
1821 public int compareTo(ColumnImpl other) {
1822 if (_columnNumber > other.getColumnNumber()) {
1823 return 1;
1824 } else if (_columnNumber < other.getColumnNumber()) {
1825 return -1;
1826 } else {
1827 return 0;
1828 }
1829 }
1830
1831
1832
1833
1834
1835
1836 public static short countVariableLength(List<ColumnBuilder> columns) {
1837 short rtn = 0;
1838 for (ColumnBuilder col : columns) {
1839 if (col.isVariableLength()) {
1840 rtn++;
1841 }
1842 }
1843 return rtn;
1844 }
1845
1846
1847
1848
1849
1850
1851 BigDecimal toBigDecimal(Object value)
1852 {
1853 return toBigDecimal(value, getDatabase());
1854 }
1855
1856
1857
1858
1859
1860
1861 static BigDecimal toBigDecimal(Object value, DatabaseImpl db)
1862 {
1863 if(value == null) {
1864 return BigDecimal.ZERO;
1865 } else if(value instanceof BigDecimal) {
1866 return (BigDecimal)value;
1867 } else if(value instanceof BigInteger) {
1868 return new BigDecimal((BigInteger)value);
1869 } else if(value instanceof Number) {
1870 return new BigDecimal(((Number)value).doubleValue());
1871 } else if(value instanceof Boolean) {
1872
1873 return ((Boolean)value) ? BigDecimal.valueOf(-1) : BigDecimal.ZERO;
1874 } else if(value instanceof Date) {
1875 return new BigDecimal(toDateDouble(value, db));
1876 } else if(value instanceof LocalDateTime) {
1877 return new BigDecimal(toDateDouble((LocalDateTime)value));
1878 }
1879 return new BigDecimal(value.toString());
1880 }
1881
1882
1883
1884
1885
1886
1887 private Number toNumber(Object value)
1888 {
1889 return toNumber(value, getDatabase());
1890 }
1891
1892
1893
1894
1895
1896
1897 private static Number toNumber(Object value, DatabaseImpl db)
1898 {
1899 if(value == null) {
1900 return BigDecimal.ZERO;
1901 } else if(value instanceof Number) {
1902 return (Number)value;
1903 } else if(value instanceof Boolean) {
1904
1905 return ((Boolean)value) ? -1 : 0;
1906 } else if(value instanceof Date) {
1907 return toDateDouble(value, db);
1908 } else if(value instanceof LocalDateTime) {
1909 return toDateDouble((LocalDateTime)value);
1910 }
1911 return Double.valueOf(value.toString());
1912 }
1913
1914
1915
1916
1917
1918 public static CharSequence toCharSequence(Object value)
1919 throws IOException
1920 {
1921 if(value == null) {
1922 return null;
1923 } else if(value instanceof CharSequence) {
1924 return (CharSequence)value;
1925 } else if(SqlHelper.INSTANCE.isClob(value)) {
1926 return SqlHelper.INSTANCE.getClobString(value);
1927 } else if(value instanceof Reader) {
1928 char[] buf = new char[8 * 1024];
1929 StringBuilder sout = new StringBuilder();
1930 Reader in = (Reader)value;
1931 int read = 0;
1932 while((read = in.read(buf)) != -1) {
1933 sout.append(buf, 0, read);
1934 }
1935 return sout;
1936 }
1937
1938 return value.toString();
1939 }
1940
1941
1942
1943
1944
1945 public static byte[] toByteArray(Object value)
1946 throws IOException
1947 {
1948 if(value == null) {
1949 return null;
1950 } else if(value instanceof byte[]) {
1951 return (byte[])value;
1952 } else if(value instanceof InMemoryBlob) {
1953 return ((InMemoryBlob)value).getBytes();
1954 } else if(SqlHelper.INSTANCE.isBlob(value)) {
1955 return SqlHelper.INSTANCE.getBlobBytes(value);
1956 }
1957
1958 ByteArrayOutputStream bout = new ByteArrayOutputStream();
1959
1960 if(value instanceof InputStream) {
1961 ByteUtil.copy((InputStream)value, bout);
1962 } else {
1963
1964 try(ObjectOutputStream oos = new ObjectOutputStream(bout)){
1965 oos.writeObject(value);
1966 }
1967 }
1968
1969 return bout.toByteArray();
1970 }
1971
1972
1973
1974
1975
1976 public static boolean toBooleanValue(Object obj) {
1977 if(obj == null) {
1978 return false;
1979 } else if(obj instanceof Boolean) {
1980 return ((Boolean)obj).booleanValue();
1981 } else if(obj instanceof Number) {
1982
1983 if(obj instanceof BigDecimal) {
1984 return (((BigDecimal)obj).compareTo(BigDecimal.ZERO) != 0);
1985 }
1986 if(obj instanceof BigInteger) {
1987 return (((BigInteger)obj).compareTo(BigInteger.ZERO) != 0);
1988 }
1989 return (((Number)obj).doubleValue() != 0.0d);
1990 }
1991 return Boolean.parseBoolean(obj.toString());
1992 }
1993
1994
1995
1996
1997 private static void fixNumericByteOrder(byte[] bytes)
1998 {
1999
2000 for(int i = 0; i < bytes.length; i+=4) {
2001 ByteUtil.swap4Bytes(bytes, i);
2002 }
2003 }
2004
2005
2006
2007
2008 protected static Object booleanToInteger(Object obj) {
2009 if (obj instanceof Boolean) {
2010 obj = ((Boolean) obj) ? -1 : 0;
2011 }
2012 return obj;
2013 }
2014
2015
2016
2017
2018
2019
2020 public static RawData rawDataWrapper(byte[] bytes) {
2021 return new RawData(bytes);
2022 }
2023
2024
2025
2026
2027
2028
2029 public static boolean isRawData(Object value) {
2030 return(value instanceof RawData);
2031 }
2032
2033
2034
2035
2036
2037 protected static void writeDefinitions(TableCreator creator, ByteBuffer buffer)
2038 {
2039
2040
2041
2042 int longVariableOffset = creator.countNonLongVariableLength();
2043 creator.setColumnOffsets(0, 0, longVariableOffset);
2044
2045 for (ColumnBuilder col : creator.getColumns()) {
2046 writeDefinition(creator, col, buffer);
2047 }
2048
2049 for (ColumnBuilder col : creator.getColumns()) {
2050 TableImpl.writeName(buffer, col.getName(), creator.getCharset());
2051 }
2052 }
2053
2054 protected static void writeDefinition(
2055 TableMutator mutator, ColumnBuilder col, ByteBuffer buffer)
2056 {
2057 TableMutator.ColumnOffsets colOffsets = mutator.getColumnOffsets();
2058
2059 buffer.put(col.getType().getValue());
2060 buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER);
2061 buffer.putShort(col.getColumnNumber());
2062
2063 buffer.putShort(colOffsets.getNextVariableOffset(col));
2064
2065 buffer.putShort(col.getColumnNumber());
2066
2067 if(col.getType().isTextual()) {
2068
2069
2070 writeSortOrder(buffer, col.getTextSortOrder(), mutator.getFormat());
2071 } else {
2072
2073 if(col.getType().getHasScalePrecision() && !col.isCalculated()) {
2074 buffer.put(col.getPrecision());
2075 buffer.put(col.getScale());
2076 } else {
2077 buffer.put((byte) 0x00);
2078 buffer.put((byte) 0x00);
2079 }
2080 buffer.putShort((short) 0);
2081 }
2082
2083 buffer.put(getColumnBitFlags(col));
2084
2085
2086 if(col.isCalculated()) {
2087 buffer.put(CALCULATED_EXT_FLAG_MASK);
2088 } else if (col.isCompressedUnicode()) {
2089 buffer.put(COMPRESSED_UNICODE_EXT_FLAG_MASK);
2090 } else {
2091 buffer.put((byte)0);
2092 }
2093
2094 buffer.putInt(0);
2095
2096
2097 if(col.isVariableLength()) {
2098 buffer.putShort((short) 0);
2099 } else {
2100 buffer.putShort(colOffsets.getNextFixedOffset(col));
2101 }
2102
2103 if(!col.getType().isLongValue()) {
2104 short length = col.getLength();
2105 if(col.isCalculated()) {
2106
2107 if(!col.getType().isVariableLength() ||
2108 col.getType().getHasScalePrecision()) {
2109 length = CalculatedColumnUtil.CALC_FIXED_FIELD_LEN;
2110 } else {
2111 length += CalculatedColumnUtil.CALC_EXTRA_DATA_LEN;
2112 }
2113 }
2114 buffer.putShort(length);
2115 } else {
2116 buffer.putShort((short)0x0000);
2117 }
2118 }
2119
2120 protected static void writeColUsageMapDefinitions(
2121 TableCreator creator, ByteBuffer buffer)
2122 {
2123
2124 for(ColumnBuilder lvalCol : creator.getLongValueColumns()) {
2125 writeColUsageMapDefinition(creator, lvalCol, buffer);
2126 }
2127 }
2128
2129 protected static void writeColUsageMapDefinition(
2130 TableMutator creator, ColumnBuilder lvalCol, ByteBuffer buffer)
2131 {
2132 TableMutator.ColumnState colState = creator.getColumnState(lvalCol);
2133
2134 buffer.putShort(lvalCol.getColumnNumber());
2135
2136
2137 buffer.put(colState.getUmapOwnedRowNumber());
2138 ByteUtil.put3ByteInt(buffer, colState.getUmapPageNumber());
2139
2140 buffer.put(colState.getUmapFreeRowNumber());
2141 ByteUtil.put3ByteInt(buffer, colState.getUmapPageNumber());
2142 }
2143
2144
2145
2146
2147 static SortOrder readSortOrder(ByteBuffer buffer, int position,
2148 JetFormat format)
2149 {
2150 short value = buffer.getShort(position);
2151
2152 if(value == 0) {
2153
2154 return format.DEFAULT_SORT_ORDER;
2155 }
2156
2157 short version = format.DEFAULT_SORT_ORDER.getVersion();
2158 if(format.SIZE_SORT_ORDER == 4) {
2159 version = buffer.get(position + 3);
2160 }
2161
2162 if(value == GENERAL_SORT_ORDER_VALUE) {
2163 if(version == GENERAL_SORT_ORDER.getVersion()) {
2164 return GENERAL_SORT_ORDER;
2165 }
2166 if(version == GENERAL_LEGACY_SORT_ORDER.getVersion()) {
2167 return GENERAL_LEGACY_SORT_ORDER;
2168 }
2169 if(version == GENERAL_97_SORT_ORDER.getVersion()) {
2170 return GENERAL_97_SORT_ORDER;
2171 }
2172 }
2173 return new SortOrder(value, version);
2174 }
2175
2176
2177
2178
2179
2180 static short readCodePage(ByteBuffer buffer, int offset, JetFormat format)
2181 {
2182 int cpOffset = format.OFFSET_COLUMN_CODE_PAGE;
2183 return ((cpOffset >= 0) ? buffer.getShort(offset + cpOffset) : 0);
2184 }
2185
2186
2187
2188
2189 static byte readExtraFlags(ByteBuffer buffer, int offset, JetFormat format)
2190 {
2191 int extFlagsOffset = format.OFFSET_COLUMN_EXT_FLAGS;
2192 return ((extFlagsOffset >= 0) ? buffer.get(offset + extFlagsOffset) : 0);
2193 }
2194
2195
2196
2197
2198 private static void writeSortOrder(ByteBuffer buffer, SortOrder sortOrder,
2199 JetFormat format) {
2200 if(sortOrder == null) {
2201 sortOrder = format.DEFAULT_SORT_ORDER;
2202 }
2203 buffer.putShort(sortOrder.getValue());
2204 if(format.SIZE_SORT_ORDER == 4) {
2205 buffer.put((byte)0x00);
2206 buffer.put((byte)sortOrder.getVersion());
2207 }
2208 }
2209
2210
2211
2212
2213
2214 static boolean isImmutableValue(Object value) {
2215
2216 return !(value instanceof byte[]);
2217 }
2218
2219
2220
2221
2222
2223 public static Object toInternalValue(DataType dataType, Object value,
2224 DatabaseImpl db)
2225 throws IOException
2226 {
2227 return toInternalValue(dataType, value, db, null);
2228 }
2229
2230 static Object toInternalValue(DataType dataType, Object value,
2231 DatabaseImpl db,
2232 ColumnImpl.DateTimeFactory factory)
2233 throws IOException
2234 {
2235 if(value == null) {
2236 return null;
2237 }
2238
2239 switch(dataType) {
2240 case BOOLEAN:
2241 return ((value instanceof Boolean) ? value : toBooleanValue(value));
2242 case BYTE:
2243 return ((value instanceof Byte) ? value : toNumber(value, db).byteValue());
2244 case INT:
2245 return ((value instanceof Short) ? value :
2246 toNumber(value, db).shortValue());
2247 case LONG:
2248 return ((value instanceof Integer) ? value :
2249 toNumber(value, db).intValue());
2250 case MONEY:
2251 return toBigDecimal(value, db);
2252 case FLOAT:
2253 return ((value instanceof Float) ? value :
2254 toNumber(value, db).floatValue());
2255 case DOUBLE:
2256 return ((value instanceof Double) ? value :
2257 toNumber(value, db).doubleValue());
2258 case SHORT_DATE_TIME:
2259 if(factory == null) {
2260 factory = db.getDateTimeFactory();
2261 }
2262 return factory.toInternalValue(db, value);
2263 case TEXT:
2264 case MEMO:
2265 case GUID:
2266 return ((value instanceof String) ? value :
2267 toCharSequence(value).toString());
2268 case NUMERIC:
2269 return toBigDecimal(value, db);
2270 case COMPLEX_TYPE:
2271
2272 return value;
2273 case BIG_INT:
2274 return ((value instanceof Long) ? value :
2275 toNumber(value, db).longValue());
2276 case EXT_DATE_TIME:
2277 return toLocalDateTime(value, db);
2278 default:
2279
2280 return toByteArray(value);
2281 }
2282 }
2283
2284 protected static DateTimeFactory getDateTimeFactory(DateTimeType type) {
2285 return ((type == DateTimeType.LOCAL_DATE_TIME) ?
2286 LDT_DATE_TIME_FACTORY : DEF_DATE_TIME_FACTORY);
2287 }
2288
2289 String withErrorContext(String msg) {
2290 return withErrorContext(msg, getDatabase(), getTable().getName(), getName());
2291 }
2292
2293 boolean isThisColumn(Identifier identifier) {
2294 return(getTable().isThisTable(identifier) &&
2295 getName().equalsIgnoreCase(identifier.getObjectName()));
2296 }
2297
2298 private static String withErrorContext(
2299 String msg, DatabaseImpl db, String tableName, String colName) {
2300 return msg + " (Db=" + db.getName() + ";Table=" + tableName + ";Column=" +
2301 colName + ")";
2302 }
2303
2304
2305
2306
2307
2308
2309 @SuppressWarnings("deprecation")
2310 private static final class DateExt extends Date
2311 {
2312 private static final long serialVersionUID = 0L;
2313
2314
2315 private transient final long _dateBits;
2316
2317 private DateExt(long time, long dateBits) {
2318 super(time);
2319 _dateBits = dateBits;
2320 }
2321
2322 public long getDateBits() {
2323 return _dateBits;
2324 }
2325
2326 @Override
2327 public void setDate(int time) {
2328 throw new UnsupportedOperationException();
2329 }
2330
2331 @Override
2332 public void setHours(int time) {
2333 throw new UnsupportedOperationException();
2334 }
2335
2336 @Override
2337 public void setMinutes(int time) {
2338 throw new UnsupportedOperationException();
2339 }
2340
2341 @Override
2342 public void setMonth(int time) {
2343 throw new UnsupportedOperationException();
2344 }
2345
2346 @Override
2347 public void setSeconds(int time) {
2348 throw new UnsupportedOperationException();
2349 }
2350
2351 @Override
2352 public void setYear(int time) {
2353 throw new UnsupportedOperationException();
2354 }
2355
2356 @Override
2357 public void setTime(long time) {
2358 throw new UnsupportedOperationException();
2359 }
2360
2361 private Object writeReplace() throws ObjectStreamException {
2362
2363
2364 return new Date(super.getTime());
2365 }
2366 }
2367
2368
2369
2370
2371 private static final class RawData implements Serializable, InMemoryBlob
2372 {
2373 private static final long serialVersionUID = 0L;
2374
2375 private final byte[] _bytes;
2376
2377 private RawData(byte[] bytes) {
2378 _bytes = bytes;
2379 }
2380
2381 @Override
2382 public byte[] getBytes() {
2383 return _bytes;
2384 }
2385
2386 @Override
2387 public String toString() {
2388 return CustomToStringStyle.valueBuilder(this)
2389 .append(null, getBytes())
2390 .toString();
2391 }
2392
2393 private Object writeReplace() throws ObjectStreamException {
2394
2395
2396 return getBytes();
2397 }
2398 }
2399
2400
2401
2402
2403
2404 public abstract class AutoNumberGenerator
2405 {
2406 protected AutoNumberGenerator() {}
2407
2408
2409
2410
2411
2412 public abstract Object getLast();
2413
2414
2415
2416
2417
2418
2419
2420 public abstract Object getNext(TableImpl.WriteRowState writeRowState);
2421
2422
2423
2424
2425
2426
2427
2428 public abstract Object handleInsert(
2429 TableImpl.WriteRowState writeRowState, Object inRowValue)
2430 throws IOException;
2431
2432
2433
2434
2435 public abstract void restoreLast(Object last);
2436
2437
2438
2439
2440 public abstract DataType getType();
2441 }
2442
2443 private final class LongAutoNumberGenerator extends AutoNumberGenerator
2444 {
2445 private LongAutoNumberGenerator() {}
2446
2447 @Override
2448 public Object getLast() {
2449
2450 return getTable().getLastLongAutoNumber();
2451 }
2452
2453 @Override
2454 public Object getNext(TableImpl.WriteRowState writeRowState) {
2455
2456 return getTable().getNextLongAutoNumber();
2457 }
2458
2459 @Override
2460 public Object handleInsert(TableImpl.WriteRowState writeRowState,
2461 Object inRowValue)
2462 throws IOException
2463 {
2464 int inAutoNum = toNumber(inRowValue).intValue();
2465 if(inAutoNum <= INVALID_AUTO_NUMBER &&
2466 !getTable().isAllowAutoNumberInsert()) {
2467 throw new InvalidValueException(withErrorContext(
2468 "Invalid auto number value " + inAutoNum));
2469 }
2470
2471 getTable().adjustLongAutoNumber(inAutoNum);
2472 return inAutoNum;
2473 }
2474
2475 @Override
2476 public void restoreLast(Object last) {
2477 if(last instanceof Integer) {
2478 getTable().restoreLastLongAutoNumber((Integer)last);
2479 }
2480 }
2481
2482 @Override
2483 public DataType getType() {
2484 return DataType.LONG;
2485 }
2486 }
2487
2488 private final class GuidAutoNumberGenerator extends AutoNumberGenerator
2489 {
2490 private Object _lastAutoNumber;
2491
2492 private GuidAutoNumberGenerator() {}
2493
2494 @Override
2495 public Object getLast() {
2496 return _lastAutoNumber;
2497 }
2498
2499 @Override
2500 public Object getNext(TableImpl.WriteRowState writeRowState) {
2501
2502 _lastAutoNumber = "{" + UUID.randomUUID() + "}";
2503 return _lastAutoNumber;
2504 }
2505
2506 @Override
2507 public Object handleInsert(TableImpl.WriteRowState writeRowState,
2508 Object inRowValue)
2509 throws IOException
2510 {
2511 _lastAutoNumber = toCharSequence(inRowValue);
2512 return _lastAutoNumber;
2513 }
2514
2515 @Override
2516 public void restoreLast(Object last) {
2517 _lastAutoNumber = null;
2518 }
2519
2520 @Override
2521 public DataType getType() {
2522 return DataType.GUID;
2523 }
2524 }
2525
2526 private final class ComplexTypeAutoNumberGenerator extends AutoNumberGenerator
2527 {
2528 private ComplexTypeAutoNumberGenerator() {}
2529
2530 @Override
2531 public Object getLast() {
2532
2533 return getTable().getLastComplexTypeAutoNumber();
2534 }
2535
2536 @Override
2537 public Object getNext(TableImpl.WriteRowState writeRowState) {
2538
2539 int nextComplexAutoNum = writeRowState.getComplexAutoNumber();
2540 if(nextComplexAutoNum <= INVALID_AUTO_NUMBER) {
2541
2542 nextComplexAutoNum = getTable().getNextComplexTypeAutoNumber();
2543 writeRowState.setComplexAutoNumber(nextComplexAutoNum);
2544 }
2545 return new ComplexValueForeignKeyImpl(ColumnImpl.this,
2546 nextComplexAutoNum);
2547 }
2548
2549 @Override
2550 public Object handleInsert(TableImpl.WriteRowState writeRowState,
2551 Object inRowValue)
2552 throws IOException
2553 {
2554 ComplexValueForeignKey inComplexFK = null;
2555 if(inRowValue instanceof ComplexValueForeignKey) {
2556 inComplexFK = (ComplexValueForeignKey)inRowValue;
2557 } else {
2558 inComplexFK = new ComplexValueForeignKeyImpl(
2559 ColumnImpl.this, toNumber(inRowValue).intValue());
2560 }
2561
2562 if(inComplexFK.getColumn() != ColumnImpl.this) {
2563 throw new InvalidValueException(withErrorContext(
2564 "Wrong column for complex value foreign key, found " +
2565 inComplexFK.getColumn().getName()));
2566 }
2567 if(inComplexFK.get() < 1) {
2568 throw new InvalidValueException(withErrorContext(
2569 "Invalid complex value foreign key value " + inComplexFK.get()));
2570 }
2571
2572 int prevRowValue = writeRowState.getComplexAutoNumber();
2573 if(prevRowValue <= INVALID_AUTO_NUMBER) {
2574 writeRowState.setComplexAutoNumber(inComplexFK.get());
2575 } else if(prevRowValue != inComplexFK.get()) {
2576 throw new InvalidValueException(withErrorContext(
2577 "Inconsistent complex value foreign key values: found " +
2578 prevRowValue + ", given " + inComplexFK));
2579 }
2580
2581
2582 getTable().adjustComplexTypeAutoNumber(inComplexFK.get());
2583
2584 return inComplexFK;
2585 }
2586
2587 @Override
2588 public void restoreLast(Object last) {
2589 if(last instanceof ComplexValueForeignKey) {
2590 getTable().restoreLastComplexTypeAutoNumber(
2591 ((ComplexValueForeignKey)last).get());
2592 }
2593 }
2594
2595 @Override
2596 public DataType getType() {
2597 return DataType.COMPLEX_TYPE;
2598 }
2599 }
2600
2601 private final class UnsupportedAutoNumberGenerator extends AutoNumberGenerator
2602 {
2603 private final DataType _genType;
2604
2605 private UnsupportedAutoNumberGenerator(DataType genType) {
2606 _genType = genType;
2607 }
2608
2609 @Override
2610 public Object getLast() {
2611 return null;
2612 }
2613
2614 @Override
2615 public Object getNext(TableImpl.WriteRowState writeRowState) {
2616 throw new UnsupportedOperationException();
2617 }
2618
2619 @Override
2620 public Object handleInsert(TableImpl.WriteRowState writeRowState,
2621 Object inRowValue) {
2622 throw new UnsupportedOperationException();
2623 }
2624
2625 @Override
2626 public void restoreLast(Object last) {
2627 throw new UnsupportedOperationException();
2628 }
2629
2630 @Override
2631 public DataType getType() {
2632 return _genType;
2633 }
2634 }
2635
2636
2637
2638
2639
2640
2641 public static final class SortOrder
2642 {
2643 private final short _value;
2644 private final short _version;
2645
2646 public SortOrder(short value, short version) {
2647 _value = value;
2648 _version = version;
2649 }
2650
2651 public short getValue() {
2652 return _value;
2653 }
2654
2655 public short getVersion() {
2656 return _version;
2657 }
2658
2659 @Override
2660 public int hashCode() {
2661 return _value;
2662 }
2663
2664 @Override
2665 public boolean equals(Object o) {
2666 return ((this == o) ||
2667 ((o != null) && (getClass() == o.getClass()) &&
2668 (_value == ((SortOrder)o)._value) &&
2669 (_version == ((SortOrder)o)._version)));
2670 }
2671
2672 @Override
2673 public String toString() {
2674 return CustomToStringStyle.valueBuilder(this)
2675 .append(null, _value + "(" + _version + ")")
2676 .toString();
2677 }
2678 }
2679
2680
2681
2682
2683 static final class InitArgs
2684 {
2685 public final TableImpl table;
2686 public final ByteBuffer buffer;
2687 public final int offset;
2688 public final String name;
2689 public final int displayIndex;
2690 public final byte colType;
2691 public final byte flags;
2692 public final byte extFlags;
2693 public DataType type;
2694
2695 InitArgs(TableImpl table, ByteBuffer buffer, int offset, String name,
2696 int displayIndex) {
2697 this.table = table;
2698 this.buffer = buffer;
2699 this.offset = offset;
2700 this.name = name;
2701 this.displayIndex = displayIndex;
2702
2703 this.colType = buffer.get(offset + table.getFormat().OFFSET_COLUMN_TYPE);
2704 this.flags = buffer.get(offset + table.getFormat().OFFSET_COLUMN_FLAGS);
2705 this.extFlags = readExtraFlags(buffer, offset, table.getFormat());
2706 }
2707 }
2708
2709
2710
2711
2712
2713 private static final class RequiredColValidator extends InternalColumnValidator
2714 {
2715 private RequiredColValidator(ColumnValidator delegate) {
2716 super(delegate);
2717 }
2718
2719 @Override
2720 protected Object internalValidate(Column col, Object val)
2721 throws IOException
2722 {
2723 if(val == null) {
2724 throw new InvalidValueException(
2725 ((ColumnImpl)col).withErrorContext(
2726 "Missing value for required column"));
2727 }
2728 return val;
2729 }
2730
2731 @Override
2732 protected void appendToString(StringBuilder sb) {
2733 sb.append("required=true");
2734 }
2735 }
2736
2737
2738
2739
2740
2741 private static final class NoZeroLenColValidator extends InternalColumnValidator
2742 {
2743 private NoZeroLenColValidator(ColumnValidator delegate) {
2744 super(delegate);
2745 }
2746
2747 @Override
2748 protected Object internalValidate(Column col, Object val)
2749 throws IOException
2750 {
2751 CharSequence valStr = toCharSequence(val);
2752
2753 if((valStr != null) && valStr.length() == 0) {
2754 throw new InvalidValueException(
2755 ((ColumnImpl)col).withErrorContext(
2756 "Zero length string is not allowed"));
2757 }
2758 return valStr;
2759 }
2760
2761 @Override
2762 protected void appendToString(StringBuilder sb) {
2763 sb.append("allowZeroLength=false");
2764 }
2765 }
2766
2767
2768
2769
2770 protected static abstract class DateTimeFactory
2771 {
2772 public abstract DateTimeType getType();
2773
2774 public abstract Object fromDateBits(ColumnImpl col, long dateBits);
2775
2776 public abstract double toDateDouble(Object value, DateTimeContext dtc);
2777
2778 public abstract Object toInternalValue(DatabaseImpl db, Object value);
2779 }
2780
2781
2782
2783
2784 private static final class DefaultDateTimeFactory extends DateTimeFactory
2785 {
2786 @Override
2787 public DateTimeType getType() {
2788 return DateTimeType.DATE;
2789 }
2790
2791 @Override
2792 public Object fromDateBits(ColumnImpl col, long dateBits) {
2793 long time = col.fromDateDouble(
2794 Double.longBitsToDouble(dateBits));
2795 return new DateExt(time, dateBits);
2796 }
2797
2798 @Override
2799 public double toDateDouble(Object value, DateTimeContext dtc) {
2800
2801
2802
2803 long time = 0L;
2804 if(value instanceof TemporalAccessor) {
2805 time = toInstant((TemporalAccessor)value, dtc).toEpochMilli();
2806 } else {
2807 time = toDateLong(value);
2808 }
2809
2810
2811 time += getToLocalTimeZoneOffset(time, dtc.getTimeZone());
2812 return toLocalDateDouble(time);
2813 }
2814
2815 @Override
2816 public Object toInternalValue(DatabaseImpl db, Object value) {
2817 return ((value instanceof Date) ? value :
2818 new Date(toDateLong(value)));
2819 }
2820 }
2821
2822
2823
2824
2825 private static final class LDTDateTimeFactory extends DateTimeFactory
2826 {
2827 @Override
2828 public DateTimeType getType() {
2829 return DateTimeType.LOCAL_DATE_TIME;
2830 }
2831
2832 @Override
2833 public Object fromDateBits(ColumnImpl col, long dateBits) {
2834 return ldtFromLocalDateDouble(Double.longBitsToDouble(dateBits));
2835 }
2836
2837 @Override
2838 public double toDateDouble(Object value, DateTimeContext dtc) {
2839
2840
2841
2842 if(!(value instanceof TemporalAccessor)) {
2843 value = Instant.ofEpochMilli(toDateLong(value));
2844 }
2845 return ColumnImpl.toDateDouble(
2846 temporalToLocalDateTime((TemporalAccessor)value, dtc));
2847 }
2848
2849 @Override
2850 public Object toInternalValue(DatabaseImpl db, Object value) {
2851 return toLocalDateTime(value, db);
2852 }
2853 }
2854
2855
2856 static interface InMemoryBlob {
2857 public byte[] getBytes() throws IOException;
2858 }
2859 }