Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

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