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.LocaleUtil;
66 import com.healthmarketscience.jackcess.impl.expr.NumberFormatter;
67 import com.healthmarketscience.jackcess.util.ColumnValidator;
68 import com.healthmarketscience.jackcess.util.SimpleColumnValidator;
69 import org.apache.commons.lang3.builder.ToStringBuilder;
70 import org.apache.commons.logging.Log;
71 import org.apache.commons.logging.LogFactory;
72
73
74
75
76
77
78 public class ColumnImpl implements Column, Comparable<ColumnImpl>, DateTimeContext
79 {
80
81 protected static final Log LOG = LogFactory.getLog(ColumnImpl.class);
82
83
84
85
86
87
88
89
90 public static final Object RETURN_ROW_ID = "<RETURN_ROW_ID>";
91
92
93
94
95 private static final long MILLISECONDS_PER_DAY = (24L * 60L * 60L * 1000L);
96 private static final long SECONDS_PER_DAY = (24L * 60L * 60L);
97 private static final long NANOS_PER_SECOND = 1_000_000_000L;
98 private static final long NANOS_PER_MILLI = 1_000_000L;
99 private static final long MILLIS_PER_SECOND = 1000L;
100
101
102
103
104
105
106
107 static final long MILLIS_BETWEEN_EPOCH_AND_1900 =
108 25569L * MILLISECONDS_PER_DAY;
109
110 public static final LocalDate BASE_LD = LocalDate.of(1899, 12, 30);
111 public static final LocalTime BASE_LT = LocalTime.of(0, 0);
112 public static final LocalDateTime BASE_LDT = LocalDateTime.of(BASE_LD, BASE_LT);
113
114 private static final LocalDate BASE_EXT_LD = LocalDate.of(1, 1, 1);
115 private static final LocalTime BASE_EXT_LT = LocalTime.of(0, 0);
116 private static final LocalDateTime BASE_EXT_LDT =
117 LocalDateTime.of(BASE_EXT_LD, BASE_EXT_LT);
118 private static final byte[] EXT_LDT_TRAILER = {':', '7', 0x00};
119
120 private static final DateTimeFactory DEF_DATE_TIME_FACTORY =
121 new DefaultDateTimeFactory();
122
123 static final DateTimeFactory LDT_DATE_TIME_FACTORY =
124 new LDTDateTimeFactory();
125
126
127
128
129
130 public static final byte FIXED_LEN_FLAG_MASK = (byte)0x01;
131
132
133
134
135
136 public static final byte AUTO_NUMBER_FLAG_MASK = (byte)0x04;
137
138
139
140
141
142 public static final byte AUTO_NUMBER_GUID_FLAG_MASK = (byte)0x40;
143
144
145
146
147
148 public static final byte HYPERLINK_FLAG_MASK = (byte)0x80;
149
150
151
152
153
154 public static final byte UPDATABLE_FLAG_MASK = (byte)0x02;
155
156
157
158
159 protected static final byte COMPRESSED_UNICODE_EXT_FLAG_MASK = (byte)0x01;
160 private static final byte CALCULATED_EXT_FLAG_MASK = (byte)0xC0;
161
162 static final byte NUMERIC_NEGATIVE_BYTE = (byte)0x80;
163
164
165 private static final short GENERAL_SORT_ORDER_VALUE = 1033;
166
167
168
169
170
171 public static final SortOrder GENERAL_97_SORT_ORDER =
172 new SortOrder(GENERAL_SORT_ORDER_VALUE, (short)-1);
173
174
175
176
177
178 public static final SortOrder GENERAL_LEGACY_SORT_ORDER =
179 new SortOrder(GENERAL_SORT_ORDER_VALUE, (short)0);
180
181
182
183
184
185 public static final SortOrder GENERAL_SORT_ORDER =
186 new SortOrder(GENERAL_SORT_ORDER_VALUE, (short)1);
187
188
189
190 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*");
191
192
193 private static final byte[] TEXT_COMPRESSION_HEADER =
194 { (byte)0xFF, (byte)0XFE };
195 private static final char MIN_COMPRESS_CHAR = 1;
196 private static final char MAX_COMPRESS_CHAR = 0xFF;
197
198
199 static final int INVALID_AUTO_NUMBER = 0;
200
201 static final int INVALID_LENGTH = -1;
202
203
204
205 private final TableImpl _table;
206
207 private final boolean _variableLength;
208
209 private final boolean _autoNumber;
210
211 private final boolean _calculated;
212
213 private final DataType _type;
214
215 private final short _columnLength;
216
217 private final short _columnNumber;
218
219 private int _columnIndex;
220
221 private final int _displayIndex;
222
223 private final String _name;
224
225 private final int _fixedDataOffset;
226
227 private final int _varLenTableIndex;
228
229 private final AutoNumberGenerator _autoNumberGenerator;
230
231 private PropertyMap _props;
232
233 private ColumnValidator _validator = SimpleColumnValidator.INSTANCE;
234
235 private ColDefaultValueEvalContext _defValue;
236
237 private int _lengthInUnits = INVALID_LENGTH;
238
239
240
241
242 protected ColumnImpl(TableImpl table, String name, DataType type,
243 int colNumber, int fixedOffset, int varLenIndex) {
244 _table = table;
245 _name = name;
246 _type = type;
247
248 if(!_type.isVariableLength()) {
249 _columnLength = (short)type.getFixedSize();
250 } else {
251 _columnLength = (short)type.getMaxSize();
252 }
253 _variableLength = type.isVariableLength();
254 _autoNumber = false;
255 _calculated = false;
256 _autoNumberGenerator = null;
257 _columnNumber = (short)colNumber;
258 _columnIndex = colNumber;
259 _displayIndex = colNumber;
260 _fixedDataOffset = fixedOffset;
261 _varLenTableIndex = varLenIndex;
262 }
263
264
265
266
267
268 ColumnImpl(InitArgs args)
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 return ByteBuffer.wrap(toByteArray(obj)).order(order);
1483 }
1484
1485
1486
1487
1488
1489
1490
1491
1492 protected ByteBuffer writeFixedLengthField(Object obj, ByteOrder order)
1493 throws IOException
1494 {
1495 int size = getFixedDataSize();
1496
1497 ByteBuffer buffer = writeFixedLengthField(
1498 obj, PageChannel.createBuffer(size, order));
1499 buffer.flip();
1500 return buffer;
1501 }
1502
1503 protected ByteBuffer writeFixedLengthField(Object obj, ByteBuffer buffer)
1504 throws IOException
1505 {
1506
1507
1508 obj = booleanToInteger(obj);
1509
1510 switch(getType()) {
1511 case BOOLEAN:
1512
1513 break;
1514 case BYTE:
1515 buffer.put(toNumber(obj).byteValue());
1516 break;
1517 case INT:
1518 buffer.putShort(toNumber(obj).shortValue());
1519 break;
1520 case LONG:
1521 buffer.putInt(toNumber(obj).intValue());
1522 break;
1523 case MONEY:
1524 writeCurrencyValue(buffer, obj);
1525 break;
1526 case FLOAT:
1527 buffer.putFloat(toNumber(obj).floatValue());
1528 break;
1529 case DOUBLE:
1530 buffer.putDouble(toNumber(obj).doubleValue());
1531 break;
1532 case SHORT_DATE_TIME:
1533 writeDateValue(buffer, obj);
1534 break;
1535 case TEXT:
1536
1537
1538 int numChars = getLengthInUnits();
1539
1540 buffer.put(encodeTextValue(obj, numChars, numChars, true));
1541 break;
1542 case GUID:
1543 writeGUIDValue(buffer, obj);
1544 break;
1545 case NUMERIC:
1546
1547
1548 writeNumericValue(buffer, obj);
1549 break;
1550 case BINARY:
1551 case UNKNOWN_0D:
1552 case UNKNOWN_11:
1553 case COMPLEX_TYPE:
1554 buffer.putInt(toNumber(obj).intValue());
1555 break;
1556 case BIG_INT:
1557 buffer.putLong(toNumber(obj).longValue());
1558 break;
1559 case EXT_DATE_TIME:
1560 writeExtendedDateValue(buffer, obj);
1561 break;
1562 case UNSUPPORTED_FIXEDLEN:
1563 byte[] bytes = toByteArray(obj);
1564 if(bytes.length != getLength()) {
1565 throw new InvalidValueException(withErrorContext(
1566 "Invalid fixed size binary data, size "
1567 + getLength() + ", got " + bytes.length));
1568 }
1569 buffer.put(bytes);
1570 break;
1571 default:
1572 throw new IOException(withErrorContext(
1573 "Unsupported data type: " + getType()));
1574 }
1575 return buffer;
1576 }
1577
1578
1579
1580
1581 String decodeTextValue(byte[] data)
1582 {
1583
1584
1585 boolean isCompressed = ((data.length > 1) &&
1586 (data[0] == TEXT_COMPRESSION_HEADER[0]) &&
1587 (data[1] == TEXT_COMPRESSION_HEADER[1]));
1588
1589 if(isCompressed) {
1590
1591
1592
1593
1594 StringBuilder textBuf = new StringBuilder(data.length);
1595
1596 int dataStart = TEXT_COMPRESSION_HEADER.length;
1597 int dataEnd = dataStart;
1598 boolean inCompressedMode = true;
1599 while(dataEnd < data.length) {
1600 if(data[dataEnd] == (byte)0x00) {
1601
1602
1603 decodeTextSegment(data, dataStart, dataEnd, inCompressedMode,
1604 textBuf);
1605 inCompressedMode = !inCompressedMode;
1606 ++dataEnd;
1607 dataStart = dataEnd;
1608
1609 } else {
1610 ++dataEnd;
1611 }
1612 }
1613
1614 decodeTextSegment(data, dataStart, dataEnd, inCompressedMode, textBuf);
1615
1616 return textBuf.toString();
1617
1618 }
1619
1620 return decodeUncompressedText(data, getCharset());
1621 }
1622
1623
1624
1625
1626
1627 private void decodeTextSegment(byte[] data, int dataStart, int dataEnd,
1628 boolean inCompressedMode,
1629 StringBuilder textBuf)
1630 {
1631 if(dataEnd <= dataStart) {
1632
1633 return;
1634 }
1635 int dataLength = dataEnd - dataStart;
1636
1637 if(inCompressedMode) {
1638 byte[] tmpData = new byte[dataLength * 2];
1639 int tmpIdx = 0;
1640 for(int i = dataStart; i < dataEnd; ++i) {
1641 tmpData[tmpIdx] = data[i];
1642 tmpIdx += 2;
1643 }
1644 data = tmpData;
1645 dataStart = 0;
1646 dataLength = data.length;
1647 }
1648
1649 textBuf.append(decodeUncompressedText(data, dataStart, dataLength,
1650 getCharset()));
1651 }
1652
1653
1654
1655
1656
1657 private static CharBuffer decodeUncompressedText(
1658 byte[] textBytes, int startPos, int length, Charset charset)
1659 {
1660 return charset.decode(ByteBuffer.wrap(textBytes, startPos, length));
1661 }
1662
1663
1664
1665
1666 ByteBuffer encodeTextValue(Object obj, int minChars, int maxChars,
1667 boolean forceUncompressed)
1668 throws IOException
1669 {
1670 CharSequence text = toCharSequence(obj);
1671 if((text.length() > maxChars) || (text.length() < minChars)) {
1672 throw new InvalidValueException(withErrorContext(
1673 "Text is wrong length for " + getType() +
1674 " column, max " + maxChars
1675 + ", min " + minChars + ", got " + text.length()));
1676 }
1677
1678
1679 if(!forceUncompressed && isCompressedUnicode() &&
1680 (text.length() <= getFormat().MAX_COMPRESSED_UNICODE_SIZE) &&
1681 isUnicodeCompressible(text)) {
1682
1683 byte[] encodedChars = new byte[TEXT_COMPRESSION_HEADER.length +
1684 text.length()];
1685 encodedChars[0] = TEXT_COMPRESSION_HEADER[0];
1686 encodedChars[1] = TEXT_COMPRESSION_HEADER[1];
1687 for(int i = 0; i < text.length(); ++i) {
1688 encodedChars[i + TEXT_COMPRESSION_HEADER.length] =
1689 (byte)text.charAt(i);
1690 }
1691 return ByteBuffer.wrap(encodedChars);
1692 }
1693
1694 return encodeUncompressedText(text, getCharset());
1695 }
1696
1697
1698
1699
1700
1701 private static boolean isUnicodeCompressible(CharSequence text) {
1702
1703
1704 if(text.length() <= TEXT_COMPRESSION_HEADER.length) {
1705 return false;
1706 }
1707
1708 for(int i = 0; i < text.length(); ++i) {
1709 char c = text.charAt(i);
1710 if((c < MIN_COMPRESS_CHAR) || (c > MAX_COMPRESS_CHAR)) {
1711 return false;
1712 }
1713 }
1714 return true;
1715 }
1716
1717
1718
1719
1720 private static byte getColumnBitFlags(ColumnBuilder col) {
1721 byte flags = UPDATABLE_FLAG_MASK;
1722 if(!col.isVariableLength()) {
1723 flags |= FIXED_LEN_FLAG_MASK;
1724 }
1725 if(col.isAutoNumber()) {
1726 byte autoNumFlags = 0;
1727 switch(col.getType()) {
1728 case LONG:
1729 case COMPLEX_TYPE:
1730 autoNumFlags = AUTO_NUMBER_FLAG_MASK;
1731 break;
1732 case GUID:
1733 autoNumFlags = AUTO_NUMBER_GUID_FLAG_MASK;
1734 break;
1735 default:
1736
1737 }
1738 flags |= autoNumFlags;
1739 }
1740 if(col.isHyperlink()) {
1741 flags |= HYPERLINK_FLAG_MASK;
1742 }
1743 return flags;
1744 }
1745
1746 @Override
1747 public String toString() {
1748 ToStringBuilder sb = CustomToStringStyle.builder(this)
1749 .append("name", "(" + _table.getName() + ") " + _name);
1750 byte typeValue = getOriginalDataType();
1751 sb.append("type", "0x" + Integer.toHexString(typeValue) +
1752 " (" + _type + ")")
1753 .append("number", _columnNumber)
1754 .append("length", _columnLength)
1755 .append("variableLength", _variableLength);
1756 if(_calculated) {
1757 sb.append("calculated", _calculated)
1758 .append("expression",
1759 CustomToStringStyle.ignoreNull(getCalculationContext()));
1760 }
1761 if(_type.isTextual()) {
1762 sb.append("compressedUnicode", isCompressedUnicode())
1763 .append("textSortOrder", getTextSortOrder());
1764 if(getTextCodePage() > 0) {
1765 sb.append("textCodePage", getTextCodePage());
1766 }
1767 if(isAppendOnly()) {
1768 sb.append("appendOnly", isAppendOnly());
1769 }
1770 if(isHyperlink()) {
1771 sb.append("hyperlink", isHyperlink());
1772 }
1773 }
1774 if(_type.getHasScalePrecision()) {
1775 sb.append("precision", getPrecision())
1776 .append("scale", getScale());
1777 }
1778 if(_autoNumber) {
1779 sb.append("lastAutoNumber", _autoNumberGenerator.getLast());
1780 }
1781 sb.append("complexInfo", CustomToStringStyle.ignoreNull(getComplexInfo()))
1782 .append("validator", CustomToStringStyle.ignoreNull(
1783 ((_validator != SimpleColumnValidator.INSTANCE) ?
1784 _validator : null)))
1785 .append("defaultValue", CustomToStringStyle.ignoreNull(_defValue));
1786 return sb.toString();
1787 }
1788
1789
1790
1791
1792
1793
1794
1795 public static String decodeUncompressedText(byte[] textBytes,
1796 Charset charset)
1797 {
1798 return decodeUncompressedText(textBytes, 0, textBytes.length, charset)
1799 .toString();
1800 }
1801
1802
1803
1804
1805
1806
1807
1808 public static ByteBuffer encodeUncompressedText(CharSequence text,
1809 Charset charset)
1810 {
1811 CharBuffer cb = ((text instanceof CharBuffer) ?
1812 (CharBuffer)text : CharBuffer.wrap(text));
1813 return charset.encode(cb);
1814 }
1815
1816
1817
1818
1819
1820
1821 @Override
1822 public int compareTo(ColumnImpl other) {
1823 if (_columnNumber > other.getColumnNumber()) {
1824 return 1;
1825 } else if (_columnNumber < other.getColumnNumber()) {
1826 return -1;
1827 } else {
1828 return 0;
1829 }
1830 }
1831
1832
1833
1834
1835
1836
1837 public static short countVariableLength(List<ColumnBuilder> columns) {
1838 short rtn = 0;
1839 for (ColumnBuilder col : columns) {
1840 if (col.isVariableLength()) {
1841 rtn++;
1842 }
1843 }
1844 return rtn;
1845 }
1846
1847
1848
1849
1850
1851
1852 BigDecimal toBigDecimal(Object value)
1853 {
1854 return toBigDecimal(value, getDatabase());
1855 }
1856
1857
1858
1859
1860
1861
1862 static BigDecimal toBigDecimal(Object value, DatabaseImpl db)
1863 {
1864 if(value == null) {
1865 return BigDecimal.ZERO;
1866 } else if(value instanceof BigDecimal) {
1867 return (BigDecimal)value;
1868 } else if(value instanceof BigInteger) {
1869 return new BigDecimal((BigInteger)value);
1870 } else if(value instanceof Number) {
1871 return new BigDecimal(((Number)value).doubleValue());
1872 } else if(value instanceof Boolean) {
1873
1874 return ((Boolean)value) ? BigDecimal.valueOf(-1) : BigDecimal.ZERO;
1875 } else if(value instanceof Date) {
1876 return new BigDecimal(toDateDouble(value, db));
1877 } else if(value instanceof LocalDateTime) {
1878 return new BigDecimal(toDateDouble((LocalDateTime)value));
1879 }
1880 return new BigDecimal(value.toString());
1881 }
1882
1883
1884
1885
1886
1887
1888 private Number toNumber(Object value)
1889 {
1890 return toNumber(value, getDatabase());
1891 }
1892
1893
1894
1895
1896
1897
1898 private static Number toNumber(Object value, DatabaseImpl db)
1899 {
1900 if(value == null) {
1901 return BigDecimal.ZERO;
1902 } else if(value instanceof Number) {
1903 return (Number)value;
1904 } else if(value instanceof Boolean) {
1905
1906 return ((Boolean)value) ? -1 : 0;
1907 } else if(value instanceof Date) {
1908 return toDateDouble(value, db);
1909 } else if(value instanceof LocalDateTime) {
1910 return toDateDouble((LocalDateTime)value);
1911 }
1912 return Double.valueOf(value.toString());
1913 }
1914
1915
1916
1917
1918
1919 public static CharSequence toCharSequence(Object value)
1920 throws IOException
1921 {
1922 if(value == null) {
1923 return null;
1924 } else if(value instanceof CharSequence) {
1925 return (CharSequence)value;
1926 } else if(SqlHelper.INSTANCE.isClob(value)) {
1927 return SqlHelper.INSTANCE.getClobString(value);
1928 } else if(value instanceof Reader) {
1929 char[] buf = new char[8 * 1024];
1930 StringBuilder sout = new StringBuilder();
1931 Reader in = (Reader)value;
1932 int read = 0;
1933 while((read = in.read(buf)) != -1) {
1934 sout.append(buf, 0, read);
1935 }
1936 return sout;
1937 }
1938
1939 return value.toString();
1940 }
1941
1942
1943
1944
1945
1946 public static byte[] toByteArray(Object value)
1947 throws IOException
1948 {
1949 if(value == null) {
1950 return null;
1951 } else if(value instanceof byte[]) {
1952 return (byte[])value;
1953 } else if(value instanceof InMemoryBlob) {
1954 return ((InMemoryBlob)value).getBytes();
1955 } else if(SqlHelper.INSTANCE.isBlob(value)) {
1956 return SqlHelper.INSTANCE.getBlobBytes(value);
1957 }
1958
1959 ByteArrayOutputStream bout = new ByteArrayOutputStream();
1960
1961 if(value instanceof InputStream) {
1962 ByteUtil.copy((InputStream)value, bout);
1963 } else {
1964
1965 try(ObjectOutputStream oos = new ObjectOutputStream(bout)){
1966 oos.writeObject(value);
1967 }
1968 }
1969
1970 return bout.toByteArray();
1971 }
1972
1973
1974
1975
1976
1977 public static boolean toBooleanValue(Object obj) {
1978 if(obj == null) {
1979 return false;
1980 } else if(obj instanceof Boolean) {
1981 return ((Boolean)obj).booleanValue();
1982 } else if(obj instanceof Number) {
1983
1984 if(obj instanceof BigDecimal) {
1985 return (((BigDecimal)obj).compareTo(BigDecimal.ZERO) != 0);
1986 }
1987 if(obj instanceof BigInteger) {
1988 return (((BigInteger)obj).compareTo(BigInteger.ZERO) != 0);
1989 }
1990 return (((Number)obj).doubleValue() != 0.0d);
1991 }
1992 return Boolean.parseBoolean(obj.toString());
1993 }
1994
1995
1996
1997
1998 private static void fixNumericByteOrder(byte[] bytes)
1999 {
2000
2001 for(int i = 0; i < bytes.length; i+=4) {
2002 ByteUtil.swap4Bytes(bytes, i);
2003 }
2004 }
2005
2006
2007
2008
2009 protected static Object booleanToInteger(Object obj) {
2010 if (obj instanceof Boolean) {
2011 obj = ((Boolean) obj) ? -1 : 0;
2012 }
2013 return obj;
2014 }
2015
2016
2017
2018
2019
2020
2021 public static RawData rawDataWrapper(byte[] bytes) {
2022 return new RawData(bytes);
2023 }
2024
2025
2026
2027
2028
2029
2030 public static boolean isRawData(Object value) {
2031 return(value instanceof RawData);
2032 }
2033
2034
2035
2036
2037
2038 protected static void writeDefinitions(TableCreator creator, ByteBuffer buffer)
2039 {
2040
2041
2042
2043 int longVariableOffset = creator.countNonLongVariableLength();
2044 creator.setColumnOffsets(0, 0, longVariableOffset);
2045
2046 for (ColumnBuilder col : creator.getColumns()) {
2047 writeDefinition(creator, col, buffer);
2048 }
2049
2050 for (ColumnBuilder col : creator.getColumns()) {
2051 TableImpl.writeName(buffer, col.getName(), creator.getCharset());
2052 }
2053 }
2054
2055 protected static void writeDefinition(
2056 TableMutator mutator, ColumnBuilder col, ByteBuffer buffer)
2057 {
2058 TableMutator.ColumnOffsets colOffsets = mutator.getColumnOffsets();
2059
2060 buffer.put(col.getType().getValue());
2061 buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER);
2062 buffer.putShort(col.getColumnNumber());
2063
2064 buffer.putShort(colOffsets.getNextVariableOffset(col));
2065
2066 buffer.putShort(col.getColumnNumber());
2067
2068 if(col.getType().isTextual()) {
2069
2070
2071 writeSortOrder(buffer, col.getTextSortOrder(), mutator.getFormat());
2072 } else {
2073
2074 if(col.getType().getHasScalePrecision() && !col.isCalculated()) {
2075 buffer.put(col.getPrecision());
2076 buffer.put(col.getScale());
2077 } else {
2078 buffer.put((byte) 0x00);
2079 buffer.put((byte) 0x00);
2080 }
2081 buffer.putShort((short) 0);
2082 }
2083
2084 buffer.put(getColumnBitFlags(col));
2085
2086
2087 if(col.isCalculated()) {
2088 buffer.put(CALCULATED_EXT_FLAG_MASK);
2089 } else if (col.isCompressedUnicode()) {
2090 buffer.put(COMPRESSED_UNICODE_EXT_FLAG_MASK);
2091 } else {
2092 buffer.put((byte)0);
2093 }
2094
2095 buffer.putInt(0);
2096
2097
2098 if(col.isVariableLength()) {
2099 buffer.putShort((short) 0);
2100 } else {
2101 buffer.putShort(colOffsets.getNextFixedOffset(col));
2102 }
2103
2104 if(!col.getType().isLongValue()) {
2105 short length = col.getLength();
2106 if(col.isCalculated()) {
2107
2108 if(!col.getType().isVariableLength() ||
2109 col.getType().getHasScalePrecision()) {
2110 length = CalculatedColumnUtil.CALC_FIXED_FIELD_LEN;
2111 } else {
2112 length += CalculatedColumnUtil.CALC_EXTRA_DATA_LEN;
2113 }
2114 }
2115 buffer.putShort(length);
2116 } else {
2117 buffer.putShort((short)0x0000);
2118 }
2119 }
2120
2121 protected static void writeColUsageMapDefinitions(
2122 TableCreator creator, ByteBuffer buffer)
2123 {
2124
2125 for(ColumnBuilder lvalCol : creator.getLongValueColumns()) {
2126 writeColUsageMapDefinition(creator, lvalCol, buffer);
2127 }
2128 }
2129
2130 protected static void writeColUsageMapDefinition(
2131 TableMutator creator, ColumnBuilder lvalCol, ByteBuffer buffer)
2132 {
2133 TableMutator.ColumnState colState = creator.getColumnState(lvalCol);
2134
2135 buffer.putShort(lvalCol.getColumnNumber());
2136
2137
2138 buffer.put(colState.getUmapOwnedRowNumber());
2139 ByteUtil.put3ByteInt(buffer, colState.getUmapPageNumber());
2140
2141 buffer.put(colState.getUmapFreeRowNumber());
2142 ByteUtil.put3ByteInt(buffer, colState.getUmapPageNumber());
2143 }
2144
2145
2146
2147
2148 static SortOrder readSortOrder(ByteBuffer buffer, int position,
2149 JetFormat format)
2150 {
2151 short value = buffer.getShort(position);
2152
2153 if(value == 0) {
2154
2155 return format.DEFAULT_SORT_ORDER;
2156 }
2157
2158 short version = format.DEFAULT_SORT_ORDER.getVersion();
2159 if(format.SIZE_SORT_ORDER == 4) {
2160 version = buffer.get(position + 3);
2161 }
2162
2163 if(value == GENERAL_SORT_ORDER_VALUE) {
2164 if(version == GENERAL_SORT_ORDER.getVersion()) {
2165 return GENERAL_SORT_ORDER;
2166 }
2167 if(version == GENERAL_LEGACY_SORT_ORDER.getVersion()) {
2168 return GENERAL_LEGACY_SORT_ORDER;
2169 }
2170 if(version == GENERAL_97_SORT_ORDER.getVersion()) {
2171 return GENERAL_97_SORT_ORDER;
2172 }
2173 }
2174 return new SortOrder(value, version);
2175 }
2176
2177
2178
2179
2180
2181 static short readCodePage(ByteBuffer buffer, int offset, JetFormat format)
2182 {
2183 int cpOffset = format.OFFSET_COLUMN_CODE_PAGE;
2184 return ((cpOffset >= 0) ? buffer.getShort(offset + cpOffset) : 0);
2185 }
2186
2187
2188
2189
2190 static byte readExtraFlags(ByteBuffer buffer, int offset, JetFormat format)
2191 {
2192 int extFlagsOffset = format.OFFSET_COLUMN_EXT_FLAGS;
2193 return ((extFlagsOffset >= 0) ? buffer.get(offset + extFlagsOffset) : 0);
2194 }
2195
2196
2197
2198
2199 private static void writeSortOrder(ByteBuffer buffer, SortOrder sortOrder,
2200 JetFormat format) {
2201 if(sortOrder == null) {
2202 sortOrder = format.DEFAULT_SORT_ORDER;
2203 }
2204 buffer.putShort(sortOrder.getValue());
2205 if(format.SIZE_SORT_ORDER == 4) {
2206 buffer.put((byte)0x00);
2207 buffer.put((byte)sortOrder.getVersion());
2208 }
2209 }
2210
2211
2212
2213
2214
2215 static boolean isImmutableValue(Object value) {
2216
2217 return !(value instanceof byte[]);
2218 }
2219
2220
2221
2222
2223
2224 public static Object toInternalValue(DataType dataType, Object value,
2225 DatabaseImpl db)
2226 throws IOException
2227 {
2228 return toInternalValue(dataType, value, db, null);
2229 }
2230
2231 static Object toInternalValue(DataType dataType, Object value,
2232 DatabaseImpl db,
2233 ColumnImpl.DateTimeFactory factory)
2234 throws IOException
2235 {
2236 if(value == null) {
2237 return null;
2238 }
2239
2240 switch(dataType) {
2241 case BOOLEAN:
2242 return ((value instanceof Boolean) ? value : toBooleanValue(value));
2243 case BYTE:
2244 return ((value instanceof Byte) ? value : toNumber(value, db).byteValue());
2245 case INT:
2246 return ((value instanceof Short) ? value :
2247 toNumber(value, db).shortValue());
2248 case LONG:
2249 return ((value instanceof Integer) ? value :
2250 toNumber(value, db).intValue());
2251 case MONEY:
2252 return toBigDecimal(value, db);
2253 case FLOAT:
2254 return ((value instanceof Float) ? value :
2255 toNumber(value, db).floatValue());
2256 case DOUBLE:
2257 return ((value instanceof Double) ? value :
2258 toNumber(value, db).doubleValue());
2259 case SHORT_DATE_TIME:
2260 if(factory == null) {
2261 factory = db.getDateTimeFactory();
2262 }
2263 return factory.toInternalValue(db, value);
2264 case TEXT:
2265 case MEMO:
2266 case GUID:
2267 return ((value instanceof String) ? value :
2268 toCharSequence(value).toString());
2269 case NUMERIC:
2270 return toBigDecimal(value, db);
2271 case COMPLEX_TYPE:
2272
2273 return value;
2274 case BIG_INT:
2275 return ((value instanceof Long) ? value :
2276 toNumber(value, db).longValue());
2277 case EXT_DATE_TIME:
2278 return toLocalDateTime(value, db);
2279 default:
2280
2281 return toByteArray(value);
2282 }
2283 }
2284
2285 protected static DateTimeFactory getDateTimeFactory(DateTimeType type) {
2286 return ((type == DateTimeType.LOCAL_DATE_TIME) ?
2287 LDT_DATE_TIME_FACTORY : DEF_DATE_TIME_FACTORY);
2288 }
2289
2290 String withErrorContext(String msg) {
2291 return withErrorContext(msg, getDatabase(), getTable().getName(), getName());
2292 }
2293
2294 boolean isThisColumn(Identifier identifier) {
2295 return(getTable().isThisTable(identifier) &&
2296 getName().equalsIgnoreCase(identifier.getObjectName()));
2297 }
2298
2299 private static String withErrorContext(
2300 String msg, DatabaseImpl db, String tableName, String colName) {
2301 return msg + " (Db=" + db.getName() + ";Table=" + tableName + ";Column=" +
2302 colName + ")";
2303 }
2304
2305
2306
2307
2308
2309
2310 @SuppressWarnings("deprecation")
2311 private static final class DateExt extends Date
2312 {
2313 private static final long serialVersionUID = 0L;
2314
2315
2316 private transient final long _dateBits;
2317
2318 private DateExt(long time, long dateBits) {
2319 super(time);
2320 _dateBits = dateBits;
2321 }
2322
2323 public long getDateBits() {
2324 return _dateBits;
2325 }
2326
2327 @Override
2328 public void setDate(int time) {
2329 throw new UnsupportedOperationException();
2330 }
2331
2332 @Override
2333 public void setHours(int time) {
2334 throw new UnsupportedOperationException();
2335 }
2336
2337 @Override
2338 public void setMinutes(int time) {
2339 throw new UnsupportedOperationException();
2340 }
2341
2342 @Override
2343 public void setMonth(int time) {
2344 throw new UnsupportedOperationException();
2345 }
2346
2347 @Override
2348 public void setSeconds(int time) {
2349 throw new UnsupportedOperationException();
2350 }
2351
2352 @Override
2353 public void setYear(int time) {
2354 throw new UnsupportedOperationException();
2355 }
2356
2357 @Override
2358 public void setTime(long time) {
2359 throw new UnsupportedOperationException();
2360 }
2361
2362 private Object writeReplace() throws ObjectStreamException {
2363
2364
2365 return new Date(super.getTime());
2366 }
2367 }
2368
2369
2370
2371
2372 private static final class RawData implements Serializable, InMemoryBlob
2373 {
2374 private static final long serialVersionUID = 0L;
2375
2376 private final byte[] _bytes;
2377
2378 private RawData(byte[] bytes) {
2379 _bytes = bytes;
2380 }
2381
2382 @Override
2383 public byte[] getBytes() {
2384 return _bytes;
2385 }
2386
2387 @Override
2388 public String toString() {
2389 return CustomToStringStyle.valueBuilder(this)
2390 .append(null, getBytes())
2391 .toString();
2392 }
2393
2394 private Object writeReplace() throws ObjectStreamException {
2395
2396
2397 return getBytes();
2398 }
2399 }
2400
2401
2402
2403
2404
2405 public abstract class AutoNumberGenerator
2406 {
2407 protected AutoNumberGenerator() {}
2408
2409
2410
2411
2412
2413 public abstract Object getLast();
2414
2415
2416
2417
2418
2419
2420
2421 public abstract Object getNext(TableImpl.WriteRowState writeRowState);
2422
2423
2424
2425
2426
2427
2428
2429 public abstract Object handleInsert(
2430 TableImpl.WriteRowState writeRowState, Object inRowValue)
2431 throws IOException;
2432
2433
2434
2435
2436 public abstract void restoreLast(Object last);
2437
2438
2439
2440
2441 public abstract DataType getType();
2442 }
2443
2444 private final class LongAutoNumberGenerator extends AutoNumberGenerator
2445 {
2446 private LongAutoNumberGenerator() {}
2447
2448 @Override
2449 public Object getLast() {
2450
2451 return getTable().getLastLongAutoNumber();
2452 }
2453
2454 @Override
2455 public Object getNext(TableImpl.WriteRowState writeRowState) {
2456
2457 return getTable().getNextLongAutoNumber();
2458 }
2459
2460 @Override
2461 public Object handleInsert(TableImpl.WriteRowState writeRowState,
2462 Object inRowValue)
2463 throws IOException
2464 {
2465 int inAutoNum = toNumber(inRowValue).intValue();
2466 if(inAutoNum <= INVALID_AUTO_NUMBER &&
2467 !getTable().isAllowAutoNumberInsert()) {
2468 throw new InvalidValueException(withErrorContext(
2469 "Invalid auto number value " + inAutoNum));
2470 }
2471
2472 getTable().adjustLongAutoNumber(inAutoNum);
2473 return inAutoNum;
2474 }
2475
2476 @Override
2477 public void restoreLast(Object last) {
2478 if(last instanceof Integer) {
2479 getTable().restoreLastLongAutoNumber((Integer)last);
2480 }
2481 }
2482
2483 @Override
2484 public DataType getType() {
2485 return DataType.LONG;
2486 }
2487 }
2488
2489 private final class GuidAutoNumberGenerator extends AutoNumberGenerator
2490 {
2491 private Object _lastAutoNumber;
2492
2493 private GuidAutoNumberGenerator() {}
2494
2495 @Override
2496 public Object getLast() {
2497 return _lastAutoNumber;
2498 }
2499
2500 @Override
2501 public Object getNext(TableImpl.WriteRowState writeRowState) {
2502
2503 _lastAutoNumber = "{" + UUID.randomUUID() + "}";
2504 return _lastAutoNumber;
2505 }
2506
2507 @Override
2508 public Object handleInsert(TableImpl.WriteRowState writeRowState,
2509 Object inRowValue)
2510 throws IOException
2511 {
2512 _lastAutoNumber = toCharSequence(inRowValue);
2513 return _lastAutoNumber;
2514 }
2515
2516 @Override
2517 public void restoreLast(Object last) {
2518 _lastAutoNumber = null;
2519 }
2520
2521 @Override
2522 public DataType getType() {
2523 return DataType.GUID;
2524 }
2525 }
2526
2527 private final class ComplexTypeAutoNumberGenerator extends AutoNumberGenerator
2528 {
2529 private ComplexTypeAutoNumberGenerator() {}
2530
2531 @Override
2532 public Object getLast() {
2533
2534 return getTable().getLastComplexTypeAutoNumber();
2535 }
2536
2537 @Override
2538 public Object getNext(TableImpl.WriteRowState writeRowState) {
2539
2540 int nextComplexAutoNum = writeRowState.getComplexAutoNumber();
2541 if(nextComplexAutoNum <= INVALID_AUTO_NUMBER) {
2542
2543 nextComplexAutoNum = getTable().getNextComplexTypeAutoNumber();
2544 writeRowState.setComplexAutoNumber(nextComplexAutoNum);
2545 }
2546 return new ComplexValueForeignKeyImpl(ColumnImpl.this,
2547 nextComplexAutoNum);
2548 }
2549
2550 @Override
2551 public Object handleInsert(TableImpl.WriteRowState writeRowState,
2552 Object inRowValue)
2553 throws IOException
2554 {
2555 ComplexValueForeignKey inComplexFK = null;
2556 if(inRowValue instanceof ComplexValueForeignKey) {
2557 inComplexFK = (ComplexValueForeignKey)inRowValue;
2558 } else {
2559 inComplexFK = new ComplexValueForeignKeyImpl(
2560 ColumnImpl.this, toNumber(inRowValue).intValue());
2561 }
2562
2563 if(inComplexFK.getColumn() != ColumnImpl.this) {
2564 throw new InvalidValueException(withErrorContext(
2565 "Wrong column for complex value foreign key, found " +
2566 inComplexFK.getColumn().getName()));
2567 }
2568 if(inComplexFK.get() < 1) {
2569 throw new InvalidValueException(withErrorContext(
2570 "Invalid complex value foreign key value " + inComplexFK.get()));
2571 }
2572
2573 int prevRowValue = writeRowState.getComplexAutoNumber();
2574 if(prevRowValue <= INVALID_AUTO_NUMBER) {
2575 writeRowState.setComplexAutoNumber(inComplexFK.get());
2576 } else if(prevRowValue != inComplexFK.get()) {
2577 throw new InvalidValueException(withErrorContext(
2578 "Inconsistent complex value foreign key values: found " +
2579 prevRowValue + ", given " + inComplexFK));
2580 }
2581
2582
2583 getTable().adjustComplexTypeAutoNumber(inComplexFK.get());
2584
2585 return inComplexFK;
2586 }
2587
2588 @Override
2589 public void restoreLast(Object last) {
2590 if(last instanceof ComplexValueForeignKey) {
2591 getTable().restoreLastComplexTypeAutoNumber(
2592 ((ComplexValueForeignKey)last).get());
2593 }
2594 }
2595
2596 @Override
2597 public DataType getType() {
2598 return DataType.COMPLEX_TYPE;
2599 }
2600 }
2601
2602 private final class UnsupportedAutoNumberGenerator extends AutoNumberGenerator
2603 {
2604 private final DataType _genType;
2605
2606 private UnsupportedAutoNumberGenerator(DataType genType) {
2607 _genType = genType;
2608 }
2609
2610 @Override
2611 public Object getLast() {
2612 return null;
2613 }
2614
2615 @Override
2616 public Object getNext(TableImpl.WriteRowState writeRowState) {
2617 throw new UnsupportedOperationException();
2618 }
2619
2620 @Override
2621 public Object handleInsert(TableImpl.WriteRowState writeRowState,
2622 Object inRowValue) {
2623 throw new UnsupportedOperationException();
2624 }
2625
2626 @Override
2627 public void restoreLast(Object last) {
2628 throw new UnsupportedOperationException();
2629 }
2630
2631 @Override
2632 public DataType getType() {
2633 return _genType;
2634 }
2635 }
2636
2637
2638
2639
2640
2641
2642 public static final class SortOrder
2643 {
2644 private final short _value;
2645 private final short _version;
2646
2647 public SortOrder(short value, short version) {
2648 _value = value;
2649 _version = version;
2650 }
2651
2652 public short getValue() {
2653 return _value;
2654 }
2655
2656 public short getVersion() {
2657 return _version;
2658 }
2659
2660 @Override
2661 public int hashCode() {
2662 return _value;
2663 }
2664
2665 @Override
2666 public boolean equals(Object o) {
2667 return ((this == o) ||
2668 ((o != null) && (getClass() == o.getClass()) &&
2669 (_value == ((SortOrder)o)._value) &&
2670 (_version == ((SortOrder)o)._version)));
2671 }
2672
2673 @Override
2674 public String toString() {
2675 LocaleUtil.LcidInfo info = LocaleUtil.getInfo(_value);
2676 String valueStr = _value + "(" + _version + ")";
2677 return CustomToStringStyle.valueBuilder(this)
2678 .append(null, (info != null) ? (valueStr + ", " + info) : valueStr)
2679 .toString();
2680 }
2681 }
2682
2683
2684
2685
2686 static final class InitArgs
2687 {
2688 public final TableImpl table;
2689 public final ByteBuffer buffer;
2690 public final int offset;
2691 public final String name;
2692 public final int displayIndex;
2693 public final byte colType;
2694 public final byte flags;
2695 public final byte extFlags;
2696 public DataType type;
2697
2698 InitArgs(TableImpl newTable, ByteBuffer newBuffer, int newOffset,
2699 String newName, int newDisplayIndex) {
2700 this.table = newTable;
2701 this.buffer = newBuffer;
2702 this.offset = newOffset;
2703 this.name = newName;
2704 this.displayIndex = newDisplayIndex;
2705
2706 this.colType = buffer.get(offset + table.getFormat().OFFSET_COLUMN_TYPE);
2707 this.flags = buffer.get(offset + table.getFormat().OFFSET_COLUMN_FLAGS);
2708 this.extFlags = readExtraFlags(buffer, offset, table.getFormat());
2709 }
2710 }
2711
2712
2713
2714
2715
2716 private static final class RequiredColValidator extends InternalColumnValidator
2717 {
2718 private RequiredColValidator(ColumnValidator delegate) {
2719 super(delegate);
2720 }
2721
2722 @Override
2723 protected Object internalValidate(Column col, Object val)
2724 throws IOException
2725 {
2726 if(val == null) {
2727 throw new InvalidValueException(
2728 ((ColumnImpl)col).withErrorContext(
2729 "Missing value for required column"));
2730 }
2731 return val;
2732 }
2733
2734 @Override
2735 protected void appendToString(StringBuilder sb) {
2736 sb.append("required=true");
2737 }
2738 }
2739
2740
2741
2742
2743
2744 private static final class NoZeroLenColValidator extends InternalColumnValidator
2745 {
2746 private NoZeroLenColValidator(ColumnValidator delegate) {
2747 super(delegate);
2748 }
2749
2750 @Override
2751 protected Object internalValidate(Column col, Object val)
2752 throws IOException
2753 {
2754 CharSequence valStr = toCharSequence(val);
2755
2756 if((valStr != null) && valStr.length() == 0) {
2757 throw new InvalidValueException(
2758 ((ColumnImpl)col).withErrorContext(
2759 "Zero length string is not allowed"));
2760 }
2761 return valStr;
2762 }
2763
2764 @Override
2765 protected void appendToString(StringBuilder sb) {
2766 sb.append("allowZeroLength=false");
2767 }
2768 }
2769
2770
2771
2772
2773 protected static abstract class DateTimeFactory
2774 {
2775 public abstract DateTimeType getType();
2776
2777 public abstract Object fromDateBits(ColumnImpl col, long dateBits);
2778
2779 public abstract double toDateDouble(Object value, DateTimeContext dtc);
2780
2781 public abstract Object toInternalValue(DatabaseImpl db, Object value);
2782 }
2783
2784
2785
2786
2787 private static final class DefaultDateTimeFactory extends DateTimeFactory
2788 {
2789 @Override
2790 public DateTimeType getType() {
2791 return DateTimeType.DATE;
2792 }
2793
2794 @Override
2795 public Object fromDateBits(ColumnImpl col, long dateBits) {
2796 long time = col.fromDateDouble(
2797 Double.longBitsToDouble(dateBits));
2798 return new DateExt(time, dateBits);
2799 }
2800
2801 @Override
2802 public double toDateDouble(Object value, DateTimeContext dtc) {
2803
2804
2805
2806 long time = 0L;
2807 if(value instanceof TemporalAccessor) {
2808 time = toInstant((TemporalAccessor)value, dtc).toEpochMilli();
2809 } else {
2810 time = toDateLong(value);
2811 }
2812
2813
2814 time += getToLocalTimeZoneOffset(time, dtc.getTimeZone());
2815 return toLocalDateDouble(time);
2816 }
2817
2818 @Override
2819 public Object toInternalValue(DatabaseImpl db, Object value) {
2820 return ((value instanceof Date) ? value :
2821 new Date(toDateLong(value)));
2822 }
2823 }
2824
2825
2826
2827
2828 private static final class LDTDateTimeFactory extends DateTimeFactory
2829 {
2830 @Override
2831 public DateTimeType getType() {
2832 return DateTimeType.LOCAL_DATE_TIME;
2833 }
2834
2835 @Override
2836 public Object fromDateBits(ColumnImpl col, long dateBits) {
2837 return ldtFromLocalDateDouble(Double.longBitsToDouble(dateBits));
2838 }
2839
2840 @Override
2841 public double toDateDouble(Object value, DateTimeContext dtc) {
2842
2843
2844
2845 if(!(value instanceof TemporalAccessor)) {
2846 value = Instant.ofEpochMilli(toDateLong(value));
2847 }
2848 return ColumnImpl.toDateDouble(
2849 temporalToLocalDateTime((TemporalAccessor)value, dtc));
2850 }
2851
2852 @Override
2853 public Object toInternalValue(DatabaseImpl db, Object value) {
2854 return toLocalDateTime(value, db);
2855 }
2856 }
2857
2858
2859 static interface InMemoryBlob {
2860 public byte[] getBytes() throws IOException;
2861 }
2862 }