All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dbunit.util.search.DepthFirstSearch Maven / Gradle / Ivy

The newest version!
/*
 *
 * The DbUnit Database Testing Framework
 * Copyright (C)2005-2008, DbUnit.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

package org.dbunit.util.search;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;

import org.apache.commons.collections.set.ListOrderedSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.dbunit.util.CollectionsHelper;

/**
 * Search using depth-first algorithm.
*
* An instance of this class must be used only once, as it maintains the * internal state of the search.
*
* * @author gommma (gommma AT users.sourceforge.net) * @author Last changed by: $Author$ * @version $Revision$ $Date$ * @since 2.4.0 */ public class DepthFirstSearch implements ISearchAlgorithm { // nodes that were already scanned during the search private Set scannedNodes; private Set reverseScannedNodes; protected final Logger logger = LoggerFactory.getLogger(getClass()); // result of the search private ListOrderedSet result; // input of the search private Set nodesFrom; // callback used to help the search private ISearchCallback callback; // flag, as one instance cannot be used more than once private boolean searching = false; /** * The search depth to be used when recursing through the child nodes */ private int searchDepth = Integer.MAX_VALUE; /** * Creates a new depth-first algorithm using the maximum search depth for recursing over the nodes. */ public DepthFirstSearch() { super(); } /** * Creates a new depth-first algorithm * @param searchDepth The search depth to be used when traversing the nodes recursively. Must be > 0. * @since 2.4 */ public DepthFirstSearch(int searchDepth) { super(); if(searchDepth<=0){ throw new IllegalArgumentException("The searchDepth must be > 0. Given: " + searchDepth); } this.searchDepth = searchDepth; } /** * Alternative option to search() that takes an array of nodes as input (instead of a Set) * @see ISearchAlgorithm */ public ListOrderedSet search(Object[] nodesFrom, ISearchCallback callback) throws SearchException { if(logger.isDebugEnabled()) logger.debug("search(nodesFrom={}, callback={}) - start", nodesFrom, callback); return search(CollectionsHelper.objectsToSet(nodesFrom), callback); } /** * @see ISearchAlgorithm */ public ListOrderedSet search(Set nodesFrom, ISearchCallback callback) throws SearchException { if(logger.isDebugEnabled()) logger.debug("search(nodesFrom={}, callback={}) - start", nodesFrom, callback); synchronized (this) { if (searching) { throw new IllegalStateException("already searching/searched"); } this.searching = true; } // set of tables that will be returned (i.e, the declared tables and its // dependencies) this.result = new ListOrderedSet(); // callback used to help the search this.callback = callback; this.nodesFrom = new ListOrderedSet(); int sizeNodesFromBefore = 0; int sizeResultBefore = 0; boolean keepSearching = true; this.scannedNodes = new HashSet(); this.reverseScannedNodes = new HashSet(); this.scannedNodes = new HashSet(); do { // In a traditional depth-first search, the getEdges() method should return only // edges where this node is the 'from' vertex, as the graph is known in advance. // But in our case, the graph is built 'on the fly', so it's possible that the // getEdges() also returns edges where the node is the 'to' vertex. // So, before we do the "real" search, we need to do a reverse search to find out // all the nodes that should be part of the input. Iterator iterator = nodesFrom.iterator(); while (iterator.hasNext()) { Object node = iterator.next(); reverseSearch(node, 0); } // this.nodesFrom = nodesFrom; // now that the input is adjusted, do the search iterator = this.nodesFrom.iterator(); while (iterator.hasNext()) { Object node = iterator.next(); search(node, 0); } nodesFrom = new HashSet(this.result); // decides if we continue searching boolean sizesDontMatch = this.result.size() != this.nodesFrom.size(); boolean resultChanged = this.result.size() != sizeResultBefore; boolean nodesFromChanged = this.nodesFrom.size() != sizeNodesFromBefore; sizeNodesFromBefore = this.nodesFrom.size(); sizeResultBefore = this.result.size(); keepSearching = sizesDontMatch && ( resultChanged || nodesFromChanged ); } while ( keepSearching ); return this.result; } /** * This is the real depth first search algorithm, which is called recursively. * * @param node node where the search starts * @param currentSearchDepth the search depth in the recursion * @return true if the node has been already searched before * @throws Exception if an exception occurs while getting the edges */ private boolean search(Object node, int currentSearchDepth) throws SearchException { if ( this.logger.isDebugEnabled() ) { this.logger.debug( "search:" + node ); } if (this.scannedNodes.contains(node)) { if ( this.logger.isDebugEnabled() ) { this.logger.debug( "already searched; returning true" ); } return true; } if (!this.callback.searchNode(node)) { if ( this.logger.isDebugEnabled() ) { this.logger.debug( "Callback handler blocked search for node " + node ); } return true; } if ( this.logger.isDebugEnabled() ) { this.logger.debug("Pushing " + node); } this.scannedNodes.add(node); if(currentSearchDepth < this.searchDepth) { // first, search the nodes the node depends on SortedSet edges = this.callback.getEdges(node); if (edges != null) { Iterator iterator = edges.iterator(); while (iterator.hasNext()) { // and recursively search these nodes IEdge edge = (IEdge) iterator.next(); Object toNode = edge.getTo(); search(toNode, currentSearchDepth++); } } } // finally, add the node to the result if ( this.logger.isDebugEnabled() ) { this.logger.debug( "Adding node " + node + " to the final result" ); } // notify the callback a node was added this.callback.nodeAdded(node); result.add(node); return false; } /** * Do a reverse search (i.e, searching the other way of the edges) in order * to adjust the input before the real search. * @param node node where the search starts * @param currentSearchDepth the search depth in the recursion * @return true if the node has been already reverse-searched before * @throws Exception if an exception occurs while getting the edges */ private boolean reverseSearch(Object node, int currentSearchDepth) throws SearchException { if ( this.logger.isDebugEnabled() ) { this.logger.debug( "reverseSearch:" + node ); } if (this.reverseScannedNodes.contains(node)) { if ( this.logger.isDebugEnabled() ) { this.logger.debug( "already searched; returning true" ); } return true; } if (!this.callback.searchNode(node)) { if ( this.logger.isDebugEnabled() ) { this.logger.debug( "callback handler blocked reverse search for node " + node ); } return true; } if ( this.logger.isDebugEnabled() ) { this.logger.debug("Pushing (reverse) " + node); } this.reverseScannedNodes.add(node); if(currentSearchDepth < this.searchDepth) { // first, search the nodes the node depends on SortedSet edges = this.callback.getEdges(node); if (edges != null) { Iterator iterator = edges.iterator(); while (iterator.hasNext()) { // and recursively search these nodes if we find a match IEdge edge = (IEdge) iterator.next(); Object toNode = edge.getTo(); if ( toNode.equals(node) ) { Object fromNode = edge.getFrom(); reverseSearch(fromNode, currentSearchDepth++); } } } } // finally, add the node to the input this.nodesFrom.add(node); return false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy