You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ColumnImpl.java 82KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780
  1. /*
  2. Copyright (c) 2005 Health Market Science, Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package com.healthmarketscience.jackcess.impl;
  14. import java.io.ByteArrayOutputStream;
  15. import java.io.IOException;
  16. import java.io.InputStream;
  17. import java.io.ObjectOutputStream;
  18. import java.io.ObjectStreamException;
  19. import java.io.Reader;
  20. import java.io.Serializable;
  21. import java.math.BigDecimal;
  22. import java.math.BigInteger;
  23. import java.nio.ByteBuffer;
  24. import java.nio.ByteOrder;
  25. import java.nio.CharBuffer;
  26. import java.nio.charset.Charset;
  27. import java.time.DateTimeException;
  28. import java.time.Duration;
  29. import java.time.Instant;
  30. import java.time.LocalDate;
  31. import java.time.LocalDateTime;
  32. import java.time.LocalTime;
  33. import java.time.ZoneId;
  34. import java.time.ZonedDateTime;
  35. import java.time.temporal.TemporalAccessor;
  36. import java.time.temporal.TemporalQueries;
  37. import java.util.Calendar;
  38. import java.util.Collection;
  39. import java.util.Date;
  40. import java.util.List;
  41. import java.util.Map;
  42. import java.util.TimeZone;
  43. import java.util.UUID;
  44. import java.util.regex.Matcher;
  45. import java.util.regex.Pattern;
  46. import com.healthmarketscience.jackcess.Column;
  47. import com.healthmarketscience.jackcess.ColumnBuilder;
  48. import com.healthmarketscience.jackcess.DataType;
  49. import com.healthmarketscience.jackcess.DateTimeType;
  50. import com.healthmarketscience.jackcess.InvalidValueException;
  51. import com.healthmarketscience.jackcess.PropertyMap;
  52. import com.healthmarketscience.jackcess.Table;
  53. import com.healthmarketscience.jackcess.complex.ComplexColumnInfo;
  54. import com.healthmarketscience.jackcess.complex.ComplexValue;
  55. import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
  56. import com.healthmarketscience.jackcess.expr.Identifier;
  57. import com.healthmarketscience.jackcess.impl.complex.ComplexValueForeignKeyImpl;
  58. import com.healthmarketscience.jackcess.impl.expr.NumberFormatter;
  59. import com.healthmarketscience.jackcess.util.ColumnValidator;
  60. import com.healthmarketscience.jackcess.util.SimpleColumnValidator;
  61. import org.apache.commons.lang3.builder.ToStringBuilder;
  62. import org.apache.commons.logging.Log;
  63. import org.apache.commons.logging.LogFactory;
  64. /**
  65. * Access database column definition
  66. * @author Tim McCune
  67. * @usage _intermediate_class_
  68. */
  69. public class ColumnImpl implements Column, Comparable<ColumnImpl>, DateTimeContext
  70. {
  71. protected static final Log LOG = LogFactory.getLog(ColumnImpl.class);
  72. /**
  73. * Placeholder object for adding rows which indicates that the caller wants
  74. * the RowId of the new row. Must be added as an extra value at the end of
  75. * the row values array.
  76. * @see TableImpl#asRowWithRowId
  77. * @usage _intermediate_field_
  78. */
  79. public static final Object RETURN_ROW_ID = "<RETURN_ROW_ID>";
  80. /**
  81. * Access stores numeric dates in days. Java stores them in milliseconds.
  82. */
  83. private static final long MILLISECONDS_PER_DAY = (24L * 60L * 60L * 1000L);
  84. private static final long SECONDS_PER_DAY = (24L * 60L * 60L);
  85. private static final long NANOS_PER_SECOND = 1_000_000_000L;
  86. private static final long NANOS_PER_MILLI = 1_000_000L;
  87. private static final long MILLIS_PER_SECOND = 1000L;
  88. /**
  89. * Access starts counting dates at Dec 30, 1899 (note, this strange date
  90. * seems to be caused by MS compatibility with Lotus-1-2-3 and incorrect
  91. * leap years). Java starts counting at Jan 1, 1970. This is the # of
  92. * millis between them for conversion.
  93. */
  94. static final long MILLIS_BETWEEN_EPOCH_AND_1900 =
  95. 25569L * MILLISECONDS_PER_DAY;
  96. public static final LocalDate BASE_LD = LocalDate.of(1899, 12, 30);
  97. public static final LocalTime BASE_LT = LocalTime.of(0, 0);
  98. public static final LocalDateTime BASE_LDT = LocalDateTime.of(BASE_LD, BASE_LT);
  99. private static final DateTimeFactory DEF_DATE_TIME_FACTORY =
  100. new DefaultDateTimeFactory();
  101. static final DateTimeFactory LDT_DATE_TIME_FACTORY =
  102. new LDTDateTimeFactory();
  103. /**
  104. * mask for the fixed len bit
  105. * @usage _advanced_field_
  106. */
  107. public static final byte FIXED_LEN_FLAG_MASK = (byte)0x01;
  108. /**
  109. * mask for the auto number bit
  110. * @usage _advanced_field_
  111. */
  112. public static final byte AUTO_NUMBER_FLAG_MASK = (byte)0x04;
  113. /**
  114. * mask for the auto number guid bit
  115. * @usage _advanced_field_
  116. */
  117. public static final byte AUTO_NUMBER_GUID_FLAG_MASK = (byte)0x40;
  118. /**
  119. * mask for the hyperlink bit (on memo types)
  120. * @usage _advanced_field_
  121. */
  122. public static final byte HYPERLINK_FLAG_MASK = (byte)0x80;
  123. /**
  124. * mask for the "is updatable" field bit
  125. * @usage _advanced_field_
  126. */
  127. public static final byte UPDATABLE_FLAG_MASK = (byte)0x02;
  128. // some other flags?
  129. // 0x10: replication related field (or hidden?)
  130. protected static final byte COMPRESSED_UNICODE_EXT_FLAG_MASK = (byte)0x01;
  131. private static final byte CALCULATED_EXT_FLAG_MASK = (byte)0xC0;
  132. static final byte NUMERIC_NEGATIVE_BYTE = (byte)0x80;
  133. /** the value for the "general" sort order */
  134. private static final short GENERAL_SORT_ORDER_VALUE = 1033;
  135. /**
  136. * the "general" text sort order, version (access 1997)
  137. * @usage _intermediate_field_
  138. */
  139. public static final SortOrder GENERAL_97_SORT_ORDER =
  140. new SortOrder(GENERAL_SORT_ORDER_VALUE, (short)-1);
  141. /**
  142. * the "general" text sort order, legacy version (access 2000-2007)
  143. * @usage _intermediate_field_
  144. */
  145. public static final SortOrder GENERAL_LEGACY_SORT_ORDER =
  146. new SortOrder(GENERAL_SORT_ORDER_VALUE, (short)0);
  147. /**
  148. * the "general" text sort order, latest version (access 2010+)
  149. * @usage _intermediate_field_
  150. */
  151. public static final SortOrder GENERAL_SORT_ORDER =
  152. new SortOrder(GENERAL_SORT_ORDER_VALUE, (short)1);
  153. /** pattern matching textual guid strings (allows for optional surrounding
  154. '{' and '}') */
  155. 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*");
  156. /** header used to indicate unicode text compression */
  157. private static final byte[] TEXT_COMPRESSION_HEADER =
  158. { (byte)0xFF, (byte)0XFE };
  159. private static final char MIN_COMPRESS_CHAR = 1;
  160. private static final char MAX_COMPRESS_CHAR = 0xFF;
  161. /** auto numbers must be > 0 */
  162. static final int INVALID_AUTO_NUMBER = 0;
  163. static final int INVALID_LENGTH = -1;
  164. /** owning table */
  165. private final TableImpl _table;
  166. /** Whether or not the column is of variable length */
  167. private final boolean _variableLength;
  168. /** Whether or not the column is an autonumber column */
  169. private final boolean _autoNumber;
  170. /** Whether or not the column is a calculated column */
  171. private final boolean _calculated;
  172. /** Data type */
  173. private final DataType _type;
  174. /** Maximum column length */
  175. private final short _columnLength;
  176. /** 0-based column number */
  177. private final short _columnNumber;
  178. /** index of the data for this column within a list of row data */
  179. private int _columnIndex;
  180. /** display index of the data for this column */
  181. private final int _displayIndex;
  182. /** Column name */
  183. private final String _name;
  184. /** the offset of the fixed data in the row */
  185. private final int _fixedDataOffset;
  186. /** the index of the variable length data in the var len offset table */
  187. private final int _varLenTableIndex;
  188. /** the auto number generator for this column (if autonumber column) */
  189. private final AutoNumberGenerator _autoNumberGenerator;
  190. /** properties for this column, if any */
  191. private PropertyMap _props;
  192. /** Validator for writing new values */
  193. private ColumnValidator _validator = SimpleColumnValidator.INSTANCE;
  194. /** default value generator */
  195. private ColDefaultValueEvalContext _defValue;
  196. /** length of the column in units, lazily computed */
  197. private int _lengthInUnits = INVALID_LENGTH;
  198. /**
  199. * @usage _advanced_method_
  200. */
  201. protected ColumnImpl(TableImpl table, String name, DataType type,
  202. int colNumber, int fixedOffset, int varLenIndex) {
  203. _table = table;
  204. _name = name;
  205. _type = type;
  206. if(!_type.isVariableLength()) {
  207. _columnLength = (short)type.getFixedSize();
  208. } else {
  209. _columnLength = (short)type.getMaxSize();
  210. }
  211. _variableLength = type.isVariableLength();
  212. _autoNumber = false;
  213. _calculated = false;
  214. _autoNumberGenerator = null;
  215. _columnNumber = (short)colNumber;
  216. _columnIndex = colNumber;
  217. _displayIndex = colNumber;
  218. _fixedDataOffset = fixedOffset;
  219. _varLenTableIndex = varLenIndex;
  220. }
  221. /**
  222. * Read a column definition in from a buffer
  223. * @usage _advanced_method_
  224. */
  225. ColumnImpl(InitArgs args)
  226. throws IOException
  227. {
  228. _table = args.table;
  229. _name = args.name;
  230. _displayIndex = args.displayIndex;
  231. _type = args.type;
  232. _columnNumber = args.buffer.getShort(
  233. args.offset + getFormat().OFFSET_COLUMN_NUMBER);
  234. _columnLength = args.buffer.getShort(
  235. args.offset + getFormat().OFFSET_COLUMN_LENGTH);
  236. _variableLength = ((args.flags & FIXED_LEN_FLAG_MASK) == 0);
  237. _autoNumber = ((args.flags &
  238. (AUTO_NUMBER_FLAG_MASK | AUTO_NUMBER_GUID_FLAG_MASK)) != 0);
  239. _calculated = ((args.extFlags & CALCULATED_EXT_FLAG_MASK) != 0);
  240. _autoNumberGenerator = createAutoNumberGenerator();
  241. if(_variableLength) {
  242. _varLenTableIndex = args.buffer.getShort(
  243. args.offset + getFormat().OFFSET_COLUMN_VARIABLE_TABLE_INDEX);
  244. _fixedDataOffset = 0;
  245. } else {
  246. _fixedDataOffset = args.buffer.getShort(
  247. args.offset + getFormat().OFFSET_COLUMN_FIXED_DATA_OFFSET);
  248. _varLenTableIndex = 0;
  249. }
  250. }
  251. /**
  252. * Creates the appropriate ColumnImpl class and reads a column definition in
  253. * from a buffer
  254. * @param table owning table
  255. * @param buffer Buffer containing column definition
  256. * @param offset Offset in the buffer at which the column definition starts
  257. * @usage _advanced_method_
  258. */
  259. public static ColumnImpl create(TableImpl table, ByteBuffer buffer,
  260. int offset, String name, int displayIndex)
  261. throws IOException
  262. {
  263. InitArgs args = new InitArgs(table, buffer, offset, name, displayIndex);
  264. boolean calculated = ((args.extFlags & CALCULATED_EXT_FLAG_MASK) != 0);
  265. byte colType = args.colType;
  266. if(calculated) {
  267. // "real" data type is in the "result type" property
  268. PropertyMap colProps = table.getPropertyMaps().get(name);
  269. Byte resultType = (Byte)colProps.getValue(PropertyMap.RESULT_TYPE_PROP);
  270. if(resultType != null) {
  271. colType = resultType;
  272. }
  273. }
  274. try {
  275. args.type = DataType.fromByte(colType);
  276. } catch(IOException e) {
  277. LOG.warn(withErrorContext("Unsupported column type " + colType,
  278. table.getDatabase(), table.getName(), name));
  279. boolean variableLength = ((args.flags & FIXED_LEN_FLAG_MASK) == 0);
  280. args.type = (variableLength ? DataType.UNSUPPORTED_VARLEN :
  281. DataType.UNSUPPORTED_FIXEDLEN);
  282. return new UnsupportedColumnImpl(args);
  283. }
  284. if(calculated) {
  285. return CalculatedColumnUtil.create(args);
  286. }
  287. switch(args.type) {
  288. case TEXT:
  289. return new TextColumnImpl(args);
  290. case MEMO:
  291. return new MemoColumnImpl(args);
  292. case COMPLEX_TYPE:
  293. return new ComplexColumnImpl(args);
  294. default:
  295. // fall through
  296. }
  297. if(args.type.getHasScalePrecision()) {
  298. return new NumericColumnImpl(args);
  299. }
  300. if(args.type.isLongValue()) {
  301. return new LongValueColumnImpl(args);
  302. }
  303. return new ColumnImpl(args);
  304. }
  305. /**
  306. * Sets the usage maps for this column.
  307. */
  308. void setUsageMaps(UsageMap ownedPages, UsageMap freeSpacePages) {
  309. // base does nothing
  310. }
  311. void collectUsageMapPages(Collection<Integer> pages) {
  312. // base does nothing
  313. }
  314. /**
  315. * Secondary column initialization after the table is fully loaded.
  316. */
  317. void postTableLoadInit() throws IOException {
  318. // base does nothing
  319. }
  320. @Override
  321. public TableImpl getTable() {
  322. return _table;
  323. }
  324. @Override
  325. public DatabaseImpl getDatabase() {
  326. return getTable().getDatabase();
  327. }
  328. /**
  329. * @usage _advanced_method_
  330. */
  331. public JetFormat getFormat() {
  332. return getDatabase().getFormat();
  333. }
  334. /**
  335. * @usage _advanced_method_
  336. */
  337. public PageChannel getPageChannel() {
  338. return getDatabase().getPageChannel();
  339. }
  340. @Override
  341. public String getName() {
  342. return _name;
  343. }
  344. @Override
  345. public boolean isVariableLength() {
  346. return _variableLength;
  347. }
  348. @Override
  349. public boolean isAutoNumber() {
  350. return _autoNumber;
  351. }
  352. /**
  353. * @usage _advanced_method_
  354. */
  355. public short getColumnNumber() {
  356. return _columnNumber;
  357. }
  358. @Override
  359. public int getColumnIndex() {
  360. return _columnIndex;
  361. }
  362. /**
  363. * @usage _advanced_method_
  364. */
  365. public void setColumnIndex(int newColumnIndex) {
  366. _columnIndex = newColumnIndex;
  367. }
  368. /**
  369. * @usage _advanced_method_
  370. */
  371. public int getDisplayIndex() {
  372. return _displayIndex;
  373. }
  374. @Override
  375. public DataType getType() {
  376. return _type;
  377. }
  378. @Override
  379. public int getSQLType() throws IOException {
  380. return _type.getSQLType();
  381. }
  382. @Override
  383. public boolean isCompressedUnicode() {
  384. return false;
  385. }
  386. @Override
  387. public byte getPrecision() {
  388. return (byte)getType().getDefaultPrecision();
  389. }
  390. @Override
  391. public byte getScale() {
  392. return (byte)getType().getDefaultScale();
  393. }
  394. /**
  395. * @usage _intermediate_method_
  396. */
  397. public SortOrder getTextSortOrder() {
  398. return null;
  399. }
  400. /**
  401. * @usage _intermediate_method_
  402. */
  403. public short getTextCodePage() {
  404. return 0;
  405. }
  406. @Override
  407. public short getLength() {
  408. return _columnLength;
  409. }
  410. @Override
  411. public final short getLengthInUnits() {
  412. if(_lengthInUnits == INVALID_LENGTH) {
  413. _lengthInUnits = calcLengthInUnits();
  414. }
  415. return (short)_lengthInUnits;
  416. }
  417. protected int calcLengthInUnits() {
  418. return getType().toUnitSize(getLength(), getFormat());
  419. }
  420. @Override
  421. public boolean isCalculated() {
  422. return _calculated;
  423. }
  424. /**
  425. * @usage _advanced_method_
  426. */
  427. public int getVarLenTableIndex() {
  428. return _varLenTableIndex;
  429. }
  430. /**
  431. * @usage _advanced_method_
  432. */
  433. public int getFixedDataOffset() {
  434. return _fixedDataOffset;
  435. }
  436. protected Charset getCharset() {
  437. return getDatabase().getCharset();
  438. }
  439. @Override
  440. public TimeZone getTimeZone() {
  441. return getDatabase().getTimeZone();
  442. }
  443. @Override
  444. public ZoneId getZoneId() {
  445. return getDatabase().getZoneId();
  446. }
  447. @Override
  448. public DateTimeFactory getDateTimeFactory() {
  449. return getDatabase().getDateTimeFactory();
  450. }
  451. @Override
  452. public boolean isAppendOnly() {
  453. return (getVersionHistoryColumn() != null);
  454. }
  455. @Override
  456. public ColumnImpl getVersionHistoryColumn() {
  457. return null;
  458. }
  459. /**
  460. * Returns the number of database pages owned by this column.
  461. * @usage _intermediate_method_
  462. */
  463. public int getOwnedPageCount() {
  464. return 0;
  465. }
  466. /**
  467. * @usage _advanced_method_
  468. */
  469. public void setVersionHistoryColumn(ColumnImpl versionHistoryCol) {
  470. throw new UnsupportedOperationException();
  471. }
  472. @Override
  473. public boolean isHyperlink() {
  474. return false;
  475. }
  476. @Override
  477. public ComplexColumnInfo<? extends ComplexValue> getComplexInfo() {
  478. return null;
  479. }
  480. void initColumnValidator() throws IOException {
  481. if(getDatabase().isReadOnly()) {
  482. // validators are irrelevant for read-only databases
  483. return;
  484. }
  485. // first initialize any "external" (user-defined) validator
  486. setColumnValidator(null);
  487. // next, initialize any "internal" (property defined) validators
  488. reloadPropertiesValidators();
  489. }
  490. void reloadPropertiesValidators() throws IOException {
  491. if(isAutoNumber()) {
  492. // none of the props stuff applies to autonumber columns
  493. return;
  494. }
  495. if(isCalculated()) {
  496. CalcColEvalContext calcCol = null;
  497. if(getDatabase().isEvaluateExpressions()) {
  498. // init calc col expression evaluator
  499. PropertyMap props = getProperties();
  500. String calcExpr = (String)props.getValue(PropertyMap.EXPRESSION_PROP);
  501. calcCol = new CalcColEvalContext(this).setExpr(calcExpr);
  502. }
  503. setCalcColEvalContext(calcCol);
  504. // none of the remaining props stuff applies to calculated columns
  505. return;
  506. }
  507. // discard any existing internal validators and re-compute them
  508. // (essentially unwrap the external validator)
  509. _validator = getColumnValidator();
  510. _defValue = null;
  511. PropertyMap props = getProperties();
  512. // if the "required" property is enabled, add appropriate validator
  513. boolean required = (Boolean)props.getValue(PropertyMap.REQUIRED_PROP,
  514. Boolean.FALSE);
  515. if(required) {
  516. _validator = new RequiredColValidator(_validator);
  517. }
  518. // if the "allow zero len" property is disabled (textual columns only),
  519. // add appropriate validator
  520. boolean allowZeroLen =
  521. !getType().isTextual() ||
  522. (Boolean)props.getValue(PropertyMap.ALLOW_ZERO_LEN_PROP,
  523. Boolean.TRUE);
  524. if(!allowZeroLen) {
  525. _validator = new NoZeroLenColValidator(_validator);
  526. }
  527. // only check for props based exprs if this is enabled
  528. if(!getDatabase().isEvaluateExpressions()) {
  529. return;
  530. }
  531. String exprStr = PropertyMaps.getTrimmedStringProperty(
  532. props, PropertyMap.VALIDATION_RULE_PROP);
  533. if(exprStr != null) {
  534. String helpStr = PropertyMaps.getTrimmedStringProperty(
  535. props, PropertyMap.VALIDATION_TEXT_PROP);
  536. _validator = new ColValidatorEvalContext(this)
  537. .setExpr(exprStr, helpStr)
  538. .toColumnValidator(_validator);
  539. }
  540. String defValueStr = PropertyMaps.getTrimmedStringProperty(
  541. props, PropertyMap.DEFAULT_VALUE_PROP);
  542. if(defValueStr != null) {
  543. _defValue = new ColDefaultValueEvalContext(this)
  544. .setExpr(defValueStr);
  545. }
  546. }
  547. void propertiesUpdated() throws IOException {
  548. reloadPropertiesValidators();
  549. }
  550. @Override
  551. public ColumnValidator getColumnValidator() {
  552. // unwrap any "internal" validator
  553. return ((_validator instanceof InternalColumnValidator) ?
  554. ((InternalColumnValidator)_validator).getExternal() : _validator);
  555. }
  556. @Override
  557. public void setColumnValidator(ColumnValidator newValidator) {
  558. if(isAutoNumber()) {
  559. // cannot set autonumber validator (autonumber values are controlled
  560. // internally)
  561. if(newValidator != null) {
  562. throw new IllegalArgumentException(withErrorContext(
  563. "Cannot set ColumnValidator for autonumber columns"));
  564. }
  565. // just leave default validator instance alone
  566. return;
  567. }
  568. if(newValidator == null) {
  569. newValidator = getDatabase().getColumnValidatorFactory()
  570. .createValidator(this);
  571. if(newValidator == null) {
  572. newValidator = SimpleColumnValidator.INSTANCE;
  573. }
  574. }
  575. // handle delegation if "internal" validator in use
  576. if(_validator instanceof InternalColumnValidator) {
  577. ((InternalColumnValidator)_validator).setExternal(newValidator);
  578. } else {
  579. _validator = newValidator;
  580. }
  581. }
  582. byte getOriginalDataType() {
  583. return _type.getValue();
  584. }
  585. private AutoNumberGenerator createAutoNumberGenerator() {
  586. if(!_autoNumber || (_type == null)) {
  587. return null;
  588. }
  589. switch(_type) {
  590. case LONG:
  591. return new LongAutoNumberGenerator();
  592. case GUID:
  593. return new GuidAutoNumberGenerator();
  594. case COMPLEX_TYPE:
  595. return new ComplexTypeAutoNumberGenerator();
  596. default:
  597. LOG.warn(withErrorContext("Unknown auto number column type " + _type));
  598. return new UnsupportedAutoNumberGenerator(_type);
  599. }
  600. }
  601. /**
  602. * Returns the AutoNumberGenerator for this column if this is an autonumber
  603. * column, {@code null} otherwise.
  604. * @usage _advanced_method_
  605. */
  606. public AutoNumberGenerator getAutoNumberGenerator() {
  607. return _autoNumberGenerator;
  608. }
  609. @Override
  610. public PropertyMap getProperties() throws IOException {
  611. if(_props == null) {
  612. _props = getTable().getPropertyMaps().get(getName());
  613. }
  614. return _props;
  615. }
  616. @Override
  617. public Object setRowValue(Object[] rowArray, Object value) {
  618. rowArray[_columnIndex] = value;
  619. return value;
  620. }
  621. @Override
  622. public Object setRowValue(Map<String,Object> rowMap, Object value) {
  623. rowMap.put(_name, value);
  624. return value;
  625. }
  626. @Override
  627. public Object getRowValue(Object[] rowArray) {
  628. return rowArray[_columnIndex];
  629. }
  630. @Override
  631. public Object getRowValue(Map<String,?> rowMap) {
  632. return rowMap.get(_name);
  633. }
  634. public boolean storeInNullMask() {
  635. return (getType() == DataType.BOOLEAN);
  636. }
  637. public boolean writeToNullMask(Object value) {
  638. return toBooleanValue(value);
  639. }
  640. public Object readFromNullMask(boolean isNull) {
  641. return Boolean.valueOf(!isNull);
  642. }
  643. /**
  644. * Deserialize a raw byte value for this column into an Object
  645. * @param data The raw byte value
  646. * @return The deserialized Object
  647. * @usage _advanced_method_
  648. */
  649. public Object read(byte[] data) throws IOException {
  650. return read(data, PageChannel.DEFAULT_BYTE_ORDER);
  651. }
  652. /**
  653. * Deserialize a raw byte value for this column into an Object
  654. * @param data The raw byte value
  655. * @param order Byte order in which the raw value is stored
  656. * @return The deserialized Object
  657. * @usage _advanced_method_
  658. */
  659. public Object read(byte[] data, ByteOrder order) throws IOException {
  660. ByteBuffer buffer = ByteBuffer.wrap(data).order(order);
  661. switch(getType()) {
  662. case BOOLEAN:
  663. throw new IOException(withErrorContext("Tried to read a boolean from data instead of null mask."));
  664. case BYTE:
  665. return Byte.valueOf(buffer.get());
  666. case INT:
  667. return Short.valueOf(buffer.getShort());
  668. case LONG:
  669. return Integer.valueOf(buffer.getInt());
  670. case DOUBLE:
  671. return Double.valueOf(buffer.getDouble());
  672. case FLOAT:
  673. return Float.valueOf(buffer.getFloat());
  674. case SHORT_DATE_TIME:
  675. return readDateValue(buffer);
  676. case BINARY:
  677. return data;
  678. case TEXT:
  679. return decodeTextValue(data);
  680. case MONEY:
  681. return readCurrencyValue(buffer);
  682. case NUMERIC:
  683. return readNumericValue(buffer);
  684. case GUID:
  685. return readGUIDValue(buffer, order);
  686. case UNKNOWN_0D:
  687. case UNKNOWN_11:
  688. // treat like "binary" data
  689. return data;
  690. case COMPLEX_TYPE:
  691. return new ComplexValueForeignKeyImpl(this, buffer.getInt());
  692. case BIG_INT:
  693. return Long.valueOf(buffer.getLong());
  694. default:
  695. throw new IOException(withErrorContext("Unrecognized data type: " + _type));
  696. }
  697. }
  698. /**
  699. * Decodes "Currency" values.
  700. *
  701. * @param buffer Column value that points to currency data
  702. * @return BigDecimal representing the monetary value
  703. * @throws IOException if the value cannot be parsed
  704. */
  705. private BigDecimal readCurrencyValue(ByteBuffer buffer)
  706. throws IOException
  707. {
  708. if(buffer.remaining() != 8) {
  709. throw new IOException(withErrorContext("Invalid money value"));
  710. }
  711. return new BigDecimal(BigInteger.valueOf(buffer.getLong(0)), 4);
  712. }
  713. /**
  714. * Writes "Currency" values.
  715. */
  716. private void writeCurrencyValue(ByteBuffer buffer, Object value)
  717. throws IOException
  718. {
  719. Object inValue = value;
  720. try {
  721. BigDecimal decVal = toBigDecimal(value);
  722. inValue = decVal;
  723. // adjust scale (will cause the an ArithmeticException if number has too
  724. // many decimal places)
  725. decVal = decVal.setScale(4);
  726. // now, remove scale and convert to long (this will throw if the value is
  727. // too big)
  728. buffer.putLong(decVal.movePointRight(4).longValueExact());
  729. } catch(ArithmeticException e) {
  730. throw (IOException)
  731. new IOException(withErrorContext(
  732. "Currency value '" + inValue + "' out of range"))
  733. .initCause(e);
  734. }
  735. }
  736. /**
  737. * Decodes a NUMERIC field.
  738. */
  739. private BigDecimal readNumericValue(ByteBuffer buffer)
  740. {
  741. boolean negate = (buffer.get() != 0);
  742. byte[] tmpArr = ByteUtil.getBytes(buffer, 16);
  743. if(buffer.order() != ByteOrder.BIG_ENDIAN) {
  744. fixNumericByteOrder(tmpArr);
  745. }
  746. return toBigDecimal(tmpArr, negate, getScale());
  747. }
  748. static BigDecimal toBigDecimal(byte[] bytes, boolean negate, int scale)
  749. {
  750. if((bytes[0] & 0x80) != 0) {
  751. // the data is effectively unsigned, but the BigInteger handles it as
  752. // signed twos complement. we need to add an extra byte to the input so
  753. // that it will be treated as unsigned
  754. bytes = ByteUtil.copyOf(bytes, 0, bytes.length + 1, 1);
  755. }
  756. BigInteger intVal = new BigInteger(bytes);
  757. if(negate) {
  758. intVal = intVal.negate();
  759. }
  760. return new BigDecimal(intVal, scale);
  761. }
  762. /**
  763. * Writes a numeric value.
  764. */
  765. private void writeNumericValue(ByteBuffer buffer, Object value)
  766. throws IOException
  767. {
  768. Object inValue = value;
  769. try {
  770. BigDecimal decVal = toBigDecimal(value);
  771. inValue = decVal;
  772. int signum = decVal.signum();
  773. if(signum < 0) {
  774. decVal = decVal.negate();
  775. }
  776. // write sign byte
  777. buffer.put((signum < 0) ? NUMERIC_NEGATIVE_BYTE : 0);
  778. // adjust scale according to this column type (will cause the an
  779. // ArithmeticException if number has too many decimal places)
  780. decVal = decVal.setScale(getScale());
  781. // check precision
  782. if(decVal.precision() > getPrecision()) {
  783. throw new InvalidValueException(withErrorContext(
  784. "Numeric value is too big for specified precision "
  785. + getPrecision() + ": " + decVal));
  786. }
  787. // convert to unscaled BigInteger, big-endian bytes
  788. byte[] intValBytes = toUnscaledByteArray(
  789. decVal, getType().getFixedSize() - 1);
  790. if(buffer.order() != ByteOrder.BIG_ENDIAN) {
  791. fixNumericByteOrder(intValBytes);
  792. }
  793. buffer.put(intValBytes);
  794. } catch(ArithmeticException e) {
  795. throw (IOException)
  796. new IOException(withErrorContext(
  797. "Numeric value '" + inValue + "' out of range"))
  798. .initCause(e);
  799. }
  800. }
  801. byte[] toUnscaledByteArray(BigDecimal decVal, int maxByteLen)
  802. throws IOException
  803. {
  804. // convert to unscaled BigInteger, big-endian bytes
  805. byte[] intValBytes = decVal.unscaledValue().toByteArray();
  806. if(intValBytes.length > maxByteLen) {
  807. if((intValBytes[0] == 0) && ((intValBytes.length - 1) == maxByteLen)) {
  808. // in order to not return a negative two's complement value,
  809. // toByteArray() may return an extra leading 0 byte. we are working
  810. // with unsigned values, so we can drop the extra leading 0
  811. intValBytes = ByteUtil.copyOf(intValBytes, 1, maxByteLen);
  812. } else {
  813. throw new InvalidValueException(withErrorContext(
  814. "Too many bytes for valid BigInteger?"));
  815. }
  816. } else if(intValBytes.length < maxByteLen) {
  817. intValBytes = ByteUtil.copyOf(intValBytes, 0, maxByteLen,
  818. (maxByteLen - intValBytes.length));
  819. }
  820. return intValBytes;
  821. }
  822. /**
  823. * Decodes a date value.
  824. */
  825. private Object readDateValue(ByteBuffer buffer) {
  826. long dateBits = buffer.getLong();
  827. return getDateTimeFactory().fromDateBits(this, dateBits);
  828. }
  829. /**
  830. * Returns a java long time value converted from an access date double.
  831. * @usage _advanced_method_
  832. */
  833. public long fromDateDouble(double value) {
  834. return fromDateDouble(value, getTimeZone());
  835. }
  836. private static long fromDateDouble(double value, TimeZone tz) {
  837. long localTime = fromLocalDateDouble(value);
  838. return localTime - getFromLocalTimeZoneOffset(localTime, tz);
  839. }
  840. static long fromLocalDateDouble(double value) {
  841. long datePart = ((long)value) * MILLISECONDS_PER_DAY;
  842. // the fractional part of the double represents the time. it is always
  843. // a positive fraction of the day (even if the double is negative),
  844. // _not_ the time distance from zero (as one would expect with "normal"
  845. // numbers). therefore, we need to do a little number logic to convert
  846. // the absolute time fraction into a normal distance from zero number.
  847. long timePart = Math.round((Math.abs(value) % 1.0d) *
  848. MILLISECONDS_PER_DAY);
  849. long time = datePart + timePart;
  850. return time - MILLIS_BETWEEN_EPOCH_AND_1900;
  851. }
  852. public static LocalDateTime ldtFromLocalDateDouble(double value) {
  853. Duration dateTimeOffset = durationFromLocalDateDouble(value);
  854. return BASE_LDT.plus(dateTimeOffset);
  855. }
  856. private static Duration durationFromLocalDateDouble(double value) {
  857. long dateSeconds = ((long)value) * SECONDS_PER_DAY;
  858. // the fractional part of the double represents the time. it is always
  859. // a positive fraction of the day (even if the double is negative),
  860. // _not_ the time distance from zero (as one would expect with "normal"
  861. // numbers). therefore, we need to do a little number logic to convert
  862. // the absolute time fraction into a normal distance from zero number.
  863. double secondsDouble = (Math.abs(value) % 1.0d) * SECONDS_PER_DAY;
  864. long timeSeconds = (long)secondsDouble;
  865. long timeMillis = (long)(roundToMillis(secondsDouble % 1.0d) *
  866. MILLIS_PER_SECOND);
  867. return Duration.ofSeconds(dateSeconds + timeSeconds,
  868. timeMillis * NANOS_PER_MILLI);
  869. }
  870. /**
  871. * Writes a date value.
  872. */
  873. private void writeDateValue(ByteBuffer buffer, Object value)
  874. throws InvalidValueException
  875. {
  876. if(value == null) {
  877. buffer.putDouble(0d);
  878. } else if(value instanceof DateExt) {
  879. // this is a Date value previously read from readDateValue(). use the
  880. // original bits to store the value so we don't lose any precision
  881. buffer.putLong(((DateExt)value).getDateBits());
  882. } else {
  883. buffer.putDouble(toDateDouble(value));
  884. }
  885. }
  886. /**
  887. * Returns an access date double converted from a java Date/Calendar/Number
  888. * time value.
  889. * @usage _advanced_method_
  890. */
  891. public double toDateDouble(Object value)
  892. throws InvalidValueException
  893. {
  894. try {
  895. return toDateDouble(value, this);
  896. } catch(IllegalArgumentException iae) {
  897. throw new InvalidValueException(withErrorContext(iae.getMessage()), iae);
  898. }
  899. }
  900. /**
  901. * Returns an access date double converted from a java
  902. * Date/Calendar/Number/Temporal time value.
  903. * @usage _advanced_method_
  904. */
  905. private static double toDateDouble(Object value, DateTimeContext dtc) {
  906. return dtc.getDateTimeFactory().toDateDouble(value, dtc);
  907. }
  908. private static LocalDateTime toLocalDateTime(
  909. TemporalAccessor value, DateTimeContext dtc) {
  910. // handle some common Temporal types
  911. if(value instanceof LocalDateTime) {
  912. return (LocalDateTime)value;
  913. }
  914. if(value instanceof ZonedDateTime) {
  915. // if the temporal value has a timezone, convert it to this db's timezone
  916. return ((ZonedDateTime)value).withZoneSameInstant(
  917. dtc.getZoneId()).toLocalDateTime();
  918. }
  919. if(value instanceof Instant) {
  920. return LocalDateTime.ofInstant((Instant)value, dtc.getZoneId());
  921. }
  922. if(value instanceof LocalDate) {
  923. return ((LocalDate)value).atTime(BASE_LT);
  924. }
  925. if(value instanceof LocalTime) {
  926. return ((LocalTime)value).atDate(BASE_LD);
  927. }
  928. // generic handling for many other Temporal types
  929. try {
  930. LocalDate ld = value.query(TemporalQueries.localDate());
  931. if(ld == null) {
  932. ld = BASE_LD;
  933. }
  934. LocalTime lt = value.query(TemporalQueries.localTime());
  935. if(lt == null) {
  936. lt = BASE_LT;
  937. }
  938. ZoneId zone = value.query(TemporalQueries.zone());
  939. if(zone != null) {
  940. // the Temporal has a zone, see if it is the right zone. if not,
  941. // adjust it
  942. ZoneId zoneId = dtc.getZoneId();
  943. if(!zoneId.equals(zone)) {
  944. return ZonedDateTime.of(ld, lt, zone).withZoneSameInstant(zoneId)
  945. .toLocalDateTime();
  946. }
  947. }
  948. return LocalDateTime.of(ld, lt);
  949. } catch(DateTimeException | ArithmeticException e) {
  950. throw new IllegalArgumentException(
  951. "Unsupported temporal type " + value.getClass(), e);
  952. }
  953. }
  954. private static Instant toInstant(TemporalAccessor value, DateTimeContext dtc) {
  955. if(value instanceof ZonedDateTime) {
  956. return ((ZonedDateTime)value).toInstant();
  957. }
  958. if(value instanceof Instant) {
  959. return (Instant)value;
  960. }
  961. return toLocalDateTime(value, dtc).atZone(dtc.getZoneId()).toInstant();
  962. }
  963. static double toLocalDateDouble(long time) {
  964. time += MILLIS_BETWEEN_EPOCH_AND_1900;
  965. if(time < 0L) {
  966. // reverse the crazy math described in fromLocalDateDouble
  967. long timePart = -time % MILLISECONDS_PER_DAY;
  968. if(timePart > 0) {
  969. time -= (2 * (MILLISECONDS_PER_DAY - timePart));
  970. }
  971. }
  972. return time / (double)MILLISECONDS_PER_DAY;
  973. }
  974. public static double toDateDouble(LocalDateTime ldt) {
  975. Duration dateTimeOffset = Duration.between(BASE_LDT, ldt);
  976. return toLocalDateDouble(dateTimeOffset);
  977. }
  978. private static double toLocalDateDouble(Duration time) {
  979. long dateTimeSeconds = time.getSeconds();
  980. long timeSeconds = dateTimeSeconds % SECONDS_PER_DAY;
  981. if(timeSeconds < 0) {
  982. timeSeconds += SECONDS_PER_DAY;
  983. }
  984. long dateSeconds = dateTimeSeconds - timeSeconds;
  985. long timeNanos = time.getNano();
  986. // we have a difficult choice to make here between keeping a value which
  987. // most accurately represents the bits saved and rounding to a value that
  988. // would match what the user would expect too see. since we do a double
  989. // to long conversion, we end up in a situation where the value might be
  990. // 19.9999 seconds. access will display this as 20 seconds (access seems
  991. // to only record times to second precision). if we return 19.9999, then
  992. // when the value is written back out it will be exactly the same double
  993. // (good), but will display as 19 seconds (bad because it looks wrong to
  994. // the user). on the flip side, if we round, the value will display
  995. // "correctly" to the user, but if the value is written back out, it will
  996. // be a slightly different double value. this may not be a problem for
  997. // most situations, but may result in incorrect index based lookups. in
  998. // the old date time handling we use DateExt to store the original bits.
  999. // in jdk8, we cannot extend LocalDateTime. for now, we will try
  1000. // returning the value rounded to milliseconds (technically still more
  1001. // precision than access uses but more likely to round trip to the same
  1002. // value).
  1003. double timeDouble = ((roundToMillis((double)timeNanos / NANOS_PER_SECOND) +
  1004. timeSeconds) / SECONDS_PER_DAY);
  1005. double dateDouble = ((double)dateSeconds / SECONDS_PER_DAY);
  1006. if(dateSeconds < 0) {
  1007. timeDouble = -timeDouble;
  1008. }
  1009. return dateDouble + timeDouble;
  1010. }
  1011. /**
  1012. * Rounds the given decimal to milliseconds (3 decimal places) using the
  1013. * standard access rounding mode.
  1014. */
  1015. private static double roundToMillis(double dbl) {
  1016. return ((dbl == 0d) ? dbl :
  1017. new BigDecimal(dbl).setScale(3, NumberFormatter.ROUND_MODE)
  1018. .doubleValue());
  1019. }
  1020. /**
  1021. * @return an appropriate Date long value for the given object
  1022. */
  1023. private static long toDateLong(Object value) {
  1024. return ((value instanceof Date) ?
  1025. ((Date)value).getTime() :
  1026. ((value instanceof Calendar) ?
  1027. ((Calendar)value).getTimeInMillis() :
  1028. ((Number)value).longValue()));
  1029. }
  1030. /**
  1031. * Gets the timezone offset from UTC to local time for the given time
  1032. * (including DST).
  1033. */
  1034. private static long getToLocalTimeZoneOffset(long time, TimeZone tz) {
  1035. return tz.getOffset(time);
  1036. }
  1037. /**
  1038. * Gets the timezone offset from local time to UTC for the given time
  1039. * (including DST).
  1040. */
  1041. private static long getFromLocalTimeZoneOffset(long time, TimeZone tz) {
  1042. // getting from local time back to UTC is a little wonky (and not
  1043. // guaranteed to get you back to where you started). apply the zone
  1044. // offset first to get us closer to the original time
  1045. return tz.getOffset(time - tz.getRawOffset());
  1046. }
  1047. /**
  1048. * Decodes a GUID value.
  1049. */
  1050. private static String readGUIDValue(ByteBuffer buffer, ByteOrder order)
  1051. {
  1052. if(order != ByteOrder.BIG_ENDIAN) {
  1053. byte[] tmpArr = ByteUtil.getBytes(buffer, 16);
  1054. // the first 3 guid components are integer components which need to
  1055. // respect endianness, so swap 4-byte int, 2-byte int, 2-byte int
  1056. ByteUtil.swap4Bytes(tmpArr, 0);
  1057. ByteUtil.swap2Bytes(tmpArr, 4);
  1058. ByteUtil.swap2Bytes(tmpArr, 6);
  1059. buffer = ByteBuffer.wrap(tmpArr);
  1060. }
  1061. StringBuilder sb = new StringBuilder(22);
  1062. sb.append("{");
  1063. sb.append(ByteUtil.toHexString(buffer, 0, 4,
  1064. false));
  1065. sb.append("-");
  1066. sb.append(ByteUtil.toHexString(buffer, 4, 2,
  1067. false));
  1068. sb.append("-");
  1069. sb.append(ByteUtil.toHexString(buffer, 6, 2,
  1070. false));
  1071. sb.append("-");
  1072. sb.append(ByteUtil.toHexString(buffer, 8, 2,
  1073. false));
  1074. sb.append("-");
  1075. sb.append(ByteUtil.toHexString(buffer, 10, 6,
  1076. false));
  1077. sb.append("}");
  1078. return (sb.toString());
  1079. }
  1080. /**
  1081. * Writes a GUID value.
  1082. */
  1083. private void writeGUIDValue(ByteBuffer buffer, Object value)
  1084. throws IOException
  1085. {
  1086. Matcher m = GUID_PATTERN.matcher(toCharSequence(value));
  1087. if(!m.matches()) {
  1088. throw new InvalidValueException(
  1089. withErrorContext("Invalid GUID: " + value));
  1090. }
  1091. ByteBuffer origBuffer = null;
  1092. byte[] tmpBuf = null;
  1093. if(buffer.order() != ByteOrder.BIG_ENDIAN) {
  1094. // write to a temp buf so we can do some swapping below
  1095. origBuffer = buffer;
  1096. tmpBuf = new byte[16];
  1097. buffer = ByteBuffer.wrap(tmpBuf);
  1098. }
  1099. ByteUtil.writeHexString(buffer, m.group(1));
  1100. ByteUtil.writeHexString(buffer, m.group(2));
  1101. ByteUtil.writeHexString(buffer, m.group(3));
  1102. ByteUtil.writeHexString(buffer, m.group(4));
  1103. ByteUtil.writeHexString(buffer, m.group(5));
  1104. if(tmpBuf != null) {
  1105. // the first 3 guid components are integer components which need to
  1106. // respect endianness, so swap 4-byte int, 2-byte int, 2-byte int
  1107. ByteUtil.swap4Bytes(tmpBuf, 0);
  1108. ByteUtil.swap2Bytes(tmpBuf, 4);
  1109. ByteUtil.swap2Bytes(tmpBuf, 6);
  1110. origBuffer.put(tmpBuf);
  1111. }
  1112. }
  1113. /**
  1114. * Returns {@code true} if the given value is a "guid" value.
  1115. */
  1116. static boolean isGUIDValue(Object value) throws IOException {
  1117. return GUID_PATTERN.matcher(toCharSequence(value)).matches();
  1118. }
  1119. /**
  1120. * Returns a default value for this column
  1121. */
  1122. public Object generateDefaultValue() throws IOException {
  1123. return ((_defValue != null) ? _defValue.eval() : null);
  1124. }
  1125. /**
  1126. * Passes the given obj through the currently configured validator for this
  1127. * column and returns the result.
  1128. */
  1129. public Object validate(Object obj) throws IOException {
  1130. return _validator.validate(this, obj);
  1131. }
  1132. /**
  1133. * Returns the context used to manage calculated column values.
  1134. */
  1135. protected CalcColEvalContext getCalculationContext() {
  1136. throw new UnsupportedOperationException();
  1137. }
  1138. protected void setCalcColEvalContext(CalcColEvalContext calcCol) {
  1139. throw new UnsupportedOperationException();
  1140. }
  1141. /**
  1142. * Serialize an Object into a raw byte value for this column in little
  1143. * endian order
  1144. * @param obj Object to serialize
  1145. * @return A buffer containing the bytes
  1146. * @usage _advanced_method_
  1147. */
  1148. public ByteBuffer write(Object obj, int remainingRowLength)
  1149. throws IOException
  1150. {
  1151. return write(obj, remainingRowLength, PageChannel.DEFAULT_BYTE_ORDER);
  1152. }
  1153. /**
  1154. * Serialize an Object into a raw byte value for this column
  1155. * @param obj Object to serialize
  1156. * @param order Order in which to serialize
  1157. * @return A buffer containing the bytes
  1158. * @usage _advanced_method_
  1159. */
  1160. public ByteBuffer write(Object obj, int remainingRowLength, ByteOrder order)
  1161. throws IOException
  1162. {
  1163. if(isRawData(obj)) {
  1164. // just slap it right in (not for the faint of heart!)
  1165. return ByteBuffer.wrap(((RawData)obj).getBytes());
  1166. }
  1167. return writeRealData(obj, remainingRowLength, order);
  1168. }
  1169. protected ByteBuffer writeRealData(Object obj, int remainingRowLength,
  1170. ByteOrder order)
  1171. throws IOException
  1172. {
  1173. if(!isVariableLength() || !getType().isVariableLength()) {
  1174. return writeFixedLengthField(obj, order);
  1175. }
  1176. // this is an "inline" var length field
  1177. switch(getType()) {
  1178. case NUMERIC:
  1179. // don't ask me why numerics are "var length" columns...
  1180. ByteBuffer buffer = PageChannel.createBuffer(
  1181. getType().getFixedSize(), order);
  1182. writeNumericValue(buffer, obj);
  1183. buffer.flip();
  1184. return buffer;
  1185. case TEXT:
  1186. return encodeTextValue(
  1187. obj, 0, getLengthInUnits(), false).order(order);
  1188. case BINARY:
  1189. case UNKNOWN_0D:
  1190. case UNSUPPORTED_VARLEN:
  1191. // should already be "encoded"
  1192. break;
  1193. default:
  1194. throw new RuntimeException(withErrorContext(
  1195. "unexpected inline var length type: " + getType()));
  1196. }
  1197. ByteBuffer buffer = ByteBuffer.wrap(toByteArray(obj)).order(order);
  1198. return buffer;
  1199. }
  1200. /**
  1201. * Serialize an Object into a raw byte value for this column
  1202. * @param obj Object to serialize
  1203. * @param order Order in which to serialize
  1204. * @return A buffer containing the bytes
  1205. * @usage _advanced_method_
  1206. */
  1207. protected ByteBuffer writeFixedLengthField(Object obj, ByteOrder order)
  1208. throws IOException
  1209. {
  1210. int size = getType().getFixedSize(_columnLength);
  1211. ByteBuffer buffer = writeFixedLengthField(
  1212. obj, PageChannel.createBuffer(size, order));
  1213. buffer.flip();
  1214. return buffer;
  1215. }
  1216. protected ByteBuffer writeFixedLengthField(Object obj, ByteBuffer buffer)
  1217. throws IOException
  1218. {
  1219. // since booleans are not written by this method, it's safe to convert any
  1220. // incoming boolean into an integer.
  1221. obj = booleanToInteger(obj);
  1222. switch(getType()) {
  1223. case BOOLEAN:
  1224. //Do nothing
  1225. break;
  1226. case BYTE:
  1227. buffer.put(toNumber(obj).byteValue());
  1228. break;
  1229. case INT:
  1230. buffer.putShort(toNumber(obj).shortValue());
  1231. break;
  1232. case LONG:
  1233. buffer.putInt(toNumber(obj).intValue());
  1234. break;
  1235. case MONEY:
  1236. writeCurrencyValue(buffer, obj);
  1237. break;
  1238. case FLOAT:
  1239. buffer.putFloat(toNumber(obj).floatValue());
  1240. break;
  1241. case DOUBLE:
  1242. buffer.putDouble(toNumber(obj).doubleValue());
  1243. break;
  1244. case SHORT_DATE_TIME:
  1245. writeDateValue(buffer, obj);
  1246. break;
  1247. case TEXT:
  1248. // apparently text numeric values are also occasionally written as fixed
  1249. // length...
  1250. int numChars = getLengthInUnits();
  1251. // force uncompressed encoding for fixed length text
  1252. buffer.put(encodeTextValue(obj, numChars, numChars, true));
  1253. break;
  1254. case GUID:
  1255. writeGUIDValue(buffer, obj);
  1256. break;
  1257. case NUMERIC:
  1258. // yes, that's right, occasionally numeric values are written as fixed
  1259. // length...
  1260. writeNumericValue(buffer, obj);
  1261. break;
  1262. case BINARY:
  1263. case UNKNOWN_0D:
  1264. case UNKNOWN_11:
  1265. case COMPLEX_TYPE:
  1266. buffer.putInt(toNumber(obj).intValue());
  1267. break;
  1268. case BIG_INT:
  1269. buffer.putLong(toNumber(obj).longValue());
  1270. break;
  1271. case UNSUPPORTED_FIXEDLEN:
  1272. byte[] bytes = toByteArray(obj);
  1273. if(bytes.length != getLength()) {
  1274. throw new InvalidValueException(withErrorContext(
  1275. "Invalid fixed size binary data, size "
  1276. + getLength() + ", got " + bytes.length));
  1277. }
  1278. buffer.put(bytes);
  1279. break;
  1280. default:
  1281. throw new IOException(withErrorContext(
  1282. "Unsupported data type: " + getType()));
  1283. }
  1284. return buffer;
  1285. }
  1286. /**
  1287. * Decodes a compressed or uncompressed text value.
  1288. */
  1289. String decodeTextValue(byte[] data)
  1290. throws IOException
  1291. {
  1292. // see if data is compressed. the 0xFF, 0xFE sequence indicates that
  1293. // compression is used (sort of, see algorithm below)
  1294. boolean isCompressed = ((data.length > 1) &&
  1295. (data[0] == TEXT_COMPRESSION_HEADER[0]) &&
  1296. (data[1] == TEXT_COMPRESSION_HEADER[1]));
  1297. if(isCompressed) {
  1298. // this is a whacky compression combo that switches back and forth
  1299. // between compressed/uncompressed using a 0x00 byte (starting in
  1300. // compressed mode)
  1301. StringBuilder textBuf = new StringBuilder(data.length);
  1302. // start after two bytes indicating compression use
  1303. int dataStart = TEXT_COMPRESSION_HEADER.length;
  1304. int dataEnd = dataStart;
  1305. boolean inCompressedMode = true;
  1306. while(dataEnd < data.length) {
  1307. if(data[dataEnd] == (byte)0x00) {
  1308. // handle current segment
  1309. decodeTextSegment(data, dataStart, dataEnd, inCompressedMode,
  1310. textBuf);
  1311. inCompressedMode = !inCompressedMode;
  1312. ++dataEnd;
  1313. dataStart = dataEnd;
  1314. } else {
  1315. ++dataEnd;
  1316. }
  1317. }
  1318. // handle last segment
  1319. decodeTextSegment(data, dataStart, dataEnd, inCompressedMode, textBuf);
  1320. return textBuf.toString();
  1321. }
  1322. return decodeUncompressedText(data, getCharset());
  1323. }
  1324. /**
  1325. * Decodes a segnment of a text value into the given buffer according to the
  1326. * given status of the segment (compressed/uncompressed).
  1327. */
  1328. private void decodeTextSegment(byte[] data, int dataStart, int dataEnd,
  1329. boolean inCompressedMode,
  1330. StringBuilder textBuf)
  1331. {
  1332. if(dataEnd <= dataStart) {
  1333. // no data
  1334. return;
  1335. }
  1336. int dataLength = dataEnd - dataStart;
  1337. if(inCompressedMode) {
  1338. byte[] tmpData = new byte[dataLength * 2];
  1339. int tmpIdx = 0;
  1340. for(int i = dataStart; i < dataEnd; ++i) {
  1341. tmpData[tmpIdx] = data[i];
  1342. tmpIdx += 2;
  1343. }
  1344. data = tmpData;
  1345. dataStart = 0;
  1346. dataLength = data.length;
  1347. }
  1348. textBuf.append(decodeUncompressedText(data, dataStart, dataLength,
  1349. getCharset()));
  1350. }
  1351. /**
  1352. * @param textBytes bytes of text to decode
  1353. * @return the decoded string
  1354. */
  1355. private static CharBuffer decodeUncompressedText(
  1356. byte[] textBytes, int startPos, int length, Charset charset)
  1357. {
  1358. return charset.decode(ByteBuffer.wrap(textBytes, startPos, length));
  1359. }
  1360. /**
  1361. * Encodes a text value, possibly compressing.
  1362. */
  1363. ByteBuffer encodeTextValue(Object obj, int minChars, int maxChars,
  1364. boolean forceUncompressed)
  1365. throws IOException
  1366. {
  1367. CharSequence text = toCharSequence(obj);
  1368. if((text.length() > maxChars) || (text.length() < minChars)) {
  1369. throw new InvalidValueException(withErrorContext(
  1370. "Text is wrong length for " + getType() +
  1371. " column, max " + maxChars
  1372. + ", min " + minChars + ", got " + text.length()));
  1373. }
  1374. // may only compress if column type allows it
  1375. if(!forceUncompressed && isCompressedUnicode() &&
  1376. (text.length() <= getFormat().MAX_COMPRESSED_UNICODE_SIZE) &&
  1377. isUnicodeCompressible(text)) {
  1378. byte[] encodedChars = new byte[TEXT_COMPRESSION_HEADER.length +
  1379. text.length()];
  1380. encodedChars[0] = TEXT_COMPRESSION_HEADER[0];
  1381. encodedChars[1] = TEXT_COMPRESSION_HEADER[1];
  1382. for(int i = 0; i < text.length(); ++i) {
  1383. encodedChars[i + TEXT_COMPRESSION_HEADER.length] =
  1384. (byte)text.charAt(i);
  1385. }
  1386. return ByteBuffer.wrap(encodedChars);
  1387. }
  1388. return encodeUncompressedText(text, getCharset());
  1389. }
  1390. /**
  1391. * Returns {@code true} if the given text can be compressed using compressed
  1392. * unicode, {@code false} otherwise.
  1393. */
  1394. private static boolean isUnicodeCompressible(CharSequence text) {
  1395. // only attempt to compress > 2 chars (compressing less than 3 chars would
  1396. // not result in a space savings due to the 2 byte compression header)
  1397. if(text.length() <= TEXT_COMPRESSION_HEADER.length) {
  1398. return false;
  1399. }
  1400. // now, see if it is all compressible characters
  1401. for(int i = 0; i < text.length(); ++i) {
  1402. char c = text.charAt(i);
  1403. if((c < MIN_COMPRESS_CHAR) || (c > MAX_COMPRESS_CHAR)) {
  1404. return false;
  1405. }
  1406. }
  1407. return true;
  1408. }
  1409. /**
  1410. * Constructs a byte containing the flags for this column.
  1411. */
  1412. private static byte getColumnBitFlags(ColumnBuilder col) {
  1413. byte flags = UPDATABLE_FLAG_MASK;
  1414. if(!col.isVariableLength()) {
  1415. flags |= FIXED_LEN_FLAG_MASK;
  1416. }
  1417. if(col.isAutoNumber()) {
  1418. byte autoNumFlags = 0;
  1419. switch(col.getType()) {
  1420. case LONG:
  1421. case COMPLEX_TYPE:
  1422. autoNumFlags = AUTO_NUMBER_FLAG_MASK;
  1423. break;
  1424. case GUID:
  1425. autoNumFlags = AUTO_NUMBER_GUID_FLAG_MASK;
  1426. break;
  1427. default:
  1428. // unknown autonum type
  1429. }
  1430. flags |= autoNumFlags;
  1431. }
  1432. if(col.isHyperlink()) {
  1433. flags |= HYPERLINK_FLAG_MASK;
  1434. }
  1435. return flags;
  1436. }
  1437. @Override
  1438. public String toString() {
  1439. ToStringBuilder sb = CustomToStringStyle.builder(this)
  1440. .append("name", "(" + _table.getName() + ") " + _name);
  1441. byte typeValue = getOriginalDataType();
  1442. sb.append("type", "0x" + Integer.toHexString(typeValue) +
  1443. " (" + _type + ")")
  1444. .append("number", _columnNumber)
  1445. .append("length", _columnLength)
  1446. .append("variableLength", _variableLength);
  1447. if(_calculated) {
  1448. sb.append("calculated", _calculated)
  1449. .append("expression",
  1450. CustomToStringStyle.ignoreNull(getCalculationContext()));
  1451. }
  1452. if(_type.isTextual()) {
  1453. sb.append("compressedUnicode", isCompressedUnicode())
  1454. .append("textSortOrder", getTextSortOrder());
  1455. if(getTextCodePage() > 0) {
  1456. sb.append("textCodePage", getTextCodePage());
  1457. }
  1458. if(isAppendOnly()) {
  1459. sb.append("appendOnly", isAppendOnly());
  1460. }
  1461. if(isHyperlink()) {
  1462. sb.append("hyperlink", isHyperlink());
  1463. }
  1464. }
  1465. if(_type.getHasScalePrecision()) {
  1466. sb.append("precision", getPrecision())
  1467. .append("scale", getScale());
  1468. }
  1469. if(_autoNumber) {
  1470. sb.append("lastAutoNumber", _autoNumberGenerator.getLast());
  1471. }
  1472. sb.append("complexInfo", CustomToStringStyle.ignoreNull(getComplexInfo()))
  1473. .append("validator", CustomToStringStyle.ignoreNull(
  1474. ((_validator != SimpleColumnValidator.INSTANCE) ?
  1475. _validator : null)))
  1476. .append("defaultValue", CustomToStringStyle.ignoreNull(_defValue));
  1477. return sb.toString();
  1478. }
  1479. /**
  1480. * @param textBytes bytes of text to decode
  1481. * @param charset relevant charset
  1482. * @return the decoded string
  1483. * @usage _advanced_method_
  1484. */
  1485. public static String decodeUncompressedText(byte[] textBytes,
  1486. Charset charset)
  1487. {
  1488. return decodeUncompressedText(textBytes, 0, textBytes.length, charset)
  1489. .toString();
  1490. }
  1491. /**
  1492. * @param text Text to encode
  1493. * @param charset database charset
  1494. * @return A buffer with the text encoded
  1495. * @usage _advanced_method_
  1496. */
  1497. public static ByteBuffer encodeUncompressedText(CharSequence text,
  1498. Charset charset)
  1499. {
  1500. CharBuffer cb = ((text instanceof CharBuffer) ?
  1501. (CharBuffer)text : CharBuffer.wrap(text));
  1502. return charset.encode(cb);
  1503. }
  1504. /**
  1505. * Orders Columns by column number.
  1506. * @usage _general_method_
  1507. */
  1508. @Override
  1509. public int compareTo(ColumnImpl other) {
  1510. if (_columnNumber > other.getColumnNumber()) {
  1511. return 1;
  1512. } else if (_columnNumber < other.getColumnNumber()) {
  1513. return -1;
  1514. } else {
  1515. return 0;
  1516. }
  1517. }
  1518. /**
  1519. * @param columns A list of columns in a table definition
  1520. * @return The number of variable length columns found in the list
  1521. * @usage _advanced_method_
  1522. */
  1523. public static short countVariableLength(List<ColumnBuilder> columns) {
  1524. short rtn = 0;
  1525. for (ColumnBuilder col : columns) {
  1526. if (col.isVariableLength()) {
  1527. rtn++;
  1528. }
  1529. }
  1530. return rtn;
  1531. }
  1532. /**
  1533. * @return an appropriate BigDecimal representation of the given object.
  1534. * <code>null</code> is returned as 0 and Numbers are converted
  1535. * using their double representation.
  1536. */
  1537. BigDecimal toBigDecimal(Object value)
  1538. {
  1539. return toBigDecimal(value, getDatabase());
  1540. }
  1541. /**
  1542. * @return an appropriate BigDecimal representation of the given object.
  1543. * <code>null</code> is returned as 0 and Numbers are converted
  1544. * using their double representation.
  1545. */
  1546. static BigDecimal toBigDecimal(Object value, DatabaseImpl db)
  1547. {
  1548. if(value == null) {
  1549. return BigDecimal.ZERO;
  1550. } else if(value instanceof BigDecimal) {
  1551. return (BigDecimal)value;
  1552. } else if(value instanceof BigInteger) {
  1553. return new BigDecimal((BigInteger)value);
  1554. } else if(value instanceof Number) {
  1555. return new BigDecimal(((Number)value).doubleValue());
  1556. } else if(value instanceof Boolean) {
  1557. // access seems to like -1 for true and 0 for false
  1558. return ((Boolean)value) ? BigDecimal.valueOf(-1) : BigDecimal.ZERO;
  1559. } else if(value instanceof Date) {
  1560. return new BigDecimal(toDateDouble(value, db));
  1561. } else if(value instanceof LocalDateTime) {
  1562. return new BigDecimal(toDateDouble((LocalDateTime)value));
  1563. }
  1564. return new BigDecimal(value.toString());
  1565. }
  1566. /**
  1567. * @return an appropriate Number representation of the given object.
  1568. * <code>null</code> is returned as 0 and Strings are parsed as
  1569. * Doubles.
  1570. */
  1571. private Number toNumber(Object value)
  1572. {
  1573. return toNumber(value, getDatabase());
  1574. }
  1575. /**
  1576. * @return an appropriate Number representation of the given object.
  1577. * <code>null</code> is returned as 0 and Strings are parsed as
  1578. * Doubles.
  1579. */
  1580. private static Number toNumber(Object value, DatabaseImpl db)
  1581. {
  1582. if(value == null) {
  1583. return BigDecimal.ZERO;
  1584. } else if(value instanceof Number) {
  1585. return (Number)value;
  1586. } else if(value instanceof Boolean) {
  1587. // access seems to like -1 for true and 0 for false
  1588. return ((Boolean)value) ? -1 : 0;
  1589. } else if(value instanceof Date) {
  1590. return toDateDouble(value, db);
  1591. } else if(value instanceof LocalDateTime) {
  1592. return toDateDouble((LocalDateTime)value);
  1593. }
  1594. return Double.valueOf(value.toString());
  1595. }
  1596. /**
  1597. * @return an appropriate CharSequence representation of the given object.
  1598. * @usage _advanced_method_
  1599. */
  1600. public static CharSequence toCharSequence(Object value)
  1601. throws IOException
  1602. {
  1603. if(value == null) {
  1604. return null;
  1605. } else if(value instanceof CharSequence) {
  1606. return (CharSequence)value;
  1607. } else if(SqlHelper.INSTANCE.isClob(value)) {
  1608. return SqlHelper.INSTANCE.getClobString(value);
  1609. } else if(value instanceof Reader) {
  1610. char[] buf = new char[8 * 1024];
  1611. StringBuilder sout = new StringBuilder();
  1612. Reader in = (Reader)value;
  1613. int read = 0;
  1614. while((read = in.read(buf)) != -1) {
  1615. sout.append(buf, 0, read);
  1616. }
  1617. return sout;
  1618. }
  1619. return value.toString();
  1620. }
  1621. /**
  1622. * @return an appropriate byte[] representation of the given object.
  1623. * @usage _advanced_method_
  1624. */
  1625. public static byte[] toByteArray(Object value)
  1626. throws IOException
  1627. {
  1628. if(value == null) {
  1629. return null;
  1630. } else if(value instanceof byte[]) {
  1631. return (byte[])value;
  1632. } else if(value instanceof InMemoryBlob) {
  1633. return ((InMemoryBlob)value).getBytes();
  1634. } else if(SqlHelper.INSTANCE.isBlob(value)) {
  1635. return SqlHelper.INSTANCE.getBlobBytes(value);
  1636. }
  1637. ByteArrayOutputStream bout = new ByteArrayOutputStream();
  1638. if(value instanceof InputStream) {
  1639. ByteUtil.copy((InputStream)value, bout);
  1640. } else {
  1641. // if all else fails, serialize it
  1642. ObjectOutputStream oos = new ObjectOutputStream(bout);
  1643. oos.writeObject(value);
  1644. oos.close();
  1645. }
  1646. return bout.toByteArray();
  1647. }
  1648. /**
  1649. * Interpret a boolean value (null == false)
  1650. * @usage _advanced_method_
  1651. */
  1652. public static boolean toBooleanValue(Object obj) {
  1653. if(obj == null) {
  1654. return false;
  1655. } else if(obj instanceof Boolean) {
  1656. return ((Boolean)obj).booleanValue();
  1657. } else if(obj instanceof Number) {
  1658. // Access considers 0 as "false"
  1659. if(obj instanceof BigDecimal) {
  1660. return (((BigDecimal)obj).compareTo(BigDecimal.ZERO) != 0);
  1661. }
  1662. if(obj instanceof BigInteger) {
  1663. return (((BigInteger)obj).compareTo(BigInteger.ZERO) != 0);
  1664. }
  1665. return (((Number)obj).doubleValue() != 0.0d);
  1666. }
  1667. return Boolean.parseBoolean(obj.toString());
  1668. }
  1669. /**
  1670. * Swaps the bytes of the given numeric in place.
  1671. */
  1672. private static void fixNumericByteOrder(byte[] bytes)
  1673. {
  1674. // fix endianness of each 4 byte segment
  1675. for(int i = 0; i < bytes.length; i+=4) {
  1676. ByteUtil.swap4Bytes(bytes, i);
  1677. }
  1678. }
  1679. /**
  1680. * Treat booleans as integers (access-style).
  1681. */
  1682. protected static Object booleanToInteger(Object obj) {
  1683. if (obj instanceof Boolean) {
  1684. obj = ((Boolean) obj) ? -1 : 0;
  1685. }
  1686. return obj;
  1687. }
  1688. /**
  1689. * Returns a wrapper for raw column data that can be written without
  1690. * understanding the data. Useful for wrapping unparseable data for
  1691. * re-writing.
  1692. */
  1693. public static RawData rawDataWrapper(byte[] bytes) {
  1694. return new RawData(bytes);
  1695. }
  1696. /**
  1697. * Returns {@code true} if the given value is "raw" column data,
  1698. * {@code false} otherwise.
  1699. * @usage _advanced_method_
  1700. */
  1701. public static boolean isRawData(Object value) {
  1702. return(value instanceof RawData);
  1703. }
  1704. /**
  1705. * Writes the column definitions into a table definition buffer.
  1706. * @param buffer Buffer to write to
  1707. */
  1708. protected static void writeDefinitions(TableCreator creator, ByteBuffer buffer)
  1709. throws IOException
  1710. {
  1711. // we specifically put the "long variable" values after the normal
  1712. // variable length values so that we have a better chance of fitting it
  1713. // all (because "long variable" values can go in separate pages)
  1714. int longVariableOffset = creator.countNonLongVariableLength();
  1715. creator.setColumnOffsets(0, 0, longVariableOffset);
  1716. for (ColumnBuilder col : creator.getColumns()) {
  1717. writeDefinition(creator, col, buffer);
  1718. }
  1719. for (ColumnBuilder col : creator.getColumns()) {
  1720. TableImpl.writeName(buffer, col.getName(), creator.getCharset());
  1721. }
  1722. }
  1723. protected static void writeDefinition(
  1724. TableMutator mutator, ColumnBuilder col, ByteBuffer buffer)
  1725. throws IOException
  1726. {
  1727. TableMutator.ColumnOffsets colOffsets = mutator.getColumnOffsets();
  1728. buffer.put(col.getType().getValue());
  1729. buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER); //constant magic number
  1730. buffer.putShort(col.getColumnNumber()); //Column Number
  1731. if(col.isVariableLength()) {
  1732. buffer.putShort(colOffsets.getNextVariableOffset(col));
  1733. } else {
  1734. buffer.putShort((short) 0);
  1735. }
  1736. buffer.putShort(col.getColumnNumber()); //Column Number again
  1737. if(col.getType().isTextual()) {
  1738. // this will write 4 bytes (note we don't support writing dbs which
  1739. // use the text code page)
  1740. writeSortOrder(buffer, col.getTextSortOrder(), mutator.getFormat());
  1741. } else {
  1742. // note scale/precision not stored for calculated numeric fields
  1743. if(col.getType().getHasScalePrecision() && !col.isCalculated()) {
  1744. buffer.put(col.getPrecision()); // numeric precision
  1745. buffer.put(col.getScale()); // numeric scale
  1746. } else {
  1747. buffer.put((byte) 0x00); //unused
  1748. buffer.put((byte) 0x00); //unused
  1749. }
  1750. buffer.putShort((short) 0); //Unknown
  1751. }
  1752. buffer.put(getColumnBitFlags(col)); // misc col flags
  1753. // note access doesn't seem to allow unicode compression for calced fields
  1754. if(col.isCalculated()) {
  1755. buffer.put(CALCULATED_EXT_FLAG_MASK);
  1756. } else if (col.isCompressedUnicode()) { //Compressed
  1757. buffer.put(COMPRESSED_UNICODE_EXT_FLAG_MASK);
  1758. } else {
  1759. buffer.put((byte)0);
  1760. }
  1761. buffer.putInt(0); //Unknown, but always 0.
  1762. //Offset for fixed length columns
  1763. if(col.isVariableLength()) {
  1764. buffer.putShort((short) 0);
  1765. } else {
  1766. buffer.putShort(colOffsets.getNextFixedOffset(col));
  1767. }
  1768. if(!col.getType().isLongValue()) {
  1769. short length = col.getLength();
  1770. if(col.isCalculated()) {
  1771. // calced columns have additional value overhead
  1772. if(!col.getType().isVariableLength() ||
  1773. col.getType().getHasScalePrecision()) {
  1774. length = CalculatedColumnUtil.CALC_FIXED_FIELD_LEN;
  1775. } else {
  1776. length += CalculatedColumnUtil.CALC_EXTRA_DATA_LEN;
  1777. }
  1778. }
  1779. buffer.putShort(length); //Column length
  1780. } else {
  1781. buffer.putShort((short)0x0000); // unused
  1782. }
  1783. }
  1784. protected static void writeColUsageMapDefinitions(
  1785. TableCreator creator, ByteBuffer buffer)
  1786. throws IOException
  1787. {
  1788. // write long value column usage map references
  1789. for(ColumnBuilder lvalCol : creator.getLongValueColumns()) {
  1790. writeColUsageMapDefinition(creator, lvalCol, buffer);
  1791. }
  1792. }
  1793. protected static void writeColUsageMapDefinition(
  1794. TableMutator creator, ColumnBuilder lvalCol, ByteBuffer buffer)
  1795. throws IOException
  1796. {
  1797. TableMutator.ColumnState colState = creator.getColumnState(lvalCol);
  1798. buffer.putShort(lvalCol.getColumnNumber());
  1799. // owned pages umap (both are on same page)
  1800. buffer.put(colState.getUmapOwnedRowNumber());
  1801. ByteUtil.put3ByteInt(buffer, colState.getUmapPageNumber());
  1802. // free space pages umap
  1803. buffer.put(colState.getUmapFreeRowNumber());
  1804. ByteUtil.put3ByteInt(buffer, colState.getUmapPageNumber());
  1805. }
  1806. /**
  1807. * Reads the sort order info from the given buffer from the given position.
  1808. */
  1809. static SortOrder readSortOrder(ByteBuffer buffer, int position,
  1810. JetFormat format)
  1811. {
  1812. short value = buffer.getShort(position);
  1813. if(value == 0) {
  1814. // probably a file we wrote, before handling sort order
  1815. return format.DEFAULT_SORT_ORDER;
  1816. }
  1817. short version = format.DEFAULT_SORT_ORDER.getVersion();
  1818. if(format.SIZE_SORT_ORDER == 4) {
  1819. version = buffer.get(position + 3);
  1820. }
  1821. if(value == GENERAL_SORT_ORDER_VALUE) {
  1822. if(version == GENERAL_SORT_ORDER.getVersion()) {
  1823. return GENERAL_SORT_ORDER;
  1824. }
  1825. if(version == GENERAL_LEGACY_SORT_ORDER.getVersion()) {
  1826. return GENERAL_LEGACY_SORT_ORDER;
  1827. }
  1828. if(version == GENERAL_97_SORT_ORDER.getVersion()) {
  1829. return GENERAL_97_SORT_ORDER;
  1830. }
  1831. }
  1832. return new SortOrder(value, version);
  1833. }
  1834. /**
  1835. * Reads the column cade page info from the given buffer, if supported for
  1836. * this db.
  1837. */
  1838. static short readCodePage(ByteBuffer buffer, int offset, JetFormat format)
  1839. {
  1840. int cpOffset = format.OFFSET_COLUMN_CODE_PAGE;
  1841. return ((cpOffset >= 0) ? buffer.getShort(offset + cpOffset) : 0);
  1842. }
  1843. /**
  1844. * Read the extra flags field for a column definition.
  1845. */
  1846. static byte readExtraFlags(ByteBuffer buffer, int offset, JetFormat format)
  1847. {
  1848. int extFlagsOffset = format.OFFSET_COLUMN_EXT_FLAGS;
  1849. return ((extFlagsOffset >= 0) ? buffer.get(offset + extFlagsOffset) : 0);
  1850. }
  1851. /**
  1852. * Writes the sort order info to the given buffer at the current position.
  1853. */
  1854. private static void writeSortOrder(ByteBuffer buffer, SortOrder sortOrder,
  1855. JetFormat format) {
  1856. if(sortOrder == null) {
  1857. sortOrder = format.DEFAULT_SORT_ORDER;
  1858. }
  1859. buffer.putShort(sortOrder.getValue());
  1860. if(format.SIZE_SORT_ORDER == 4) {
  1861. buffer.put((byte)0x00); // unknown
  1862. buffer.put((byte)sortOrder.getVersion());
  1863. }
  1864. }
  1865. /**
  1866. * Returns {@code true} if the value is immutable, {@code false} otherwise.
  1867. * This only handles values that are returned from the {@link #read} method.
  1868. */
  1869. static boolean isImmutableValue(Object value) {
  1870. // for now, the only mutable value this class returns is byte[]
  1871. return !(value instanceof byte[]);
  1872. }
  1873. /**
  1874. * Converts the given value to the "internal" representation for the given
  1875. * data type.
  1876. */
  1877. public static Object toInternalValue(DataType dataType, Object value,
  1878. DatabaseImpl db)
  1879. throws IOException
  1880. {
  1881. return toInternalValue(dataType, value, db, null);
  1882. }
  1883. static Object toInternalValue(DataType dataType, Object value,
  1884. DatabaseImpl db,
  1885. ColumnImpl.DateTimeFactory factory)
  1886. throws IOException
  1887. {
  1888. if(value == null) {
  1889. return null;
  1890. }
  1891. switch(dataType) {
  1892. case BOOLEAN:
  1893. return ((value instanceof Boolean) ? value : toBooleanValue(value));
  1894. case BYTE:
  1895. return ((value instanceof Byte) ? value : toNumber(value, db).byteValue());
  1896. case INT:
  1897. return ((value instanceof Short) ? value :
  1898. toNumber(value, db).shortValue());
  1899. case LONG:
  1900. return ((value instanceof Integer) ? value :
  1901. toNumber(value, db).intValue());
  1902. case MONEY:
  1903. return toBigDecimal(value, db);
  1904. case FLOAT:
  1905. return ((value instanceof Float) ? value :
  1906. toNumber(value, db).floatValue());
  1907. case DOUBLE:
  1908. return ((value instanceof Double) ? value :
  1909. toNumber(value, db).doubleValue());
  1910. case SHORT_DATE_TIME:
  1911. if(factory == null) {
  1912. factory = db.getDateTimeFactory();
  1913. }
  1914. return factory.toInternalValue(db, value);
  1915. case TEXT:
  1916. case MEMO:
  1917. case GUID:
  1918. return ((value instanceof String) ? value :
  1919. toCharSequence(value).toString());
  1920. case NUMERIC:
  1921. return toBigDecimal(value, db);
  1922. case COMPLEX_TYPE:
  1923. // leave alone for now?
  1924. return value;
  1925. case BIG_INT:
  1926. return ((value instanceof Long) ? value :
  1927. toNumber(value, db).longValue());
  1928. default:
  1929. // some variation of binary data
  1930. return toByteArray(value);
  1931. }
  1932. }
  1933. protected static DateTimeFactory getDateTimeFactory(DateTimeType type) {
  1934. return ((type == DateTimeType.LOCAL_DATE_TIME) ?
  1935. LDT_DATE_TIME_FACTORY : DEF_DATE_TIME_FACTORY);
  1936. }
  1937. String withErrorContext(String msg) {
  1938. return withErrorContext(msg, getDatabase(), getTable().getName(), getName());
  1939. }
  1940. boolean isThisColumn(Identifier identifier) {
  1941. return(getTable().isThisTable(identifier) &&
  1942. getName().equalsIgnoreCase(identifier.getObjectName()));
  1943. }
  1944. private static String withErrorContext(
  1945. String msg, DatabaseImpl db, String tableName, String colName) {
  1946. return msg + " (Db=" + db.getName() + ";Table=" + tableName + ";Column=" +
  1947. colName + ")";
  1948. }
  1949. /**
  1950. * Date subclass which stashes the original date bits, in case we attempt to
  1951. * re-write the value (will not lose precision). Also, this implementation
  1952. * is immutable.
  1953. */
  1954. @SuppressWarnings("deprecation")
  1955. private static final class DateExt extends Date
  1956. {
  1957. private static final long serialVersionUID = 0L;
  1958. /** cached bits of the original date value */
  1959. private transient final long _dateBits;
  1960. private DateExt(long time, long dateBits) {
  1961. super(time);
  1962. _dateBits = dateBits;
  1963. }
  1964. public long getDateBits() {
  1965. return _dateBits;
  1966. }
  1967. @Override
  1968. public void setDate(int time) {
  1969. throw new UnsupportedOperationException();
  1970. }
  1971. @Override
  1972. public void setHours(int time) {
  1973. throw new UnsupportedOperationException();
  1974. }
  1975. @Override
  1976. public void setMinutes(int time) {
  1977. throw new UnsupportedOperationException();
  1978. }
  1979. @Override
  1980. public void setMonth(int time) {
  1981. throw new UnsupportedOperationException();
  1982. }
  1983. @Override
  1984. public void setSeconds(int time) {
  1985. throw new UnsupportedOperationException();
  1986. }
  1987. @Override
  1988. public void setYear(int time) {
  1989. throw new UnsupportedOperationException();
  1990. }
  1991. @Override
  1992. public void setTime(long time) {
  1993. throw new UnsupportedOperationException();
  1994. }
  1995. private Object writeReplace() throws ObjectStreamException {
  1996. // if we are going to serialize this Date, convert it back to a normal
  1997. // Date (in case it is restored outside of the context of jackcess)
  1998. return new Date(super.getTime());
  1999. }
  2000. }
  2001. /**
  2002. * Wrapper for raw column data which can be re-written.
  2003. */
  2004. private static final class RawData implements Serializable, InMemoryBlob
  2005. {
  2006. private static final long serialVersionUID = 0L;
  2007. private final byte[] _bytes;
  2008. private RawData(byte[] bytes) {
  2009. _bytes = bytes;
  2010. }
  2011. @Override
  2012. public byte[] getBytes() {
  2013. return _bytes;
  2014. }
  2015. @Override
  2016. public String toString() {
  2017. return CustomToStringStyle.valueBuilder(this)
  2018. .append(null, getBytes())
  2019. .toString();
  2020. }
  2021. private Object writeReplace() throws ObjectStreamException {
  2022. // if we are going to serialize this, convert it back to a normal
  2023. // byte[] (in case it is restored outside of the context of jackcess)
  2024. return getBytes();
  2025. }
  2026. }
  2027. /**
  2028. * Base class for the supported autonumber types.
  2029. * @usage _advanced_class_
  2030. */
  2031. public abstract class AutoNumberGenerator
  2032. {
  2033. protected AutoNumberGenerator() {}
  2034. /**
  2035. * Returns the last autonumber generated by this generator. Only valid
  2036. * after a call to {@link Table#addRow}, otherwise undefined.
  2037. */
  2038. public abstract Object getLast();
  2039. /**
  2040. * Returns the next autonumber for this generator.
  2041. * <p>
  2042. * <i>Warning, calling this externally will result in this value being
  2043. * "lost" for the table.</i>
  2044. */
  2045. public abstract Object getNext(TableImpl.WriteRowState writeRowState);
  2046. /**
  2047. * Returns a valid autonumber for this generator.
  2048. * <p>
  2049. * <i>Warning, calling this externally may result in this value being
  2050. * "lost" for the table.</i>
  2051. */
  2052. public abstract Object handleInsert(
  2053. TableImpl.WriteRowState writeRowState, Object inRowValue)
  2054. throws IOException;
  2055. /**
  2056. * Restores a previous autonumber generated by this generator.
  2057. */
  2058. public abstract void restoreLast(Object last);
  2059. /**
  2060. * Returns the type of values generated by this generator.
  2061. */
  2062. public abstract DataType getType();
  2063. }
  2064. private final class LongAutoNumberGenerator extends AutoNumberGenerator
  2065. {
  2066. private LongAutoNumberGenerator() {}
  2067. @Override
  2068. public Object getLast() {
  2069. // the table stores the last long autonumber used
  2070. return getTable().getLastLongAutoNumber();
  2071. }
  2072. @Override
  2073. public Object getNext(TableImpl.WriteRowState writeRowState) {
  2074. // the table stores the last long autonumber used
  2075. return getTable().getNextLongAutoNumber();
  2076. }
  2077. @Override
  2078. public Object handleInsert(TableImpl.WriteRowState writeRowState,
  2079. Object inRowValue)
  2080. throws IOException
  2081. {
  2082. int inAutoNum = toNumber(inRowValue).intValue();
  2083. if(inAutoNum <= INVALID_AUTO_NUMBER &&
  2084. !getTable().isAllowAutoNumberInsert()) {
  2085. throw new InvalidValueException(withErrorContext(
  2086. "Invalid auto number value " + inAutoNum));
  2087. }
  2088. // the table stores the last long autonumber used
  2089. getTable().adjustLongAutoNumber(inAutoNum);
  2090. return inAutoNum;
  2091. }
  2092. @Override
  2093. public void restoreLast(Object last) {
  2094. if(last instanceof Integer) {
  2095. getTable().restoreLastLongAutoNumber((Integer)last);
  2096. }
  2097. }
  2098. @Override
  2099. public DataType getType() {
  2100. return DataType.LONG;
  2101. }
  2102. }
  2103. private final class GuidAutoNumberGenerator extends AutoNumberGenerator
  2104. {
  2105. private Object _lastAutoNumber;
  2106. private GuidAutoNumberGenerator() {}
  2107. @Override
  2108. public Object getLast() {
  2109. return _lastAutoNumber;
  2110. }
  2111. @Override
  2112. public Object getNext(TableImpl.WriteRowState writeRowState) {
  2113. // format guids consistently w/ Column.readGUIDValue()
  2114. _lastAutoNumber = "{" + UUID.randomUUID() + "}";
  2115. return _lastAutoNumber;
  2116. }
  2117. @Override
  2118. public Object handleInsert(TableImpl.WriteRowState writeRowState,
  2119. Object inRowValue)
  2120. throws IOException
  2121. {
  2122. _lastAutoNumber = toCharSequence(inRowValue);
  2123. return _lastAutoNumber;
  2124. }
  2125. @Override
  2126. public void restoreLast(Object last) {
  2127. _lastAutoNumber = null;
  2128. }
  2129. @Override
  2130. public DataType getType() {
  2131. return DataType.GUID;
  2132. }
  2133. }
  2134. private final class ComplexTypeAutoNumberGenerator extends AutoNumberGenerator
  2135. {
  2136. private ComplexTypeAutoNumberGenerator() {}
  2137. @Override
  2138. public Object getLast() {
  2139. // the table stores the last ComplexType autonumber used
  2140. return getTable().getLastComplexTypeAutoNumber();
  2141. }
  2142. @Override
  2143. public Object getNext(TableImpl.WriteRowState writeRowState) {
  2144. // same value is shared across all ComplexType values in a row
  2145. int nextComplexAutoNum = writeRowState.getComplexAutoNumber();
  2146. if(nextComplexAutoNum <= INVALID_AUTO_NUMBER) {
  2147. // the table stores the last ComplexType autonumber used
  2148. nextComplexAutoNum = getTable().getNextComplexTypeAutoNumber();
  2149. writeRowState.setComplexAutoNumber(nextComplexAutoNum);
  2150. }
  2151. return new ComplexValueForeignKeyImpl(ColumnImpl.this,
  2152. nextComplexAutoNum);
  2153. }
  2154. @Override
  2155. public Object handleInsert(TableImpl.WriteRowState writeRowState,
  2156. Object inRowValue)
  2157. throws IOException
  2158. {
  2159. ComplexValueForeignKey inComplexFK = null;
  2160. if(inRowValue instanceof ComplexValueForeignKey) {
  2161. inComplexFK = (ComplexValueForeignKey)inRowValue;
  2162. } else {
  2163. inComplexFK = new ComplexValueForeignKeyImpl(
  2164. ColumnImpl.this, toNumber(inRowValue).intValue());
  2165. }
  2166. if(inComplexFK.getColumn() != ColumnImpl.this) {
  2167. throw new InvalidValueException(withErrorContext(
  2168. "Wrong column for complex value foreign key, found " +
  2169. inComplexFK.getColumn().getName()));
  2170. }
  2171. if(inComplexFK.get() < 1) {
  2172. throw new InvalidValueException(withErrorContext(
  2173. "Invalid complex value foreign key value " + inComplexFK.get()));
  2174. }
  2175. // same value is shared across all ComplexType values in a row
  2176. int prevRowValue = writeRowState.getComplexAutoNumber();
  2177. if(prevRowValue <= INVALID_AUTO_NUMBER) {
  2178. writeRowState.setComplexAutoNumber(inComplexFK.get());
  2179. } else if(prevRowValue != inComplexFK.get()) {
  2180. throw new InvalidValueException(withErrorContext(
  2181. "Inconsistent complex value foreign key values: found " +
  2182. prevRowValue + ", given " + inComplexFK));
  2183. }
  2184. // the table stores the last ComplexType autonumber used
  2185. getTable().adjustComplexTypeAutoNumber(inComplexFK.get());
  2186. return inComplexFK;
  2187. }
  2188. @Override
  2189. public void restoreLast(Object last) {
  2190. if(last instanceof ComplexValueForeignKey) {
  2191. getTable().restoreLastComplexTypeAutoNumber(
  2192. ((ComplexValueForeignKey)last).get());
  2193. }
  2194. }
  2195. @Override
  2196. public DataType getType() {
  2197. return DataType.COMPLEX_TYPE;
  2198. }
  2199. }
  2200. private final class UnsupportedAutoNumberGenerator extends AutoNumberGenerator
  2201. {
  2202. private final DataType _genType;
  2203. private UnsupportedAutoNumberGenerator(DataType genType) {
  2204. _genType = genType;
  2205. }
  2206. @Override
  2207. public Object getLast() {
  2208. return null;
  2209. }
  2210. @Override
  2211. public Object getNext(TableImpl.WriteRowState writeRowState) {
  2212. throw new UnsupportedOperationException();
  2213. }
  2214. @Override
  2215. public Object handleInsert(TableImpl.WriteRowState writeRowState,
  2216. Object inRowValue) {
  2217. throw new UnsupportedOperationException();
  2218. }
  2219. @Override
  2220. public void restoreLast(Object last) {
  2221. throw new UnsupportedOperationException();
  2222. }
  2223. @Override
  2224. public DataType getType() {
  2225. return _genType;
  2226. }
  2227. }
  2228. /**
  2229. * Information about the sort order (collation) for a textual column.
  2230. * @usage _intermediate_class_
  2231. */
  2232. public static final class SortOrder
  2233. {
  2234. private final short _value;
  2235. private final short _version;
  2236. public SortOrder(short value, short version) {
  2237. _value = value;
  2238. _version = version;
  2239. }
  2240. public short getValue() {
  2241. return _value;
  2242. }
  2243. public short getVersion() {
  2244. return _version;
  2245. }
  2246. @Override
  2247. public int hashCode() {
  2248. return _value;
  2249. }
  2250. @Override
  2251. public boolean equals(Object o) {
  2252. return ((this == o) ||
  2253. ((o != null) && (getClass() == o.getClass()) &&
  2254. (_value == ((SortOrder)o)._value) &&
  2255. (_version == ((SortOrder)o)._version)));
  2256. }
  2257. @Override
  2258. public String toString() {
  2259. return CustomToStringStyle.valueBuilder(this)
  2260. .append(null, _value + "(" + _version + ")")
  2261. .toString();
  2262. }
  2263. }
  2264. /**
  2265. * Utility struct for passing params through ColumnImpl constructors.
  2266. */
  2267. static final class InitArgs
  2268. {
  2269. public final TableImpl table;
  2270. public final ByteBuffer buffer;
  2271. public final int offset;
  2272. public final String name;
  2273. public final int displayIndex;
  2274. public final byte colType;
  2275. public final byte flags;
  2276. public final byte extFlags;
  2277. public DataType type;
  2278. InitArgs(TableImpl table, ByteBuffer buffer, int offset, String name,
  2279. int displayIndex) {
  2280. this.table = table;
  2281. this.buffer = buffer;
  2282. this.offset = offset;
  2283. this.name = name;
  2284. this.displayIndex = displayIndex;
  2285. this.colType = buffer.get(offset + table.getFormat().OFFSET_COLUMN_TYPE);
  2286. this.flags = buffer.get(offset + table.getFormat().OFFSET_COLUMN_FLAGS);
  2287. this.extFlags = readExtraFlags(buffer, offset, table.getFormat());
  2288. }
  2289. }
  2290. /**
  2291. * "Internal" column validator for columns with the "required" property
  2292. * enabled.
  2293. */
  2294. private static final class RequiredColValidator extends InternalColumnValidator
  2295. {
  2296. private RequiredColValidator(ColumnValidator delegate) {
  2297. super(delegate);
  2298. }
  2299. @Override
  2300. protected Object internalValidate(Column col, Object val)
  2301. throws IOException
  2302. {
  2303. if(val == null) {
  2304. throw new InvalidValueException(
  2305. ((ColumnImpl)col).withErrorContext(
  2306. "Missing value for required column"));
  2307. }
  2308. return val;
  2309. }
  2310. @Override
  2311. protected void appendToString(StringBuilder sb) {
  2312. sb.append("required=true");
  2313. }
  2314. }
  2315. /**
  2316. * "Internal" column validator for text columns with the "allow zero len"
  2317. * property disabled.
  2318. */
  2319. private static final class NoZeroLenColValidator extends InternalColumnValidator
  2320. {
  2321. private NoZeroLenColValidator(ColumnValidator delegate) {
  2322. super(delegate);
  2323. }
  2324. @Override
  2325. protected Object internalValidate(Column col, Object val)
  2326. throws IOException
  2327. {
  2328. CharSequence valStr = ColumnImpl.toCharSequence(val);
  2329. // oddly enough null is allowed for non-zero len strings
  2330. if((valStr != null) && valStr.length() == 0) {
  2331. throw new InvalidValueException(
  2332. ((ColumnImpl)col).withErrorContext(
  2333. "Zero length string is not allowed"));
  2334. }
  2335. return valStr;
  2336. }
  2337. @Override
  2338. protected void appendToString(StringBuilder sb) {
  2339. sb.append("allowZeroLength=false");
  2340. }
  2341. }
  2342. /**
  2343. * Factory which handles date/time values appropriately for a DateTimeType.
  2344. */
  2345. protected static abstract class DateTimeFactory
  2346. {
  2347. public abstract DateTimeType getType();
  2348. public abstract Object fromDateBits(ColumnImpl col, long dateBits);
  2349. public abstract double toDateDouble(Object value, DateTimeContext dtc);
  2350. public abstract Object toInternalValue(DatabaseImpl db, Object value);
  2351. }
  2352. /**
  2353. * Factory impl for legacy Date handling.
  2354. */
  2355. private static final class DefaultDateTimeFactory extends DateTimeFactory
  2356. {
  2357. @Override
  2358. public DateTimeType getType() {
  2359. return DateTimeType.DATE;
  2360. }
  2361. @Override
  2362. public Object fromDateBits(ColumnImpl col, long dateBits) {
  2363. long time = col.fromDateDouble(
  2364. Double.longBitsToDouble(dateBits));
  2365. return new DateExt(time, dateBits);
  2366. }
  2367. @Override
  2368. public double toDateDouble(Object value, DateTimeContext dtc) {
  2369. // ZoneId and TimeZone have different rules for older timezones, so we
  2370. // need to consistently use one or the other depending on the date/time
  2371. // type
  2372. long time = 0L;
  2373. if(value instanceof TemporalAccessor) {
  2374. time = toInstant((TemporalAccessor)value, dtc).toEpochMilli();
  2375. } else {
  2376. time = toDateLong(value);
  2377. }
  2378. // seems access stores dates in the local timezone. guess you just
  2379. // hope you read it in the same timezone in which it was written!
  2380. time += getToLocalTimeZoneOffset(time, dtc.getTimeZone());
  2381. return toLocalDateDouble(time);
  2382. }
  2383. @Override
  2384. public Object toInternalValue(DatabaseImpl db, Object value) {
  2385. return ((value instanceof Date) ? value :
  2386. new Date(toDateLong(value)));
  2387. }
  2388. }
  2389. /**
  2390. * Factory impl for LocalDateTime handling.
  2391. */
  2392. private static final class LDTDateTimeFactory extends DateTimeFactory
  2393. {
  2394. @Override
  2395. public DateTimeType getType() {
  2396. return DateTimeType.LOCAL_DATE_TIME;
  2397. }
  2398. @Override
  2399. public Object fromDateBits(ColumnImpl col, long dateBits) {
  2400. return ldtFromLocalDateDouble(Double.longBitsToDouble(dateBits));
  2401. }
  2402. @Override
  2403. public double toDateDouble(Object value, DateTimeContext dtc) {
  2404. // ZoneId and TimeZone have different rules for older timezones, so we
  2405. // need to consistently use one or the other depending on the date/time
  2406. // type
  2407. if(!(value instanceof TemporalAccessor)) {
  2408. value = Instant.ofEpochMilli(toDateLong(value));
  2409. }
  2410. return ColumnImpl.toDateDouble(
  2411. toLocalDateTime((TemporalAccessor)value, dtc));
  2412. }
  2413. @Override
  2414. public Object toInternalValue(DatabaseImpl db, Object value) {
  2415. if(value instanceof TemporalAccessor) {
  2416. return toLocalDateTime((TemporalAccessor)value, db);
  2417. }
  2418. Instant inst = Instant.ofEpochMilli(toDateLong(value));
  2419. return LocalDateTime.ofInstant(inst, db.getZoneId());
  2420. }
  2421. }
  2422. /** internal interface for types which hold bytes in memory */
  2423. static interface InMemoryBlob {
  2424. public byte[] getBytes() throws IOException;
  2425. }
  2426. }