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

com.ibm.wala.util.graph.traverse.BFSPathFinder Maven / Gradle / Ivy

There is a newer version: 1.6.6
Show newest version
/*
 * Copyright (c) 2002 - 2006 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 */
package com.ibm.wala.util.graph.traverse;

import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.graph.Graph;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;

/**
 * This class searches breadth-first for node that matches some criteria. If found, it reports a
 * path to the first node found.
 *
 * 

This class follows the outNodes of the graph nodes to define the graph, but this behavior can * be changed by overriding the getConnected method. * *

TODO: if finding many paths, use a dynamic programming algorithm instead of calling this * repeatedly. */ public class BFSPathFinder { private final boolean DEBUG = false; /** The graph to search */ private final Graph G; /** The Filter which defines the target set of nodes to find */ private final Predicate filter; /** an enumeration of all nodes to search from */ private final Iterator roots; /** * Construct a breadth-first enumerator starting with a particular node in a directed graph. * * @param G the graph whose nodes to enumerate */ public BFSPathFinder(Graph G, T N, Predicate f) { if (G == null) { throw new IllegalArgumentException("G is null"); } if (f == null) { throw new IllegalArgumentException("null f"); } this.G = G; this.roots = new NonNullSingletonIterator<>(N); this.filter = f; } /** * Construct a breadth-first enumerator starting with a particular node in a directed graph. * * @param G the graph whose nodes to enumerate * @throws IllegalArgumentException if G is null */ public BFSPathFinder(Graph G, T src, final T target) throws IllegalArgumentException { if (G == null) { throw new IllegalArgumentException("G is null"); } this.G = G; this.roots = new NonNullSingletonIterator<>(src); if (!G.containsNode(src)) { throw new IllegalArgumentException("src is not in graph " + src); } this.filter = target::equals; } /** * Construct a breadth-first enumerator starting with a particular node in a directed graph. * * @param G the graph whose nodes to enumerate */ public BFSPathFinder(Graph G, T src, Iterator targets) { if (targets == null) { throw new IllegalArgumentException("targets is null"); } final Set ts = HashSetFactory.make(); while (targets.hasNext()) { ts.add(targets.next()); } this.G = G; this.roots = new NonNullSingletonIterator<>(src); this.filter = ts::contains; } /** * Construct a breadth-first enumerator starting with any of a set of nodes in a directed graph. * * @param G the graph whose nodes to enumerate */ public BFSPathFinder(Graph G, Iterator sources, final T target) { if (G == null) { throw new IllegalArgumentException("G is null"); } if (sources == null) { throw new IllegalArgumentException("sources is null"); } this.G = G; this.roots = sources; this.filter = target::equals; } /** * Construct a breadth-first enumerator across the (possibly improper) subset of nodes reachable * from the nodes in the given enumeration. * * @param nodes the set of nodes from which to start searching */ public BFSPathFinder(Graph G, Iterator nodes, Predicate f) { this.G = G; this.roots = nodes; this.filter = f; if (G == null) { throw new IllegalArgumentException("G is null"); } if (roots == null) { throw new IllegalArgumentException("roots is null"); } } private @Nullable ArrayDeque Q = null; private @Nullable HashMap history = null; /** * @return a List of nodes that specifies the first path found from a root to a node accepted by * the filter. Returns null if no path found. */ @NullUnmarked public @Nullable List find() { if (Q == null) { Q = new ArrayDeque<>(); history = HashMapFactory.make(); while (roots.hasNext()) { T next = roots.next(); Q.addLast(next); history.put(next, null); } } while (!Q.isEmpty()) { T N = Q.removeFirst(); if (DEBUG) { System.err.println(("visit " + N)); } if (filter.test(N)) { return makePath(N, history); } Iterator children = getConnected(N); while (children.hasNext()) { T c = children.next(); if (!history.containsKey(c)) { Q.addLast(c); history.put(c, N); } } } return null; } /** * @return a List which represents a path in the breadth-first search to Q[i]. Q holds the nodes * visited during the BFS, in order. */ @NullUnmarked private List makePath(T node, @Nullable Map history) { ArrayList result = new ArrayList<>(); T n = node; result.add(n); while (true) { T parent = history.get(n); if (parent == null) return result; else { result.add(parent); n = parent; } } } /** * get the out edges of a given node * * @param n the node of which to get the out edges * @return the out edges */ protected Iterator getConnected(T n) { return G.getSuccNodes(n); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy