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.

Column.java 69KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402
  1. /*
  2. Copyright (c) 2005 Health Market Science, Inc.
  3. This library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public
  5. License as published by the Free Software Foundation; either
  6. version 2.1 of the License, or (at your option) any later version.
  7. This library is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public
  12. License along with this library; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  14. USA
  15. You can contact Health Market Science at info@healthmarketscience.com
  16. or at the following address:
  17. Health Market Science
  18. 2700 Horizon Drive
  19. Suite 200
  20. King of Prussia, PA 19406
  21. */
  22. package com.healthmarketscience.jackcess;
  23. import java.io.ByteArrayOutputStream;
  24. import java.io.IOException;
  25. import java.io.InputStream;
  26. import java.io.ObjectOutputStream;
  27. import java.io.ObjectStreamException;
  28. import java.io.Reader;
  29. import java.io.Serializable;
  30. import java.math.BigDecimal;
  31. import java.math.BigInteger;
  32. import java.nio.ByteBuffer;
  33. import java.nio.ByteOrder;
  34. import java.nio.CharBuffer;
  35. import java.nio.charset.Charset;
  36. import java.sql.Blob;
  37. import java.sql.Clob;
  38. import java.sql.SQLException;
  39. import java.util.Calendar;
  40. import java.util.Date;
  41. import java.util.List;
  42. import java.util.Map;
  43. import java.util.TimeZone;
  44. import java.util.UUID;
  45. import java.util.regex.Matcher;
  46. import java.util.regex.Pattern;
  47. import com.healthmarketscience.jackcess.complex.ComplexColumnInfo;
  48. import com.healthmarketscience.jackcess.complex.ComplexValue;
  49. import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
  50. import com.healthmarketscience.jackcess.scsu.Compress;
  51. import com.healthmarketscience.jackcess.scsu.EndOfInputException;
  52. import com.healthmarketscience.jackcess.scsu.Expand;
  53. import com.healthmarketscience.jackcess.scsu.IllegalInputException;
  54. import org.apache.commons.logging.Log;
  55. import org.apache.commons.logging.LogFactory;
  56. /**
  57. * Access database column definition
  58. * @author Tim McCune
  59. * @usage _general_class_
  60. */
  61. public class Column implements Comparable<Column> {
  62. private static final Log LOG = LogFactory.getLog(Column.class);
  63. /**
  64. * Meaningless placeholder object for inserting values in an autonumber
  65. * column. it is not required that this value be used (any passed in value
  66. * is ignored), but using this placeholder may make code more obvious.
  67. * @usage _general_field_
  68. */
  69. public static final Object AUTO_NUMBER = "<AUTO_NUMBER>";
  70. /**
  71. * Meaningless placeholder object for updating rows which indicates that a
  72. * given column should keep its existing value.
  73. * @usage _general_field_
  74. */
  75. public static final Object KEEP_VALUE = "<KEEP_VALUE>";
  76. /**
  77. * Access stores numeric dates in days. Java stores them in milliseconds.
  78. */
  79. private static final double MILLISECONDS_PER_DAY =
  80. (24L * 60L * 60L * 1000L);
  81. /**
  82. * Access starts counting dates at Jan 1, 1900. Java starts counting
  83. * at Jan 1, 1970. This is the # of millis between them for conversion.
  84. */
  85. private static final long MILLIS_BETWEEN_EPOCH_AND_1900 =
  86. 25569L * (long)MILLISECONDS_PER_DAY;
  87. /**
  88. * Long value (LVAL) type that indicates that the value is stored on the
  89. * same page
  90. */
  91. private static final byte LONG_VALUE_TYPE_THIS_PAGE = (byte) 0x80;
  92. /**
  93. * Long value (LVAL) type that indicates that the value is stored on another
  94. * page
  95. */
  96. private static final byte LONG_VALUE_TYPE_OTHER_PAGE = (byte) 0x40;
  97. /**
  98. * Long value (LVAL) type that indicates that the value is stored on
  99. * multiple other pages
  100. */
  101. private static final byte LONG_VALUE_TYPE_OTHER_PAGES = (byte) 0x00;
  102. /**
  103. * Mask to apply the long length in order to get the flag bits (only the
  104. * first 2 bits are type flags).
  105. */
  106. private static final int LONG_VALUE_TYPE_MASK = 0xC0000000;
  107. /**
  108. * mask for the fixed len bit
  109. * @usage _advanced_field_
  110. */
  111. public static final byte FIXED_LEN_FLAG_MASK = (byte)0x01;
  112. /**
  113. * mask for the auto number bit
  114. * @usage _advanced_field_
  115. */
  116. public static final byte AUTO_NUMBER_FLAG_MASK = (byte)0x04;
  117. /**
  118. * mask for the auto number guid bit
  119. * @usage _advanced_field_
  120. */
  121. public static final byte AUTO_NUMBER_GUID_FLAG_MASK = (byte)0x40;
  122. /**
  123. * mask for the unknown bit (possible "can be null"?)
  124. * @usage _advanced_field_
  125. */
  126. public static final byte UNKNOWN_FLAG_MASK = (byte)0x02;
  127. // some other flags?
  128. // 0x10: replication related field (or hidden?)
  129. // 0x80: hyperlink (some memo based thing)
  130. /** the value for the "general" sort order */
  131. private static final short GENERAL_SORT_ORDER_VALUE = 1033;
  132. /**
  133. * the "general" text sort order, legacy version (access 2000-2007)
  134. * @usage _intermediate_field_
  135. */
  136. public static final SortOrder GENERAL_LEGACY_SORT_ORDER =
  137. new SortOrder(GENERAL_SORT_ORDER_VALUE, (byte)0);
  138. /**
  139. * the "general" text sort order, latest version (access 2010+)
  140. * @usage _intermediate_field_
  141. */
  142. public static final SortOrder GENERAL_SORT_ORDER =
  143. new SortOrder(GENERAL_SORT_ORDER_VALUE, (byte)1);
  144. /** pattern matching textual guid strings (allows for optional surrounding
  145. '{' and '}') */
  146. 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*");
  147. /** header used to indicate unicode text compression */
  148. private static final byte[] TEXT_COMPRESSION_HEADER =
  149. { (byte)0xFF, (byte)0XFE };
  150. /** placeholder for column which is not numeric */
  151. private static final NumericInfo DEFAULT_NUMERIC_INFO = new NumericInfo();
  152. /** placeholder for column which is not textual */
  153. private static final TextInfo DEFAULT_TEXT_INFO = new TextInfo();
  154. /** owning table */
  155. private final Table _table;
  156. /** Whether or not the column is of variable length */
  157. private boolean _variableLength;
  158. /** Whether or not the column is an autonumber column */
  159. private boolean _autoNumber;
  160. /** Data type */
  161. private DataType _type;
  162. /** Maximum column length */
  163. private short _columnLength;
  164. /** 0-based column number */
  165. private short _columnNumber;
  166. /** index of the data for this column within a list of row data */
  167. private int _columnIndex;
  168. /** display index of the data for this column */
  169. private int _displayIndex;
  170. /** Column name */
  171. private String _name;
  172. /** the offset of the fixed data in the row */
  173. private int _fixedDataOffset;
  174. /** the index of the variable length data in the var len offset table */
  175. private int _varLenTableIndex;
  176. /** information specific to numeric columns */
  177. private NumericInfo _numericInfo = DEFAULT_NUMERIC_INFO;
  178. /** information specific to text columns */
  179. private TextInfo _textInfo = DEFAULT_TEXT_INFO;
  180. /** the auto number generator for this column (if autonumber column) */
  181. private AutoNumberGenerator _autoNumberGenerator;
  182. /** additional information specific to complex columns */
  183. private ComplexColumnInfo<? extends ComplexValue> _complexInfo;
  184. /** properties for this column, if any */
  185. private PropertyMap _props;
  186. /**
  187. * @usage _general_method_
  188. */
  189. public Column() {
  190. this(null);
  191. }
  192. /**
  193. * @usage _advanced_method_
  194. */
  195. public Column(JetFormat format) {
  196. _table = null;
  197. }
  198. /**
  199. * Only used by unit tests
  200. */
  201. Column(boolean testing, Table table) {
  202. if(!testing) {
  203. throw new IllegalArgumentException();
  204. }
  205. _table = table;
  206. }
  207. /**
  208. * Read a column definition in from a buffer
  209. * @param table owning table
  210. * @param buffer Buffer containing column definition
  211. * @param offset Offset in the buffer at which the column definition starts
  212. * @usage _advanced_method_
  213. */
  214. public Column(Table table, ByteBuffer buffer, int offset, int displayIndex)
  215. throws IOException
  216. {
  217. _table = table;
  218. _displayIndex = displayIndex;
  219. if (LOG.isDebugEnabled()) {
  220. LOG.debug("Column def block:\n" + ByteUtil.toHexString(buffer, offset, 25));
  221. }
  222. byte colType = buffer.get(offset + getFormat().OFFSET_COLUMN_TYPE);
  223. _columnNumber = buffer.getShort(offset + getFormat().OFFSET_COLUMN_NUMBER);
  224. _columnLength = buffer.getShort(offset + getFormat().OFFSET_COLUMN_LENGTH);
  225. byte flags = buffer.get(offset + getFormat().OFFSET_COLUMN_FLAGS);
  226. _variableLength = ((flags & FIXED_LEN_FLAG_MASK) == 0);
  227. _autoNumber = ((flags & (AUTO_NUMBER_FLAG_MASK | AUTO_NUMBER_GUID_FLAG_MASK)) != 0);
  228. try {
  229. _type = DataType.fromByte(colType);
  230. } catch(IOException e) {
  231. LOG.warn("Unsupported column type " + colType);
  232. _type = (_variableLength ? DataType.UNSUPPORTED_VARLEN :
  233. DataType.UNSUPPORTED_FIXEDLEN);
  234. setUnknownDataType(colType);
  235. }
  236. if (_type.getHasScalePrecision()) {
  237. modifyNumericInfo();
  238. _numericInfo._precision = buffer.get(offset +
  239. getFormat().OFFSET_COLUMN_PRECISION);
  240. _numericInfo._scale = buffer.get(offset + getFormat().OFFSET_COLUMN_SCALE);
  241. } else if(_type.isTextual()) {
  242. modifyTextInfo();
  243. // co-located w/ precision/scale
  244. _textInfo._sortOrder = readSortOrder(
  245. buffer, offset + getFormat().OFFSET_COLUMN_SORT_ORDER, getFormat());
  246. int cpOffset = getFormat().OFFSET_COLUMN_CODE_PAGE;
  247. if(cpOffset >= 0) {
  248. _textInfo._codePage = buffer.getShort(offset + cpOffset);
  249. }
  250. _textInfo._compressedUnicode = ((buffer.get(offset +
  251. getFormat().OFFSET_COLUMN_COMPRESSED_UNICODE) & 1) == 1);
  252. }
  253. setAutoNumberGenerator();
  254. if(_variableLength) {
  255. _varLenTableIndex = buffer.getShort(offset + getFormat().OFFSET_COLUMN_VARIABLE_TABLE_INDEX);
  256. } else {
  257. _fixedDataOffset = buffer.getShort(offset + getFormat().OFFSET_COLUMN_FIXED_DATA_OFFSET);
  258. }
  259. // load complex info
  260. if(_type == DataType.COMPLEX_TYPE) {
  261. _complexInfo = ComplexColumnInfo.create(this, buffer, offset);
  262. }
  263. }
  264. /**
  265. * Secondary column initialization after the table is fully loaded.
  266. */
  267. void postTableLoadInit() throws IOException {
  268. if(_complexInfo != null) {
  269. _complexInfo.postTableLoadInit();
  270. }
  271. }
  272. /**
  273. * @usage _general_method_
  274. */
  275. public Table getTable() {
  276. return _table;
  277. }
  278. /**
  279. * @usage _general_method_
  280. */
  281. public Database getDatabase() {
  282. return getTable().getDatabase();
  283. }
  284. /**
  285. * @usage _advanced_method_
  286. */
  287. public JetFormat getFormat() {
  288. return getDatabase().getFormat();
  289. }
  290. /**
  291. * @usage _advanced_method_
  292. */
  293. public PageChannel getPageChannel() {
  294. return getDatabase().getPageChannel();
  295. }
  296. /**
  297. * @usage _general_method_
  298. */
  299. public String getName() {
  300. return _name;
  301. }
  302. /**
  303. * @usage _advanced_method_
  304. */
  305. public void setName(String name) {
  306. _name = name;
  307. }
  308. /**
  309. * @usage _advanced_method_
  310. */
  311. public boolean isVariableLength() {
  312. return _variableLength;
  313. }
  314. /**
  315. * @usage _advanced_method_
  316. */
  317. public void setVariableLength(boolean variableLength) {
  318. _variableLength = variableLength;
  319. }
  320. /**
  321. * @usage _general_method_
  322. */
  323. public boolean isAutoNumber() {
  324. return _autoNumber;
  325. }
  326. /**
  327. * @usage _general_method_
  328. */
  329. public void setAutoNumber(boolean autoNumber) {
  330. _autoNumber = autoNumber;
  331. setAutoNumberGenerator();
  332. }
  333. /**
  334. * @usage _advanced_method_
  335. */
  336. public short getColumnNumber() {
  337. return _columnNumber;
  338. }
  339. /**
  340. * @usage _advanced_method_
  341. */
  342. public void setColumnNumber(short newColumnNumber) {
  343. _columnNumber = newColumnNumber;
  344. }
  345. /**
  346. * @usage _advanced_method_
  347. */
  348. public int getColumnIndex() {
  349. return _columnIndex;
  350. }
  351. /**
  352. * @usage _advanced_method_
  353. */
  354. public void setColumnIndex(int newColumnIndex) {
  355. _columnIndex = newColumnIndex;
  356. }
  357. /**
  358. * @usage _advanced_method_
  359. */
  360. public int getDisplayIndex() {
  361. return _displayIndex;
  362. }
  363. /**
  364. * Also sets the length and the variable length flag, inferred from the
  365. * type. For types with scale/precision, sets the scale and precision to
  366. * default values.
  367. * @usage _general_method_
  368. */
  369. public void setType(DataType type) {
  370. _type = type;
  371. if(!type.isVariableLength()) {
  372. setLength((short)type.getFixedSize());
  373. } else if(!type.isLongValue()) {
  374. setLength((short)type.getDefaultSize());
  375. }
  376. setVariableLength(type.isVariableLength());
  377. if(type.getHasScalePrecision()) {
  378. setScale((byte)type.getDefaultScale());
  379. setPrecision((byte)type.getDefaultPrecision());
  380. }
  381. }
  382. /**
  383. * @usage _general_method_
  384. */
  385. public DataType getType() {
  386. return _type;
  387. }
  388. /**
  389. * @usage _general_method_
  390. */
  391. public int getSQLType() throws SQLException {
  392. return _type.getSQLType();
  393. }
  394. /**
  395. * @usage _general_method_
  396. */
  397. public void setSQLType(int type) throws SQLException {
  398. setSQLType(type, 0);
  399. }
  400. /**
  401. * @usage _general_method_
  402. */
  403. public void setSQLType(int type, int lengthInUnits) throws SQLException {
  404. setType(DataType.fromSQLType(type, lengthInUnits));
  405. }
  406. /**
  407. * @usage _general_method_
  408. */
  409. public boolean isCompressedUnicode() {
  410. return _textInfo._compressedUnicode;
  411. }
  412. /**
  413. * @usage _general_method_
  414. */
  415. public void setCompressedUnicode(boolean newCompessedUnicode) {
  416. modifyTextInfo();
  417. _textInfo._compressedUnicode = newCompessedUnicode;
  418. }
  419. /**
  420. * @usage _general_method_
  421. */
  422. public byte getPrecision() {
  423. return _numericInfo._precision;
  424. }
  425. /**
  426. * @usage _general_method_
  427. */
  428. public void setPrecision(byte newPrecision) {
  429. modifyNumericInfo();
  430. _numericInfo._precision = newPrecision;
  431. }
  432. /**
  433. * @usage _general_method_
  434. */
  435. public byte getScale() {
  436. return _numericInfo._scale;
  437. }
  438. /**
  439. * @usage _general_method_
  440. */
  441. public void setScale(byte newScale) {
  442. modifyNumericInfo();
  443. _numericInfo._scale = newScale;
  444. }
  445. /**
  446. * @usage _intermediate_method_
  447. */
  448. public SortOrder getTextSortOrder() {
  449. return _textInfo._sortOrder;
  450. }
  451. /**
  452. * @usage _advanced_method_
  453. */
  454. public void setTextSortOrder(SortOrder newTextSortOrder) {
  455. modifyTextInfo();
  456. _textInfo._sortOrder = newTextSortOrder;
  457. }
  458. /**
  459. * @usage _intermediate_method_
  460. */
  461. public short getTextCodePage() {
  462. return _textInfo._codePage;
  463. }
  464. /**
  465. * @usage _general_method_
  466. */
  467. public void setLength(short length) {
  468. _columnLength = length;
  469. }
  470. /**
  471. * @usage _general_method_
  472. */
  473. public short getLength() {
  474. return _columnLength;
  475. }
  476. /**
  477. * @usage _general_method_
  478. */
  479. public void setLengthInUnits(short unitLength) {
  480. setLength((short)getType().fromUnitSize(unitLength));
  481. }
  482. /**
  483. * @usage _general_method_
  484. */
  485. public short getLengthInUnits() {
  486. return (short)getType().toUnitSize(getLength());
  487. }
  488. /**
  489. * @usage _advanced_method_
  490. */
  491. public void setVarLenTableIndex(int idx) {
  492. _varLenTableIndex = idx;
  493. }
  494. /**
  495. * @usage _advanced_method_
  496. */
  497. public int getVarLenTableIndex() {
  498. return _varLenTableIndex;
  499. }
  500. /**
  501. * @usage _advanced_method_
  502. */
  503. public void setFixedDataOffset(int newOffset) {
  504. _fixedDataOffset = newOffset;
  505. }
  506. /**
  507. * @usage _advanced_method_
  508. */
  509. public int getFixedDataOffset() {
  510. return _fixedDataOffset;
  511. }
  512. protected Charset getCharset() {
  513. return getDatabase().getCharset();
  514. }
  515. protected TimeZone getTimeZone() {
  516. return getDatabase().getTimeZone();
  517. }
  518. /**
  519. * Whether or not this column is "append only" (its history is tracked by a
  520. * separate version history column).
  521. * @usage _general_method_
  522. */
  523. public boolean isAppendOnly() {
  524. return (getVersionHistoryColumn() != null);
  525. }
  526. /**
  527. * Returns the column which tracks the version history for an "append only"
  528. * column.
  529. * @usage _intermediate_method_
  530. */
  531. public Column getVersionHistoryColumn() {
  532. return _textInfo._versionHistoryCol;
  533. }
  534. /**
  535. * @usage _advanced_method_
  536. */
  537. public void setVersionHistoryColumn(Column versionHistoryCol) {
  538. modifyTextInfo();
  539. _textInfo._versionHistoryCol = versionHistoryCol;
  540. }
  541. /**
  542. * Returns extended functionality for "complex" columns.
  543. * @usage _general_method_
  544. */
  545. public ComplexColumnInfo<? extends ComplexValue> getComplexInfo() {
  546. return _complexInfo;
  547. }
  548. private void setUnknownDataType(byte type) {
  549. // slight hack, stash the original type in the _scale
  550. modifyNumericInfo();
  551. _numericInfo._scale = type;
  552. }
  553. private byte getUnknownDataType() {
  554. // slight hack, we stashed the real type in the _scale
  555. return _numericInfo._scale;
  556. }
  557. private void setAutoNumberGenerator()
  558. {
  559. if(!_autoNumber || (_type == null)) {
  560. _autoNumberGenerator = null;
  561. return;
  562. }
  563. if((_autoNumberGenerator != null) &&
  564. (_autoNumberGenerator.getType() == _type)) {
  565. // keep existing
  566. return;
  567. }
  568. switch(_type) {
  569. case LONG:
  570. _autoNumberGenerator = new LongAutoNumberGenerator();
  571. break;
  572. case GUID:
  573. _autoNumberGenerator = new GuidAutoNumberGenerator();
  574. break;
  575. case COMPLEX_TYPE:
  576. _autoNumberGenerator = new ComplexTypeAutoNumberGenerator();
  577. break;
  578. default:
  579. LOG.warn("Unknown auto number column type " + _type);
  580. _autoNumberGenerator = new UnsupportedAutoNumberGenerator(_type);
  581. }
  582. }
  583. /**
  584. * Returns the AutoNumberGenerator for this column if this is an autonumber
  585. * column, {@code null} otherwise.
  586. * @usage _advanced_method_
  587. */
  588. public AutoNumberGenerator getAutoNumberGenerator() {
  589. return _autoNumberGenerator;
  590. }
  591. /**
  592. * @return the properties for this column
  593. * @usage _general_method_
  594. */
  595. public PropertyMap getProperties() throws IOException {
  596. if(_props == null) {
  597. _props = getTable().getPropertyMaps().get(getName());
  598. }
  599. return _props;
  600. }
  601. private void modifyNumericInfo() {
  602. if(_numericInfo == DEFAULT_NUMERIC_INFO) {
  603. _numericInfo = new NumericInfo();
  604. }
  605. }
  606. private void modifyTextInfo() {
  607. if(_textInfo == DEFAULT_TEXT_INFO) {
  608. _textInfo = new TextInfo();
  609. }
  610. }
  611. /**
  612. * Checks that this column definition is valid.
  613. *
  614. * @throws IllegalArgumentException if this column definition is invalid.
  615. * @usage _advanced_method_
  616. */
  617. public void validate(JetFormat format) {
  618. if(getType() == null) {
  619. throw new IllegalArgumentException("must have type");
  620. }
  621. Database.validateIdentifierName(getName(), format.MAX_COLUMN_NAME_LENGTH,
  622. "column");
  623. if(getType().isUnsupported()) {
  624. throw new IllegalArgumentException(
  625. "Cannot create column with unsupported type " + getType());
  626. }
  627. if(!format.isSupportedDataType(getType())) {
  628. throw new IllegalArgumentException(
  629. "Database format " + format + " does not support type " + getType());
  630. }
  631. if(isVariableLength() != getType().isVariableLength()) {
  632. throw new IllegalArgumentException("invalid variable length setting");
  633. }
  634. if(!isVariableLength()) {
  635. if(getLength() != getType().getFixedSize()) {
  636. if(getLength() < getType().getFixedSize()) {
  637. throw new IllegalArgumentException("invalid fixed length size");
  638. }
  639. LOG.warn("Column length " + getLength() +
  640. " longer than expected fixed size " +
  641. getType().getFixedSize());
  642. }
  643. } else if(!getType().isLongValue()) {
  644. if(!getType().isValidSize(getLength())) {
  645. throw new IllegalArgumentException("var length out of range");
  646. }
  647. }
  648. if(getType().getHasScalePrecision()) {
  649. if(!getType().isValidScale(getScale())) {
  650. throw new IllegalArgumentException(
  651. "Scale must be from " + getType().getMinScale() + " to " +
  652. getType().getMaxScale() + " inclusive");
  653. }
  654. if(!getType().isValidPrecision(getPrecision())) {
  655. throw new IllegalArgumentException(
  656. "Precision must be from " + getType().getMinPrecision() + " to " +
  657. getType().getMaxPrecision() + " inclusive");
  658. }
  659. }
  660. if(isAutoNumber()) {
  661. if(!getType().mayBeAutoNumber()) {
  662. throw new IllegalArgumentException(
  663. "Auto number column must be long integer or guid");
  664. }
  665. }
  666. if(isCompressedUnicode()) {
  667. if(!getType().isTextual()) {
  668. throw new IllegalArgumentException(
  669. "Only textual columns allow unicode compression (text/memo)");
  670. }
  671. }
  672. }
  673. public Object setRowValue(Object[] rowArray, Object value) {
  674. rowArray[_columnIndex] = value;
  675. return value;
  676. }
  677. public Object setRowValue(Map<String,Object> rowMap, Object value) {
  678. rowMap.put(_name, value);
  679. return value;
  680. }
  681. public Object getRowValue(Object[] rowArray) {
  682. return rowArray[_columnIndex];
  683. }
  684. public Object getRowValue(Map<String,?> rowMap) {
  685. return rowMap.get(_name);
  686. }
  687. /**
  688. * Deserialize a raw byte value for this column into an Object
  689. * @param data The raw byte value
  690. * @return The deserialized Object
  691. * @usage _advanced_method_
  692. */
  693. public Object read(byte[] data) throws IOException {
  694. return read(data, PageChannel.DEFAULT_BYTE_ORDER);
  695. }
  696. /**
  697. * Deserialize a raw byte value for this column into an Object
  698. * @param data The raw byte value
  699. * @param order Byte order in which the raw value is stored
  700. * @return The deserialized Object
  701. * @usage _advanced_method_
  702. */
  703. public Object read(byte[] data, ByteOrder order) throws IOException {
  704. ByteBuffer buffer = ByteBuffer.wrap(data);
  705. buffer.order(order);
  706. if (_type == DataType.BOOLEAN) {
  707. throw new IOException("Tried to read a boolean from data instead of null mask.");
  708. } else if (_type == DataType.BYTE) {
  709. return Byte.valueOf(buffer.get());
  710. } else if (_type == DataType.INT) {
  711. return Short.valueOf(buffer.getShort());
  712. } else if (_type == DataType.LONG) {
  713. return Integer.valueOf(buffer.getInt());
  714. } else if (_type == DataType.DOUBLE) {
  715. return Double.valueOf(buffer.getDouble());
  716. } else if (_type == DataType.FLOAT) {
  717. return Float.valueOf(buffer.getFloat());
  718. } else if (_type == DataType.SHORT_DATE_TIME) {
  719. return readDateValue(buffer);
  720. } else if (_type == DataType.BINARY) {
  721. return data;
  722. } else if (_type == DataType.TEXT) {
  723. return decodeTextValue(data);
  724. } else if (_type == DataType.MONEY) {
  725. return readCurrencyValue(buffer);
  726. } else if (_type == DataType.OLE) {
  727. if (data.length > 0) {
  728. return readLongValue(data);
  729. }
  730. return null;
  731. } else if (_type == DataType.MEMO) {
  732. if (data.length > 0) {
  733. return readLongStringValue(data);
  734. }
  735. return null;
  736. } else if (_type == DataType.NUMERIC) {
  737. return readNumericValue(buffer);
  738. } else if (_type == DataType.GUID) {
  739. return readGUIDValue(buffer, order);
  740. } else if ((_type == DataType.UNKNOWN_0D) ||
  741. (_type == DataType.UNKNOWN_11)) {
  742. // treat like "binary" data
  743. return data;
  744. } else if (_type == DataType.COMPLEX_TYPE) {
  745. return new ComplexValueForeignKey(this, buffer.getInt());
  746. } else if(_type.isUnsupported()) {
  747. return rawDataWrapper(data);
  748. } else {
  749. throw new IOException("Unrecognized data type: " + _type);
  750. }
  751. }
  752. /**
  753. * @param lvalDefinition Column value that points to an LVAL record
  754. * @return The LVAL data
  755. */
  756. private byte[] readLongValue(byte[] lvalDefinition)
  757. throws IOException
  758. {
  759. ByteBuffer def = ByteBuffer.wrap(lvalDefinition)
  760. .order(PageChannel.DEFAULT_BYTE_ORDER);
  761. int lengthWithFlags = def.getInt();
  762. int length = lengthWithFlags & (~LONG_VALUE_TYPE_MASK);
  763. byte[] rtn = new byte[length];
  764. byte type = (byte)((lengthWithFlags & LONG_VALUE_TYPE_MASK) >>> 24);
  765. if(type == LONG_VALUE_TYPE_THIS_PAGE) {
  766. // inline long value
  767. def.getInt(); //Skip over lval_dp
  768. def.getInt(); //Skip over unknown
  769. def.get(rtn);
  770. } else {
  771. // long value on other page(s)
  772. if (lvalDefinition.length != getFormat().SIZE_LONG_VALUE_DEF) {
  773. throw new IOException("Expected " + getFormat().SIZE_LONG_VALUE_DEF +
  774. " bytes in long value definition, but found " +
  775. lvalDefinition.length);
  776. }
  777. int rowNum = ByteUtil.getUnsignedByte(def);
  778. int pageNum = ByteUtil.get3ByteInt(def, def.position());
  779. ByteBuffer lvalPage = getPageChannel().createPageBuffer();
  780. switch (type) {
  781. case LONG_VALUE_TYPE_OTHER_PAGE:
  782. {
  783. getPageChannel().readPage(lvalPage, pageNum);
  784. short rowStart = Table.findRowStart(lvalPage, rowNum, getFormat());
  785. short rowEnd = Table.findRowEnd(lvalPage, rowNum, getFormat());
  786. if((rowEnd - rowStart) != length) {
  787. throw new IOException("Unexpected lval row length");
  788. }
  789. lvalPage.position(rowStart);
  790. lvalPage.get(rtn);
  791. }
  792. break;
  793. case LONG_VALUE_TYPE_OTHER_PAGES:
  794. ByteBuffer rtnBuf = ByteBuffer.wrap(rtn);
  795. int remainingLen = length;
  796. while(remainingLen > 0) {
  797. lvalPage.clear();
  798. getPageChannel().readPage(lvalPage, pageNum);
  799. short rowStart = Table.findRowStart(lvalPage, rowNum, getFormat());
  800. short rowEnd = Table.findRowEnd(lvalPage, rowNum, getFormat());
  801. // read next page information
  802. lvalPage.position(rowStart);
  803. rowNum = ByteUtil.getUnsignedByte(lvalPage);
  804. pageNum = ByteUtil.get3ByteInt(lvalPage);
  805. // update rowEnd and remainingLen based on chunkLength
  806. int chunkLength = (rowEnd - rowStart) - 4;
  807. if(chunkLength > remainingLen) {
  808. rowEnd = (short)(rowEnd - (chunkLength - remainingLen));
  809. chunkLength = remainingLen;
  810. }
  811. remainingLen -= chunkLength;
  812. lvalPage.limit(rowEnd);
  813. rtnBuf.put(lvalPage);
  814. }
  815. break;
  816. default:
  817. throw new IOException("Unrecognized long value type: " + type);
  818. }
  819. }
  820. return rtn;
  821. }
  822. /**
  823. * @param lvalDefinition Column value that points to an LVAL record
  824. * @return The LVAL data
  825. */
  826. private String readLongStringValue(byte[] lvalDefinition)
  827. throws IOException
  828. {
  829. byte[] binData = readLongValue(lvalDefinition);
  830. if(binData == null) {
  831. return null;
  832. }
  833. return decodeTextValue(binData);
  834. }
  835. /**
  836. * Decodes "Currency" values.
  837. *
  838. * @param buffer Column value that points to currency data
  839. * @return BigDecimal representing the monetary value
  840. * @throws IOException if the value cannot be parsed
  841. */
  842. private static BigDecimal readCurrencyValue(ByteBuffer buffer)
  843. throws IOException
  844. {
  845. if(buffer.remaining() != 8) {
  846. throw new IOException("Invalid money value.");
  847. }
  848. return new BigDecimal(BigInteger.valueOf(buffer.getLong(0)), 4);
  849. }
  850. /**
  851. * Writes "Currency" values.
  852. */
  853. private static void writeCurrencyValue(ByteBuffer buffer, Object value)
  854. throws IOException
  855. {
  856. Object inValue = value;
  857. try {
  858. BigDecimal decVal = toBigDecimal(value);
  859. inValue = decVal;
  860. // adjust scale (will cause the an ArithmeticException if number has too
  861. // many decimal places)
  862. decVal = decVal.setScale(4);
  863. // now, remove scale and convert to long (this will throw if the value is
  864. // too big)
  865. buffer.putLong(decVal.movePointRight(4).longValueExact());
  866. } catch(ArithmeticException e) {
  867. throw (IOException)
  868. new IOException("Currency value '" + inValue + "' out of range")
  869. .initCause(e);
  870. }
  871. }
  872. /**
  873. * Decodes a NUMERIC field.
  874. */
  875. private BigDecimal readNumericValue(ByteBuffer buffer)
  876. {
  877. boolean negate = (buffer.get() != 0);
  878. byte[] tmpArr = ByteUtil.getBytes(buffer, 16);
  879. if(buffer.order() != ByteOrder.BIG_ENDIAN) {
  880. fixNumericByteOrder(tmpArr);
  881. }
  882. BigInteger intVal = new BigInteger(tmpArr);
  883. if(negate) {
  884. intVal = intVal.negate();
  885. }
  886. return new BigDecimal(intVal, getScale());
  887. }
  888. /**
  889. * Writes a numeric value.
  890. */
  891. private void writeNumericValue(ByteBuffer buffer, Object value)
  892. throws IOException
  893. {
  894. Object inValue = value;
  895. try {
  896. BigDecimal decVal = toBigDecimal(value);
  897. inValue = decVal;
  898. boolean negative = (decVal.compareTo(BigDecimal.ZERO) < 0);
  899. if(negative) {
  900. decVal = decVal.negate();
  901. }
  902. // write sign byte
  903. buffer.put(negative ? (byte)0x80 : (byte)0);
  904. // adjust scale according to this column type (will cause the an
  905. // ArithmeticException if number has too many decimal places)
  906. decVal = decVal.setScale(getScale());
  907. // check precision
  908. if(decVal.precision() > getPrecision()) {
  909. throw new IOException(
  910. "Numeric value is too big for specified precision "
  911. + getPrecision() + ": " + decVal);
  912. }
  913. // convert to unscaled BigInteger, big-endian bytes
  914. byte[] intValBytes = decVal.unscaledValue().toByteArray();
  915. int maxByteLen = getType().getFixedSize() - 1;
  916. if(intValBytes.length > maxByteLen) {
  917. throw new IOException("Too many bytes for valid BigInteger?");
  918. }
  919. if(intValBytes.length < maxByteLen) {
  920. byte[] tmpBytes = new byte[maxByteLen];
  921. System.arraycopy(intValBytes, 0, tmpBytes,
  922. (maxByteLen - intValBytes.length),
  923. intValBytes.length);
  924. intValBytes = tmpBytes;
  925. }
  926. if(buffer.order() != ByteOrder.BIG_ENDIAN) {
  927. fixNumericByteOrder(intValBytes);
  928. }
  929. buffer.put(intValBytes);
  930. } catch(ArithmeticException e) {
  931. throw (IOException)
  932. new IOException("Numeric value '" + inValue + "' out of range")
  933. .initCause(e);
  934. }
  935. }
  936. /**
  937. * Decodes a date value.
  938. */
  939. private Date readDateValue(ByteBuffer buffer)
  940. {
  941. // seems access stores dates in the local timezone. guess you just hope
  942. // you read it in the same timezone in which it was written!
  943. long dateBits = buffer.getLong();
  944. long time = fromDateDouble(Double.longBitsToDouble(dateBits));
  945. return new DateExt(time, dateBits);
  946. }
  947. /**
  948. * Returns a java long time value converted from an access date double.
  949. */
  950. long fromDateDouble(double value)
  951. {
  952. long time = Math.round(value * MILLISECONDS_PER_DAY);
  953. time -= MILLIS_BETWEEN_EPOCH_AND_1900;
  954. time -= getFromLocalTimeZoneOffset(time);
  955. return time;
  956. }
  957. /**
  958. * Writes a date value.
  959. */
  960. private void writeDateValue(ByteBuffer buffer, Object value)
  961. {
  962. if(value == null) {
  963. buffer.putDouble(0d);
  964. } else if(value instanceof DateExt) {
  965. // this is a Date value previously read from readDateValue(). use the
  966. // original bits to store the value so we don't lose any precision
  967. buffer.putLong(((DateExt)value).getDateBits());
  968. } else {
  969. buffer.putDouble(toDateDouble(value));
  970. }
  971. }
  972. /**
  973. * Returns an access date double converted from a java Date/Calendar/Number
  974. * time value.
  975. */
  976. double toDateDouble(Object value)
  977. {
  978. // seems access stores dates in the local timezone. guess you just
  979. // hope you read it in the same timezone in which it was written!
  980. long time = ((value instanceof Date) ?
  981. ((Date)value).getTime() :
  982. ((value instanceof Calendar) ?
  983. ((Calendar)value).getTimeInMillis() :
  984. ((Number)value).longValue()));
  985. time += getToLocalTimeZoneOffset(time);
  986. time += MILLIS_BETWEEN_EPOCH_AND_1900;
  987. return time / MILLISECONDS_PER_DAY;
  988. }
  989. /**
  990. * Gets the timezone offset from UTC to local time for the given time
  991. * (including DST).
  992. */
  993. private long getToLocalTimeZoneOffset(long time)
  994. {
  995. Calendar c = Calendar.getInstance(getTimeZone());
  996. c.setTimeInMillis(time);
  997. return ((long)c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET));
  998. }
  999. /**
  1000. * Gets the timezone offset from local time to UTC for the given time
  1001. * (including DST).
  1002. */
  1003. private long getFromLocalTimeZoneOffset(long time)
  1004. {
  1005. // getting from local time back to UTC is a little wonky (and not
  1006. // guaranteed to get you back to where you started)
  1007. Calendar c = Calendar.getInstance(getTimeZone());
  1008. c.setTimeInMillis(time);
  1009. // apply the zone offset first to get us closer to the original time
  1010. c.setTimeInMillis(time - c.get(Calendar.ZONE_OFFSET));
  1011. return ((long)c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET));
  1012. }
  1013. /**
  1014. * Decodes a GUID value.
  1015. */
  1016. private static String readGUIDValue(ByteBuffer buffer, ByteOrder order)
  1017. {
  1018. if(order != ByteOrder.BIG_ENDIAN) {
  1019. byte[] tmpArr = ByteUtil.getBytes(buffer, 16);
  1020. // the first 3 guid components are integer components which need to
  1021. // respect endianness, so swap 4-byte int, 2-byte int, 2-byte int
  1022. ByteUtil.swap4Bytes(tmpArr, 0);
  1023. ByteUtil.swap2Bytes(tmpArr, 4);
  1024. ByteUtil.swap2Bytes(tmpArr, 6);
  1025. buffer = ByteBuffer.wrap(tmpArr);
  1026. }
  1027. StringBuilder sb = new StringBuilder(22);
  1028. sb.append("{");
  1029. sb.append(ByteUtil.toHexString(buffer, 0, 4,
  1030. false));
  1031. sb.append("-");
  1032. sb.append(ByteUtil.toHexString(buffer, 4, 2,
  1033. false));
  1034. sb.append("-");
  1035. sb.append(ByteUtil.toHexString(buffer, 6, 2,
  1036. false));
  1037. sb.append("-");
  1038. sb.append(ByteUtil.toHexString(buffer, 8, 2,
  1039. false));
  1040. sb.append("-");
  1041. sb.append(ByteUtil.toHexString(buffer, 10, 6,
  1042. false));
  1043. sb.append("}");
  1044. return (sb.toString());
  1045. }
  1046. /**
  1047. * Writes a GUID value.
  1048. */
  1049. private static void writeGUIDValue(ByteBuffer buffer, Object value,
  1050. ByteOrder order)
  1051. throws IOException
  1052. {
  1053. Matcher m = GUID_PATTERN.matcher(toCharSequence(value));
  1054. if(m.matches()) {
  1055. ByteBuffer origBuffer = null;
  1056. byte[] tmpBuf = null;
  1057. if(order != ByteOrder.BIG_ENDIAN) {
  1058. // write to a temp buf so we can do some swapping below
  1059. origBuffer = buffer;
  1060. tmpBuf = new byte[16];
  1061. buffer = ByteBuffer.wrap(tmpBuf);
  1062. }
  1063. ByteUtil.writeHexString(buffer, m.group(1));
  1064. ByteUtil.writeHexString(buffer, m.group(2));
  1065. ByteUtil.writeHexString(buffer, m.group(3));
  1066. ByteUtil.writeHexString(buffer, m.group(4));
  1067. ByteUtil.writeHexString(buffer, m.group(5));
  1068. if(tmpBuf != null) {
  1069. // the first 3 guid components are integer components which need to
  1070. // respect endianness, so swap 4-byte int, 2-byte int, 2-byte int
  1071. ByteUtil.swap4Bytes(tmpBuf, 0);
  1072. ByteUtil.swap2Bytes(tmpBuf, 4);
  1073. ByteUtil.swap2Bytes(tmpBuf, 6);
  1074. origBuffer.put(tmpBuf);
  1075. }
  1076. } else {
  1077. throw new IOException("Invalid GUID: " + value);
  1078. }
  1079. }
  1080. /**
  1081. * Write an LVAL column into a ByteBuffer inline if it fits, otherwise in
  1082. * other data page(s).
  1083. * @param value Value of the LVAL column
  1084. * @return A buffer containing the LVAL definition and (possibly) the column
  1085. * value (unless written to other pages)
  1086. * @usage _advanced_method_
  1087. */
  1088. public ByteBuffer writeLongValue(byte[] value,
  1089. int remainingRowLength) throws IOException
  1090. {
  1091. if(value.length > getType().getMaxSize()) {
  1092. throw new IOException("value too big for column, max " +
  1093. getType().getMaxSize() + ", got " +
  1094. value.length);
  1095. }
  1096. // determine which type to write
  1097. byte type = 0;
  1098. int lvalDefLen = getFormat().SIZE_LONG_VALUE_DEF;
  1099. if(((getFormat().SIZE_LONG_VALUE_DEF + value.length) <= remainingRowLength)
  1100. && (value.length <= getFormat().MAX_INLINE_LONG_VALUE_SIZE)) {
  1101. type = LONG_VALUE_TYPE_THIS_PAGE;
  1102. lvalDefLen += value.length;
  1103. } else if(value.length <= getFormat().MAX_LONG_VALUE_ROW_SIZE) {
  1104. type = LONG_VALUE_TYPE_OTHER_PAGE;
  1105. } else {
  1106. type = LONG_VALUE_TYPE_OTHER_PAGES;
  1107. }
  1108. ByteBuffer def = getPageChannel().createBuffer(lvalDefLen);
  1109. // take length and apply type to first byte
  1110. int lengthWithFlags = value.length | (type << 24);
  1111. def.putInt(lengthWithFlags);
  1112. if(type == LONG_VALUE_TYPE_THIS_PAGE) {
  1113. // write long value inline
  1114. def.putInt(0);
  1115. def.putInt(0); //Unknown
  1116. def.put(value);
  1117. } else {
  1118. TempPageHolder lvalBufferH = getTable().getLongValueBuffer();
  1119. ByteBuffer lvalPage = null;
  1120. int firstLvalPageNum = PageChannel.INVALID_PAGE_NUMBER;
  1121. byte firstLvalRow = 0;
  1122. // write other page(s)
  1123. switch(type) {
  1124. case LONG_VALUE_TYPE_OTHER_PAGE:
  1125. lvalPage = getLongValuePage(value.length, lvalBufferH);
  1126. firstLvalPageNum = lvalBufferH.getPageNumber();
  1127. firstLvalRow = (byte)Table.addDataPageRow(lvalPage, value.length,
  1128. getFormat(), 0);
  1129. lvalPage.put(value);
  1130. getPageChannel().writePage(lvalPage, firstLvalPageNum);
  1131. break;
  1132. case LONG_VALUE_TYPE_OTHER_PAGES:
  1133. ByteBuffer buffer = ByteBuffer.wrap(value);
  1134. int remainingLen = buffer.remaining();
  1135. buffer.limit(0);
  1136. lvalPage = getLongValuePage(getFormat().MAX_LONG_VALUE_ROW_SIZE,
  1137. lvalBufferH);
  1138. firstLvalPageNum = lvalBufferH.getPageNumber();
  1139. int lvalPageNum = firstLvalPageNum;
  1140. ByteBuffer nextLvalPage = null;
  1141. int nextLvalPageNum = 0;
  1142. while(remainingLen > 0) {
  1143. lvalPage.clear();
  1144. // figure out how much we will put in this page (we need 4 bytes for
  1145. // the next page pointer)
  1146. int chunkLength = Math.min(getFormat().MAX_LONG_VALUE_ROW_SIZE - 4,
  1147. remainingLen);
  1148. // figure out if we will need another page, and if so, allocate it
  1149. if(chunkLength < remainingLen) {
  1150. // force a new page to be allocated
  1151. lvalBufferH.clear();
  1152. nextLvalPage = getLongValuePage(
  1153. getFormat().MAX_LONG_VALUE_ROW_SIZE, lvalBufferH);
  1154. nextLvalPageNum = lvalBufferH.getPageNumber();
  1155. } else {
  1156. nextLvalPage = null;
  1157. nextLvalPageNum = 0;
  1158. }
  1159. // add row to this page
  1160. byte lvalRow = (byte)Table.addDataPageRow(lvalPage, chunkLength + 4,
  1161. getFormat(), 0);
  1162. // write next page info (we'll always be writing into row 0 for
  1163. // newly created pages)
  1164. lvalPage.put((byte)0); // row number
  1165. ByteUtil.put3ByteInt(lvalPage, nextLvalPageNum); // page number
  1166. // write this page's chunk of data
  1167. buffer.limit(buffer.limit() + chunkLength);
  1168. lvalPage.put(buffer);
  1169. remainingLen -= chunkLength;
  1170. // write new page to database
  1171. getPageChannel().writePage(lvalPage, lvalPageNum);
  1172. if(lvalPageNum == firstLvalPageNum) {
  1173. // save initial row info
  1174. firstLvalRow = lvalRow;
  1175. } else {
  1176. // check assertion that we wrote to row 0 for all subsequent pages
  1177. if(lvalRow != (byte)0) {
  1178. throw new IllegalStateException("Expected row 0, but was " +
  1179. lvalRow);
  1180. }
  1181. }
  1182. // move to next page
  1183. lvalPage = nextLvalPage;
  1184. lvalPageNum = nextLvalPageNum;
  1185. }
  1186. break;
  1187. default:
  1188. throw new IOException("Unrecognized long value type: " + type);
  1189. }
  1190. // update def
  1191. def.put(firstLvalRow);
  1192. ByteUtil.put3ByteInt(def, firstLvalPageNum);
  1193. def.putInt(0); //Unknown
  1194. }
  1195. def.flip();
  1196. return def;
  1197. }
  1198. /**
  1199. * Writes the header info for a long value page.
  1200. */
  1201. private void writeLongValueHeader(ByteBuffer lvalPage)
  1202. {
  1203. lvalPage.put(PageTypes.DATA); //Page type
  1204. lvalPage.put((byte) 1); //Unknown
  1205. lvalPage.putShort((short)getFormat().DATA_PAGE_INITIAL_FREE_SPACE); //Free space
  1206. lvalPage.put((byte) 'L');
  1207. lvalPage.put((byte) 'V');
  1208. lvalPage.put((byte) 'A');
  1209. lvalPage.put((byte) 'L');
  1210. lvalPage.putInt(0); //unknown
  1211. lvalPage.putShort((short)0); // num rows in page
  1212. }
  1213. /**
  1214. * Returns a long value data page with space for data of the given length.
  1215. */
  1216. private ByteBuffer getLongValuePage(int dataLength,
  1217. TempPageHolder lvalBufferH)
  1218. throws IOException
  1219. {
  1220. ByteBuffer lvalPage = null;
  1221. if(lvalBufferH.getPageNumber() != PageChannel.INVALID_PAGE_NUMBER) {
  1222. lvalPage = lvalBufferH.getPage(getPageChannel());
  1223. if(Table.rowFitsOnDataPage(dataLength, lvalPage, getFormat())) {
  1224. // the current page has space
  1225. return lvalPage;
  1226. }
  1227. }
  1228. // need new page
  1229. lvalPage = lvalBufferH.setNewPage(getPageChannel());
  1230. writeLongValueHeader(lvalPage);
  1231. return lvalPage;
  1232. }
  1233. /**
  1234. * Serialize an Object into a raw byte value for this column in little
  1235. * endian order
  1236. * @param obj Object to serialize
  1237. * @return A buffer containing the bytes
  1238. * @usage _advanced_method_
  1239. */
  1240. public ByteBuffer write(Object obj, int remainingRowLength)
  1241. throws IOException
  1242. {
  1243. return write(obj, remainingRowLength, PageChannel.DEFAULT_BYTE_ORDER);
  1244. }
  1245. /**
  1246. * Serialize an Object into a raw byte value for this column
  1247. * @param obj Object to serialize
  1248. * @param order Order in which to serialize
  1249. * @return A buffer containing the bytes
  1250. * @usage _advanced_method_
  1251. */
  1252. public ByteBuffer write(Object obj, int remainingRowLength, ByteOrder order)
  1253. throws IOException
  1254. {
  1255. if(isRawData(obj)) {
  1256. // just slap it right in (not for the faint of heart!)
  1257. return ByteBuffer.wrap(((RawData)obj).getBytes());
  1258. }
  1259. if(!isVariableLength() || !getType().isVariableLength()) {
  1260. return writeFixedLengthField(obj, order);
  1261. }
  1262. // var length column
  1263. if(!getType().isLongValue()) {
  1264. // this is an "inline" var length field
  1265. switch(getType()) {
  1266. case NUMERIC:
  1267. // don't ask me why numerics are "var length" columns...
  1268. ByteBuffer buffer = getPageChannel().createBuffer(
  1269. getType().getFixedSize(), order);
  1270. writeNumericValue(buffer, obj);
  1271. buffer.flip();
  1272. return buffer;
  1273. case TEXT:
  1274. byte[] encodedData = encodeTextValue(
  1275. obj, 0, getLengthInUnits(), false).array();
  1276. obj = encodedData;
  1277. break;
  1278. case BINARY:
  1279. case UNKNOWN_0D:
  1280. case UNSUPPORTED_VARLEN:
  1281. // should already be "encoded"
  1282. break;
  1283. default:
  1284. throw new RuntimeException("unexpected inline var length type: " +
  1285. getType());
  1286. }
  1287. ByteBuffer buffer = ByteBuffer.wrap(toByteArray(obj));
  1288. buffer.order(order);
  1289. return buffer;
  1290. }
  1291. // var length, long value column
  1292. switch(getType()) {
  1293. case OLE:
  1294. // should already be "encoded"
  1295. break;
  1296. case MEMO:
  1297. int maxMemoChars = DataType.MEMO.toUnitSize(DataType.MEMO.getMaxSize());
  1298. obj = encodeTextValue(obj, 0, maxMemoChars, false).array();
  1299. break;
  1300. default:
  1301. throw new RuntimeException("unexpected var length, long value type: " +
  1302. getType());
  1303. }
  1304. // create long value buffer
  1305. return writeLongValue(toByteArray(obj), remainingRowLength);
  1306. }
  1307. /**
  1308. * Serialize an Object into a raw byte value for this column
  1309. * @param obj Object to serialize
  1310. * @param order Order in which to serialize
  1311. * @return A buffer containing the bytes
  1312. * @usage _advanced_method_
  1313. */
  1314. public ByteBuffer writeFixedLengthField(Object obj, ByteOrder order)
  1315. throws IOException
  1316. {
  1317. int size = getType().getFixedSize(_columnLength);
  1318. // create buffer for data
  1319. ByteBuffer buffer = getPageChannel().createBuffer(size, order);
  1320. // since booleans are not written by this method, it's safe to convert any
  1321. // incoming boolean into an integer.
  1322. obj = booleanToInteger(obj);
  1323. switch(getType()) {
  1324. case BOOLEAN:
  1325. //Do nothing
  1326. break;
  1327. case BYTE:
  1328. buffer.put(toNumber(obj).byteValue());
  1329. break;
  1330. case INT:
  1331. buffer.putShort(toNumber(obj).shortValue());
  1332. break;
  1333. case LONG:
  1334. buffer.putInt(toNumber(obj).intValue());
  1335. break;
  1336. case MONEY:
  1337. writeCurrencyValue(buffer, obj);
  1338. break;
  1339. case FLOAT:
  1340. buffer.putFloat(toNumber(obj).floatValue());
  1341. break;
  1342. case DOUBLE:
  1343. buffer.putDouble(toNumber(obj).doubleValue());
  1344. break;
  1345. case SHORT_DATE_TIME:
  1346. writeDateValue(buffer, obj);
  1347. break;
  1348. case TEXT:
  1349. // apparently text numeric values are also occasionally written as fixed
  1350. // length...
  1351. int numChars = getLengthInUnits();
  1352. // force uncompressed encoding for fixed length text
  1353. buffer.put(encodeTextValue(obj, numChars, numChars, true));
  1354. break;
  1355. case GUID:
  1356. writeGUIDValue(buffer, obj, order);
  1357. break;
  1358. case NUMERIC:
  1359. // yes, that's right, occasionally numeric values are written as fixed
  1360. // length...
  1361. writeNumericValue(buffer, obj);
  1362. break;
  1363. case BINARY:
  1364. case UNKNOWN_0D:
  1365. case UNKNOWN_11:
  1366. case COMPLEX_TYPE:
  1367. buffer.putInt(toNumber(obj).intValue());
  1368. break;
  1369. case UNSUPPORTED_FIXEDLEN:
  1370. byte[] bytes = toByteArray(obj);
  1371. if(bytes.length != getLength()) {
  1372. throw new IOException("Invalid fixed size binary data, size "
  1373. + getLength() + ", got " + bytes.length);
  1374. }
  1375. buffer.put(bytes);
  1376. break;
  1377. default:
  1378. throw new IOException("Unsupported data type: " + getType());
  1379. }
  1380. buffer.flip();
  1381. return buffer;
  1382. }
  1383. /**
  1384. * Decodes a compressed or uncompressed text value.
  1385. */
  1386. private String decodeTextValue(byte[] data)
  1387. throws IOException
  1388. {
  1389. try {
  1390. // see if data is compressed. the 0xFF, 0xFE sequence indicates that
  1391. // compression is used (sort of, see algorithm below)
  1392. boolean isCompressed = ((data.length > 1) &&
  1393. (data[0] == TEXT_COMPRESSION_HEADER[0]) &&
  1394. (data[1] == TEXT_COMPRESSION_HEADER[1]));
  1395. if(isCompressed) {
  1396. Expand expander = new Expand();
  1397. // this is a whacky compression combo that switches back and forth
  1398. // between compressed/uncompressed using a 0x00 byte (starting in
  1399. // compressed mode)
  1400. StringBuilder textBuf = new StringBuilder(data.length);
  1401. // start after two bytes indicating compression use
  1402. int dataStart = TEXT_COMPRESSION_HEADER.length;
  1403. int dataEnd = dataStart;
  1404. boolean inCompressedMode = true;
  1405. while(dataEnd < data.length) {
  1406. if(data[dataEnd] == (byte)0x00) {
  1407. // handle current segment
  1408. decodeTextSegment(data, dataStart, dataEnd, inCompressedMode,
  1409. expander, textBuf);
  1410. inCompressedMode = !inCompressedMode;
  1411. ++dataEnd;
  1412. dataStart = dataEnd;
  1413. } else {
  1414. ++dataEnd;
  1415. }
  1416. }
  1417. // handle last segment
  1418. decodeTextSegment(data, dataStart, dataEnd, inCompressedMode,
  1419. expander, textBuf);
  1420. return textBuf.toString();
  1421. }
  1422. return decodeUncompressedText(data, getCharset());
  1423. } catch (IllegalInputException e) {
  1424. throw (IOException)
  1425. new IOException("Can't expand text column").initCause(e);
  1426. } catch (EndOfInputException e) {
  1427. throw (IOException)
  1428. new IOException("Can't expand text column").initCause(e);
  1429. }
  1430. }
  1431. /**
  1432. * Decodes a segnment of a text value into the given buffer according to the
  1433. * given status of the segment (compressed/uncompressed).
  1434. */
  1435. private void decodeTextSegment(byte[] data, int dataStart, int dataEnd,
  1436. boolean inCompressedMode, Expand expander,
  1437. StringBuilder textBuf)
  1438. throws IllegalInputException, EndOfInputException
  1439. {
  1440. if(dataEnd <= dataStart) {
  1441. // no data
  1442. return;
  1443. }
  1444. int dataLength = dataEnd - dataStart;
  1445. if(inCompressedMode) {
  1446. // handle compressed data
  1447. byte[] tmpData = ByteUtil.copyOf(data, dataStart, dataLength);
  1448. expander.reset();
  1449. textBuf.append(expander.expand(tmpData));
  1450. } else {
  1451. // handle uncompressed data
  1452. textBuf.append(decodeUncompressedText(data, dataStart, dataLength,
  1453. getCharset()));
  1454. }
  1455. }
  1456. /**
  1457. * @param textBytes bytes of text to decode
  1458. * @return the decoded string
  1459. */
  1460. private static CharBuffer decodeUncompressedText(
  1461. byte[] textBytes, int startPos, int length, Charset charset)
  1462. {
  1463. return charset.decode(ByteBuffer.wrap(textBytes, startPos, length));
  1464. }
  1465. /**
  1466. * Encodes a text value, possibly compressing.
  1467. */
  1468. private ByteBuffer encodeTextValue(Object obj, int minChars, int maxChars,
  1469. boolean forceUncompressed)
  1470. throws IOException
  1471. {
  1472. CharSequence text = toCharSequence(obj);
  1473. if((text.length() > maxChars) || (text.length() < minChars)) {
  1474. throw new IOException("Text is wrong length for " + getType() +
  1475. " column, max " + maxChars
  1476. + ", min " + minChars + ", got " + text.length());
  1477. }
  1478. // may only compress if column type allows it
  1479. if(!forceUncompressed && isCompressedUnicode()) {
  1480. // for now, only do very simple compression (only compress text which is
  1481. // all ascii text)
  1482. if(isAsciiCompressible(text)) {
  1483. byte[] encodedChars = new byte[TEXT_COMPRESSION_HEADER.length +
  1484. text.length()];
  1485. encodedChars[0] = TEXT_COMPRESSION_HEADER[0];
  1486. encodedChars[1] = TEXT_COMPRESSION_HEADER[1];
  1487. for(int i = 0; i < text.length(); ++i) {
  1488. encodedChars[i + TEXT_COMPRESSION_HEADER.length] =
  1489. (byte)text.charAt(i);
  1490. }
  1491. return ByteBuffer.wrap(encodedChars);
  1492. }
  1493. }
  1494. return encodeUncompressedText(text, getCharset());
  1495. }
  1496. /**
  1497. * Returns {@code true} if the given text can be compressed using simple
  1498. * ASCII encoding, {@code false} otherwise.
  1499. */
  1500. private static boolean isAsciiCompressible(CharSequence text) {
  1501. // only attempt to compress > 2 chars (compressing less than 3 chars would
  1502. // not result in a space savings due to the 2 byte compression header)
  1503. if(text.length() <= TEXT_COMPRESSION_HEADER.length) {
  1504. return false;
  1505. }
  1506. // now, see if it is all printable ASCII
  1507. for(int i = 0; i < text.length(); ++i) {
  1508. char c = text.charAt(i);
  1509. if(!Compress.isAsciiCrLfOrTab(c)) {
  1510. return false;
  1511. }
  1512. }
  1513. return true;
  1514. }
  1515. /**
  1516. * Constructs a byte containing the flags for this column.
  1517. */
  1518. private byte getColumnBitFlags() {
  1519. byte flags = Column.UNKNOWN_FLAG_MASK;
  1520. if(!isVariableLength()) {
  1521. flags |= Column.FIXED_LEN_FLAG_MASK;
  1522. }
  1523. if(isAutoNumber()) {
  1524. flags |= getAutoNumberGenerator().getColumnFlags();
  1525. }
  1526. return flags;
  1527. }
  1528. @Override
  1529. public String toString() {
  1530. StringBuilder rtn = new StringBuilder();
  1531. rtn.append("\tName: (" + _table.getName() + ") " + _name);
  1532. byte typeValue = _type.getValue();
  1533. if(_type.isUnsupported()) {
  1534. typeValue = getUnknownDataType();
  1535. }
  1536. rtn.append("\n\tType: 0x" + Integer.toHexString(typeValue) +
  1537. " (" + _type + ")");
  1538. rtn.append("\n\tNumber: " + _columnNumber);
  1539. rtn.append("\n\tLength: " + _columnLength);
  1540. rtn.append("\n\tVariable length: " + _variableLength);
  1541. if(_type.isTextual()) {
  1542. rtn.append("\n\tCompressed Unicode: " + _textInfo._compressedUnicode);
  1543. rtn.append("\n\tText Sort order: " + _textInfo._sortOrder);
  1544. if(_textInfo._codePage > 0) {
  1545. rtn.append("\n\tText Code Page: " + _textInfo._codePage);
  1546. }
  1547. if(isAppendOnly()) {
  1548. rtn.append("\n\tAppend only: " + isAppendOnly());
  1549. }
  1550. }
  1551. if(_autoNumber) {
  1552. rtn.append("\n\tLast AutoNumber: " + _autoNumberGenerator.getLast());
  1553. }
  1554. if(_complexInfo != null) {
  1555. rtn.append("\n\tComplexInfo: " + _complexInfo);
  1556. }
  1557. rtn.append("\n\n");
  1558. return rtn.toString();
  1559. }
  1560. /**
  1561. * @param textBytes bytes of text to decode
  1562. * @param charset relevant charset
  1563. * @return the decoded string
  1564. * @usage _advanced_method_
  1565. */
  1566. public static String decodeUncompressedText(byte[] textBytes,
  1567. Charset charset)
  1568. {
  1569. return decodeUncompressedText(textBytes, 0, textBytes.length, charset)
  1570. .toString();
  1571. }
  1572. /**
  1573. * @param text Text to encode
  1574. * @param charset database charset
  1575. * @return A buffer with the text encoded
  1576. * @usage _advanced_method_
  1577. */
  1578. public static ByteBuffer encodeUncompressedText(CharSequence text,
  1579. Charset charset)
  1580. {
  1581. CharBuffer cb = ((text instanceof CharBuffer) ?
  1582. (CharBuffer)text : CharBuffer.wrap(text));
  1583. return charset.encode(cb);
  1584. }
  1585. /**
  1586. * Orders Columns by column number.
  1587. * @usage _general_method_
  1588. */
  1589. public int compareTo(Column other) {
  1590. if (_columnNumber > other.getColumnNumber()) {
  1591. return 1;
  1592. } else if (_columnNumber < other.getColumnNumber()) {
  1593. return -1;
  1594. } else {
  1595. return 0;
  1596. }
  1597. }
  1598. /**
  1599. * @param columns A list of columns in a table definition
  1600. * @return The number of variable length columns found in the list
  1601. * @usage _advanced_method_
  1602. */
  1603. public static short countVariableLength(List<Column> columns) {
  1604. short rtn = 0;
  1605. for (Column col : columns) {
  1606. if (col.isVariableLength()) {
  1607. rtn++;
  1608. }
  1609. }
  1610. return rtn;
  1611. }
  1612. /**
  1613. * @param columns A list of columns in a table definition
  1614. * @return The number of variable length columns which are not long values
  1615. * found in the list
  1616. * @usage _advanced_method_
  1617. */
  1618. public static short countNonLongVariableLength(List<Column> columns) {
  1619. short rtn = 0;
  1620. for (Column col : columns) {
  1621. if (col.isVariableLength() && !col.getType().isLongValue()) {
  1622. rtn++;
  1623. }
  1624. }
  1625. return rtn;
  1626. }
  1627. /**
  1628. * @return an appropriate BigDecimal representation of the given object.
  1629. * <code>null</code> is returned as 0 and Numbers are converted
  1630. * using their double representation.
  1631. */
  1632. private static BigDecimal toBigDecimal(Object value)
  1633. {
  1634. if(value == null) {
  1635. return BigDecimal.ZERO;
  1636. } else if(value instanceof BigDecimal) {
  1637. return (BigDecimal)value;
  1638. } else if(value instanceof BigInteger) {
  1639. return new BigDecimal((BigInteger)value);
  1640. } else if(value instanceof Number) {
  1641. return new BigDecimal(((Number)value).doubleValue());
  1642. }
  1643. return new BigDecimal(value.toString());
  1644. }
  1645. /**
  1646. * @return an appropriate Number representation of the given object.
  1647. * <code>null</code> is returned as 0 and Strings are parsed as
  1648. * Doubles.
  1649. */
  1650. private static Number toNumber(Object value)
  1651. {
  1652. if(value == null) {
  1653. return BigDecimal.ZERO;
  1654. } if(value instanceof Number) {
  1655. return (Number)value;
  1656. }
  1657. return Double.valueOf(value.toString());
  1658. }
  1659. /**
  1660. * @return an appropriate CharSequence representation of the given object.
  1661. * @usage _advanced_method_
  1662. */
  1663. public static CharSequence toCharSequence(Object value)
  1664. throws IOException
  1665. {
  1666. if(value == null) {
  1667. return null;
  1668. } else if(value instanceof CharSequence) {
  1669. return (CharSequence)value;
  1670. } else if(value instanceof Clob) {
  1671. try {
  1672. Clob c = (Clob)value;
  1673. // note, start pos is 1-based
  1674. return c.getSubString(1L, (int)c.length());
  1675. } catch(SQLException e) {
  1676. throw (IOException)(new IOException(e.getMessage())).initCause(e);
  1677. }
  1678. } else if(value instanceof Reader) {
  1679. char[] buf = new char[8 * 1024];
  1680. StringBuilder sout = new StringBuilder();
  1681. Reader in = (Reader)value;
  1682. int read = 0;
  1683. while((read = in.read(buf)) != -1) {
  1684. sout.append(buf, 0, read);
  1685. }
  1686. return sout;
  1687. }
  1688. return value.toString();
  1689. }
  1690. /**
  1691. * @return an appropriate byte[] representation of the given object.
  1692. * @usage _advanced_method_
  1693. */
  1694. public static byte[] toByteArray(Object value)
  1695. throws IOException
  1696. {
  1697. if(value == null) {
  1698. return null;
  1699. } else if(value instanceof byte[]) {
  1700. return (byte[])value;
  1701. } else if(value instanceof Blob) {
  1702. try {
  1703. Blob b = (Blob)value;
  1704. // note, start pos is 1-based
  1705. return b.getBytes(1L, (int)b.length());
  1706. } catch(SQLException e) {
  1707. throw (IOException)(new IOException(e.getMessage())).initCause(e);
  1708. }
  1709. }
  1710. ByteArrayOutputStream bout = new ByteArrayOutputStream();
  1711. if(value instanceof InputStream) {
  1712. byte[] buf = new byte[8 * 1024];
  1713. InputStream in = (InputStream)value;
  1714. int read = 0;
  1715. while((read = in.read(buf)) != -1) {
  1716. bout.write(buf, 0, read);
  1717. }
  1718. } else {
  1719. // if all else fails, serialize it
  1720. ObjectOutputStream oos = new ObjectOutputStream(bout);
  1721. oos.writeObject(value);
  1722. oos.close();
  1723. }
  1724. return bout.toByteArray();
  1725. }
  1726. /**
  1727. * Interpret a boolean value (null == false)
  1728. * @usage _advanced_method_
  1729. */
  1730. public static boolean toBooleanValue(Object obj) {
  1731. return ((obj != null) && ((Boolean)obj).booleanValue());
  1732. }
  1733. /**
  1734. * Swaps the bytes of the given numeric in place.
  1735. */
  1736. private static void fixNumericByteOrder(byte[] bytes)
  1737. {
  1738. // fix endianness of each 4 byte segment
  1739. for(int i = 0; i < 4; ++i) {
  1740. ByteUtil.swap4Bytes(bytes, i * 4);
  1741. }
  1742. }
  1743. /**
  1744. * Treat booleans as integers (C-style).
  1745. */
  1746. protected static Object booleanToInteger(Object obj) {
  1747. if (obj instanceof Boolean) {
  1748. obj = ((Boolean) obj) ? 1 : 0;
  1749. }
  1750. return obj;
  1751. }
  1752. /**
  1753. * Returns a wrapper for raw column data that can be written without
  1754. * understanding the data. Useful for wrapping unparseable data for
  1755. * re-writing.
  1756. */
  1757. static RawData rawDataWrapper(byte[] bytes) {
  1758. return new RawData(bytes);
  1759. }
  1760. /**
  1761. * Returs {@code true} if the given value is "raw" column data,
  1762. * {@code false} otherwise.
  1763. */
  1764. static boolean isRawData(Object value) {
  1765. return(value instanceof RawData);
  1766. }
  1767. /**
  1768. * Writes the column definitions into a table definition buffer.
  1769. * @param buffer Buffer to write to
  1770. * @param columns List of Columns to write definitions for
  1771. */
  1772. protected static void writeDefinitions(
  1773. TableCreator creator, ByteBuffer buffer)
  1774. throws IOException
  1775. {
  1776. List<Column> columns = creator.getColumns();
  1777. short columnNumber = (short) 0;
  1778. short fixedOffset = (short) 0;
  1779. short variableOffset = (short) 0;
  1780. // we specifically put the "long variable" values after the normal
  1781. // variable length values so that we have a better chance of fitting it
  1782. // all (because "long variable" values can go in separate pages)
  1783. short longVariableOffset =
  1784. Column.countNonLongVariableLength(columns);
  1785. for (Column col : columns) {
  1786. // record this for later use when writing indexes
  1787. col.setColumnNumber(columnNumber);
  1788. int position = buffer.position();
  1789. buffer.put(col.getType().getValue());
  1790. buffer.putInt(Table.MAGIC_TABLE_NUMBER); //constant magic number
  1791. buffer.putShort(columnNumber); //Column Number
  1792. if (col.isVariableLength()) {
  1793. if(!col.getType().isLongValue()) {
  1794. buffer.putShort(variableOffset++);
  1795. } else {
  1796. buffer.putShort(longVariableOffset++);
  1797. }
  1798. } else {
  1799. buffer.putShort((short) 0);
  1800. }
  1801. buffer.putShort(columnNumber); //Column Number again
  1802. if(col.getType().isTextual()) {
  1803. // this will write 4 bytes (note we don't support writing dbs which
  1804. // use the text code page)
  1805. writeSortOrder(buffer, col.getTextSortOrder(), creator.getFormat());
  1806. } else {
  1807. if(col.getType().getHasScalePrecision()) {
  1808. buffer.put(col.getPrecision()); // numeric precision
  1809. buffer.put(col.getScale()); // numeric scale
  1810. } else {
  1811. buffer.put((byte) 0x00); //unused
  1812. buffer.put((byte) 0x00); //unused
  1813. }
  1814. buffer.putShort((short) 0); //Unknown
  1815. }
  1816. buffer.put(col.getColumnBitFlags()); // misc col flags
  1817. if (col.isCompressedUnicode()) { //Compressed
  1818. buffer.put((byte) 1);
  1819. } else {
  1820. buffer.put((byte) 0);
  1821. }
  1822. buffer.putInt(0); //Unknown, but always 0.
  1823. //Offset for fixed length columns
  1824. if (col.isVariableLength()) {
  1825. buffer.putShort((short) 0);
  1826. } else {
  1827. buffer.putShort(fixedOffset);
  1828. fixedOffset += col.getType().getFixedSize(col.getLength());
  1829. }
  1830. if(!col.getType().isLongValue()) {
  1831. buffer.putShort(col.getLength()); //Column length
  1832. } else {
  1833. buffer.putShort((short)0x0000); // unused
  1834. }
  1835. columnNumber++;
  1836. if (LOG.isDebugEnabled()) {
  1837. LOG.debug("Creating new column def block\n" + ByteUtil.toHexString(
  1838. buffer, position, creator.getFormat().SIZE_COLUMN_DEF_BLOCK));
  1839. }
  1840. }
  1841. for (Column col : columns) {
  1842. Table.writeName(buffer, col.getName(), creator.getCharset());
  1843. }
  1844. }
  1845. /**
  1846. * Reads the sort order info from the given buffer from the given position.
  1847. */
  1848. static SortOrder readSortOrder(ByteBuffer buffer, int position,
  1849. JetFormat format)
  1850. {
  1851. short value = buffer.getShort(position);
  1852. byte version = 0;
  1853. if(format.SIZE_SORT_ORDER == 4) {
  1854. version = buffer.get(position + 3);
  1855. }
  1856. if(value == 0) {
  1857. // probably a file we wrote, before handling sort order
  1858. return format.DEFAULT_SORT_ORDER;
  1859. }
  1860. if(value == GENERAL_SORT_ORDER_VALUE) {
  1861. if(version == GENERAL_LEGACY_SORT_ORDER.getVersion()) {
  1862. return GENERAL_LEGACY_SORT_ORDER;
  1863. }
  1864. if(version == GENERAL_SORT_ORDER.getVersion()) {
  1865. return GENERAL_SORT_ORDER;
  1866. }
  1867. }
  1868. return new SortOrder(value, version);
  1869. }
  1870. /**
  1871. * Writes the sort order info to the given buffer at the current position.
  1872. */
  1873. private static void writeSortOrder(ByteBuffer buffer, SortOrder sortOrder,
  1874. JetFormat format) {
  1875. if(sortOrder == null) {
  1876. sortOrder = format.DEFAULT_SORT_ORDER;
  1877. }
  1878. buffer.putShort(sortOrder.getValue());
  1879. if(format.SIZE_SORT_ORDER == 4) {
  1880. buffer.put((byte)0x00); // unknown
  1881. buffer.put(sortOrder.getVersion());
  1882. }
  1883. }
  1884. /**
  1885. * Date subclass which stashes the original date bits, in case we attempt to
  1886. * re-write the value (will not lose precision).
  1887. */
  1888. private static final class DateExt extends Date
  1889. {
  1890. private static final long serialVersionUID = 0L;
  1891. /** cached bits of the original date value */
  1892. private transient final long _dateBits;
  1893. private DateExt(long time, long dateBits) {
  1894. super(time);
  1895. _dateBits = dateBits;
  1896. }
  1897. public long getDateBits() {
  1898. return _dateBits;
  1899. }
  1900. private Object writeReplace() throws ObjectStreamException {
  1901. // if we are going to serialize this Date, convert it back to a normal
  1902. // Date (in case it is restored outside of the context of jackcess)
  1903. return new Date(super.getTime());
  1904. }
  1905. }
  1906. /**
  1907. * Wrapper for raw column data which can be re-written.
  1908. */
  1909. private static class RawData implements Serializable
  1910. {
  1911. private static final long serialVersionUID = 0L;
  1912. private final byte[] _bytes;
  1913. private RawData(byte[] bytes) {
  1914. _bytes = bytes;
  1915. }
  1916. private byte[] getBytes() {
  1917. return _bytes;
  1918. }
  1919. @Override
  1920. public String toString() {
  1921. return "RawData: " + ByteUtil.toHexString(getBytes());
  1922. }
  1923. private Object writeReplace() throws ObjectStreamException {
  1924. // if we are going to serialize this, convert it back to a normal
  1925. // byte[] (in case it is restored outside of the context of jackcess)
  1926. return getBytes();
  1927. }
  1928. }
  1929. /**
  1930. * Base class for the supported autonumber types.
  1931. * @usage _advanced_class_
  1932. */
  1933. public abstract class AutoNumberGenerator
  1934. {
  1935. protected AutoNumberGenerator() {}
  1936. /**
  1937. * Returns the last autonumber generated by this generator. Only valid
  1938. * after a call to {@link Table#addRow}, otherwise undefined.
  1939. */
  1940. public abstract Object getLast();
  1941. /**
  1942. * Returns the next autonumber for this generator.
  1943. * <p>
  1944. * <i>Warning, calling this externally will result in this value being
  1945. * "lost" for the table.</i>
  1946. */
  1947. public abstract Object getNext(Object prevRowValue);
  1948. /**
  1949. * Returns the flags used when writing this column.
  1950. */
  1951. public abstract int getColumnFlags();
  1952. /**
  1953. * Returns the type of values generated by this generator.
  1954. */
  1955. public abstract DataType getType();
  1956. }
  1957. private final class LongAutoNumberGenerator extends AutoNumberGenerator
  1958. {
  1959. private LongAutoNumberGenerator() {}
  1960. @Override
  1961. public Object getLast() {
  1962. // the table stores the last long autonumber used
  1963. return getTable().getLastLongAutoNumber();
  1964. }
  1965. @Override
  1966. public Object getNext(Object prevRowValue) {
  1967. // the table stores the last long autonumber used
  1968. return getTable().getNextLongAutoNumber();
  1969. }
  1970. @Override
  1971. public int getColumnFlags() {
  1972. return AUTO_NUMBER_FLAG_MASK;
  1973. }
  1974. @Override
  1975. public DataType getType() {
  1976. return DataType.LONG;
  1977. }
  1978. }
  1979. private final class GuidAutoNumberGenerator extends AutoNumberGenerator
  1980. {
  1981. private Object _lastAutoNumber;
  1982. private GuidAutoNumberGenerator() {}
  1983. @Override
  1984. public Object getLast() {
  1985. return _lastAutoNumber;
  1986. }
  1987. @Override
  1988. public Object getNext(Object prevRowValue) {
  1989. // format guids consistently w/ Column.readGUIDValue()
  1990. _lastAutoNumber = "{" + UUID.randomUUID() + "}";
  1991. return _lastAutoNumber;
  1992. }
  1993. @Override
  1994. public int getColumnFlags() {
  1995. return AUTO_NUMBER_GUID_FLAG_MASK;
  1996. }
  1997. @Override
  1998. public DataType getType() {
  1999. return DataType.GUID;
  2000. }
  2001. }
  2002. private final class ComplexTypeAutoNumberGenerator extends AutoNumberGenerator
  2003. {
  2004. private ComplexTypeAutoNumberGenerator() {}
  2005. @Override
  2006. public Object getLast() {
  2007. // the table stores the last ComplexType autonumber used
  2008. return getTable().getLastComplexTypeAutoNumber();
  2009. }
  2010. @Override
  2011. public Object getNext(Object prevRowValue) {
  2012. int nextComplexAutoNum =
  2013. ((prevRowValue == null) ?
  2014. // the table stores the last ComplexType autonumber used
  2015. getTable().getNextComplexTypeAutoNumber() :
  2016. // same value is shared across all ComplexType values in a row
  2017. ((ComplexValueForeignKey)prevRowValue).get());
  2018. return new ComplexValueForeignKey(Column.this, nextComplexAutoNum);
  2019. }
  2020. @Override
  2021. public int getColumnFlags() {
  2022. return AUTO_NUMBER_FLAG_MASK;
  2023. }
  2024. @Override
  2025. public DataType getType() {
  2026. return DataType.COMPLEX_TYPE;
  2027. }
  2028. }
  2029. private final class UnsupportedAutoNumberGenerator extends AutoNumberGenerator
  2030. {
  2031. private final DataType _genType;
  2032. private UnsupportedAutoNumberGenerator(DataType genType) {
  2033. _genType = genType;
  2034. }
  2035. @Override
  2036. public Object getLast() {
  2037. return null;
  2038. }
  2039. @Override
  2040. public Object getNext(Object prevRowValue) {
  2041. throw new UnsupportedOperationException();
  2042. }
  2043. @Override
  2044. public int getColumnFlags() {
  2045. throw new UnsupportedOperationException();
  2046. }
  2047. @Override
  2048. public DataType getType() {
  2049. return _genType;
  2050. }
  2051. }
  2052. /**
  2053. * Information about the sort order (collation) for a textual column.
  2054. * @usage _intermediate_class_
  2055. */
  2056. public static final class SortOrder
  2057. {
  2058. private final short _value;
  2059. private final byte _version;
  2060. public SortOrder(short value, byte version) {
  2061. _value = value;
  2062. _version = version;
  2063. }
  2064. public short getValue() {
  2065. return _value;
  2066. }
  2067. public byte getVersion() {
  2068. return _version;
  2069. }
  2070. @Override
  2071. public int hashCode() {
  2072. return _value;
  2073. }
  2074. @Override
  2075. public boolean equals(Object o) {
  2076. return ((this == o) ||
  2077. ((o != null) && (getClass() == o.getClass()) &&
  2078. (_value == ((SortOrder)o)._value) &&
  2079. (_version == ((SortOrder)o)._version)));
  2080. }
  2081. @Override
  2082. public String toString() {
  2083. return _value + "(" + _version + ")";
  2084. }
  2085. }
  2086. /**
  2087. * Information specific to numeric types.
  2088. */
  2089. private static final class NumericInfo
  2090. {
  2091. /** Numeric precision */
  2092. private byte _precision;
  2093. /** Numeric scale */
  2094. private byte _scale;
  2095. }
  2096. /**
  2097. * Information specific to textual types.
  2098. */
  2099. private static final class TextInfo
  2100. {
  2101. /** whether or not they are compressed */
  2102. private boolean _compressedUnicode;
  2103. /** the collating sort order for a text field */
  2104. private SortOrder _sortOrder;
  2105. /** the code page for a text field (for certain db versions) */
  2106. private short _codePage;
  2107. /** complex column which tracks the version history for this "append only"
  2108. column */
  2109. private Column _versionHistoryCol;
  2110. }
  2111. }