KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > database > JoinedTable


1 /**
2  * com.mckoi.database.JoinedTable 20 Sep 2002
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.database;
26
27 import com.mckoi.util.IntegerVector;
28 import com.mckoi.util.BlockIntegerList;
29
30 /**
31  * A Table that represents the result of one or more other tables joined
32  * together. VirtualTable and NaturallyJoinedTable are derived from this
33  * class.
34  *
35  * @author Tobias Downer
36  */

37
38 public abstract class JoinedTable extends Table {
39
40   /**
41    * The list of tables that make up the join.
42    */

43   protected Table[] reference_list;
44   
45   /**
46    * The schemes to describe the entity relation in the given column.
47    */

48   protected SelectableScheme[] column_scheme;
49
50   /**
51    * These two arrays are lookup tables created in the constructor. They allow
52    * for quick resolution of where a given column should be 'routed' to in
53    * the ancestors.
54    */

55   /**
56    * Maps the column number in this table to the reference_list array to route
57    * to.
58    */

59   protected int[] column_table;
60
61   /**
62    * Gives a column filter to the given column to route correctly to the
63    * ancestor.
64    */

65   protected int[] column_filter;
66
67   /**
68    * The column that we are sorted against. This is an optimization set by
69    * the 'optimisedPostSet' method.
70    */

71   private int sorted_against_column = -1;
72
73   /**
74    * The DataTableDef object that describes the columns and name of this
75    * table.
76    */

77   private DataTableDef vt_table_def;
78
79   /**
80    * Incremented when the roots are locked.
81    * See the 'lockRoot' and 'unlockRoot' methods.
82    * NOTE: This should only ever be 1 or 0.
83    */

84   private byte roots_locked;
85
86   /**
87    * Constructs the JoinedTable with the list of tables in the parent.
88    */

89   JoinedTable(Table[] tables) {
90     super();
91     init(tables);
92   }
93
94   /**
95    * Constructs the JoinedTable with a single table.
96    */

97   JoinedTable(Table table) {
98     super();
99     Table[] tables = new Table[1];
100     tables[0] = table;
101     init(tables);
102   }
103
104   /**
105    * Protected constructor.
106    */

107   protected JoinedTable() {
108     super();
109   }
110   
111   /**
112    * Helper function for initializing the variables in the joined table.
113    */

114   protected void init(Table[] tables) {
115     int table_count = tables.length;
116     reference_list = tables;
117
118     final int col_count = getColumnCount();
119     column_scheme = new SelectableScheme[col_count];
120
121     vt_table_def = new DataTableDef();
122
123     // Generate look up tables for column_table and column_filter information
124

125     column_table = new int[col_count];
126     column_filter = new int[col_count];
127     int index = 0;
128     for (int i = 0; i < reference_list.length; ++i) {
129
130       Table cur_table = reference_list[i];
131       DataTableDef cur_table_def = cur_table.getDataTableDef();
132       int ref_col_count = cur_table.getColumnCount();
133
134       // For each column
135
for (int n = 0; n < ref_col_count; ++n) {
136         column_filter[index] = n;
137         column_table[index] = i;
138         ++index;
139         
140         // Add this column to the data table def of this table.
141
vt_table_def.addVirtualColumn(
142                          new DataTableColumnDef(cur_table_def.columnAt(n)));
143       }
144
145     }
146
147     // Final setup the DataTableDef for this virtual table
148

149     vt_table_def.setTableName(new TableName(null, "#VIRTUAL TABLE#"));
150     
151     vt_table_def.setImmutable();
152
153   }
154
155   /**
156    * Returns a row reference list. This is an IntegerVector that represents a
157    * 'reference' to the rows in our virtual table.
158    * <p>
159    * ISSUE: We should be able to optimise these types of things out.
160    */

161   private IntegerVector calculateRowReferenceList() {
162     int size = getRowCount();
163     IntegerVector all_list = new IntegerVector(size);
164     for (int i = 0; i < size; ++i) {
165       all_list.addInt(i);
166     }
167     return all_list;
168   }
169
170   /**
171    * We simply pick the first table to resolve the Database object.
172    */

173   public Database getDatabase() {
174     return reference_list[0].getDatabase();
175   }
176
177   /**
178    * Returns the number of columns in the table. This simply returns the
179    * column counts in the parent table(s).
180    */

181   public int getColumnCount() {
182     int column_count_sum = 0;
183     for (int i = 0; i < reference_list.length; ++i) {
184       column_count_sum += reference_list[i].getColumnCount();
185     }
186     return column_count_sum;
187   }
188
189   /**
190    * Given a fully qualified variable field name, ie. 'APP.CUSTOMER.CUSTOMERID'
191    * this will return the column number the field is at. Returns -1 if the
192    * field does not exist in the table.
193    */

194   public int findFieldName(Variable v) {
195     int col_index = 0;
196     for (int i = 0; i < reference_list.length; ++i) {
197       int col = reference_list[i].findFieldName(v);
198       if (col != -1) {
199         return col + col_index;
200       }
201       col_index += reference_list[i].getColumnCount();
202     }
203     return -1;
204   }
205
206   /**
207    * Returns a fully qualified Variable object that represents the name of
208    * the column at the given index. For example,
209    * new Variable(new TableName("APP", "CUSTOMER"), "ID")
210    */

211   public final Variable getResolvedVariable(int column) {
212     Table parent_table = reference_list[column_table[column]];
213     return parent_table.getResolvedVariable(column_filter[column]);
214   }
215
216   /**
217    * Returns the list of Table objects that represent this VirtualTable.
218    */

219   protected final Table[] getReferenceTables() {
220     return reference_list;
221   }
222
223   /**
224    * This is an optimisation that should only be called _after_ a 'set' method
225    * has been called. Because the 'select' operation returns a set that is
226    * ordered by the given column, we can very easily generate a
227    * SelectableScheme object that can handle this column.
228    * So 'column' is the column in which this virtual table is naturally ordered
229    * by.
230    * NOTE: The internals of this method may be totally commented out and the
231    * database will still operate correctly. However this greatly speeds up
232    * situations when you perform multiple consequtive operations on the same
233    * column.
234    */

235   void optimisedPostSet(int column) {
236     sorted_against_column = column;
237   }
238
239   /**
240    * Returns a SelectableScheme for the given column in the given VirtualTable
241    * row domain. This searches down through the tables ancestors until it
242    * comes across a table with a SelectableScheme where the given column is
243    * fully resolved. In most cases, this will be the root DataTable.
244    */

245   SelectableScheme getSelectableSchemeFor(int column, int original_column,
246                                           Table table) {
247
248     // First check if the given SelectableScheme is in the column_scheme array
249
SelectableScheme scheme = column_scheme[column];
250     if (scheme != null) {
251       if (table == this) {
252         return scheme;
253       }
254       else {
255         return scheme.getSubsetScheme(table, original_column);
256       }
257     }
258
259     // If it isn't then we need to calculate it
260
SelectableScheme ss;
261
262     // Optimization: The table may be naturally ordered by a column. If it
263
// is we don't try to generate an ordered set.
264
if (sorted_against_column != -1 &&
265         sorted_against_column == column) {
266       InsertSearch isop =
267                   new InsertSearch(this, column, calculateRowReferenceList());
268       isop.RECORD_UID = false;
269       ss = isop;
270       column_scheme[column] = ss;
271       if (table != this) {
272         ss = ss.getSubsetScheme(table, original_column);
273       }
274
275     }
276     else {
277       // Otherwise we must generate the ordered set from the information in
278
// a parent index.
279
Table parent_table = reference_list[column_table[column]];
280       ss = parent_table.getSelectableSchemeFor(
281                                column_filter[column], original_column, table);
282       if (table == this) {
283         column_scheme[column] = ss;
284       }
285     }
286
287     return ss;
288   }
289
290   /**
291    * Given a set, this trickles down through the Table hierarchy resolving
292    * the given row_set to a form that the given ancestor understands.
293    * Say you give the set { 0, 1, 2, 3, 4, 5, 6 }, this function may check
294    * down three levels and return a new 7 element set with the rows fully
295    * resolved to the given ancestors domain.
296    */

297   void setToRowTableDomain(int column, IntegerVector row_set,
298                            TableDataSource ancestor) {
299
300     if (ancestor == this) {
301       return;
302     }
303     else {
304
305       int table_num = column_table[column];
306       Table parent_table = reference_list[table_num];
307
308       // Resolve the rows into the parents indices. (MANGLES row_set)
309
resolveAllRowsForTableAt(row_set, table_num);
310
311       parent_table.setToRowTableDomain(column_filter[column], row_set, ancestor);
312       return;
313     }
314   }
315
316   /**
317    * Returns an object that contains fully resolved, one level only information
318    * about the DataTable and the row indices of the data in this table.
319    * This information can be used to construct a new VirtualTable. We need
320    * to supply an empty RawTableInformation object.
321    */

322   RawTableInformation resolveToRawTable(RawTableInformation info,
323                                         IntegerVector row_set) {
324                                           
325     if (this instanceof RootTable) {
326       info.add((RootTable) this, calculateRowReferenceList());
327     }
328     else {
329       for (int i = 0; i < reference_list.length; ++i) {
330
331         IntegerVector new_row_set = new IntegerVector(row_set);
332
333         // Resolve the rows into the parents indices.
334
resolveAllRowsForTableAt(new_row_set, i);
335
336         Table table = reference_list[i];
337         if (table instanceof RootTable) {
338           info.add((RootTable) table, new_row_set);
339         }
340         else {
341           ((JoinedTable) table).resolveToRawTable(info, new_row_set);
342         }
343       }
344     }
345
346     return info;
347   }
348
349   /**
350    * Return the list of DataTable and row sets that make up the raw information
351    * in this table.
352    */

353   RawTableInformation resolveToRawTable(RawTableInformation info) {
354     IntegerVector all_list = new IntegerVector();
355     int size = getRowCount();
356     for (int i = 0; i < size; ++i) {
357       all_list.addInt(i);
358     }
359     return resolveToRawTable(info, all_list);
360   }
361
362   /**
363    * Returns the DataTableDef object that describes the columns in this
364    * table. For a VirtualTable, this object contains the union of
365    * all the columns in the children in the order set. The name of a
366    * virtual table is the concat of all the parent table names. The
367    * schema is set to null.
368    */

369   public DataTableDef getDataTableDef() {
370     return vt_table_def;
371   }
372
373   /**
374    * Returns an object that represents the information in the given cell
375    * in the table.
376    */

377   public TObject getCellContents(int column, int row) {
378     int table_num = column_table[column];
379     Table parent_table = reference_list[table_num];
380     row = resolveRowForTableAt(row, table_num);
381     return parent_table.getCellContents(column_filter[column], row);
382   }
383
384   /**
385    * Returns an Enumeration of the rows in this table.
386    * The Enumeration is a fast way of retrieving consequtive rows in the table.
387    */

388   public RowEnumeration rowEnumeration() {
389     return new SimpleRowEnumeration(getRowCount());
390   }
391
392   /**
393    * Adds a DataTableListener to the DataTable objects at the root of this
394    * table tree hierarchy. If this table represents the join of a number of
395    * tables then the DataTableListener is added to all the DataTable objects
396    * at the root.
397    * <p>
398    * A DataTableListener is notified of all modifications to the raw entries
399    * of the table. This listener can be used for detecting changes in VIEWs,
400    * for triggers or for caching of common queries.
401    */

402   void addDataTableListener(DataTableListener listener) {
403     for (int i = 0; i < reference_list.length; ++i) {
404       reference_list[i].addDataTableListener(listener);
405     }
406   }
407
408   /**
409    * Removes a DataTableListener from the DataTable objects at the root of
410    * this table tree hierarchy. If this table represents the join of a
411    * number of tables, then the DataTableListener is removed from all the
412    * DataTable objects at the root.
413    */

414   void removeDataTableListener(DataTableListener listener) {
415     for (int i = 0; i < reference_list.length; ++i) {
416       reference_list[i].removeDataTableListener(listener);
417     }
418   }
419
420
421   /**
422    * Locks the root table(s) of this table so that it is impossible to
423    * overwrite the underlying rows that may appear in this table.
424    * This is used when cells in the table need to be accessed 'outside' the
425    * lock. So we may have late access to cells in the table.
426    * 'lock_key' is a given key that will also unlock the root table(s).
427    * NOTE: This is nothing to do with the 'LockingMechanism' object.
428    */

429   public void lockRoot(int lock_key) {
430     // For each table, recurse.
431
roots_locked++;
432     for (int i = 0; i < reference_list.length; ++i) {
433       reference_list[i].lockRoot(lock_key);
434     }
435   }
436
437   /**
438    * Unlocks the root tables so that the underlying rows may
439    * once again be used if they are not locked and have been removed. This
440    * should be called some time after the rows have been locked.
441    */

442   public void unlockRoot(int lock_key) {
443     // For each table, recurse.
444
roots_locked--;
445     for (int i = 0; i < reference_list.length; ++i) {
446       reference_list[i].unlockRoot(lock_key);
447     }
448   }
449
450   /**
451    * Returns true if the table has its row roots locked (via the lockRoot(int)
452    * method.
453    */

454   public boolean hasRootsLocked() {
455     return roots_locked != 0;
456   }
457
458
459   /**
460    * Prints a graph of the table hierarchy to the stream.
461    */

462   public void printGraph(java.io.PrintStream JavaDoc out, int indent) {
463     for (int i = 0; i < indent; ++i) {
464       out.print(' ');
465     }
466     out.println("JT[" + getClass());
467
468     for (int i = 0; i < reference_list.length; ++i) {
469       reference_list[i].printGraph(out, indent + 2);
470     }
471
472     for (int i = 0; i < indent; ++i) {
473       out.print(' ');
474     }
475     out.println("]");
476   }
477
478   // ---------- Abstract methods ----------
479

480   /**
481    * Given a row and a table index (to a parent reference table), this will
482    * return the row index in the given parent table for the given row.
483    */

484   protected abstract int resolveRowForTableAt(int row_number, int table_num);
485
486   /**
487    * Given an IntegerVector that represents a list of pointers to rows in this
488    * table, this resolves the rows to row indexes in the given parent table.
489    * This method changes the 'row_set' IntegerVector object.
490    */

491   protected abstract void
492               resolveAllRowsForTableAt(IntegerVector row_set, int table_num);
493
494
495 }
496
497
Popular Tags