package edu.northwestern.bioinformatics.studycalendar.test;

import org.dbunit.dataset.filter.SequenceTableFilter;
import org.dbunit.dataset.DataSetException;
import org.dbunit.database.IDatabaseConnection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.DirectedGraph;
import org.jgrapht.traverse.TopologicalOrderIterator;
import org.jgrapht.alg.StrongConnectivityInspector;

import java.sql.SQLException;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

/**
 * @author Rhett Sutphin
 */
public class ForeignKeySequenceFilter extends SequenceTableFilter {
    private static final Log log = LogFactory.getLog(ForeignKeySequenceFilter.class);

    public ForeignKeySequenceFilter(IDatabaseConnection conn, String[] tableNames) throws SQLException {
        super(new Reorderer(conn, tableNames).newOrder());
    }

    public ForeignKeySequenceFilter(IDatabaseConnection conn) throws SQLException, DataSetException {
        this(conn, conn.createDataSet().getTableNames());
    }

    private static class Reorderer {
        private String[] originalTableNames;
        private IDatabaseConnection conn;
        private DatabaseMetaData metadata;
        private DirectedGraph<String, DefaultEdge> graph;

        public Reorderer(IDatabaseConnection conn, String[] tableNames) throws SQLException {
            this.conn = conn;
            this.originalTableNames = tableNames;
            this.metadata = conn.getConnection().getMetaData();
            initGraph();
        }

        public String[] newOrder() {
            StrongConnectivityInspector<String,DefaultEdge> inspector
                = new StrongConnectivityInspector<String, DefaultEdge>(graph);

            for (Set<String> set : inspector.stronglyConnectedSets()) {
                if (set.size() > 1) {
                    log.warn("Table set contains a circular FK dependency: " + set + ".  Order of these tables will be arbitrary.");
                }
            }

            Iterator<String> topo
                = new TopologicalOrderIterator<String, DefaultEdge>(graph);
            List<String> ordered = new ArrayList<String>(graph.vertexSet().size());
            while (topo.hasNext()) {
                ordered.add(topo.next());
            }

            return ordered.toArray(new String[ordered.size()]);
        }

        private void initGraph() throws SQLException {
            graph = new DefaultDirectedGraph<String, DefaultEdge>(DefaultEdge.class);

            for (String tableName : originalTableNames) {
                graph.addVertex(tableName);
            }

            for (String parent : originalTableNames) {
                for (String child : getChildTableNames(parent)) {
                    graph.addEdge(parent, child);
                }
            }
        }

        private Collection<String> getChildTableNames(String parent) throws SQLException {
            ResultSet resultSet = metadata.getExportedKeys(null, conn.getSchema(), parent);
            try {
                Set<String> foreignTableSet = new HashSet<String>();

                while (resultSet.next()) {
                    foreignTableSet.add(resultSet.getString(7));
                }

                return foreignTableSet;
            } finally {
                resultSet.close();
            }
        }
    }

}

@@ -80,38 +80,32 @@
 data
-set.filter.SequenceTableFilter
+base.IDatabaseConnection
 ;%0Aim
@@ -172,155 +172,124 @@
 data
-base.IDatabaseConnection;%0Aimport org.apache.commons.logging.Log;%0Aimport org.apache.commons.logging.LogFactory;%0Aimport org.jgrapht.graph.DefaultEdge
+set.filter.SequenceTableFilter;%0Aimport org.jgrapht.DirectedGraph;%0Aimport org.jgrapht.alg.StrongConnectivityInspector
 ;%0Aim
@@ -348,37 +348,41 @@
 org.jgrapht.
-DirectedGraph
+graph.DefaultEdge
 ;%0Aimport org
@@ -429,90 +429,8 @@
 or;%0A
-import org.jgrapht.alg.StrongConnectivityInspector;%0A%0Aimport java.sql.SQLException;
 %0Aimp
@@ -483,24 +483,82 @@
 .ResultSet;%0A
+import java.sql.SQLException;%0Aimport java.util.ArrayList;%0A
 import java.
@@ -578,30 +578,8 @@
 on;%0A
-import java.util.Set;%0A
 impo
@@ -617,20 +617,24 @@
 va.util.
-List
+Iterator
 ;%0Aimport
@@ -636,37 +636,32 @@
 mport java.util.
-Array
 List;%0Aimport jav
@@ -671,21 +671,509 @@
 til.
-Iterator;%0A%0A/*
+Set;%0A%0A/**%0A * This is conceptually similar to dbunit's %7B@link org.dbunit.database.DatabaseSequenceFilter%7D%0A * (it works as a drop-in replacement, in fact).  The difference is that it uses a topological%0A * sort of a directed graph of the relationships instead of the iterative approach dbunit takes.%0A * This seems to work better.%0A * %3Cp%3E%0A * Note that reordering will fail with an IllegalStateException if there are dependency loops%0A * in the given list of tables.  This may be correctable, if necessary.%0A 
 *%0A *
@@ -1271,95 +1271,8 @@
 r %7B%0A
-    private static final Log log = LogFactory.getLog(ForeignKeySequenceFilter.class);%0A%0A
     
@@ -2426,17 +2426,65 @@
     
-log.warn(
+throw new IllegalStateException(%0A                        
 %22Tab
@@ -2543,47 +2543,22 @@
 %22.  
-Order of these tables will be arbitrary
+Cannot reorder
 .%22);
@@ -2819,34 +2819,16 @@
 sNext())
- %7B%0A               
  ordered
@@ -2845,30 +2845,16 @@
 next());
-%0A            %7D
 %0A%0A      
@@ -3509,24 +3509,17 @@
 ultSet r
-esultSet
+s
  = metad
@@ -3617,31 +3617,24 @@
 String%3E 
-foreignTableSet
+children
  = new H
@@ -3651,17 +3651,16 @@
 ing%3E();%0A
-%0A
         
@@ -3675,24 +3675,17 @@
 while (r
-esultSet
+s
 .next())
@@ -3689,132 +3689,70 @@
 ()) 
-%7B%0A                    foreignTableSet.add(resultSet.getString(7));%0A                %7D%0A%0A                return foreignTableSet
+children.add(rs.getString(7));%0A                return children
 ;%0A  
@@ -3790,24 +3790,17 @@
        r
-esultSet
+s
 .close()

Throw exception on cycles; comment about algorithm, purpose