/* * Copyright (C) 2003 Anthony Smith * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * ---------------------------------------------------------------------------- * TITLE $Id$ * --------------------------------------------------------------------------- * * --------------------------------------------------------------------------*/ package opendbcopy.plugin.model.database.dependency; import opendbcopy.config.XMLTags; import opendbcopy.plugin.model.database.DatabaseModel; import opendbcopy.plugin.model.database.exception.DependencyNotSolvableException; import opendbcopy.plugin.model.exception.MissingAttributeException; import opendbcopy.plugin.model.exception.MissingElementException; import opendbcopy.plugin.model.exception.UnsupportedAttributeValueException; import org.jdom.Element; import java.util.HashMap; import java.util.Iterator; import java.util.TreeMap; /** * class description * * @author Anthony Smith * @version $Revision$ */ public class Dependency { private static final int MAX_NUMBER_RECURSIONS = 500; private static final String ROOT_NODE = "root"; private DatabaseModel pluginModel; private Element db_element; private HashMap unsortedNodes; private TreeMap sortedNodes; private Node rootNode; private int nbrTables = 0; /** * Creates a new Dependency object. * * @param DatabasePluginModel DOCUMENT ME! * @param db_element DOCUMENT ME! * * @throws DependencyNotSolvableException DOCUMENT ME! * @throws MissingAttributeException DOCUMENT ME! * @throws UnsupportedAttributeValueException DOCUMENT ME! * @throws MissingElementException DOCUMENT ME! */ public Dependency(DatabaseModel DatabasePluginModel, Element db_element) throws DependencyNotSolvableException, MissingAttributeException, UnsupportedAttributeValueException, MissingElementException { this.pluginModel = DatabasePluginModel; this.db_element = db_element; this.unsortedNodes = new HashMap(); this.sortedNodes = new TreeMap(); removeOldProcessOrderAttributes(); setupRootNode(); traverseDependencies(); } /** * DOCUMENT ME! * * @throws MissingAttributeException DOCUMENT ME! * @throws UnsupportedAttributeValueException DOCUMENT ME! * @throws MissingElementException DOCUMENT ME! */ public final void setProcessOrder() throws MissingAttributeException, UnsupportedAttributeValueException, MissingElementException { Node table = null; Iterator itSortedTables = sortedNodes.values().iterator(); int processOrder = 0; while (itSortedTables.hasNext()) { table = (Node) itSortedTables.next(); if (pluginModel.getDbMode() == pluginModel.SINGLE_MODE) { pluginModel.getSourceTable(table.getName()).setAttribute(XMLTags.PROCESS_ORDER, Integer.toString(processOrder)); } else { pluginModel.getMappingDestinationTable(table.getName()).setAttribute(XMLTags.PROCESS_ORDER, Integer.toString(processOrder)); } processOrder++; } } /** * DOCUMENT ME! * * @throws UnsupportedAttributeValueException DOCUMENT ME! * @throws MissingAttributeException DOCUMENT ME! * @throws MissingElementException DOCUMENT ME! */ private void removeOldProcessOrderAttributes() throws UnsupportedAttributeValueException, MissingAttributeException, MissingElementException { Iterator itTables; Element table; if (pluginModel.getDbMode() == pluginModel.DUAL_MODE) { itTables = pluginModel.getMappingTables().iterator(); while (itTables.hasNext()) { table = (Element) itTables.next(); if (table.getAttributeValue(XMLTags.PROCESS_ORDER) != null) { table.removeAttribute(XMLTags.PROCESS_ORDER); } } } else { itTables = pluginModel.getSourceTables().iterator(); while (itTables.hasNext()) { table = (Element) itTables.next(); if (table.getAttributeValue(XMLTags.PROCESS_ORDER) != null) { table.removeAttribute(XMLTags.PROCESS_ORDER); } } } } /** * DOCUMENT ME! * * @throws DependencyNotSolvableException DOCUMENT ME! * @throws UnsupportedAttributeValueException DOCUMENT ME! * @throws MissingAttributeException DOCUMENT ME! * @throws MissingElementException DOCUMENT ME! */ private void traverseDependencies() throws DependencyNotSolvableException, UnsupportedAttributeValueException, MissingAttributeException, MissingElementException { this.nbrTables = getNbrTables(); addNodesToRootWithNoParentRelation(); // do not continue if all tables do not have parent relations if (getNbrTables() > sortedNodes.size()) { readUnsortedNodes(); boolean jobDone = false; int nbrLoops = 0; jobDone = completeTheGame(); while (!jobDone && (nbrLoops < MAX_NUMBER_RECURSIONS)) { jobDone = completeTheGame(); nbrLoops++; } if (nbrLoops == MAX_NUMBER_RECURSIONS) { throw new DependencyNotSolvableException(unsortedNodes, sortedNodes, nbrLoops, "Tried to resolve foreign key dependencies but cannot complete providing a final list of sorted tables. Check your foreign keys (referential integrity) for loops!"); } } } /** * DOCUMENT ME! */ private void setupRootNode() { rootNode = new Node(ROOT_NODE); } /** * DOCUMENT ME! * * @throws MissingAttributeException DOCUMENT ME! * @throws UnsupportedAttributeValueException DOCUMENT ME! * @throws MissingElementException DOCUMENT ME! */ private void addNodesToRootWithNoParentRelation() throws MissingAttributeException, UnsupportedAttributeValueException, MissingElementException { Iterator itTables = db_element.getChildren(XMLTags.TABLE).iterator(); Element table = null; String tableName = ""; while (itTables.hasNext()) { table = (Element) itTables.next(); tableName = table.getAttributeValue(XMLTags.NAME); // check if element needs to be processed if (pluginModel.getDbMode() == pluginModel.DUAL_MODE) { Element mappingTable = pluginModel.getMappingDestinationTable(tableName); if (mappingTable != null) { if (Boolean.valueOf(mappingTable.getAttributeValue(XMLTags.PROCESS)).booleanValue()) { if (getNbrImportedKeys(table) == 0) { Node node = new Node(tableName); rootNode.addChild(node); sortedNodes.put(new Integer(sortedNodes.size()), node); } else { unsortedNodes.put(tableName, new Node(tableName)); } } } } else { if (Boolean.valueOf(table.getAttributeValue(XMLTags.PROCESS)).booleanValue()) { if (getNbrImportedKeys(table) == 0) { Node node = new Node(tableName); rootNode.addChild(node); sortedNodes.put(new Integer(sortedNodes.size()), node); } else { unsortedNodes.put(tableName, new Node(tableName)); } } } } } /** * DOCUMENT ME! */ private void readUnsortedNodes() { if (unsortedNodes.size() > 0) { Node node = null; Element table = null; Iterator itUnsortedTables = unsortedNodes.values().iterator(); while (itUnsortedTables.hasNext()) { node = (Node) itUnsortedTables.next(); table = getTable(node.getName()); Iterator itImportedKeys = table.getChildren(XMLTags.IMPORTED_KEY).iterator(); while (itImportedKeys.hasNext()) { node.addParent(new Node(((Element) itImportedKeys.next()).getAttributeValue(XMLTags.PKTABLE_NAME))); } } } } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ private boolean completeTheGame() { if (unsortedNodes.size() > 0) { Node node = null; HashMap parents = null; int parentCounter = 0; Iterator itNodes = unsortedNodes.values().iterator(); while (itNodes.hasNext()) { node = (Node) itNodes.next(); parents = node.getParents(); if (parents != null) { Iterator itParents = parents.values().iterator(); boolean[] contains_parent = new boolean[parents.size()]; parentCounter = 0; while (itParents.hasNext()) { Node parent = (Node) itParents.next(); if (sortedNodes.containsValue(parent)) { contains_parent[parentCounter] = true; } else { if (node.findParent(node.getName()) != null) { contains_parent[parentCounter] = true; } else { contains_parent[parentCounter] = false; } } parentCounter++; } boolean all_parents_already_in_list = true; // if all parents are already within sortedNodes, this child can also be added to sortedNodes for (int i = 0; i < parents.size(); i++) { if (all_parents_already_in_list) { all_parents_already_in_list = contains_parent[i]; } } if (all_parents_already_in_list) { sortedNodes.put(new Integer(sortedNodes.size()), node); } } } removeSortedNodesFromUnsortedNodes(); } if (unsortedNodes.size() == 0) { return true; } else { return false; } } /** * DOCUMENT ME! */ private void removeSortedNodesFromUnsortedNodes() { Node node = null; Iterator itSortedNodes = sortedNodes.values().iterator(); while (itSortedNodes.hasNext()) { node = (Node) itSortedNodes.next(); if (unsortedNodes.containsValue(node)) { unsortedNodes.remove(node.getName()); } } } /** * DOCUMENT ME! * * @param tableName DOCUMENT ME! * * @return DOCUMENT ME! */ private Element getTable(String tableName) { Element table = null; Iterator itTables = db_element.getChildren(XMLTags.TABLE).iterator(); while (itTables.hasNext()) { table = (Element) itTables.next(); if (table.getAttributeValue(XMLTags.NAME).compareTo(tableName) == 0) { return table; } } return null; } /** * DOCUMENT ME! * * @return DOCUMENT ME! * * @throws MissingAttributeException DOCUMENT ME! * @throws UnsupportedAttributeValueException DOCUMENT ME! * @throws MissingElementException DOCUMENT ME! */ private int getNbrTables() throws MissingAttributeException, UnsupportedAttributeValueException, MissingElementException { int nbrTables = 0; Iterator itTables; if (pluginModel.getDbMode() == pluginModel.DUAL_MODE) { itTables = pluginModel.getMappingTables().iterator(); while (itTables.hasNext()) { if (Boolean.valueOf(((Element) itTables.next()).getAttributeValue(XMLTags.PROCESS)).booleanValue()) { nbrTables++; } } } else { itTables = pluginModel.getSourceTables().iterator(); while (itTables.hasNext()) { if (Boolean.valueOf(((Element) itTables.next()).getAttributeValue(XMLTags.PROCESS)).booleanValue()) { nbrTables++; } } } return nbrTables; } /** * DOCUMENT ME! * * @param table DOCUMENT ME! * * @return DOCUMENT ME! */ private int getNbrImportedKeys(Element table) { return table.getChildren(XMLTags.IMPORTED_KEY).size(); } /** * DOCUMENT ME! * * @param table DOCUMENT ME! * * @return DOCUMENT ME! */ private int getNbrExportedKeys(Element table) { return table.getChildren(XMLTags.EXPORTED_KEY).size(); } }