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