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