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 62KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136
  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.sql.Blob;
  28. import java.sql.Clob;
  29. import java.sql.SQLException;
  30. import java.util.Calendar;
  31. import java.util.Date;
  32. import java.util.List;
  33. import java.util.Map;
  34. import java.util.UUID;
  35. import java.util.regex.Matcher;
  36. import java.util.regex.Pattern;
  37. import com.healthmarketscience.jackcess.Column;
  38. import com.healthmarketscience.jackcess.ColumnBuilder;
  39. import com.healthmarketscience.jackcess.DataType;
  40. import com.healthmarketscience.jackcess.PropertyMap;
  41. import com.healthmarketscience.jackcess.Table;
  42. import com.healthmarketscience.jackcess.complex.ComplexColumnInfo;
  43. import com.healthmarketscience.jackcess.complex.ComplexValue;
  44. import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
  45. import com.healthmarketscience.jackcess.impl.complex.ComplexValueForeignKeyImpl;
  46. import com.healthmarketscience.jackcess.util.ColumnValidator;
  47. import com.healthmarketscience.jackcess.util.SimpleColumnValidator;
  48. import org.apache.commons.lang.builder.ToStringBuilder;
  49. import org.apache.commons.logging.Log;
  50. import org.apache.commons.logging.LogFactory;
  51. /**
  52. * Access database column definition
  53. * @author Tim McCune
  54. * @usage _general_class_
  55. */
  56. public class ColumnImpl implements Column, Comparable<ColumnImpl> {
  57. protected static final Log LOG = LogFactory.getLog(ColumnImpl.class);
  58. /**
  59. * Placeholder object for adding rows which indicates that the caller wants
  60. * the RowId of the new row. Must be added as an extra value at the end of
  61. * the row values array.
  62. * @see TableImpl#asRowWithRowId
  63. * @usage _intermediate_field_
  64. */
  65. public static final Object RETURN_ROW_ID = "<RETURN_ROW_ID>";
  66. /**
  67. * Access stores numeric dates in days. Java stores them in milliseconds.
  68. */
  69. private static final double MILLISECONDS_PER_DAY =
  70. (24L * 60L * 60L * 1000L);
  71. /**
  72. * Access starts counting dates at Jan 1, 1900. Java starts counting
  73. * at Jan 1, 1970. This is the # of millis between them for conversion.
  74. */
  75. private static final long MILLIS_BETWEEN_EPOCH_AND_1900 =
  76. 25569L * (long)MILLISECONDS_PER_DAY;
  77. /**
  78. * mask for the fixed len bit
  79. * @usage _advanced_field_
  80. */
  81. public static final byte FIXED_LEN_FLAG_MASK = (byte)0x01;
  82. /**
  83. * mask for the auto number bit
  84. * @usage _advanced_field_
  85. */
  86. public static final byte AUTO_NUMBER_FLAG_MASK = (byte)0x04;
  87. /**
  88. * mask for the auto number guid bit
  89. * @usage _advanced_field_
  90. */
  91. public static final byte AUTO_NUMBER_GUID_FLAG_MASK = (byte)0x40;
  92. /**
  93. * mask for the hyperlink bit (on memo types)
  94. * @usage _advanced_field_
  95. */
  96. public static final byte HYPERLINK_FLAG_MASK = (byte)0x80;
  97. /**
  98. * mask for the unknown bit (possible "can be null"?)
  99. * @usage _advanced_field_
  100. */
  101. public static final byte UNKNOWN_FLAG_MASK = (byte)0x02;
  102. // some other flags?
  103. // 0x10: replication related field (or hidden?)
  104. protected static final byte COMPRESSED_UNICODE_EXT_FLAG_MASK = (byte)0x01;
  105. private static final byte CALCULATED_EXT_FLAG_MASK = (byte)0xC0;
  106. static final byte NUMERIC_NEGATIVE_BYTE = (byte)0x80;
  107. /** the value for the "general" sort order */
  108. private static final short GENERAL_SORT_ORDER_VALUE = 1033;
  109. /**
  110. * the "general" text sort order, legacy version (access 2000-2007)
  111. * @usage _intermediate_field_
  112. */
  113. public static final SortOrder GENERAL_LEGACY_SORT_ORDER =
  114. new SortOrder(GENERAL_SORT_ORDER_VALUE, (byte)0);
  115. /**
  116. * the "general" text sort order, latest version (access 2010+)
  117. * @usage _intermediate_field_
  118. */
  119. public static final SortOrder GENERAL_SORT_ORDER =
  120. new SortOrder(GENERAL_SORT_ORDER_VALUE, (byte)1);
  121. /** pattern matching textual guid strings (allows for optional surrounding
  122. '{' and '}') */
  123. 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*");
  124. /** header used to indicate unicode text compression */
  125. private static final byte[] TEXT_COMPRESSION_HEADER =
  126. { (byte)0xFF, (byte)0XFE };
  127. private static final char MIN_COMPRESS_CHAR = 1;
  128. private static final char MAX_COMPRESS_CHAR = 0xFF;
  129. /** auto numbers must be > 0 */
  130. static final int INVALID_AUTO_NUMBER = 0;
  131. /** owning table */
  132. private final TableImpl _table;
  133. /** Whether or not the column is of variable length */
  134. private final boolean _variableLength;
  135. /** Whether or not the column is an autonumber column */
  136. private final boolean _autoNumber;
  137. /** Whether or not the column is a calculated column */
  138. private final boolean _calculated;
  139. /** Data type */
  140. private final DataType _type;
  141. /** Maximum column length */
  142. private final short _columnLength;
  143. /** 0-based column number */
  144. private final short _columnNumber;
  145. /** index of the data for this column within a list of row data */
  146. private int _columnIndex;
  147. /** display index of the data for this column */
  148. private final int _displayIndex;
  149. /** Column name */
  150. private final String _name;
  151. /** the offset of the fixed data in the row */
  152. private final int _fixedDataOffset;
  153. /** the index of the variable length data in the var len offset table */
  154. private final int _varLenTableIndex;
  155. /** the auto number generator for this column (if autonumber column) */
  156. private final AutoNumberGenerator _autoNumberGenerator;
  157. /** properties for this column, if any */
  158. private PropertyMap _props;
  159. /** Validator for writing new values */
  160. private ColumnValidator _validator = SimpleColumnValidator.INSTANCE;
  161. /**
  162. * @usage _advanced_method_
  163. */
  164. protected ColumnImpl(TableImpl table, String name, DataType type,
  165. int colNumber, int fixedOffset, int varLenIndex) {
  166. _table = table;
  167. _name = name;
  168. _type = type;
  169. if(!_type.isVariableLength()) {
  170. _columnLength = (short)type.getFixedSize();
  171. } else {
  172. _columnLength = (short)type.getMaxSize();
  173. }
  174. _variableLength = type.isVariableLength();
  175. _autoNumber = false;
  176. _calculated = false;
  177. _autoNumberGenerator = null;
  178. _columnNumber = (short)colNumber;
  179. _columnIndex = colNumber;
  180. _displayIndex = colNumber;
  181. _fixedDataOffset = fixedOffset;
  182. _varLenTableIndex = varLenIndex;
  183. }
  184. /**
  185. * Read a column definition in from a buffer
  186. * @usage _advanced_method_
  187. */
  188. ColumnImpl(InitArgs args)
  189. throws IOException
  190. {
  191. _table = args.table;
  192. _name = args.name;
  193. _displayIndex = args.displayIndex;
  194. _type = args.type;
  195. _columnNumber = args.buffer.getShort(
  196. args.offset + getFormat().OFFSET_COLUMN_NUMBER);
  197. _columnLength = args.buffer.getShort(
  198. args.offset + getFormat().OFFSET_COLUMN_LENGTH);
  199. _variableLength = ((args.flags & FIXED_LEN_FLAG_MASK) == 0);
  200. _autoNumber = ((args.flags &
  201. (AUTO_NUMBER_FLAG_MASK | AUTO_NUMBER_GUID_FLAG_MASK)) != 0);
  202. _calculated = ((args.extFlags & CALCULATED_EXT_FLAG_MASK) != 0);
  203. _autoNumberGenerator = createAutoNumberGenerator();
  204. if(_variableLength) {
  205. _varLenTableIndex = args.buffer.getShort(
  206. args.offset + getFormat().OFFSET_COLUMN_VARIABLE_TABLE_INDEX);
  207. _fixedDataOffset = 0;
  208. } else {
  209. _fixedDataOffset = args.buffer.getShort(
  210. args.offset + getFormat().OFFSET_COLUMN_FIXED_DATA_OFFSET);
  211. _varLenTableIndex = 0;
  212. }
  213. }
  214. /**
  215. * Creates the appropriate ColumnImpl class and reads a column definition in
  216. * from a buffer
  217. * @param table owning table
  218. * @param buffer Buffer containing column definition
  219. * @param offset Offset in the buffer at which the column definition starts
  220. * @usage _advanced_method_
  221. */
  222. public static ColumnImpl create(TableImpl table, ByteBuffer buffer,
  223. int offset, String name, int displayIndex)
  224. throws IOException
  225. {
  226. InitArgs args = new InitArgs(table, buffer, offset, name, displayIndex);
  227. boolean calculated = ((args.extFlags & CALCULATED_EXT_FLAG_MASK) != 0);
  228. byte colType = args.colType;
  229. if(calculated) {
  230. // "real" data type is in the "result type" property
  231. PropertyMap colProps = table.getPropertyMaps().get(name);
  232. Byte resultType = (Byte)colProps.getValue(PropertyMap.RESULT_TYPE_PROP);
  233. if(resultType != null) {
  234. colType = resultType;
  235. }
  236. }
  237. try {
  238. args.type = DataType.fromByte(colType);
  239. } catch(IOException e) {
  240. LOG.warn(withErrorContext("Unsupported column type " + colType,
  241. table.getDatabase(), table.getName(), name));
  242. boolean variableLength = ((args.flags & FIXED_LEN_FLAG_MASK) == 0);
  243. args.type = (variableLength ? DataType.UNSUPPORTED_VARLEN :
  244. DataType.UNSUPPORTED_FIXEDLEN);
  245. return new UnsupportedColumnImpl(args);
  246. }
  247. if(calculated) {
  248. return CalculatedColumnUtil.create(args);
  249. }
  250. switch(args.type) {
  251. case TEXT:
  252. return new TextColumnImpl(args);
  253. case MEMO:
  254. return new MemoColumnImpl(args);
  255. case COMPLEX_TYPE:
  256. return new ComplexColumnImpl(args);
  257. default:
  258. // fall through
  259. }
  260. if(args.type.getHasScalePrecision()) {
  261. return new NumericColumnImpl(args);
  262. }
  263. if(args.type.isLongValue()) {
  264. return new LongValueColumnImpl(args);
  265. }
  266. return new ColumnImpl(args);
  267. }
  268. /**
  269. * Sets the usage maps for this column.
  270. */
  271. void setUsageMaps(UsageMap ownedPages, UsageMap freeSpacePages) {
  272. // base does nothing
  273. }
  274. /**
  275. * Secondary column initialization after the table is fully loaded.
  276. */
  277. void postTableLoadInit() throws IOException {
  278. // base does nothing
  279. }
  280. public TableImpl getTable() {
  281. return _table;
  282. }
  283. public DatabaseImpl getDatabase() {
  284. return getTable().getDatabase();
  285. }
  286. /**
  287. * @usage _advanced_method_
  288. */
  289. public JetFormat getFormat() {
  290. return getDatabase().getFormat();
  291. }
  292. /**
  293. * @usage _advanced_method_
  294. */
  295. public PageChannel getPageChannel() {
  296. return getDatabase().getPageChannel();
  297. }
  298. public String getName() {
  299. return _name;
  300. }
  301. public boolean isVariableLength() {
  302. return _variableLength;
  303. }
  304. public boolean isAutoNumber() {
  305. return _autoNumber;
  306. }
  307. /**
  308. * @usage _advanced_method_
  309. */
  310. public short getColumnNumber() {
  311. return _columnNumber;
  312. }
  313. public int getColumnIndex() {
  314. return _columnIndex;
  315. }
  316. /**
  317. * @usage _advanced_method_
  318. */
  319. public void setColumnIndex(int newColumnIndex) {
  320. _columnIndex = newColumnIndex;
  321. }
  322. /**
  323. * @usage _advanced_method_
  324. */
  325. public int getDisplayIndex() {
  326. return _displayIndex;
  327. }
  328. public DataType getType() {
  329. return _type;
  330. }
  331. public int getSQLType() throws SQLException {
  332. return _type.getSQLType();
  333. }
  334. public boolean isCompressedUnicode() {
  335. return false;
  336. }
  337. public byte getPrecision() {
  338. return (byte)getType().getDefaultPrecision();
  339. }
  340. public byte getScale() {
  341. return (byte)getType().getDefaultScale();
  342. }
  343. /**
  344. * @usage _intermediate_method_
  345. */
  346. public SortOrder getTextSortOrder() {
  347. return null;
  348. }
  349. /**
  350. * @usage _intermediate_method_
  351. */
  352. public short getTextCodePage() {
  353. return 0;
  354. }
  355. public short getLength() {
  356. return _columnLength;
  357. }
  358. public short getLengthInUnits() {
  359. return (short)getType().toUnitSize(getLength());
  360. }
  361. public boolean isCalculated() {
  362. return _calculated;
  363. }
  364. /**
  365. * @usage _advanced_method_
  366. */
  367. public int getVarLenTableIndex() {
  368. return _varLenTableIndex;
  369. }
  370. /**
  371. * @usage _advanced_method_
  372. */
  373. public int getFixedDataOffset() {
  374. return _fixedDataOffset;
  375. }
  376. protected Charset getCharset() {
  377. return getDatabase().getCharset();
  378. }
  379. protected Calendar getCalendar() {
  380. return getDatabase().getCalendar();
  381. }
  382. public boolean isAppendOnly() {
  383. return (getVersionHistoryColumn() != null);
  384. }
  385. public ColumnImpl getVersionHistoryColumn() {
  386. return null;
  387. }
  388. /**
  389. * Returns the number of database pages owned by this column.
  390. * @usage _intermediate_method_
  391. */
  392. public int getOwnedPageCount() {
  393. return 0;
  394. }
  395. /**
  396. * @usage _advanced_method_
  397. */
  398. public void setVersionHistoryColumn(ColumnImpl versionHistoryCol) {
  399. throw new UnsupportedOperationException();
  400. }
  401. public boolean isHyperlink() {
  402. return false;
  403. }
  404. public ComplexColumnInfo<? extends ComplexValue> getComplexInfo() {
  405. return null;
  406. }
  407. public ColumnValidator getColumnValidator() {
  408. return _validator;
  409. }
  410. public void setColumnValidator(ColumnValidator newValidator) {
  411. if(isAutoNumber()) {
  412. // cannot set autonumber validator (autonumber values are controlled
  413. // internally)
  414. if(newValidator != null) {
  415. throw new IllegalArgumentException(withErrorContext(
  416. "Cannot set ColumnValidator for autonumber columns"));
  417. }
  418. // just leave default validator instance alone
  419. return;
  420. }
  421. if(newValidator == null) {
  422. newValidator = getDatabase().getColumnValidatorFactory()
  423. .createValidator(this);
  424. if(newValidator == null) {
  425. newValidator = SimpleColumnValidator.INSTANCE;
  426. }
  427. }
  428. _validator = newValidator;
  429. }
  430. byte getOriginalDataType() {
  431. return _type.getValue();
  432. }
  433. private AutoNumberGenerator createAutoNumberGenerator() {
  434. if(!_autoNumber || (_type == null)) {
  435. return null;
  436. }
  437. switch(_type) {
  438. case LONG:
  439. return new LongAutoNumberGenerator();
  440. case GUID:
  441. return new GuidAutoNumberGenerator();
  442. case COMPLEX_TYPE:
  443. return new ComplexTypeAutoNumberGenerator();
  444. default:
  445. LOG.warn(withErrorContext("Unknown auto number column type " + _type));
  446. return new UnsupportedAutoNumberGenerator(_type);
  447. }
  448. }
  449. /**
  450. * Returns the AutoNumberGenerator for this column if this is an autonumber
  451. * column, {@code null} otherwise.
  452. * @usage _advanced_method_
  453. */
  454. public AutoNumberGenerator getAutoNumberGenerator() {
  455. return _autoNumberGenerator;
  456. }
  457. public PropertyMap getProperties() throws IOException {
  458. if(_props == null) {
  459. _props = getTable().getPropertyMaps().get(getName());
  460. }
  461. return _props;
  462. }
  463. public Object setRowValue(Object[] rowArray, Object value) {
  464. rowArray[_columnIndex] = value;
  465. return value;
  466. }
  467. public Object setRowValue(Map<String,Object> rowMap, Object value) {
  468. rowMap.put(_name, value);
  469. return value;
  470. }
  471. public Object getRowValue(Object[] rowArray) {
  472. return rowArray[_columnIndex];
  473. }
  474. public Object getRowValue(Map<String,?> rowMap) {
  475. return rowMap.get(_name);
  476. }
  477. public boolean storeInNullMask() {
  478. return (getType() == DataType.BOOLEAN);
  479. }
  480. public boolean writeToNullMask(Object value) {
  481. return toBooleanValue(value);
  482. }
  483. public Object readFromNullMask(boolean isNull) {
  484. return Boolean.valueOf(!isNull);
  485. }
  486. /**
  487. * Deserialize a raw byte value for this column into an Object
  488. * @param data The raw byte value
  489. * @return The deserialized Object
  490. * @usage _advanced_method_
  491. */
  492. public Object read(byte[] data) throws IOException {
  493. return read(data, PageChannel.DEFAULT_BYTE_ORDER);
  494. }
  495. /**
  496. * Deserialize a raw byte value for this column into an Object
  497. * @param data The raw byte value
  498. * @param order Byte order in which the raw value is stored
  499. * @return The deserialized Object
  500. * @usage _advanced_method_
  501. */
  502. public Object read(byte[] data, ByteOrder order) throws IOException {
  503. ByteBuffer buffer = ByteBuffer.wrap(data).order(order);
  504. switch(getType()) {
  505. case BOOLEAN:
  506. throw new IOException(withErrorContext("Tried to read a boolean from data instead of null mask."));
  507. case BYTE:
  508. return Byte.valueOf(buffer.get());
  509. case INT:
  510. return Short.valueOf(buffer.getShort());
  511. case LONG:
  512. return Integer.valueOf(buffer.getInt());
  513. case DOUBLE:
  514. return Double.valueOf(buffer.getDouble());
  515. case FLOAT:
  516. return Float.valueOf(buffer.getFloat());
  517. case SHORT_DATE_TIME:
  518. return readDateValue(buffer);
  519. case BINARY:
  520. return data;
  521. case TEXT:
  522. return decodeTextValue(data);
  523. case MONEY:
  524. return readCurrencyValue(buffer);
  525. case NUMERIC:
  526. return readNumericValue(buffer);
  527. case GUID:
  528. return readGUIDValue(buffer, order);
  529. case UNKNOWN_0D:
  530. case UNKNOWN_11:
  531. // treat like "binary" data
  532. return data;
  533. case COMPLEX_TYPE:
  534. return new ComplexValueForeignKeyImpl(this, buffer.getInt());
  535. default:
  536. throw new IOException(withErrorContext("Unrecognized data type: " + _type));
  537. }
  538. }
  539. /**
  540. * Decodes "Currency" values.
  541. *
  542. * @param buffer Column value that points to currency data
  543. * @return BigDecimal representing the monetary value
  544. * @throws IOException if the value cannot be parsed
  545. */
  546. private BigDecimal readCurrencyValue(ByteBuffer buffer)
  547. throws IOException
  548. {
  549. if(buffer.remaining() != 8) {
  550. throw new IOException(withErrorContext("Invalid money value"));
  551. }
  552. return new BigDecimal(BigInteger.valueOf(buffer.getLong(0)), 4);
  553. }
  554. /**
  555. * Writes "Currency" values.
  556. */
  557. private void writeCurrencyValue(ByteBuffer buffer, Object value)
  558. throws IOException
  559. {
  560. Object inValue = value;
  561. try {
  562. BigDecimal decVal = toBigDecimal(value);
  563. inValue = decVal;
  564. // adjust scale (will cause the an ArithmeticException if number has too
  565. // many decimal places)
  566. decVal = decVal.setScale(4);
  567. // now, remove scale and convert to long (this will throw if the value is
  568. // too big)
  569. buffer.putLong(decVal.movePointRight(4).longValueExact());
  570. } catch(ArithmeticException e) {
  571. throw (IOException)
  572. new IOException(withErrorContext(
  573. "Currency value '" + inValue + "' out of range"))
  574. .initCause(e);
  575. }
  576. }
  577. /**
  578. * Decodes a NUMERIC field.
  579. */
  580. private BigDecimal readNumericValue(ByteBuffer buffer)
  581. {
  582. boolean negate = (buffer.get() != 0);
  583. byte[] tmpArr = ByteUtil.getBytes(buffer, 16);
  584. if(buffer.order() != ByteOrder.BIG_ENDIAN) {
  585. fixNumericByteOrder(tmpArr);
  586. }
  587. return toBigDecimal(tmpArr, negate, getScale());
  588. }
  589. static BigDecimal toBigDecimal(byte[] bytes, boolean negate, int scale)
  590. {
  591. if((bytes[0] & 0x80) != 0) {
  592. // the data is effectively unsigned, but the BigInteger handles it as
  593. // signed twos complement. we need to add an extra byte to the input so
  594. // that it will be treated as unsigned
  595. bytes = ByteUtil.copyOf(bytes, 0, bytes.length + 1, 1);
  596. }
  597. BigInteger intVal = new BigInteger(bytes);
  598. if(negate) {
  599. intVal = intVal.negate();
  600. }
  601. return new BigDecimal(intVal, scale);
  602. }
  603. /**
  604. * Writes a numeric value.
  605. */
  606. private void writeNumericValue(ByteBuffer buffer, Object value)
  607. throws IOException
  608. {
  609. Object inValue = value;
  610. try {
  611. BigDecimal decVal = toBigDecimal(value);
  612. inValue = decVal;
  613. int signum = decVal.signum();
  614. if(signum < 0) {
  615. decVal = decVal.negate();
  616. }
  617. // write sign byte
  618. buffer.put((signum < 0) ? NUMERIC_NEGATIVE_BYTE : 0);
  619. // adjust scale according to this column type (will cause the an
  620. // ArithmeticException if number has too many decimal places)
  621. decVal = decVal.setScale(getScale());
  622. // check precision
  623. if(decVal.precision() > getPrecision()) {
  624. throw new IOException(withErrorContext(
  625. "Numeric value is too big for specified precision "
  626. + getPrecision() + ": " + decVal));
  627. }
  628. // convert to unscaled BigInteger, big-endian bytes
  629. byte[] intValBytes = toUnscaledByteArray(
  630. decVal, getType().getFixedSize() - 1);
  631. if(buffer.order() != ByteOrder.BIG_ENDIAN) {
  632. fixNumericByteOrder(intValBytes);
  633. }
  634. buffer.put(intValBytes);
  635. } catch(ArithmeticException e) {
  636. throw (IOException)
  637. new IOException(withErrorContext(
  638. "Numeric value '" + inValue + "' out of range"))
  639. .initCause(e);
  640. }
  641. }
  642. byte[] toUnscaledByteArray(BigDecimal decVal, int maxByteLen)
  643. throws IOException
  644. {
  645. // convert to unscaled BigInteger, big-endian bytes
  646. byte[] intValBytes = decVal.unscaledValue().toByteArray();
  647. if(intValBytes.length > maxByteLen) {
  648. if((intValBytes[0] == 0) && ((intValBytes.length - 1) == maxByteLen)) {
  649. // in order to not return a negative two's complement value,
  650. // toByteArray() may return an extra leading 0 byte. we are working
  651. // with unsigned values, so we can drop the extra leading 0
  652. intValBytes = ByteUtil.copyOf(intValBytes, 1, maxByteLen);
  653. } else {
  654. throw new IOException(withErrorContext(
  655. "Too many bytes for valid BigInteger?"));
  656. }
  657. } else if(intValBytes.length < maxByteLen) {
  658. intValBytes = ByteUtil.copyOf(intValBytes, 0, maxByteLen,
  659. (maxByteLen - intValBytes.length));
  660. }
  661. return intValBytes;
  662. }
  663. /**
  664. * Decodes a date value.
  665. */
  666. private Date readDateValue(ByteBuffer buffer)
  667. {
  668. // seems access stores dates in the local timezone. guess you just hope
  669. // you read it in the same timezone in which it was written!
  670. long dateBits = buffer.getLong();
  671. long time = fromDateDouble(Double.longBitsToDouble(dateBits));
  672. return new DateExt(time, dateBits);
  673. }
  674. /**
  675. * Returns a java long time value converted from an access date double.
  676. * @usage _advanced_method_
  677. */
  678. public long fromDateDouble(double value)
  679. {
  680. long time = Math.round(value * MILLISECONDS_PER_DAY);
  681. time -= MILLIS_BETWEEN_EPOCH_AND_1900;
  682. time -= getFromLocalTimeZoneOffset(time);
  683. return time;
  684. }
  685. /**
  686. * Writes a date value.
  687. */
  688. private void writeDateValue(ByteBuffer buffer, Object value)
  689. {
  690. if(value == null) {
  691. buffer.putDouble(0d);
  692. } else if(value instanceof DateExt) {
  693. // this is a Date value previously read from readDateValue(). use the
  694. // original bits to store the value so we don't lose any precision
  695. buffer.putLong(((DateExt)value).getDateBits());
  696. } else {
  697. buffer.putDouble(toDateDouble(value));
  698. }
  699. }
  700. /**
  701. * Returns an access date double converted from a java Date/Calendar/Number
  702. * time value.
  703. * @usage _advanced_method_
  704. */
  705. public double toDateDouble(Object value)
  706. {
  707. // seems access stores dates in the local timezone. guess you just
  708. // hope you read it in the same timezone in which it was written!
  709. long time = toDateLong(value);
  710. time += getToLocalTimeZoneOffset(time);
  711. time += MILLIS_BETWEEN_EPOCH_AND_1900;
  712. return time / MILLISECONDS_PER_DAY;
  713. }
  714. /**
  715. * @return an appropriate Date long value for the given object
  716. */
  717. private static long toDateLong(Object value)
  718. {
  719. return ((value instanceof Date) ?
  720. ((Date)value).getTime() :
  721. ((value instanceof Calendar) ?
  722. ((Calendar)value).getTimeInMillis() :
  723. ((Number)value).longValue()));
  724. }
  725. /**
  726. * Gets the timezone offset from UTC to local time for the given time
  727. * (including DST).
  728. */
  729. private long getToLocalTimeZoneOffset(long time)
  730. {
  731. Calendar c = getCalendar();
  732. c.setTimeInMillis(time);
  733. return ((long)c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET));
  734. }
  735. /**
  736. * Gets the timezone offset from local time to UTC for the given time
  737. * (including DST).
  738. */
  739. private long getFromLocalTimeZoneOffset(long time)
  740. {
  741. // getting from local time back to UTC is a little wonky (and not
  742. // guaranteed to get you back to where you started)
  743. Calendar c = getCalendar();
  744. c.setTimeInMillis(time);
  745. // apply the zone offset first to get us closer to the original time
  746. c.setTimeInMillis(time - c.get(Calendar.ZONE_OFFSET));
  747. return ((long)c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET));
  748. }
  749. /**
  750. * Decodes a GUID value.
  751. */
  752. private static String readGUIDValue(ByteBuffer buffer, ByteOrder order)
  753. {
  754. if(order != ByteOrder.BIG_ENDIAN) {
  755. byte[] tmpArr = ByteUtil.getBytes(buffer, 16);
  756. // the first 3 guid components are integer components which need to
  757. // respect endianness, so swap 4-byte int, 2-byte int, 2-byte int
  758. ByteUtil.swap4Bytes(tmpArr, 0);
  759. ByteUtil.swap2Bytes(tmpArr, 4);
  760. ByteUtil.swap2Bytes(tmpArr, 6);
  761. buffer = ByteBuffer.wrap(tmpArr);
  762. }
  763. StringBuilder sb = new StringBuilder(22);
  764. sb.append("{");
  765. sb.append(ByteUtil.toHexString(buffer, 0, 4,
  766. false));
  767. sb.append("-");
  768. sb.append(ByteUtil.toHexString(buffer, 4, 2,
  769. false));
  770. sb.append("-");
  771. sb.append(ByteUtil.toHexString(buffer, 6, 2,
  772. false));
  773. sb.append("-");
  774. sb.append(ByteUtil.toHexString(buffer, 8, 2,
  775. false));
  776. sb.append("-");
  777. sb.append(ByteUtil.toHexString(buffer, 10, 6,
  778. false));
  779. sb.append("}");
  780. return (sb.toString());
  781. }
  782. /**
  783. * Writes a GUID value.
  784. */
  785. private void writeGUIDValue(ByteBuffer buffer, Object value)
  786. throws IOException
  787. {
  788. Matcher m = GUID_PATTERN.matcher(toCharSequence(value));
  789. if(!m.matches()) {
  790. throw new IOException(withErrorContext("Invalid GUID: " + value));
  791. }
  792. ByteBuffer origBuffer = null;
  793. byte[] tmpBuf = null;
  794. if(buffer.order() != ByteOrder.BIG_ENDIAN) {
  795. // write to a temp buf so we can do some swapping below
  796. origBuffer = buffer;
  797. tmpBuf = new byte[16];
  798. buffer = ByteBuffer.wrap(tmpBuf);
  799. }
  800. ByteUtil.writeHexString(buffer, m.group(1));
  801. ByteUtil.writeHexString(buffer, m.group(2));
  802. ByteUtil.writeHexString(buffer, m.group(3));
  803. ByteUtil.writeHexString(buffer, m.group(4));
  804. ByteUtil.writeHexString(buffer, m.group(5));
  805. if(tmpBuf != null) {
  806. // the first 3 guid components are integer components which need to
  807. // respect endianness, so swap 4-byte int, 2-byte int, 2-byte int
  808. ByteUtil.swap4Bytes(tmpBuf, 0);
  809. ByteUtil.swap2Bytes(tmpBuf, 4);
  810. ByteUtil.swap2Bytes(tmpBuf, 6);
  811. origBuffer.put(tmpBuf);
  812. }
  813. }
  814. /**
  815. * Returns {@code true} if the given value is a "guid" value.
  816. */
  817. static boolean isGUIDValue(Object value) throws IOException {
  818. return GUID_PATTERN.matcher(toCharSequence(value)).matches();
  819. }
  820. /**
  821. * Passes the given obj through the currently configured validator for this
  822. * column and returns the result.
  823. */
  824. public Object validate(Object obj) throws IOException {
  825. return _validator.validate(this, obj);
  826. }
  827. /**
  828. * Serialize an Object into a raw byte value for this column in little
  829. * endian order
  830. * @param obj Object to serialize
  831. * @return A buffer containing the bytes
  832. * @usage _advanced_method_
  833. */
  834. public ByteBuffer write(Object obj, int remainingRowLength)
  835. throws IOException
  836. {
  837. return write(obj, remainingRowLength, PageChannel.DEFAULT_BYTE_ORDER);
  838. }
  839. /**
  840. * Serialize an Object into a raw byte value for this column
  841. * @param obj Object to serialize
  842. * @param order Order in which to serialize
  843. * @return A buffer containing the bytes
  844. * @usage _advanced_method_
  845. */
  846. public ByteBuffer write(Object obj, int remainingRowLength, ByteOrder order)
  847. throws IOException
  848. {
  849. if(isRawData(obj)) {
  850. // just slap it right in (not for the faint of heart!)
  851. return ByteBuffer.wrap(((RawData)obj).getBytes());
  852. }
  853. return writeRealData(obj, remainingRowLength, order);
  854. }
  855. protected ByteBuffer writeRealData(Object obj, int remainingRowLength,
  856. ByteOrder order)
  857. throws IOException
  858. {
  859. if(!isVariableLength() || !getType().isVariableLength()) {
  860. return writeFixedLengthField(obj, order);
  861. }
  862. // this is an "inline" var length field
  863. switch(getType()) {
  864. case NUMERIC:
  865. // don't ask me why numerics are "var length" columns...
  866. ByteBuffer buffer = PageChannel.createBuffer(
  867. getType().getFixedSize(), order);
  868. writeNumericValue(buffer, obj);
  869. buffer.flip();
  870. return buffer;
  871. case TEXT:
  872. return encodeTextValue(
  873. obj, 0, getLengthInUnits(), false).order(order);
  874. case BINARY:
  875. case UNKNOWN_0D:
  876. case UNSUPPORTED_VARLEN:
  877. // should already be "encoded"
  878. break;
  879. default:
  880. throw new RuntimeException(withErrorContext(
  881. "unexpected inline var length type: " + getType()));
  882. }
  883. ByteBuffer buffer = ByteBuffer.wrap(toByteArray(obj)).order(order);
  884. return buffer;
  885. }
  886. /**
  887. * Serialize an Object into a raw byte value for this column
  888. * @param obj Object to serialize
  889. * @param order Order in which to serialize
  890. * @return A buffer containing the bytes
  891. * @usage _advanced_method_
  892. */
  893. protected ByteBuffer writeFixedLengthField(Object obj, ByteOrder order)
  894. throws IOException
  895. {
  896. int size = getType().getFixedSize(_columnLength);
  897. ByteBuffer buffer = writeFixedLengthField(
  898. obj, PageChannel.createBuffer(size, order));
  899. buffer.flip();
  900. return buffer;
  901. }
  902. protected ByteBuffer writeFixedLengthField(Object obj, ByteBuffer buffer)
  903. throws IOException
  904. {
  905. // since booleans are not written by this method, it's safe to convert any
  906. // incoming boolean into an integer.
  907. obj = booleanToInteger(obj);
  908. switch(getType()) {
  909. case BOOLEAN:
  910. //Do nothing
  911. break;
  912. case BYTE:
  913. buffer.put(toNumber(obj).byteValue());
  914. break;
  915. case INT:
  916. buffer.putShort(toNumber(obj).shortValue());
  917. break;
  918. case LONG:
  919. buffer.putInt(toNumber(obj).intValue());
  920. break;
  921. case MONEY:
  922. writeCurrencyValue(buffer, obj);
  923. break;
  924. case FLOAT:
  925. buffer.putFloat(toNumber(obj).floatValue());
  926. break;
  927. case DOUBLE:
  928. buffer.putDouble(toNumber(obj).doubleValue());
  929. break;
  930. case SHORT_DATE_TIME:
  931. writeDateValue(buffer, obj);
  932. break;
  933. case TEXT:
  934. // apparently text numeric values are also occasionally written as fixed
  935. // length...
  936. int numChars = getLengthInUnits();
  937. // force uncompressed encoding for fixed length text
  938. buffer.put(encodeTextValue(obj, numChars, numChars, true));
  939. break;
  940. case GUID:
  941. writeGUIDValue(buffer, obj);
  942. break;
  943. case NUMERIC:
  944. // yes, that's right, occasionally numeric values are written as fixed
  945. // length...
  946. writeNumericValue(buffer, obj);
  947. break;
  948. case BINARY:
  949. case UNKNOWN_0D:
  950. case UNKNOWN_11:
  951. case COMPLEX_TYPE:
  952. buffer.putInt(toNumber(obj).intValue());
  953. break;
  954. case UNSUPPORTED_FIXEDLEN:
  955. byte[] bytes = toByteArray(obj);
  956. if(bytes.length != getLength()) {
  957. throw new IOException(withErrorContext(
  958. "Invalid fixed size binary data, size "
  959. + getLength() + ", got " + bytes.length));
  960. }
  961. buffer.put(bytes);
  962. break;
  963. default:
  964. throw new IOException(withErrorContext(
  965. "Unsupported data type: " + getType()));
  966. }
  967. return buffer;
  968. }
  969. /**
  970. * Decodes a compressed or uncompressed text value.
  971. */
  972. String decodeTextValue(byte[] data)
  973. throws IOException
  974. {
  975. // see if data is compressed. the 0xFF, 0xFE sequence indicates that
  976. // compression is used (sort of, see algorithm below)
  977. boolean isCompressed = ((data.length > 1) &&
  978. (data[0] == TEXT_COMPRESSION_HEADER[0]) &&
  979. (data[1] == TEXT_COMPRESSION_HEADER[1]));
  980. if(isCompressed) {
  981. // this is a whacky compression combo that switches back and forth
  982. // between compressed/uncompressed using a 0x00 byte (starting in
  983. // compressed mode)
  984. StringBuilder textBuf = new StringBuilder(data.length);
  985. // start after two bytes indicating compression use
  986. int dataStart = TEXT_COMPRESSION_HEADER.length;
  987. int dataEnd = dataStart;
  988. boolean inCompressedMode = true;
  989. while(dataEnd < data.length) {
  990. if(data[dataEnd] == (byte)0x00) {
  991. // handle current segment
  992. decodeTextSegment(data, dataStart, dataEnd, inCompressedMode,
  993. textBuf);
  994. inCompressedMode = !inCompressedMode;
  995. ++dataEnd;
  996. dataStart = dataEnd;
  997. } else {
  998. ++dataEnd;
  999. }
  1000. }
  1001. // handle last segment
  1002. decodeTextSegment(data, dataStart, dataEnd, inCompressedMode, textBuf);
  1003. return textBuf.toString();
  1004. }
  1005. return decodeUncompressedText(data, getCharset());
  1006. }
  1007. /**
  1008. * Decodes a segnment of a text value into the given buffer according to the
  1009. * given status of the segment (compressed/uncompressed).
  1010. */
  1011. private void decodeTextSegment(byte[] data, int dataStart, int dataEnd,
  1012. boolean inCompressedMode,
  1013. StringBuilder textBuf)
  1014. {
  1015. if(dataEnd <= dataStart) {
  1016. // no data
  1017. return;
  1018. }
  1019. int dataLength = dataEnd - dataStart;
  1020. if(inCompressedMode) {
  1021. byte[] tmpData = new byte[dataLength * 2];
  1022. int tmpIdx = 0;
  1023. for(int i = dataStart; i < dataEnd; ++i) {
  1024. tmpData[tmpIdx] = data[i];
  1025. tmpIdx += 2;
  1026. }
  1027. data = tmpData;
  1028. dataStart = 0;
  1029. dataLength = data.length;
  1030. }
  1031. textBuf.append(decodeUncompressedText(data, dataStart, dataLength,
  1032. getCharset()));
  1033. }
  1034. /**
  1035. * @param textBytes bytes of text to decode
  1036. * @return the decoded string
  1037. */
  1038. private static CharBuffer decodeUncompressedText(
  1039. byte[] textBytes, int startPos, int length, Charset charset)
  1040. {
  1041. return charset.decode(ByteBuffer.wrap(textBytes, startPos, length));
  1042. }
  1043. /**
  1044. * Encodes a text value, possibly compressing.
  1045. */
  1046. ByteBuffer encodeTextValue(Object obj, int minChars, int maxChars,
  1047. boolean forceUncompressed)
  1048. throws IOException
  1049. {
  1050. CharSequence text = toCharSequence(obj);
  1051. if((text.length() > maxChars) || (text.length() < minChars)) {
  1052. throw new IOException(withErrorContext(
  1053. "Text is wrong length for " + getType() +
  1054. " column, max " + maxChars
  1055. + ", min " + minChars + ", got " + text.length()));
  1056. }
  1057. // may only compress if column type allows it
  1058. if(!forceUncompressed && isCompressedUnicode() &&
  1059. (text.length() <= getFormat().MAX_COMPRESSED_UNICODE_SIZE) &&
  1060. isUnicodeCompressible(text)) {
  1061. byte[] encodedChars = new byte[TEXT_COMPRESSION_HEADER.length +
  1062. text.length()];
  1063. encodedChars[0] = TEXT_COMPRESSION_HEADER[0];
  1064. encodedChars[1] = TEXT_COMPRESSION_HEADER[1];
  1065. for(int i = 0; i < text.length(); ++i) {
  1066. encodedChars[i + TEXT_COMPRESSION_HEADER.length] =
  1067. (byte)text.charAt(i);
  1068. }
  1069. return ByteBuffer.wrap(encodedChars);
  1070. }
  1071. return encodeUncompressedText(text, getCharset());
  1072. }
  1073. /**
  1074. * Returns {@code true} if the given text can be compressed using compressed
  1075. * unicode, {@code false} otherwise.
  1076. */
  1077. private static boolean isUnicodeCompressible(CharSequence text) {
  1078. // only attempt to compress > 2 chars (compressing less than 3 chars would
  1079. // not result in a space savings due to the 2 byte compression header)
  1080. if(text.length() <= TEXT_COMPRESSION_HEADER.length) {
  1081. return false;
  1082. }
  1083. // now, see if it is all compressible characters
  1084. for(int i = 0; i < text.length(); ++i) {
  1085. char c = text.charAt(i);
  1086. if((c < MIN_COMPRESS_CHAR) || (c > MAX_COMPRESS_CHAR)) {
  1087. return false;
  1088. }
  1089. }
  1090. return true;
  1091. }
  1092. /**
  1093. * Constructs a byte containing the flags for this column.
  1094. */
  1095. private static byte getColumnBitFlags(ColumnBuilder col) {
  1096. byte flags = UNKNOWN_FLAG_MASK;
  1097. if(!col.isVariableLength()) {
  1098. flags |= FIXED_LEN_FLAG_MASK;
  1099. }
  1100. if(col.isAutoNumber()) {
  1101. byte autoNumFlags = 0;
  1102. switch(col.getType()) {
  1103. case LONG:
  1104. case COMPLEX_TYPE:
  1105. autoNumFlags = AUTO_NUMBER_FLAG_MASK;
  1106. break;
  1107. case GUID:
  1108. autoNumFlags = AUTO_NUMBER_GUID_FLAG_MASK;
  1109. break;
  1110. default:
  1111. // unknown autonum type
  1112. }
  1113. flags |= autoNumFlags;
  1114. }
  1115. if(col.isHyperlink()) {
  1116. flags |= HYPERLINK_FLAG_MASK;
  1117. }
  1118. return flags;
  1119. }
  1120. @Override
  1121. public String toString() {
  1122. ToStringBuilder sb = CustomToStringStyle.builder(this)
  1123. .append("name", "(" + _table.getName() + ") " + _name);
  1124. byte typeValue = getOriginalDataType();
  1125. sb.append("type", "0x" + Integer.toHexString(typeValue) +
  1126. " (" + _type + ")")
  1127. .append("number", _columnNumber)
  1128. .append("length", _columnLength)
  1129. .append("variableLength", _variableLength);
  1130. if(_calculated) {
  1131. sb.append("calculated", _calculated);
  1132. }
  1133. if(_type.isTextual()) {
  1134. sb.append("compressedUnicode", isCompressedUnicode())
  1135. .append("textSortOrder", getTextSortOrder());
  1136. if(getTextCodePage() > 0) {
  1137. sb.append("textCodePage", getTextCodePage());
  1138. }
  1139. if(isAppendOnly()) {
  1140. sb.append("appendOnly", isAppendOnly());
  1141. }
  1142. if(isHyperlink()) {
  1143. sb.append("hyperlink", isHyperlink());
  1144. }
  1145. }
  1146. if(_type.getHasScalePrecision()) {
  1147. sb.append("precision", getPrecision())
  1148. .append("scale", getScale());
  1149. }
  1150. if(_autoNumber) {
  1151. sb.append("lastAutoNumber", _autoNumberGenerator.getLast());
  1152. }
  1153. if(getComplexInfo() != null) {
  1154. sb.append("complexInfo", getComplexInfo());
  1155. }
  1156. return sb.toString();
  1157. }
  1158. /**
  1159. * @param textBytes bytes of text to decode
  1160. * @param charset relevant charset
  1161. * @return the decoded string
  1162. * @usage _advanced_method_
  1163. */
  1164. public static String decodeUncompressedText(byte[] textBytes,
  1165. Charset charset)
  1166. {
  1167. return decodeUncompressedText(textBytes, 0, textBytes.length, charset)
  1168. .toString();
  1169. }
  1170. /**
  1171. * @param text Text to encode
  1172. * @param charset database charset
  1173. * @return A buffer with the text encoded
  1174. * @usage _advanced_method_
  1175. */
  1176. public static ByteBuffer encodeUncompressedText(CharSequence text,
  1177. Charset charset)
  1178. {
  1179. CharBuffer cb = ((text instanceof CharBuffer) ?
  1180. (CharBuffer)text : CharBuffer.wrap(text));
  1181. return charset.encode(cb);
  1182. }
  1183. /**
  1184. * Orders Columns by column number.
  1185. * @usage _general_method_
  1186. */
  1187. public int compareTo(ColumnImpl other) {
  1188. if (_columnNumber > other.getColumnNumber()) {
  1189. return 1;
  1190. } else if (_columnNumber < other.getColumnNumber()) {
  1191. return -1;
  1192. } else {
  1193. return 0;
  1194. }
  1195. }
  1196. /**
  1197. * @param columns A list of columns in a table definition
  1198. * @return The number of variable length columns found in the list
  1199. * @usage _advanced_method_
  1200. */
  1201. public static short countVariableLength(List<ColumnBuilder> columns) {
  1202. short rtn = 0;
  1203. for (ColumnBuilder col : columns) {
  1204. if (col.isVariableLength()) {
  1205. rtn++;
  1206. }
  1207. }
  1208. return rtn;
  1209. }
  1210. /**
  1211. * @param columns A list of columns in a table definition
  1212. * @return The number of variable length columns which are not long values
  1213. * found in the list
  1214. * @usage _advanced_method_
  1215. */
  1216. private static short countNonLongVariableLength(List<ColumnBuilder> columns) {
  1217. short rtn = 0;
  1218. for (ColumnBuilder col : columns) {
  1219. if (col.isVariableLength() && !col.getType().isLongValue()) {
  1220. rtn++;
  1221. }
  1222. }
  1223. return rtn;
  1224. }
  1225. /**
  1226. * @return an appropriate BigDecimal representation of the given object.
  1227. * <code>null</code> is returned as 0 and Numbers are converted
  1228. * using their double representation.
  1229. */
  1230. static BigDecimal toBigDecimal(Object value)
  1231. {
  1232. if(value == null) {
  1233. return BigDecimal.ZERO;
  1234. } else if(value instanceof BigDecimal) {
  1235. return (BigDecimal)value;
  1236. } else if(value instanceof BigInteger) {
  1237. return new BigDecimal((BigInteger)value);
  1238. } else if(value instanceof Number) {
  1239. return new BigDecimal(((Number)value).doubleValue());
  1240. }
  1241. return new BigDecimal(value.toString());
  1242. }
  1243. /**
  1244. * @return an appropriate Number representation of the given object.
  1245. * <code>null</code> is returned as 0 and Strings are parsed as
  1246. * Doubles.
  1247. */
  1248. private static Number toNumber(Object value)
  1249. {
  1250. if(value == null) {
  1251. return BigDecimal.ZERO;
  1252. } else if(value instanceof Number) {
  1253. return (Number)value;
  1254. }
  1255. return Double.valueOf(value.toString());
  1256. }
  1257. /**
  1258. * @return an appropriate CharSequence representation of the given object.
  1259. * @usage _advanced_method_
  1260. */
  1261. public static CharSequence toCharSequence(Object value)
  1262. throws IOException
  1263. {
  1264. if(value == null) {
  1265. return null;
  1266. } else if(value instanceof CharSequence) {
  1267. return (CharSequence)value;
  1268. } else if(value instanceof Clob) {
  1269. try {
  1270. Clob c = (Clob)value;
  1271. // note, start pos is 1-based
  1272. return c.getSubString(1L, (int)c.length());
  1273. } catch(SQLException e) {
  1274. throw (IOException)(new IOException(e.getMessage())).initCause(e);
  1275. }
  1276. } else if(value instanceof Reader) {
  1277. char[] buf = new char[8 * 1024];
  1278. StringBuilder sout = new StringBuilder();
  1279. Reader in = (Reader)value;
  1280. int read = 0;
  1281. while((read = in.read(buf)) != -1) {
  1282. sout.append(buf, 0, read);
  1283. }
  1284. return sout;
  1285. }
  1286. return value.toString();
  1287. }
  1288. /**
  1289. * @return an appropriate byte[] representation of the given object.
  1290. * @usage _advanced_method_
  1291. */
  1292. public static byte[] toByteArray(Object value)
  1293. throws IOException
  1294. {
  1295. if(value == null) {
  1296. return null;
  1297. } else if(value instanceof byte[]) {
  1298. return (byte[])value;
  1299. } else if(value instanceof OleUtil.OleBlobImpl) {
  1300. return ((OleUtil.OleBlobImpl)value).getBytes();
  1301. } else if(value instanceof Blob) {
  1302. try {
  1303. Blob b = (Blob)value;
  1304. // note, start pos is 1-based
  1305. return b.getBytes(1L, (int)b.length());
  1306. } catch(SQLException e) {
  1307. throw (IOException)(new IOException(e.getMessage())).initCause(e);
  1308. }
  1309. } else if(value instanceof RawData) {
  1310. return ((RawData)value).getBytes();
  1311. }
  1312. ByteArrayOutputStream bout = new ByteArrayOutputStream();
  1313. if(value instanceof InputStream) {
  1314. ByteUtil.copy((InputStream)value, bout);
  1315. } else {
  1316. // if all else fails, serialize it
  1317. ObjectOutputStream oos = new ObjectOutputStream(bout);
  1318. oos.writeObject(value);
  1319. oos.close();
  1320. }
  1321. return bout.toByteArray();
  1322. }
  1323. /**
  1324. * Interpret a boolean value (null == false)
  1325. * @usage _advanced_method_
  1326. */
  1327. public static boolean toBooleanValue(Object obj) {
  1328. if(obj == null) {
  1329. return false;
  1330. } else if(obj instanceof Boolean) {
  1331. return ((Boolean)obj).booleanValue();
  1332. }
  1333. return Boolean.parseBoolean(obj.toString());
  1334. }
  1335. /**
  1336. * Swaps the bytes of the given numeric in place.
  1337. */
  1338. private static void fixNumericByteOrder(byte[] bytes)
  1339. {
  1340. // fix endianness of each 4 byte segment
  1341. for(int i = 0; i < bytes.length; i+=4) {
  1342. ByteUtil.swap4Bytes(bytes, i);
  1343. }
  1344. }
  1345. /**
  1346. * Treat booleans as integers (C-style).
  1347. */
  1348. protected static Object booleanToInteger(Object obj) {
  1349. if (obj instanceof Boolean) {
  1350. obj = ((Boolean) obj) ? 1 : 0;
  1351. }
  1352. return obj;
  1353. }
  1354. /**
  1355. * Returns a wrapper for raw column data that can be written without
  1356. * understanding the data. Useful for wrapping unparseable data for
  1357. * re-writing.
  1358. */
  1359. public static RawData rawDataWrapper(byte[] bytes) {
  1360. return new RawData(bytes);
  1361. }
  1362. /**
  1363. * Returns {@code true} if the given value is "raw" column data,
  1364. * {@code false} otherwise.
  1365. * @usage _advanced_method_
  1366. */
  1367. public static boolean isRawData(Object value) {
  1368. return(value instanceof RawData);
  1369. }
  1370. /**
  1371. * Writes the column definitions into a table definition buffer.
  1372. * @param buffer Buffer to write to
  1373. */
  1374. protected static void writeDefinitions(TableCreator creator, ByteBuffer buffer)
  1375. throws IOException
  1376. {
  1377. List<ColumnBuilder> columns = creator.getColumns();
  1378. short fixedOffset = (short) 0;
  1379. short variableOffset = (short) 0;
  1380. // we specifically put the "long variable" values after the normal
  1381. // variable length values so that we have a better chance of fitting it
  1382. // all (because "long variable" values can go in separate pages)
  1383. short longVariableOffset = countNonLongVariableLength(columns);
  1384. for (ColumnBuilder col : columns) {
  1385. buffer.put(col.getType().getValue());
  1386. buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER); //constant magic number
  1387. buffer.putShort(col.getColumnNumber()); //Column Number
  1388. if(col.isVariableLength()) {
  1389. if(!col.getType().isLongValue()) {
  1390. buffer.putShort(variableOffset++);
  1391. } else {
  1392. buffer.putShort(longVariableOffset++);
  1393. }
  1394. } else {
  1395. buffer.putShort((short) 0);
  1396. }
  1397. buffer.putShort(col.getColumnNumber()); //Column Number again
  1398. if(col.getType().isTextual()) {
  1399. // this will write 4 bytes (note we don't support writing dbs which
  1400. // use the text code page)
  1401. writeSortOrder(buffer, col.getTextSortOrder(), creator.getFormat());
  1402. } else {
  1403. // note scale/precision not stored for calculated numeric fields
  1404. if(col.getType().getHasScalePrecision() && !col.isCalculated()) {
  1405. buffer.put(col.getPrecision()); // numeric precision
  1406. buffer.put(col.getScale()); // numeric scale
  1407. } else {
  1408. buffer.put((byte) 0x00); //unused
  1409. buffer.put((byte) 0x00); //unused
  1410. }
  1411. buffer.putShort((short) 0); //Unknown
  1412. }
  1413. buffer.put(getColumnBitFlags(col)); // misc col flags
  1414. // note access doesn't seem to allow unicode compression for calced fields
  1415. if(col.isCalculated()) {
  1416. buffer.put(CALCULATED_EXT_FLAG_MASK);
  1417. } else if (col.isCompressedUnicode()) { //Compressed
  1418. buffer.put(COMPRESSED_UNICODE_EXT_FLAG_MASK);
  1419. } else {
  1420. buffer.put((byte)0);
  1421. }
  1422. buffer.putInt(0); //Unknown, but always 0.
  1423. //Offset for fixed length columns
  1424. if(col.isVariableLength()) {
  1425. buffer.putShort((short) 0);
  1426. } else {
  1427. buffer.putShort(fixedOffset);
  1428. fixedOffset += col.getType().getFixedSize(col.getLength());
  1429. }
  1430. if(!col.getType().isLongValue()) {
  1431. short length = col.getLength();
  1432. if(col.isCalculated()) {
  1433. // calced columns have additional value overhead
  1434. if(!col.getType().isVariableLength() ||
  1435. col.getType().getHasScalePrecision()) {
  1436. length = CalculatedColumnUtil.CALC_FIXED_FIELD_LEN;
  1437. } else {
  1438. length += CalculatedColumnUtil.CALC_EXTRA_DATA_LEN;
  1439. }
  1440. }
  1441. buffer.putShort(length); //Column length
  1442. } else {
  1443. buffer.putShort((short)0x0000); // unused
  1444. }
  1445. }
  1446. for (ColumnBuilder col : columns) {
  1447. TableImpl.writeName(buffer, col.getName(), creator.getCharset());
  1448. }
  1449. }
  1450. /**
  1451. * Reads the sort order info from the given buffer from the given position.
  1452. */
  1453. static SortOrder readSortOrder(ByteBuffer buffer, int position,
  1454. JetFormat format)
  1455. {
  1456. short value = buffer.getShort(position);
  1457. byte version = 0;
  1458. if(format.SIZE_SORT_ORDER == 4) {
  1459. version = buffer.get(position + 3);
  1460. }
  1461. if(value == 0) {
  1462. // probably a file we wrote, before handling sort order
  1463. return format.DEFAULT_SORT_ORDER;
  1464. }
  1465. if(value == GENERAL_SORT_ORDER_VALUE) {
  1466. if(version == GENERAL_LEGACY_SORT_ORDER.getVersion()) {
  1467. return GENERAL_LEGACY_SORT_ORDER;
  1468. }
  1469. if(version == GENERAL_SORT_ORDER.getVersion()) {
  1470. return GENERAL_SORT_ORDER;
  1471. }
  1472. }
  1473. return new SortOrder(value, version);
  1474. }
  1475. /**
  1476. * Reads the column cade page info from the given buffer, if supported for
  1477. * this db.
  1478. */
  1479. static short readCodePage(ByteBuffer buffer, int offset, JetFormat format)
  1480. {
  1481. int cpOffset = format.OFFSET_COLUMN_CODE_PAGE;
  1482. return ((cpOffset >= 0) ? buffer.getShort(offset + cpOffset) : 0);
  1483. }
  1484. /**
  1485. * Read the extra flags field for a column definition.
  1486. */
  1487. static byte readExtraFlags(ByteBuffer buffer, int offset, JetFormat format)
  1488. {
  1489. int extFlagsOffset = format.OFFSET_COLUMN_EXT_FLAGS;
  1490. return ((extFlagsOffset >= 0) ? buffer.get(offset + extFlagsOffset) : 0);
  1491. }
  1492. /**
  1493. * Writes the sort order info to the given buffer at the current position.
  1494. */
  1495. private static void writeSortOrder(ByteBuffer buffer, SortOrder sortOrder,
  1496. JetFormat format) {
  1497. if(sortOrder == null) {
  1498. sortOrder = format.DEFAULT_SORT_ORDER;
  1499. }
  1500. buffer.putShort(sortOrder.getValue());
  1501. if(format.SIZE_SORT_ORDER == 4) {
  1502. buffer.put((byte)0x00); // unknown
  1503. buffer.put(sortOrder.getVersion());
  1504. }
  1505. }
  1506. /**
  1507. * Returns {@code true} if the value is immutable, {@code false} otherwise.
  1508. * This only handles values that are returned from the {@link #read} method.
  1509. */
  1510. static boolean isImmutableValue(Object value) {
  1511. // for now, the only mutable value this class returns is byte[]
  1512. return !(value instanceof byte[]);
  1513. }
  1514. /**
  1515. * Converts the given value to the "internal" representation for the given
  1516. * data type.
  1517. */
  1518. public static Object toInternalValue(DataType dataType, Object value)
  1519. throws IOException
  1520. {
  1521. if(value == null) {
  1522. return null;
  1523. }
  1524. switch(dataType) {
  1525. case BOOLEAN:
  1526. return ((value instanceof Boolean) ? value : toBooleanValue(value));
  1527. case BYTE:
  1528. return ((value instanceof Byte) ? value : toNumber(value).byteValue());
  1529. case INT:
  1530. return ((value instanceof Short) ? value :
  1531. toNumber(value).shortValue());
  1532. case LONG:
  1533. return ((value instanceof Integer) ? value :
  1534. toNumber(value).intValue());
  1535. case MONEY:
  1536. return toBigDecimal(value);
  1537. case FLOAT:
  1538. return ((value instanceof Float) ? value :
  1539. toNumber(value).floatValue());
  1540. case DOUBLE:
  1541. return ((value instanceof Double) ? value :
  1542. toNumber(value).doubleValue());
  1543. case SHORT_DATE_TIME:
  1544. return ((value instanceof DateExt) ? value :
  1545. new Date(toDateLong(value)));
  1546. case TEXT:
  1547. case MEMO:
  1548. case GUID:
  1549. return ((value instanceof String) ? value :
  1550. toCharSequence(value).toString());
  1551. case NUMERIC:
  1552. return toBigDecimal(value);
  1553. case COMPLEX_TYPE:
  1554. // leave alone for now?
  1555. return value;
  1556. default:
  1557. // some variation of binary data
  1558. return toByteArray(value);
  1559. }
  1560. }
  1561. String withErrorContext(String msg) {
  1562. return withErrorContext(msg, getDatabase(), getTable().getName(), getName());
  1563. }
  1564. private static String withErrorContext(
  1565. String msg, DatabaseImpl db, String tableName, String colName) {
  1566. return msg + " (Db=" + db.getName() + ";Table=" + tableName + ";Column=" +
  1567. colName + ")";
  1568. }
  1569. /**
  1570. * Date subclass which stashes the original date bits, in case we attempt to
  1571. * re-write the value (will not lose precision).
  1572. */
  1573. private static final class DateExt extends Date
  1574. {
  1575. private static final long serialVersionUID = 0L;
  1576. /** cached bits of the original date value */
  1577. private transient final long _dateBits;
  1578. private DateExt(long time, long dateBits) {
  1579. super(time);
  1580. _dateBits = dateBits;
  1581. }
  1582. public long getDateBits() {
  1583. return _dateBits;
  1584. }
  1585. private Object writeReplace() throws ObjectStreamException {
  1586. // if we are going to serialize this Date, convert it back to a normal
  1587. // Date (in case it is restored outside of the context of jackcess)
  1588. return new Date(super.getTime());
  1589. }
  1590. }
  1591. /**
  1592. * Wrapper for raw column data which can be re-written.
  1593. */
  1594. private static class RawData implements Serializable
  1595. {
  1596. private static final long serialVersionUID = 0L;
  1597. private final byte[] _bytes;
  1598. private RawData(byte[] bytes) {
  1599. _bytes = bytes;
  1600. }
  1601. private byte[] getBytes() {
  1602. return _bytes;
  1603. }
  1604. @Override
  1605. public String toString() {
  1606. return CustomToStringStyle.valueBuilder(this)
  1607. .append(null, getBytes())
  1608. .toString();
  1609. }
  1610. private Object writeReplace() throws ObjectStreamException {
  1611. // if we are going to serialize this, convert it back to a normal
  1612. // byte[] (in case it is restored outside of the context of jackcess)
  1613. return getBytes();
  1614. }
  1615. }
  1616. /**
  1617. * Base class for the supported autonumber types.
  1618. * @usage _advanced_class_
  1619. */
  1620. public abstract class AutoNumberGenerator
  1621. {
  1622. protected AutoNumberGenerator() {}
  1623. /**
  1624. * Returns the last autonumber generated by this generator. Only valid
  1625. * after a call to {@link Table#addRow}, otherwise undefined.
  1626. */
  1627. public abstract Object getLast();
  1628. /**
  1629. * Returns the next autonumber for this generator.
  1630. * <p>
  1631. * <i>Warning, calling this externally will result in this value being
  1632. * "lost" for the table.</i>
  1633. */
  1634. public abstract Object getNext(TableImpl.WriteRowState writeRowState);
  1635. /**
  1636. * Returns a valid autonumber for this generator.
  1637. * <p>
  1638. * <i>Warning, calling this externally may result in this value being
  1639. * "lost" for the table.</i>
  1640. */
  1641. public abstract Object handleInsert(
  1642. TableImpl.WriteRowState writeRowState, Object inRowValue)
  1643. throws IOException;
  1644. /**
  1645. * Restores a previous autonumber generated by this generator.
  1646. */
  1647. public abstract void restoreLast(Object last);
  1648. /**
  1649. * Returns the type of values generated by this generator.
  1650. */
  1651. public abstract DataType getType();
  1652. }
  1653. private final class LongAutoNumberGenerator extends AutoNumberGenerator
  1654. {
  1655. private LongAutoNumberGenerator() {}
  1656. @Override
  1657. public Object getLast() {
  1658. // the table stores the last long autonumber used
  1659. return getTable().getLastLongAutoNumber();
  1660. }
  1661. @Override
  1662. public Object getNext(TableImpl.WriteRowState writeRowState) {
  1663. // the table stores the last long autonumber used
  1664. return getTable().getNextLongAutoNumber();
  1665. }
  1666. @Override
  1667. public Object handleInsert(TableImpl.WriteRowState writeRowState,
  1668. Object inRowValue)
  1669. throws IOException
  1670. {
  1671. int inAutoNum = toNumber(inRowValue).intValue();
  1672. if(inAutoNum <= INVALID_AUTO_NUMBER) {
  1673. throw new IOException(withErrorContext(
  1674. "Invalid auto number value " + inAutoNum));
  1675. }
  1676. // the table stores the last long autonumber used
  1677. getTable().adjustLongAutoNumber(inAutoNum);
  1678. return inAutoNum;
  1679. }
  1680. @Override
  1681. public void restoreLast(Object last) {
  1682. if(last instanceof Integer) {
  1683. getTable().restoreLastLongAutoNumber((Integer)last);
  1684. }
  1685. }
  1686. @Override
  1687. public DataType getType() {
  1688. return DataType.LONG;
  1689. }
  1690. }
  1691. private final class GuidAutoNumberGenerator extends AutoNumberGenerator
  1692. {
  1693. private Object _lastAutoNumber;
  1694. private GuidAutoNumberGenerator() {}
  1695. @Override
  1696. public Object getLast() {
  1697. return _lastAutoNumber;
  1698. }
  1699. @Override
  1700. public Object getNext(TableImpl.WriteRowState writeRowState) {
  1701. // format guids consistently w/ Column.readGUIDValue()
  1702. _lastAutoNumber = "{" + UUID.randomUUID() + "}";
  1703. return _lastAutoNumber;
  1704. }
  1705. @Override
  1706. public Object handleInsert(TableImpl.WriteRowState writeRowState,
  1707. Object inRowValue)
  1708. throws IOException
  1709. {
  1710. _lastAutoNumber = toCharSequence(inRowValue);
  1711. return _lastAutoNumber;
  1712. }
  1713. @Override
  1714. public void restoreLast(Object last) {
  1715. _lastAutoNumber = null;
  1716. }
  1717. @Override
  1718. public DataType getType() {
  1719. return DataType.GUID;
  1720. }
  1721. }
  1722. private final class ComplexTypeAutoNumberGenerator extends AutoNumberGenerator
  1723. {
  1724. private ComplexTypeAutoNumberGenerator() {}
  1725. @Override
  1726. public Object getLast() {
  1727. // the table stores the last ComplexType autonumber used
  1728. return getTable().getLastComplexTypeAutoNumber();
  1729. }
  1730. @Override
  1731. public Object getNext(TableImpl.WriteRowState writeRowState) {
  1732. // same value is shared across all ComplexType values in a row
  1733. int nextComplexAutoNum = writeRowState.getComplexAutoNumber();
  1734. if(nextComplexAutoNum <= INVALID_AUTO_NUMBER) {
  1735. // the table stores the last ComplexType autonumber used
  1736. nextComplexAutoNum = getTable().getNextComplexTypeAutoNumber();
  1737. writeRowState.setComplexAutoNumber(nextComplexAutoNum);
  1738. }
  1739. return new ComplexValueForeignKeyImpl(ColumnImpl.this,
  1740. nextComplexAutoNum);
  1741. }
  1742. @Override
  1743. public Object handleInsert(TableImpl.WriteRowState writeRowState,
  1744. Object inRowValue)
  1745. throws IOException
  1746. {
  1747. ComplexValueForeignKey inComplexFK = null;
  1748. if(inRowValue instanceof ComplexValueForeignKey) {
  1749. inComplexFK = (ComplexValueForeignKey)inRowValue;
  1750. } else {
  1751. inComplexFK = new ComplexValueForeignKeyImpl(
  1752. ColumnImpl.this, toNumber(inRowValue).intValue());
  1753. }
  1754. if(inComplexFK.getColumn() != ColumnImpl.this) {
  1755. throw new IOException(withErrorContext(
  1756. "Wrong column for complex value foreign key, found " +
  1757. inComplexFK.getColumn().getName()));
  1758. }
  1759. if(inComplexFK.get() < 1) {
  1760. throw new IOException(withErrorContext(
  1761. "Invalid complex value foreign key value " + inComplexFK.get()));
  1762. }
  1763. // same value is shared across all ComplexType values in a row
  1764. int prevRowValue = writeRowState.getComplexAutoNumber();
  1765. if(prevRowValue <= INVALID_AUTO_NUMBER) {
  1766. writeRowState.setComplexAutoNumber(inComplexFK.get());
  1767. } else if(prevRowValue != inComplexFK.get()) {
  1768. throw new IOException(withErrorContext(
  1769. "Inconsistent complex value foreign key values: found " +
  1770. prevRowValue + ", given " + inComplexFK));
  1771. }
  1772. // the table stores the last ComplexType autonumber used
  1773. getTable().adjustComplexTypeAutoNumber(inComplexFK.get());
  1774. return inComplexFK;
  1775. }
  1776. @Override
  1777. public void restoreLast(Object last) {
  1778. if(last instanceof ComplexValueForeignKey) {
  1779. getTable().restoreLastComplexTypeAutoNumber(
  1780. ((ComplexValueForeignKey)last).get());
  1781. }
  1782. }
  1783. @Override
  1784. public DataType getType() {
  1785. return DataType.COMPLEX_TYPE;
  1786. }
  1787. }
  1788. private final class UnsupportedAutoNumberGenerator extends AutoNumberGenerator
  1789. {
  1790. private final DataType _genType;
  1791. private UnsupportedAutoNumberGenerator(DataType genType) {
  1792. _genType = genType;
  1793. }
  1794. @Override
  1795. public Object getLast() {
  1796. return null;
  1797. }
  1798. @Override
  1799. public Object getNext(TableImpl.WriteRowState writeRowState) {
  1800. throw new UnsupportedOperationException();
  1801. }
  1802. @Override
  1803. public Object handleInsert(TableImpl.WriteRowState writeRowState,
  1804. Object inRowValue) {
  1805. throw new UnsupportedOperationException();
  1806. }
  1807. @Override
  1808. public void restoreLast(Object last) {
  1809. throw new UnsupportedOperationException();
  1810. }
  1811. @Override
  1812. public DataType getType() {
  1813. return _genType;
  1814. }
  1815. }
  1816. /**
  1817. * Information about the sort order (collation) for a textual column.
  1818. * @usage _intermediate_class_
  1819. */
  1820. public static final class SortOrder
  1821. {
  1822. private final short _value;
  1823. private final byte _version;
  1824. public SortOrder(short value, byte version) {
  1825. _value = value;
  1826. _version = version;
  1827. }
  1828. public short getValue() {
  1829. return _value;
  1830. }
  1831. public byte getVersion() {
  1832. return _version;
  1833. }
  1834. @Override
  1835. public int hashCode() {
  1836. return _value;
  1837. }
  1838. @Override
  1839. public boolean equals(Object o) {
  1840. return ((this == o) ||
  1841. ((o != null) && (getClass() == o.getClass()) &&
  1842. (_value == ((SortOrder)o)._value) &&
  1843. (_version == ((SortOrder)o)._version)));
  1844. }
  1845. @Override
  1846. public String toString() {
  1847. return CustomToStringStyle.valueBuilder(this)
  1848. .append(null, _value + "(" + _version + ")")
  1849. .toString();
  1850. }
  1851. }
  1852. /**
  1853. * Utility struct for passing params through ColumnImpl constructors.
  1854. */
  1855. static final class InitArgs
  1856. {
  1857. public final TableImpl table;
  1858. public final ByteBuffer buffer;
  1859. public final int offset;
  1860. public final String name;
  1861. public final int displayIndex;
  1862. public final byte colType;
  1863. public final byte flags;
  1864. public final byte extFlags;
  1865. public DataType type;
  1866. InitArgs(TableImpl table, ByteBuffer buffer, int offset, String name,
  1867. int displayIndex) {
  1868. this.table = table;
  1869. this.buffer = buffer;
  1870. this.offset = offset;
  1871. this.name = name;
  1872. this.displayIndex = displayIndex;
  1873. this.colType = buffer.get(offset + table.getFormat().OFFSET_COLUMN_TYPE);
  1874. this.flags = buffer.get(offset + table.getFormat().OFFSET_COLUMN_FLAGS);
  1875. this.extFlags = readExtraFlags(buffer, offset, table.getFormat());
  1876. }
  1877. }
  1878. }