Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: javatools/db/DbConstraint.java


1   /*
2    * DbConstraint.java
3    *
4    * Created on 12 febbraio 2002, 12.16
5    *
6       Javatools (modified version) - Some useful general classes.
7       Copyright (C) 2002-2003  Chris Bitmead (original) Antonio Petrelli (modified)
8   
9       This program is free software; you can redistribute it and/or modify
10      it under the terms of the GNU General Public License as published by
11      the Free Software Foundation; either version 2 of the License, or
12      (at your option) any later version.
13  
14      This program is distributed in the hope that it will be useful,
15      but WITHOUT ANY WARRANTY; without even the implied warranty of
16      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17      GNU General Public License for more details.
18  
19      You should have received a copy of the GNU General Public License
20      along with this program; if not, write to the Free Software
21      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  
23      Contact me at: brenmcguire@users.sourceforge.net
24   */
25  
26  package javatools.db;
27  
28  import java.util.*;
29  import javatools.util.ArrayComparator;
30  import javatools.util.NullUtils;
31  
32  /**
33   * This abstract class represents a generic set of constraints for a table. It
34   * contains a lot of code already written for generic purposes, i.e. it can be
35   * derived with few code. If you want to add additional checks, you can easily add
36   * your own code, rewriting <CODE>check</CODE> and <CODE>update</CODE> methods.
37   * @author Antonio Petrelli
38   * @version 0.2.0
39   */
40  public abstract class DbConstraint {
41      /** It represents an INSERT operation.
42       */    
43      public static final int INSERT_OPERATION = 1;
44      /** It represents a DELETE operation.
45       */    
46      public static final int DELETE_OPERATION = 2;
47      /** It represents a normal UPDATE operation.
48       */    
49      public static final int UPDATE_OPERATION = 3;
50      /** It means that an UPDATE-CASCADE operation is outgoing. It is protected because
51       * it is useful only for internal checks-updates.
52       */    
53      protected static final int UPDATE_CASCADE = 4;
54  
55      /** It means that, in case of an UPDATE/DELETE operation, the table MUST NOT do
56       * anything to repair itself.
57       *
58       */    
59      public static final int DO_NOTHING      = 0;
60      /** It means that, in case of an UPDATE/DELETE operation, the constraint MUST set
61       * an array of fields in involved rows to NULL values, to repair referenced table.
62       *
63       */    
64      public static final int SET_NULL        = 1;
65      /** It means that, in case of an UPDATE/DELETE operation, the constraint MUST set
66       * an array of fields in involved rows to DEFAULT values, to repair referenced
67       * table.
68       *
69       */    
70      public static final int SET_DEFAULT     = 2;
71      /** It means that, in case of an UPDATE/DELETE operation, the constraint MUST
72       * cascade the operation, that is updating/deleting in cascade all involved rows.
73       *
74       */    
75      public static final int CASCADE         = 3;
76  
77      /** Creates new DbConstraint */
78      
79      public DbConstraint() {
80          table = null;
81          automaticChecking = true;
82          refs = null;
83          deleteCascadeChildren = null;
84          updateCascadeChildren = null;
85          canDoUpdate = true;
86          cloned = false;
87      }
88      
89      /** Creates a new DbConstraint for a specified table.
90       * @param tbl The table to be checked.
91       */    
92      public DbConstraint(DbAbstractTable tbl) {
93          table = tbl;
94          automaticChecking = true;
95          refs = null;
96          deleteCascadeChildren = null;
97          updateCascadeChildren = null;
98          canDoUpdate = true;
99          cloned = false;
100     }
101     
102     /** Returns the referenced table.
103      * @return The referenced table.
104      */    
105     public DbAbstractTable getTable() {
106         return table;
107     }
108     
109     /** Sets the "where" clause for an DELETE/UPDATE operation.
110      * @param pWhere The requested clause.
111      */    
112     public void setWhere(DbExpr pWhere) {
113         where = pWhere;
114     }
115     
116     /** Sets the selector for an INSERT operation.
117      * @param pSelector The requested selector.
118      */    
119     public void setSelector(DbSelector pSelector) {
120         selector = pSelector;
121     }
122     
123     /** Sets the lists to perform INSERT/UPDATE operations.
124      * @param pIntoList It will be put into <CODE>intoList</CODE>.
125      * @param pFromList It will be put into <CODE>fromList</CODE>.
126      */    
127     public void setValueLists(List pIntoList, List pFromList) {
128         intoList = pIntoList;
129         fromList = pFromList;
130     }
131     
132     /** Sets a flag to represent automatic checking at all times.
133      * @param value <CODE>true</CODE>: check is done at all times;
134      * <CODE>false</CODE>: check is performed only with an explicit call to
135      * <CODE>check</CODE>.
136      */    
137     public void setAutomaticChecking (boolean value) {
138         automaticChecking = value;
139     }
140     
141     /** Returns the current automatic checking setting.
142      * @return <CODE>true</CODE>: check is done at all times;
143      * <CODE>false</CODE>: check is performed only with an explicit call to
144      * <CODE>check</CODE>.
145      */    
146     public boolean getAutomaticChecking () {
147         return automaticChecking;
148     }
149     
150     /** Sets the constraint emulation.
151      * @param value <CODE>true</CODE>: constraint emulation is done as needed;
152      * <CODE>false</CODE>: constraint emulation is NEVER done.
153      */    
154     public void setConstraintEmulation(boolean value) {
155         constraintEmulation = value;
156     }
157     
158     /** Returns if constraint emulation is enabled.
159      * @return <CODE>true</CODE>: constraint emulation is done as needed;
160      * <CODE>false</CODE>: constraint emulation is NEVER done.
161      */    
162     public boolean getConstraintEmulation() {
163         return constraintEmulation;
164     }
165     
166     /** Returns the number of referenced father tables.
167      * @return The count of referenced father tables.
168      */    
169     public int getFatherTablesCount() {
170         return fatherTables.length;
171     }
172 
173     /** Returns the number of son tables.
174      * @return The son tables count.
175      */    
176     public int getSonTablesCount() {
177         return sonTables.length;
178     }
179 
180     /** Returns a father table whose index is specified.
181      * @param index The index of a father table.
182      * @throws DbException If something goes wrong.
183      * @return The requested father table.
184      */    
185     public DbAbstractTable getFatherTable(int index) throws DbException {
186         if (index >= 0 && index < fatherTables.length) {
187             return fatherTables[index];
188         }
189         else
190             throw new DbException("Index out of bounds in getting a father table");
191     }
192 
193     /** Returns a son table whose index is specified.
194      * @param index The index of a son table.
195      * @throws DbException If something goes wrong.
196      * @return The requested son table.
197      */    
198     public DbAbstractTable getSonTable(int index) throws DbException {
199         if (index >= 0 && index < sonTables.length) {
200             return sonTables[index];
201         }
202         else
203             throw new DbException("Index out of bounds in getting a son table");
204     }
205 
206     /** Returns in which position a son table references THIS table in its constraint.
207      * @param index The son table index.
208      * @throws DbException If something goes wrong.
209      * @return The needed index.
210      */    
211     public int getSonFatherIndex(int index) throws DbException {
212         if (index >= 0 && index < sonIndexes.length) {
213             return sonIndexes[index].intValue();
214         }
215         else
216             throw new DbException("Index out of bounds in getting a son table");
217     }
218 
219     /** Returns what kind of operation will be done in case of an update of the father
220      * table specified by <CODE>index</CODE>.
221      * @param index The index of the father table.
222      * @throws DbException If something goes wrong.
223      * @return The requested operation.
224      */    
225     public int getUpdateOperation(int index) throws DbException {
226         if (index >= 0 && index < updateOperations.length) {
227             return updateOperations[index].intValue();
228         }
229         else
230             throw new DbException("Index out of bounds in getting an update operation");
231     }
232 
233     /** Returns what kind of operation will be done in case of a deletion of the father
234      * table specified by <CODE>index</CODE>.
235      * @param index The index of the father table.
236      * @throws DbException If something goes wrong.
237      * @return The requested operation.
238      */    
239     public int getDeleteOperation(int index) throws DbException {
240         if (index >= 0 && index < deleteOperations.length) {
241             return deleteOperations[index].intValue();
242         }
243         else
244             throw new DbException("Index out of bounds in getting a delete operation");
245     }
246 
247     /** Returns the referenced father columns of a father table whose index is
248      * specified.
249      * @param index The index of the father table.
250      * @throws DbException If something goes wrong.
251      * @return An array containing the referenced father columns.
252      */    
253     public DbColumn[] getFatherColumns(int index) throws DbException {
254         if (index >= 0 && index < fatherTablesColumns.length) {
255             return fatherTablesColumns[index];
256         }
257         else
258             throw new DbException("Index out of bounds in getting a father-columns-array");
259     }
260 
261     /** Returns the referencing columns of THIS table for a father table whose index
262      * is specified.
263      * @param index The index of the father column.
264      * @throws DbException If something goes wrong.
265      * @return The referencing columns.
266      */    
267     public DbColumn[] getRefColumns(int index) throws DbException {
268         if (index >= 0 && index < refColumns.length) {
269             return refColumns[index];
270         }
271         else
272             throw new DbException("Index out of bounds in getting a referenced-columns-array");
273     }
274     
275     /** Returns an array containing the default values for fields of a father table
276      * whose index is specified.
277      * @param index The index of the father table.
278      * @throws DbException If something goes wrong.
279      * @return An array containing the default values for referencing columns related to
280      * requested father table.
281      */    
282     public Object[] getDefaultValues(int index) throws DbException {
283         if (index >= 0 && index < defaultValues.length) {
284             return defaultValues[index];
285         }
286         else
287             throw new DbException("Index out of bounds in getting a default-values-array");
288     }
289     
290     /** Clears everything.
291      */    
292     public void clear() {
293         automaticChecking       = true;
294         currentOperation        = 0;
295         where                   = null;
296         selector                = null;
297         intoList                = null;
298         fromList                = null;
299         searchSets              = null;
300         sonsWhere               = null;
301         fatherRecords           = null;
302         deleteCascadeChildren   = null;
303         updateCascadeChildren   = null;
304         constraints             = null;
305     }
306     
307     /** After all settings, it finally builds all static references.
308      * @throws DbException If something goes wrong.
309      */    
310     public void build() throws DbException {
311         initStaticLists();
312     }
313     
314     /** After setting all dynamic (i.e. operations, lists etc.) information, it checks
315      * explicitly if all operations can be done. I suggest to call
316      * <CODE>checkThis</CODE> method to perform base checking.
317      * @param operation The kind of operation (INSERT, UPDATE etc.) that should be done.
318      * @throws DbException If something goes wrong.
319      */    
320     public abstract void check(int operation) throws DbException;
321     
322     /** Actually updates the table and all of its son tables. I suggest to call
323      * <CODE>updateThis</CODE> method to perform base update.
324      * @param operation The kind of operation (INSERT, UPDATE etc.) that should be done.
325      * @throws DbException If something goes wrong.
326      * @return It should contain the final result of <CODE>execute</CODE> calls to inserters,
327      * deleters of updaters.
328      */    
329     public abstract int update(int operation) throws DbException;
330 
331     /** The referenced table to check.
332      */    
333     protected DbAbstractTable table;
334     /** A DbExpr clause used int DELETE/UPDATE operations.
335      */    
336     protected DbExpr where;
337     /** A selector used in INSERT operations.
338      */    
339     protected DbSelector selector;
340     /** A list of columns to put data into (used in INSERT/UPDATE operations).
341      */    
342     protected List intoList;
343     /** A list of values/expression to put data into (used in INSERT/UPDATE operations).
344      */    
345     protected List fromList;
346     /** <CODE>true</CODE>: check is done at all times;
347      * <CODE>false</CODE>: check is performed only with an explicit call to
348      * <CODE>check</CODE>.
349      */    
350     protected boolean automaticChecking;
351     /** <CODE>true</CODE>: emulation made as needed;
352      * <CODE>false</CODE>: emulation is not made.
353      */    
354     protected boolean constraintEmulation;
355     /** Represents the current operation. (INSERT, DELETE, UPDATE, UPDATE-CASCADE).
356      */    
357     protected int currentOperation;
358     /** <CODE>true</CODE>: update can be done.
359      * <CODE>false</CODE>: update CANNOT be done.
360      * It is put to <CODE>false</CODE> whenever the DBMS can perform referencial
361      * integrity check by itself; e.g. MySQL cannot, so it must be done in a
362      * "software" way.
363      */    
364     protected boolean canDoUpdate;
365     
366     /** It contains all father tables of the one referenced by this constraint.
367      */    
368     protected DbAbstractTable[] fatherTables;
369     /** It contains all son tables of the one referenced by this constraint.
370      */    
371     protected DbAbstractTable[] sonTables;
372     /** It contains the update operations for each father table.
373      */    
374     protected Integer[] updateOperations;
375     /** It contains the delete operations for each father table.
376      */    
377     protected Integer[] deleteOperations;
378     /** It contains the father keys for each father table.
379      */    
380     protected DbColumn[][] fatherTablesColumns;
381     /** It contains for each father table, the referenced column in contained table.
382      * That is each element in <CODE>fatherTablesColumns</CODE>, columns of father
383      * tables, must be equal to each element in <CODE>refColumns</CODE>, columns of
384      * THIS table.
385      */    
386     protected DbColumn[][] refColumns;
387     /** For each son, it contains the referenced column of THIS table. It is built
388      * automatically, to ease checking.
389      */    
390     protected DbColumn[][] fatherColsChildren;
391     /** For each son it contains the referenced columns of son tables. It is built
392      * automatically, to ease checking.
393      */    
394     protected DbColumn[][] refColsChildren;
395     /** For each son, it contains the position in the <CODE>fatherTables</CODE> of
396      * the son table itself.
397      */    
398     protected Integer[] sonIndexes;
399     /** It's an array built at runtime to represent, for each father, the referenced
400      * fields if they are specified in <CODE>intoList</CODE> for INSERT or UPDATE
401      * operations.
402      */    
403     protected Integer[][] refs;
404     /** Contains the search sets used to perform checks for each son table.
405      */    
406     protected TreeSet[] searchSets;
407     /** Contains the "where" clauses for each son.
408      */    
409     protected DbExpr[] sonsWhere;
410     /** Contains the father records used to find eventually involved rows.
411      */    
412     protected DbTable fatherRecords;
413     /** Currently unused.
414      */    
415     protected Integer deleteCascadeChildren[];
416     /** Currently unused.
417      */    
418     protected Integer updateCascadeChildren[];
419     /** Contains an array of constraint to perform cascade-check and cascade-updates.
420      */    
421     protected DbConstraint constraints[];
422     /** Contains default values for each referenced field of each father table.
423      */    
424     protected Object[][] defaultValues;
425     /** Contains an array of <CODE>intoList</CODE>-like lists, to perform update
426      * operations in son tables.
427      */    
428     protected List[] updateIntoLists;
429     /** Contains an array of <CODE>fromList</CODE>-like lists, to perform update
430      * operations in son tables.
431      */    
432     protected List[] updateFromLists;
433     
434     /** <CODE>true</CODE>: the object is cloned.
435      * <CODE>false</CODE>: the object is NOT cloned.
436      */    
437     protected boolean cloned;
438     
439     /** Performs base update. It also checks if automatic checking is set.
440      * @param operation The operation to be done.
441      * @throws DbException If something goes wrong.
442      * @return The real result from an inserter, updater, deleter.
443      */    
444     protected int updateThis(int operation) throws DbException {
445         DbInserter ins;
446         DbDeleter del;
447         DbUpdater upd;
448         DbSelector tempSelector;
449         int response;
450         
451         currentOperation = operation;
452         if (automaticChecking && constraintEmulation)
453             checkThis(operation);
454         else
455             if (sonTables != null && currentOperation != INSERT_OPERATION &&
456                     constraintEmulation)
457                 initLists();
458         if (currentOperation == INSERT_OPERATION) {
459             if (selector != null)
460                 ins = table.simpleInserter(selector);
461             else
462                 ins = table.simpleInserter();
463             ins.setLists(intoList, fromList);
464             response =  ins.execute();
465         }
466         else if (currentOperation == DELETE_OPERATION) {
467             response = 0;
468             if (canDoUpdate) {
469                 del = table.simpleDeleter();
470                 del.setWhere(where);
471                 response = del.execute();
472             }
473             if (constraintEmulation)
474                 cascade();
475         }
476         else if (currentOperation == UPDATE_OPERATION) {
477             response = 0;
478             if (canDoUpdate) {
479                 upd = table.simpleUpdater();
480                 upd.setLists(intoList, fromList);
481                 upd.setWhere(where);
482                 response = upd.execute();
483             }
484             if (constraintEmulation)
485                 cascade();
486         }
487         else
488             response = -1;
489         return response;
490     }
491     
492     /** Performs base checks.
493      * @param operation The operations that should be done.
494      * @throws DbException If something goes wrong.
495      */    
496     protected void checkThis(int operation) throws DbException {
497         DbSelector tempSelector;
498         DbDatabase tempDb;
499 
500         currentOperation = operation;
501         tempDb = table.getDatabase();
502         if (sonTables != null && currentOperation != INSERT_OPERATION)
503             initLists();
504         if (!tempDb.getForeignKey()) {
505             if (currentOperation != DELETE_OPERATION && currentOperation != UPDATE_CASCADE) {
506                 tempSelector = null;
507                 if (currentOperation == UPDATE_OPERATION) {
508                     tempSelector = selector;
509                     selector = null;
510                 }
511                 checkFathers();
512                 if (currentOperation == UPDATE_OPERATION)
513                     selector = tempSelector;
514             }
515             if (currentOperation != INSERT_OPERATION)
516                 checkChildren();
517         }
518     }
519     
520     /** Performs checking for father tables. Used in INSERT and UPDATE operations.
521      * N.B. Not used in UPDATE-CASCADE operation.
522      * @throws DbException If checking fails.
523      */    
524     protected void checkFathers () throws DbException {
525         int i, j, numFathers, numCols, oneIn;
526         Iterator intoit;
527         DbSelector finder;
528         DbExpr tempExpr, tempWhere;
529         DbIterator res, resFather;
530         Iterator fkIterator;
531         DbDatabase tempDb;
532         DbAbstractTable father;
533         TreeSet fkSet;
534         DbColumn[] fatherCols;
535         DbTable resTable;
536         
537         tempDb = table.getDatabase();
538         if (fatherTables != null) {
539             buildReferences();
540             numFathers = fatherTables.length;
541         }
542         else
543             numFathers = 0;
544         
545         if (selector == null) {
546             for (i=0; i < numFathers; i++) {
547                 finder = new DbSelector(tempDb);
548                 if (!onlyNullValues(i)) {
549                     numCols = refs[i].length;
550                     oneIn = 0;
551                     tempExpr = null;
552                     father = null;
553                     fatherCols = null;
554                     if (numCols > 0) {
555                         father = fatherTables[i];
556                         fatherCols = fatherTablesColumns[i];
557                         if (refs[i][0].intValue() >= 0) {
558                             finder.addColumn(fatherCols[0]);
559                             tempExpr = fatherCols[0].equal(fromList.get(refs[i][0].intValue()));
560                         }
561                         else {
562                             oneIn = 1;
563                         }
564                     }
565                     for (j=1; j < numCols && oneIn != 2; j++) {
566                         if (refs[i][j].intValue() >= 0) {
567                             if (oneIn == 1)
568                                 oneIn = 2;
569                             else {
570                                 finder.addColumn(fatherCols[j]);
571                                 tempExpr = new DbAndExpr(tempDb, tempExpr,
572                                     fatherCols[j].equal(fromList.get(refs[i][j].intValue())));
573                             }
574                         }
575                         else
576                             if (oneIn == 0)
577                                 oneIn = 2;
578                     }
579                     if (oneIn == 2)
580                         throw new DbException ("Key data not complete!");
581                     if (oneIn == 0) {
582                         finder.setWhere(tempExpr);
583                         res = finder.execute().iterator();
584                         if (!res.hasNext())
585                             throw new DbException ("Father key not found!");
586                     }
587                 }
588             }
589         }
590         else {
591             resTable = selector.execute();
592             
593             for (i=0; i < numFathers; i++) {
594                 finder = new DbSelector(tempDb);
595                 res = resTable.iterator();
596                 fkSet = buildSet(refs[i], res);
597                 if (!(fkSet.size() == 1 && NullUtils.isArrayNull((Object[]) fkSet.first()))) {
598                     numCols = refs[i].length;
599                     fatherCols = fatherTablesColumns[i];
600                     tempExpr = null;
601                     if (numCols > 0) {
602                         finder.addColumn(fatherCols[0]);
603                         finder.addGroupBy(fatherCols[0]);
604                         tempExpr = fatherCols[0].equal(selector.getColumn(refs[i][0].intValue()));
605                     }
606                     for (j=1; j < numCols; j++) {
607                         finder.addColumn(fatherCols[j]);
608                         finder.addGroupBy(fatherCols[j]);
609                         tempExpr = new DbAndExpr(tempDb, tempExpr,
610                             fatherCols[j].equal(selector.getColumn(refs[i][j].intValue())));
611                     }
612                     tempWhere = selector.getWhere();
613                     if (tempExpr != null)
614                         if (tempWhere != null)
615                             finder.setWhere(tempExpr.and(tempWhere));
616                         else
617                             finder.setWhere(tempExpr);
618                     else
619                         finder.setWhere(tempWhere);
620                     resFather = finder.execute().iterator();
621                     if (iteratorSize(resFather) != fkSet.size())
622                         throw new DbException("Father keys not found!");
623                 }
624             }
625         }
626     }
627 
628     /** Checks recursively each son.
629      * @throws DbException If checking fails.
630      */    
631     protected void checkChildren() throws DbException {
632         int i, numSons, operation, refFather;
633         DbAbstractTable tempSon;
634         DbReferencedDeleter sonDeleter;
635         DbIterator sonRecords;
636         DbConstraint tempConstraint;
637         Iterator sonDeleters;
638         Object[] defValues;
639 
640         if (sonTables != null)
641             numSons = sonTables.length;
642         else
643             numSons = 0;
644         for (i=0; i < numSons; i++) {
645             tempSon = sonTables[i];
646             tempConstraint = constraints[i];
647             if (tempConstraint != null) {
648                 refFather = sonIndexes[i].intValue();
649                 if (currentOperation == DELETE_OPERATION)
650                     operation = tempConstraint.getDeleteOperation(refFather);
651                 else
652                     operation = tempConstraint.getUpdateOperation(refFather);
653                 if (operation == CASCADE) {
654                     if (currentOperation == DELETE_OPERATION)
655                         tempConstraint.check(DELETE_OPERATION);
656                     else
657                         tempConstraint.check(UPDATE_CASCADE);
658                 }
659                 else {
660                     sonRecords = buildSonRecords(i).iterator();
661                     if (sonRecords.hasNextRow())
662                         if (operation == DO_NOTHING)
663                             throw new DbException("Delete impossible: child key found!");
664                         else {
665                             if (operation == SET_DEFAULT) {
666                                 defValues = tempConstraint.getDefaultValues(i);
667                                 if (searchSets[i].contains(defValues))
668                                     throw new DbException("Trying to delete a default-father!");
669                             }
670                             tempConstraint.check(UPDATE_OPERATION);
671                         }
672                 }
673             }
674         }
675     }
676     
677     /** Clones this object. It is used to build a temporary DbConstraint in
678      * self-referencing tables.
679      * @throws CloneNotSupportedException Never thrown... usually...
680      * @return The cloned object.
681      */    
682     protected Object clone() throws CloneNotSupportedException {
683         DbConstraint tempConstraint;
684         
685         try {
686             tempConstraint = (DbConstraint) this.getClass().newInstance();
687         }
688         catch (InstantiationException e) {
689             throw new CloneNotSupportedException(e.getMessage());
690         }
691         catch (IllegalAccessException e) {
692             throw new CloneNotSupportedException(e.getMessage());
693         }
694         tempConstraint.table = table;
695         tempConstraint.fatherTables = fatherTables;
696         tempConstraint.sonTables = sonTables;
697         tempConstraint.updateOperations = updateOperations;
698         tempConstraint.deleteOperations = deleteOperations;
699         tempConstraint.fatherTablesColumns = fatherTablesColumns;
700         tempConstraint.refColumns = refColumns;
701         tempConstraint.fatherColsChildren = fatherColsChildren;
702         tempConstraint.refColsChildren = refColsChildren;
703         tempConstraint.defaultValues = defaultValues;
704         tempConstraint.sonIndexes = sonIndexes;
705         tempConstraint.cloned = true;
706         return tempConstraint;
707     }
708     
709     private void buildReferences() throws DbException {
710         int i, numFathers;
711         Iterator intoit;
712         DbColumn col;
713         
714         if (intoList != null && fromList != null) {
715             numFathers = fatherTables.length;
716             refs = new Integer[numFathers][];
717             for (i=0; i < numFathers; i++)
718                 refs[i] = findRefPosition(i);
719         }
720     }
721     
722     private Integer[] findRefPosition(int index) throws DbException {
723         int i, j, numCols, numFatherCols, tempValue;
724         Integer[] response;
725         DbColumn[] cols;
726         String columnName;
727         boolean found;
728         
729         cols = refColumns[index];
730         numFatherCols = cols.length;
731         numCols = intoList.size();
732         response = new Integer[numFatherCols];
733         for (i=0; i < numFatherCols; i++) {
734             found = false;
735             j = 0;
736             columnName = (cols[i].getName());
737             tempValue = -1;
738             while (j < numCols && !found) {
739                 if (cols[i].equals(intoList.get(j))) {
740                     found = true;
741                     tempValue = j;
742                 }
743                 else
744                     j++;
745             }
746             response[i] = new Integer(tempValue);
747         }
748         return response;
749     }
750     
751     private int iteratorSize(Iterator refIt) {
752         int i;
753         
754         for(i=0; refIt.hasNext(); i++) 
755             refIt.next();
756         return i;
757     }
758     
759     private TreeSet buildSet(Integer[] refs, DbIterator it) throws DbException {
760         int i, numCols;
761         TreeSet tempTreeSet;
762         ArrayComparator comp;
763         Comparable[] tempArray;
764         DbRow tempRow;
765         
766         comp = new ArrayComparator();
767         tempTreeSet = new TreeSet(comp);
768         numCols = refs.length;
769         while (it.hasNextRow()) {
770             tempArray = new Comparable[numCols];
771             tempRow = it.nextRow();
772             for (i=0; i < numCols; i++)
773                 tempArray[i] = (Comparable) tempRow.getValue(refs[i].intValue());
774             tempTreeSet.add(tempArray);
775         }
776         return tempTreeSet;
777     }
778     
779 
780     private void cascade() throws DbException {
781         int i, numSons, operation, refFather;
782         DbAbstractTable tempSon;
783         DbConstraint tempConstraint;
784         DbDeleter sonDeleter;
785         DbIterator sonRecords;
786         Iterator sonDeleters;
787 
788         if (sonTables != null)
789             numSons = sonTables.length;
790         else
791             numSons = 0;
792         for (i=0; i < numSons; i++) {
793             tempSon = sonTables[i];
794             tempConstraint = constraints[i];
795             if (tempConstraint != null) {
796                 refFather = sonIndexes[i].intValue();
797                 if (currentOperation == DELETE_OPERATION)
798                     operation = tempConstraint.getDeleteOperation(refFather);
799                 else
800                     operation = tempConstraint.getUpdateOperation(refFather);
801                 if (tempConstraint != null) {
802                     tempConstraint.setAutomaticChecking(false);
803                     if (operation == CASCADE)
804                         tempConstraint.update(currentOperation);
805                     else if (operation != DO_NOTHING)
806                         tempConstraint.update(UPDATE_OPERATION);
807                 }
808             }
809         }
810     }
811 
812     private void buildSonColsList() throws DbException {
813         int i, numSons, refIndex;
814         DbAbstractTable tempSon;
815         DbConstraint tempConstraint;
816 
817         numSons = sonTables.length;
818         fatherColsChildren = new DbColumn[numSons][];
819         refColsChildren = new DbColumn[numSons][];
820 
821         for (i=0; i < numSons; i++) {
822             tempSon = sonTables[i];
823             tempConstraint = tempSon.getConstraint();
824             refIndex = sonIndexes[i].intValue();
825             fatherColsChildren[i] = tempConstraint.getFatherColumns(refIndex);
826             refColsChildren[i] = tempConstraint.getRefColumns(refIndex);
827         }
828     }
829 
830     private void buildFatherRecords() throws DbException {
831         int i, j, k, numCols, numSons, numColsSons;
832         boolean[] colNeeded;
833         boolean found;
834         DbColumn[] tempCols;
835         DbColumn tempCol;
836         DbSelector fatherFinder;
837 
838         numSons = sonTables.length;
839         if (numSons > 0) {
840             numCols = table.getColumnCount();
841             colNeeded = new boolean[numCols];
842             for (i=0; i < numCols; i++) {
843                 found = false;
844                 tempCol = table.getColumn(i);
845                 for (j=0; j < numSons && !found; j++) {
846                     tempCols = fatherColsChildren[j];
847                     numColsSons = tempCols.length;
848                     for (k=0; k < numColsSons && ! found; k++) {
849                         if (tempCol.equals(tempCols[k]))
850                             found = true;
851                     }
852                 }
853                 colNeeded[i] = found;
854             }
855             fatherFinder = new DbSelector(table.getDatabase());
856             for (i=0; i < numCols; i++) {
857                 if (colNeeded[i])
858                     fatherFinder.addColumn(table.getColumn(i));
859                 else
860                     fatherFinder.addColumn(new Integer(0));
861             }
862             fatherFinder.setWhere(where);
863             fatherRecords = fatherFinder.execute();
864         }
865     }
866 
867     private void buildSearchSets() throws DbException {
868         int i, j, numSons, numCols;
869         TreeSet tempSet;
870         DbIterator fatherIt;
871         DbRow tempRow;
872         DbColumn[] cols;
873         ArrayComparator ac;
874         Comparable[] colValues;
875 
876         numSons = sonTables.length;
877         searchSets = new TreeSet[numSons];
878         ac = new ArrayComparator();
879         for (i=0; i < numSons; i++) {
880             cols = fatherColsChildren[i];
881             numCols = cols.length;
882             tempSet = new TreeSet(ac);
883             fatherIt = fatherRecords.iterator();
884             while (fatherIt.hasNextRow()) {
885                 colValues = new Comparable[numCols];
886                 tempRow = fatherIt.nextRow();
887                 for (j=0; j < numCols; j++)
888                     colValues[j] = (Comparable) tempRow.getValue(cols[j].getName());
889                 tempSet.add(colValues);
890             }
891             searchSets[i] = tempSet;
892         }
893     }
894 
895     private DbTable buildSonRecords(int numSon) throws DbException {
896         int i, numValues;
897         DbExpr tempExpr;
898         DbColumn[] cols;
899         DbSelector finder;
900         DbDatabase tempDb;
901 
902         tempDb = table.getDatabase();
903         cols = refColsChildren[numSon];
904         numValues = cols.length;
905         finder = new DbSelector(tempDb);
906         for (i=0; i < numValues; i++)
907             finder.addColumn(cols[i]);
908         finder.setWhere(sonsWhere[numSon]);
909         return finder.execute();
910     }
911 
912     private DbExpr buildSonWhere(int numSon) {
913         int i, numValues;
914         Iterator searchIt;
915         Comparable[] colValues;
916         DbExpr tempExpr, innerTempExpr;
917         DbColumn[] cols;
918         DbDatabase tempDb;
919 
920         searchIt = searchSets[numSon].iterator();
921         tempExpr = null;
922         tempDb = table.getDatabase();
923         cols = refColsChildren[numSon];
924         numValues = cols.length;
925         while (searchIt.hasNext()) {
926             colValues = (Comparable[]) searchIt.next();
927             innerTempExpr = null;
928             for (i=0; i < numValues; i++) {
929                 if (innerTempExpr != null)
930                     innerTempExpr = innerTempExpr.and(cols[i].equal(colValues[i]));
931                 else
932                     innerTempExpr = cols[i].equal(colValues[i]);
933             }
934             if (innerTempExpr != null) {
935                 innerTempExpr = new DbParenthesis(tempDb, innerTempExpr);
936                 if (tempExpr != null)
937                     tempExpr = tempExpr.or(innerTempExpr);
938                 else
939                     tempExpr = innerTempExpr;
940             }
941         }
942         return tempExpr;
943     }
944 
945     private void buildSonsWhereList() {
946         int i, numSons;
947 
948         numSons = sonTables.length;
949         sonsWhere = new DbExpr[numSons];
950         for (i=0; i < numSons; i++)
951             sonsWhere[i] = buildSonWhere(i);
952     }
953 
954     private void buildDeleteCascadeChildren() {
955         LinkedList tempList;
956         int i, numSons;
957         
958         tempList = new LinkedList();
959         numSons = sonTables.length;
960         for (i=0; i < numSons; i++)
961             if (deleteOperations[i].intValue() == CASCADE)
962                 tempList.add(new Integer(i));
963         deleteCascadeChildren = (Integer[]) tempList.toArray();
964     }
965     
966     private void buildUpdateCascadeChildren() {
967         LinkedList tempList;
968         int i, numSons;
969         
970         tempList = new LinkedList();
971         numSons = sonTables.length;
972         for (i=0; i < numSons; i++)
973             if (updateOperations[i].intValue() == CASCADE)
974                 tempList.add(new Integer(i));
975         updateCascadeChildren = (Integer[]) tempList.toArray();
976     }
977     
978     private void buildConstraints() throws DbException {
979         int i, numSons, refFather, operation;
980         DbAbstractTable tempTable;
981         DbConstraint tempConstraint;
982         List defList, defIntoList;
983         Object[] defValues;
984         
985         numSons = sonTables.length;
986         constraints = new DbConstraint[numSons];
987         for (i=0; i < numSons; i++) {
988             tempConstraint = null;
989             if (sonsWhere[i] != null) {
990                 tempTable = sonTables[i];
991                 try {
992                     if (tempTable != table) {
993                         if (!cloned)
994                             tempConstraint = tempTable.getConstraint();
995                         else
996                             tempConstraint = (DbConstraint) tempTable.getConstraint().clone();
997                     }
998                     else {
999                             tempConstraint = (DbConstraint) this.clone();
1000                    }
1001                }
1002                catch (CloneNotSupportedException e) {
1003                    tempConstraint = null;
1004                }
1005                if (tempConstraint != null) {
1006                    refFather = sonIndexes[i].intValue();
1007                    defList = null;
1008                    defIntoList = Arrays.asList(refColsChildren[i]);
1009                    if (currentOperation == DELETE_OPERATION)
1010                        operation = tempConstraint.getDeleteOperation(refFather);
1011                    else {
1012                        operation = tempConstraint.getUpdateOperation(refFather);
1013                        if (operation == CASCADE) {
1014                            defIntoList = updateIntoLists[i];
1015                            defList = updateFromLists[i];
1016                        }
1017                    }
1018                    if (operation == SET_DEFAULT) {
1019                        defValues = tempConstraint.getDefaultValues(i);
1020                        defList = Arrays.asList(defValues);
1021                    }
1022                    else if (operation == SET_NULL) {
1023                        defList = NullUtils.nullList(refColsChildren[i].length);
1024                    }
1025                    tempConstraint.setWhere(sonsWhere[i]);
1026                    if (defList != null) {
1027                        tempConstraint.setValueLists(defIntoList, defList);
1028                    }
1029                    else if (currentOperation == UPDATE_OPERATION && operation == CASCADE)
1030                        tempConstraint = null;
1031                }
1032            }
1033            constraints[i] = tempConstraint;
1034        }
1035    }
1036    
1037    private void buildDefaultValues() throws DbException {
1038        int i, j, numFathers, numRefs;
1039        
1040        numFathers = fatherTables.length;
1041        defaultValues = new Object[numFathers][];
1042        for (i=0; i < numFathers; i++) {
1043            numRefs = refColumns[i].length;
1044            defaultValues[i] = new Object[numRefs];
1045            for (j=0; j < numRefs; j++) {
1046                defaultValues[i][j] = table.getDefault(refColumns[i][j].getName());
1047            }
1048        }
1049    }
1050    
1051    private void buildUpdateLists() throws DbException {
1052        int i, j, numSons, numCols;
1053        boolean found;
1054        Iterator intoIt, fromIt;
1055        DbColumn tempColumn;
1056        DbColumn cols[];
1057        Object tempObject;
1058        
1059        numSons = sonTables.length;
1060        updateIntoLists = new ArrayList[numSons];
1061        updateFromLists = new ArrayList[numSons];
1062        for (i=0; i < numSons; i++) {
1063            updateIntoLists[i] = new ArrayList();
1064            updateFromLists[i] = new ArrayList();
1065        }
1066        intoIt = intoList.iterator();
1067        fromIt = fromList.iterator();
1068        while (intoIt.hasNext()) {
1069            tempColumn = (DbColumn) intoIt.next();
1070            tempObject = fromIt.next();
1071            for (i=0; i < numSons; i++) {
1072                found = false;
1073                cols = fatherColsChildren[i];
1074                numCols = cols.length;
1075                for (j=0; j < numCols && !found; j++)
1076                    if (cols[j].equals(tempColumn)) {
1077                        found = true;
1078                        updateIntoLists[i].add(refColsChildren[i][j]);
1079                        updateFromLists[i].add(tempObject);
1080                    }
1081            }
1082        }
1083        for (i=0; i < numSons; i++)
1084            if (updateIntoLists[i].size() == 0) {
1085                updateIntoLists[i] = null;
1086                updateFromLists[i] = null;
1087            }
1088    }
1089    
1090    private void optimizeConstraint(DbConstraint constraint, int operation) {
1091        DbDatabase tempDb;
1092        
1093        tempDb = table.getDatabase();
1094        if (currentOperation == DELETE_OPERATION) {
1095            switch (operation) {
1096                case CASCADE:
1097                    constraint.canDoUpdate = ! tempDb.getOnDeleteCascade();
1098                    break;
1099                case SET_DEFAULT:
1100                    constraint.canDoUpdate = ! tempDb.getOnDeleteSetDefault();
1101                    break;
1102                case SET_NULL:
1103                    constraint.canDoUpdate = ! tempDb.getOnDeleteSetNull();
1104            }
1105        }
1106        else {
1107            switch (operation) {
1108                case CASCADE:
1109                    constraint.canDoUpdate = ! tempDb.getOnUpdateCascade();
1110                    break;
1111                case SET_DEFAULT:
1112                    constraint.canDoUpdate = ! tempDb.getOnUpdateSetDefault();
1113                    break;
1114                case SET_NULL:
1115                    constraint.canDoUpdate = ! tempDb.getOnUpdateSetNull();
1116            }
1117        }
1118    }
1119    
1120    private boolean onlyNullValues(int numFather) {
1121        int i, numCols, tempRef;
1122        boolean response;
1123        
1124        numCols = refs[numFather].length;
1125        response = true;
1126        for (i=0; i < numCols && response; i++) {
1127            tempRef = refs[numFather][i].intValue();
1128            if (tempRef >= 0 && fromList.get(tempRef) != null)
1129                response = false;
1130        }
1131        return response;
1132    }
1133    
1134    
1135    /** Inits static lists, like indexes of son tables, default values of father tables
1136     * etc.
1137     * @throws DbException If something goes wrong.
1138     */    
1139    protected void initStaticLists() throws DbException {
1140        if (fatherTables != null)
1141            buildDefaultValues();
1142        if (sonTables != null)
1143            buildSonColsList();
1144        // buildDeleteCascadeChildren();
1145        // buildUpdateCascadeChildren();
1146    }
1147    
1148    /** Builds all dynamic lists (where clauses, constraints etc.).
1149     * @throws DbException If something goes wrong.
1150     */    
1151    protected void initLists() throws DbException {
1152        buildFatherRecords();
1153        buildSearchSets();
1154        buildSonsWhereList();
1155        if (intoList != null && fromList != null)
1156            buildUpdateLists();
1157        buildConstraints();
1158    }
1159}