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