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.

ComplexColumnInfoImpl.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. /*
  2. Copyright (c) 2011 James Ahlborn
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package com.healthmarketscience.jackcess.impl.complex;
  14. import java.io.IOException;
  15. import java.util.ArrayList;
  16. import java.util.Collection;
  17. import java.util.Collections;
  18. import java.util.Iterator;
  19. import java.util.List;
  20. import java.util.Map;
  21. import com.healthmarketscience.jackcess.Column;
  22. import com.healthmarketscience.jackcess.DataType;
  23. import com.healthmarketscience.jackcess.Database;
  24. import com.healthmarketscience.jackcess.IndexCursor;
  25. import com.healthmarketscience.jackcess.Row;
  26. import com.healthmarketscience.jackcess.RowId;
  27. import com.healthmarketscience.jackcess.RuntimeIOException;
  28. import com.healthmarketscience.jackcess.Table;
  29. import com.healthmarketscience.jackcess.complex.ComplexColumnInfo;
  30. import com.healthmarketscience.jackcess.complex.ComplexDataType;
  31. import com.healthmarketscience.jackcess.complex.ComplexValue;
  32. import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey;
  33. import com.healthmarketscience.jackcess.impl.ColumnImpl;
  34. import com.healthmarketscience.jackcess.impl.CustomToStringStyle;
  35. import com.healthmarketscience.jackcess.impl.TableImpl;
  36. /**
  37. * Base class for the additional information tracked for complex columns.
  38. *
  39. * @author James Ahlborn
  40. */
  41. public abstract class ComplexColumnInfoImpl<V extends ComplexValue>
  42. implements ComplexColumnInfo<V>
  43. {
  44. private static final int INVALID_ID_VALUE = -1;
  45. public static final ComplexValue.Id INVALID_ID = new ComplexValueIdImpl(
  46. INVALID_ID_VALUE, null);
  47. public static final ComplexValueForeignKey INVALID_FK =
  48. new ComplexValueForeignKeyImpl(null, INVALID_ID_VALUE);
  49. private final Column _column;
  50. private final int _complexTypeId;
  51. private final Table _flatTable;
  52. private final List<Column> _typeCols;
  53. private final Column _pkCol;
  54. private final Column _complexValFkCol;
  55. private IndexCursor _complexValIdCursor;
  56. protected ComplexColumnInfoImpl(Column column, int complexTypeId,
  57. Table typeObjTable, Table flatTable)
  58. throws IOException
  59. {
  60. _column = column;
  61. _complexTypeId = complexTypeId;
  62. _flatTable = flatTable;
  63. // the flat table has all the "value" columns and 2 extra columns, a
  64. // primary key for each row, and a LONG value which is essentially a
  65. // foreign key to the main table.
  66. List<Column> typeCols = new ArrayList<Column>();
  67. List<Column> otherCols = new ArrayList<Column>();
  68. diffFlatColumns(typeObjTable, flatTable, typeCols, otherCols);
  69. _typeCols = Collections.unmodifiableList(typeCols);
  70. Column pkCol = null;
  71. Column complexValFkCol = null;
  72. for(Column col : otherCols) {
  73. if(col.isAutoNumber()) {
  74. pkCol = col;
  75. } else if(col.getType() == DataType.LONG) {
  76. complexValFkCol = col;
  77. }
  78. }
  79. if((pkCol == null) || (complexValFkCol == null)) {
  80. throw new IOException("Could not find expected columns in flat table " +
  81. flatTable.getName() + " for complex column with id "
  82. + complexTypeId);
  83. }
  84. _pkCol = pkCol;
  85. _complexValFkCol = complexValFkCol;
  86. }
  87. public void postTableLoadInit() throws IOException {
  88. // nothing to do in base class
  89. }
  90. public Column getColumn() {
  91. return _column;
  92. }
  93. public Database getDatabase() {
  94. return getColumn().getDatabase();
  95. }
  96. public Column getPrimaryKeyColumn() {
  97. return _pkCol;
  98. }
  99. public Column getComplexValueForeignKeyColumn() {
  100. return _complexValFkCol;
  101. }
  102. protected List<Column> getTypeColumns() {
  103. return _typeCols;
  104. }
  105. @Override
  106. public int countValues(int complexValueFk) throws IOException {
  107. return getRawValues(complexValueFk,
  108. Collections.singleton(_complexValFkCol.getName()))
  109. .size();
  110. }
  111. @Override
  112. public List<Row> getRawValues(int complexValueFk)
  113. throws IOException
  114. {
  115. return getRawValues(complexValueFk, null);
  116. }
  117. private Iterator<Row> getComplexValFkIter(
  118. int complexValueFk, Collection<String> columnNames)
  119. throws IOException
  120. {
  121. if(_complexValIdCursor == null) {
  122. _complexValIdCursor = _flatTable.newCursor()
  123. .setIndexByColumns(_complexValFkCol)
  124. .toIndexCursor();
  125. }
  126. return _complexValIdCursor.newEntryIterable(complexValueFk)
  127. .setColumnNames(columnNames).iterator();
  128. }
  129. @Override
  130. public List<Row> getRawValues(int complexValueFk,
  131. Collection<String> columnNames)
  132. throws IOException
  133. {
  134. Iterator<Row> entryIter =
  135. getComplexValFkIter(complexValueFk, columnNames);
  136. if(!entryIter.hasNext()) {
  137. return Collections.emptyList();
  138. }
  139. List<Row> values = new ArrayList<Row>();
  140. while(entryIter.hasNext()) {
  141. values.add(entryIter.next());
  142. }
  143. return values;
  144. }
  145. @Override
  146. public List<V> getValues(ComplexValueForeignKey complexValueFk)
  147. throws IOException
  148. {
  149. List<Row> rawValues = getRawValues(complexValueFk.get());
  150. if(rawValues.isEmpty()) {
  151. return Collections.emptyList();
  152. }
  153. return toValues(complexValueFk, rawValues);
  154. }
  155. protected List<V> toValues(ComplexValueForeignKey complexValueFk,
  156. List<Row> rawValues)
  157. throws IOException
  158. {
  159. List<V> values = new ArrayList<V>();
  160. for(Row rawValue : rawValues) {
  161. values.add(toValue(complexValueFk, rawValue));
  162. }
  163. return values;
  164. }
  165. @Override
  166. public ComplexValue.Id addRawValue(Map<String,?> rawValue)
  167. throws IOException
  168. {
  169. Object[] row = ((TableImpl)_flatTable).asRowWithRowId(rawValue);
  170. _flatTable.addRow(row);
  171. return getValueId(row);
  172. }
  173. @Override
  174. public ComplexValue.Id addValue(V value) throws IOException {
  175. Object[] row = asRow(newRowArray(), value);
  176. _flatTable.addRow(row);
  177. ComplexValue.Id id = getValueId(row);
  178. value.setId(id);
  179. return id;
  180. }
  181. @Override
  182. public void addValues(Collection<? extends V> values) throws IOException {
  183. for(V value : values) {
  184. addValue(value);
  185. }
  186. }
  187. @Override
  188. public ComplexValue.Id updateRawValue(Row rawValue) throws IOException {
  189. _flatTable.updateRow(rawValue);
  190. return getValueId(rawValue);
  191. }
  192. @Override
  193. public ComplexValue.Id updateValue(V value) throws IOException {
  194. ComplexValue.Id id = value.getId();
  195. updateRow(id, asRow(newRowArray(), value));
  196. return id;
  197. }
  198. @Override
  199. public void updateValues(Collection<? extends V> values) throws IOException {
  200. for(V value : values) {
  201. updateValue(value);
  202. }
  203. }
  204. @Override
  205. public void deleteRawValue(Row rawValue) throws IOException {
  206. deleteRow(rawValue.getId());
  207. }
  208. @Override
  209. public void deleteValue(V value) throws IOException {
  210. deleteRow(value.getId().getRowId());
  211. }
  212. @Override
  213. public void deleteValues(Collection<? extends V> values) throws IOException {
  214. for(V value : values) {
  215. deleteValue(value);
  216. }
  217. }
  218. @Override
  219. public void deleteAllValues(int complexValueFk) throws IOException {
  220. Iterator<Row> entryIter =
  221. getComplexValFkIter(complexValueFk, Collections.<String>emptySet());
  222. try {
  223. while(entryIter.hasNext()) {
  224. entryIter.next();
  225. entryIter.remove();
  226. }
  227. } catch(RuntimeIOException e) {
  228. throw (IOException)e.getCause();
  229. }
  230. }
  231. @Override
  232. public void deleteAllValues(ComplexValueForeignKey complexValueFk)
  233. throws IOException
  234. {
  235. deleteAllValues(complexValueFk.get());
  236. }
  237. private void updateRow(ComplexValue.Id id, Object[] row) throws IOException {
  238. ((TableImpl)_flatTable).updateRow(id.getRowId(), row);
  239. }
  240. private void deleteRow(RowId rowId) throws IOException {
  241. ((TableImpl)_flatTable).deleteRow(rowId);
  242. }
  243. protected ComplexValueIdImpl getValueId(Row row) {
  244. int idVal = (Integer)getPrimaryKeyColumn().getRowValue(row);
  245. return new ComplexValueIdImpl(idVal, row.getId());
  246. }
  247. protected ComplexValueIdImpl getValueId(Object[] row) {
  248. int idVal = (Integer)getPrimaryKeyColumn().getRowValue(row);
  249. return new ComplexValueIdImpl(idVal,
  250. ((TableImpl)_flatTable).getRowId(row));
  251. }
  252. protected Object[] asRow(Object[] row, V value)
  253. throws IOException
  254. {
  255. ComplexValue.Id id = value.getId();
  256. _pkCol.setRowValue(
  257. row, ((id != INVALID_ID) ? id : Column.AUTO_NUMBER));
  258. ComplexValueForeignKey cFk = value.getComplexValueForeignKey();
  259. _complexValFkCol.setRowValue(
  260. row, ((cFk != INVALID_FK) ? cFk : Column.AUTO_NUMBER));
  261. return row;
  262. }
  263. private Object[] newRowArray() {
  264. Object[] row = new Object[_flatTable.getColumnCount() + 1];
  265. row[row.length - 1] = ColumnImpl.RETURN_ROW_ID;
  266. return row;
  267. }
  268. @Override
  269. public String toString() {
  270. return CustomToStringStyle.valueBuilder(this)
  271. .append("complexType", getType())
  272. .append("complexTypeId", _complexTypeId)
  273. .toString();
  274. }
  275. protected static void diffFlatColumns(Table typeObjTable,
  276. Table flatTable,
  277. List<Column> typeCols,
  278. List<Column> otherCols)
  279. {
  280. // each "flat"" table has the columns from the "type" table, plus some
  281. // others. separate the "flat" columns into these 2 buckets
  282. for(Column col : flatTable.getColumns()) {
  283. if(((TableImpl)typeObjTable).hasColumn(col.getName())) {
  284. typeCols.add(col);
  285. } else {
  286. otherCols.add(col);
  287. }
  288. }
  289. }
  290. @Override
  291. public abstract ComplexDataType getType();
  292. protected abstract V toValue(
  293. ComplexValueForeignKey complexValueFk,
  294. Row rawValues)
  295. throws IOException;
  296. protected static abstract class ComplexValueImpl implements ComplexValue
  297. {
  298. private Id _id;
  299. private ComplexValueForeignKey _complexValueFk;
  300. protected ComplexValueImpl(Id id, ComplexValueForeignKey complexValueFk) {
  301. _id = id;
  302. _complexValueFk = complexValueFk;
  303. }
  304. @Override
  305. public Id getId() {
  306. return _id;
  307. }
  308. @Override
  309. public void setId(Id id) {
  310. if(_id == id) {
  311. // harmless, ignore
  312. return;
  313. }
  314. if(_id != INVALID_ID) {
  315. throw new IllegalStateException("id may not be reset");
  316. }
  317. _id = id;
  318. }
  319. @Override
  320. public ComplexValueForeignKey getComplexValueForeignKey() {
  321. return _complexValueFk;
  322. }
  323. @Override
  324. public void setComplexValueForeignKey(ComplexValueForeignKey complexValueFk)
  325. {
  326. if(_complexValueFk == complexValueFk) {
  327. // harmless, ignore
  328. return;
  329. }
  330. if(_complexValueFk != INVALID_FK) {
  331. throw new IllegalStateException("complexValueFk may not be reset");
  332. }
  333. _complexValueFk = complexValueFk;
  334. }
  335. @Override
  336. public Column getColumn() {
  337. return _complexValueFk.getColumn();
  338. }
  339. @Override
  340. public int hashCode() {
  341. return ((_id.get() * 37) ^ _complexValueFk.hashCode());
  342. }
  343. @Override
  344. public boolean equals(Object o) {
  345. return ((this == o) ||
  346. ((o != null) && (getClass() == o.getClass()) &&
  347. (_id == ((ComplexValueImpl)o)._id) &&
  348. _complexValueFk.equals(((ComplexValueImpl)o)._complexValueFk)));
  349. }
  350. }
  351. /**
  352. * Implementation of ComplexValue.Id.
  353. */
  354. private static final class ComplexValueIdImpl extends ComplexValue.Id
  355. {
  356. private static final long serialVersionUID = 20130318L;
  357. private final int _value;
  358. private final RowId _rowId;
  359. protected ComplexValueIdImpl(int value, RowId rowId) {
  360. _value = value;
  361. _rowId = rowId;
  362. }
  363. @Override
  364. public int get() {
  365. return _value;
  366. }
  367. @Override
  368. public RowId getRowId() {
  369. return _rowId;
  370. }
  371. }
  372. }