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.

Joiner.java 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /*
  2. Copyright (c) 2011 James Ahlborn
  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. */
  16. package com.healthmarketscience.jackcess;
  17. import java.io.IOException;
  18. import java.util.Collection;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. import java.util.Map;
  22. /**
  23. * Utility for finding rows based on pre-defined, foreign-key table
  24. * relationships.
  25. *
  26. * @author James Ahlborn
  27. */
  28. public class Joiner
  29. {
  30. private final Index _fromIndex;
  31. private final List<IndexData.ColumnDescriptor> _fromCols;
  32. private final IndexCursor _toCursor;
  33. private final Object[] _entryValues;
  34. private Joiner(Index fromIndex, IndexCursor toCursor)
  35. {
  36. _fromIndex = fromIndex;
  37. _fromCols = _fromIndex.getColumns();
  38. _entryValues = new Object[_fromCols.size()];
  39. _toCursor = toCursor;
  40. }
  41. /**
  42. * Creates a new Joiner based on the foreign-key relationship between the
  43. * given "from"" table and the given "to"" table.
  44. *
  45. * @param fromTable the "from" side of the relationship
  46. * @param toTable the "to" side of the relationship
  47. * @throws IllegalArgumentException if there is no relationship between the
  48. * given tables
  49. */
  50. public static Joiner create(Table fromTable, Table toTable)
  51. throws IOException
  52. {
  53. return create(fromTable.getForeignKeyIndex(toTable));
  54. }
  55. /**
  56. * Creates a new Joiner based on the given index which backs a foreign-key
  57. * relationship. The table of the given index will be the "from" table and
  58. * the table on the other end of the relationship will be the "to" table.
  59. *
  60. * @param fromIndex the index backing one side of a foreign-key relationship
  61. */
  62. public static Joiner create(Index fromIndex)
  63. throws IOException
  64. {
  65. Index toIndex = fromIndex.getReferencedIndex();
  66. IndexCursor toCursor = IndexCursor.createCursor(
  67. toIndex.getTable(), toIndex);
  68. // text lookups are always case-insensitive
  69. toCursor.setColumnMatcher(CaseInsensitiveColumnMatcher.INSTANCE);
  70. return new Joiner(fromIndex, toCursor);
  71. }
  72. /**
  73. * Creates a new Joiner that is the reverse of this Joiner (the "from" and
  74. * "to" tables are swapped).
  75. */
  76. public Joiner createReverse()
  77. throws IOException
  78. {
  79. return create(getToTable(), getFromTable());
  80. }
  81. public Table getFromTable()
  82. {
  83. return getFromIndex().getTable();
  84. }
  85. public Index getFromIndex()
  86. {
  87. return _fromIndex;
  88. }
  89. public Table getToTable()
  90. {
  91. return getToCursor().getTable();
  92. }
  93. public Index getToIndex()
  94. {
  95. return getToCursor().getIndex();
  96. }
  97. public IndexCursor getToCursor()
  98. {
  99. return _toCursor;
  100. }
  101. /**
  102. * Returns the first row in the "to" table based on the given columns in the
  103. * "from" table if any, {@code null} if there is no matching row.
  104. *
  105. * @param fromRow row from the "from" table (which must include the relevant
  106. * columns for this join relationship)
  107. */
  108. public Map<String,Object> findFirstRow(Map<String,?> fromRow)
  109. throws IOException
  110. {
  111. return findFirstRow(fromRow, null);
  112. }
  113. /**
  114. * Returns selected columns from the first row in the "to" table based on
  115. * the given columns in the "from" table if any, {@code null} if there is no
  116. * matching row.
  117. *
  118. * @param fromRow row from the "from" table (which must include the relevant
  119. * columns for this join relationship)
  120. * @param columnNames desired columns in the from table row
  121. */
  122. public Map<String,Object> findFirstRow(Map<String,?> fromRow,
  123. Collection<String> columnNames)
  124. throws IOException
  125. {
  126. toEntryValues(fromRow);
  127. return ((_toCursor.findFirstRowByEntry(_entryValues) ?
  128. _toCursor.getCurrentRow(columnNames) : null));
  129. }
  130. /**
  131. * Returns an Iterator over all the rows in the "to" table based on the
  132. * given columns in the "from" table.
  133. *
  134. * @param fromRow row from the "from" table (which must include the relevant
  135. * columns for this join relationship)
  136. */
  137. public Iterator<Map<String,Object>> findRows(Map<String,?> fromRow)
  138. {
  139. return findRows(fromRow, null);
  140. }
  141. /**
  142. * Returns an Iterator with the selected columns over all the rows in the
  143. * "to" table based on the given columns in the "from" table.
  144. *
  145. * @param fromRow row from the "from" table (which must include the relevant
  146. * columns for this join relationship)
  147. * @param columnNames desired columns in the from table row
  148. */
  149. public Iterator<Map<String,Object>> findRows(Map<String,?> fromRow,
  150. Collection<String> columnNames)
  151. {
  152. toEntryValues(fromRow);
  153. return _toCursor.entryIterator(columnNames, _entryValues);
  154. }
  155. /**
  156. * Returns an Iterable whose iterator() method returns the result of a call
  157. * to {@link #findRows(Map)}
  158. *
  159. * @param fromRow row from the "from" table (which must include the relevant
  160. * columns for this join relationship)
  161. * @throws IllegalStateException if an IOException is thrown by one of the
  162. * operations, the actual exception will be contained within
  163. */
  164. public Iterable<Map<String,Object>> findRowsIterable(Map<String,?> fromRow)
  165. {
  166. return findRowsIterable(fromRow, null);
  167. }
  168. /**
  169. * Returns an Iterable whose iterator() method returns the result of a call
  170. * to {@link #findRows(Map,Collection)}
  171. *
  172. * @param fromRow row from the "from" table (which must include the relevant
  173. * columns for this join relationship)
  174. * @param columnNames desired columns in the from table row
  175. * @throws IllegalStateException if an IOException is thrown by one of the
  176. * operations, the actual exception will be contained within
  177. */
  178. public Iterable<Map<String,Object>> findRowsIterable(
  179. final Map<String,?> fromRow, final Collection<String> columnNames)
  180. {
  181. return new Iterable<Map<String, Object>>() {
  182. public Iterator<Map<String, Object>> iterator() {
  183. return findRows(fromRow, columnNames);
  184. }
  185. };
  186. }
  187. /**
  188. * Fills in the _entryValues with the relevant info from the given "from"
  189. * table row.
  190. */
  191. private void toEntryValues(Map<String,?> fromRow)
  192. {
  193. for(int i = 0; i < _entryValues.length; ++i) {
  194. _entryValues[i] = fromRow.get(_fromCols.get(i).getName());
  195. }
  196. }
  197. }