1 salcianu 1.1.2.1 // SCComponent.java, created Mon Jan 24 19:26:30 2000 by salcianu
  2 cananian 1.1.2.6 // Copyright (C) 2000 Alexandru SALCIANU <salcianu@retezat.lcs.mit.edu>
  3 salcianu 1.1.2.1 // Licensed under the terms of the GNU GPL; see COPYING for details.
  4 salcianu 1.1.2.1 package harpoon.Util.Graphs;
  5 salcianu 1.1.2.1 
  6 cananian 1.11    import java.util.ArrayList;
  7 cananian 1.11    import java.util.Arrays;
  8 cananian 1.11    import java.util.Collections;
  9 salcianu 1.12    import java.util.Set;
 10 salcianu 1.1.2.1 import java.util.HashSet;
 11 salcianu 1.15    import harpoon.Util.ArraySet;
 12 salcianu 1.12    import java.util.Map;
 13 salcianu 1.12    import java.util.HashMap;
 14 cananian 1.11    import java.util.Iterator;
 15 cananian 1.11    import java.util.List;
 16 cananian 1.11    import java.util.Vector;
 17 salcianu 1.12    import java.util.Collection;
 18 salcianu 1.14    import java.util.Comparator;
 19 salcianu 1.1.2.1 
 20 salcianu 1.1.2.5 import java.io.Serializable;
 21 salcianu 1.1.2.1 
 22 salcianu 1.12    import harpoon.Util.DataStructs.Relation;
 23 salcianu 1.12    import harpoon.Util.DataStructs.RelationImpl;
 24 salcianu 1.1.2.1 
 25 salcianu 1.1.2.1 import harpoon.Util.UComp;
 26 salcianu 1.1.2.1 
 27 salcianu 1.1.2.1 import harpoon.Util.Util;
 28 salcianu 1.1.2.1 
 29 salcianu 1.1.2.1 /**
 30 cananian 1.8      * <code>SCComponent</code> models a <i>Strongly connected component</i>
 31 cananian 1.8      * of a graph.
 32 cananian 1.8      * The only way to split a graph into <code>SCComponent</code>s is through
 33 salcianu 1.12     * <code>buildSCC</code>.
 34 salcianu 1.1.2.1  * That method is quite flexible: all it needs is a root node (or a set of
 35 salcianu 1.1.2.1  * root nodes) and a <i>Navigator</i>: an object implementing the 
 36 salcianu 1.7      * <code>Navigator</code> interface that provides the 
 37 salcianu 1.1.2.1  * edges coming into/going out of a given <code>Object</code>. So, it can
 38 salcianu 1.1.2.1  * build strongly connected components even for graphs that are not built
 39 salcianu 1.1.2.1  * up from <code>CFGraphable</code> nodes, a good example being the set of
 40 salcianu 1.1.2.1  * methods where the edges represent the caller-callee relation (in this
 41 salcianu 1.1.2.1  * case, the strongly connected components group together sets of mutually
 42 salcianu 1.1.2.1  * recursive methods).
 43 salcianu 1.1.2.1  * 
 44 salcianu 1.12     * @author  Alexandru SALCIANU <salcianu@lcs.mit.edu>
 45 salcianu 1.15     * @version $Id: SCComponent.java,v 1.15 2004/03/06 21:52:32 salcianu Exp $
 46 salcianu 1.1.2.1  */
 47 salcianu 1.13    public final class SCComponent/*<Vertex>*/
 48 salcianu 1.13        implements Comparable<SCComponent/*<Vertex>*/>, Serializable {
 49 salcianu 1.1.2.1 
 50 salcianu 1.7         /** Default navigator through a component graph (a dag of strongly
 51 salcianu 1.7             connected components).  */
 52 salcianu 1.12        public static final Navigator<SCComponent> SCC_NAVIGATOR =
 53 salcianu 1.12            new Navigator<SCComponent>() {
 54 salcianu 1.12                public SCComponent[] next(SCComponent node) {
 55 salcianu 1.12                    return node.next;
 56 salcianu 1.12                }
 57 salcianu 1.12                public SCComponent[] prev(SCComponent node) {
 58 salcianu 1.12                    return node.prev;
 59 salcianu 1.12                }
 60 salcianu 1.12            };
 61 salcianu 1.12    
 62 salcianu 1.7     
 63 salcianu 1.1.2.1     // THE FIRST PART CONTAINS JUST SOME STATIC METHODS & FIELDS
 64 salcianu 1.1.2.1 
 65 salcianu 1.7         /** Convenient version of
 66 salcianu 1.7             <code>buildSCC(Object[],Navigator)</code>
 67 salcianu 1.7     
 68 salcianu 1.7             @param graph directed graph
 69 salcianu 1.7     
 70 salcianu 1.12            @return set of top-level strongly connected components (ie,
 71 salcianu 1.12            SCCs that are not pointed to by anybody). */
 72 salcianu 1.12        public static final /*<Vertex>*/ Set<SCComponent/*<Vertex>*/>
 73 salcianu 1.12            buildSCC(final DiGraph/*<Vertex>*/ graph) {
 74 salcianu 1.12            return buildSCC(graph.getRoots(), graph.getNavigator());
 75 salcianu 1.7         }
 76 salcianu 1.7         
 77 salcianu 1.7     
 78 salcianu 1.7         /** Convenient version of
 79 salcianu 1.7             <code>buildSCC(Object[],Navigator)</code>
 80 salcianu 1.7     
 81 salcianu 1.13            @param roots collection of nodes such that if we start from
 82 salcianu 1.13            these nodes and (transitively) navigate on the outgoing edges,
 83 salcianu 1.13            we explore the entire graph.
 84 salcianu 1.7     
 85 salcianu 1.7             @param navigator navigator for exploring the graph
 86 salcianu 1.7     
 87 salcianu 1.7             @return set of strongly connected components of the graph
 88 salcianu 1.7             defined by <code>roots</code> and <code>navigator</code> */
 89 salcianu 1.12        public static final /*<Vertex>*/ Set<SCComponent/*<Vertex>*/>
 90 salcianu 1.13            buildSCC(final Collection/*<Vertex>*/ roots,
 91 salcianu 1.12                     final Navigator/*<Vertex>*/ navigator) {
 92 salcianu 1.12            return 
 93 salcianu 1.13                (new BuildSCCClosure/*<Vertex>*/()).doIt(roots, navigator);
 94 salcianu 1.7         }
 95 salcianu 1.7     
 96 salcianu 1.12        // OO programming is great, especially when one encodes functional
 97 salcianu 1.12        // programming into it :)
 98 salcianu 1.12        // BuildSCCClosure.doIt computes the strongly connected
 99 salcianu 1.12        // components, using a DFS-based algorithm from CLR (page 488).
100 salcianu 1.12        private static class BuildSCCClosure/*<Vertex>*/ {
101 salcianu 1.12            // set of nodes reachable from root
102 salcianu 1.12            private Set/*<Vertex>*/ reachable_nodes;
103 salcianu 1.12            // set of reached nodes (to avoid reanalyzing them "ad infinitum")
104 salcianu 1.12            private Set/*<Vertex>*/ visited_nodes;
105 salcianu 1.12            // Mapping vertex -> SCComponent
106 salcianu 1.12            private HashMap/*<Vertex,SCComponent<Vertex>>*/ v2scc;
107 salcianu 1.12            // The vector of the reached nodes, in the order DFS finished them
108 salcianu 1.12            private Vector/*<Vertex>*/ nodes_vector;
109 salcianu 1.12            // vector to put the generated SCCs in.
110 salcianu 1.12            private Vector/*<SCComponent>*/ scc_vector;
111 salcianu 1.12    
112 salcianu 1.12            // currently generated strongly connected component
113 salcianu 1.12            private SCComponent current_scc = null;
114 salcianu 1.12            // the navigator used in the DFS algorithm
115 salcianu 1.12            private Navigator/*<Vertex>*/ nav = null;
116 salcianu 1.12    
117 salcianu 1.12            // does the real work behind SCComponent.buildSCC
118 salcianu 1.12            public final /*<Vertex>*/ Set<SCComponent/*<Vertex>*/> doIt
119 salcianu 1.13                (final Collection/*<Vertex>*/ roots,
120 salcianu 1.12                 final Navigator/*<Vertex>*/ navigator) {
121 salcianu 1.12                scc_vector     = new Vector();
122 salcianu 1.12                visited_nodes  = new HashSet();
123 salcianu 1.12                nodes_vector   = new Vector();
124 salcianu 1.12                v2scc          = new HashMap();
125 salcianu 1.12    
126 salcianu 1.12                // STEP 1: DFS exploration; add all reached nodes in
127 salcianu 1.12                // "nodes_vector", in the order of their "finished" time.
128 salcianu 1.13                direct_dfs(roots, navigator);
129 salcianu 1.12    
130 salcianu 1.12                // STEP 2. build the SCCs by doing a DFS in the reverse graph.
131 salcianu 1.12                reverse_dfs(navigator);
132 salcianu 1.1.2.1 
133 salcianu 1.12                // produce the final formal SCCs
134 salcianu 1.12                build_final_sccs(navigator);
135 salcianu 1.12                
136 salcianu 1.13                return get_root_sccs(roots);
137 salcianu 1.1.2.1         }
138 salcianu 1.1.2.1 
139 salcianu 1.1.2.1 
140 salcianu 1.13            private final void direct_dfs
141 salcianu 1.13                (Collection/*<Vertex>*/ roots, Navigator/*<Vertex>*/ navigator) {
142 salcianu 1.12                nav = navigator;
143 salcianu 1.13                for(Object vertex : roots) 
144 salcianu 1.13                    DFS_first(vertex);
145 salcianu 1.1.2.1         }
146 salcianu 1.1.2.1 
147 salcianu 1.12            // DFS for the first step: the "forward" navigation
148 salcianu 1.12            private final void DFS_first(Object node) {
149 salcianu 1.12                // skip already visited nodes
150 salcianu 1.12                if(visited_nodes.contains(node)) return;
151 salcianu 1.1.2.1 
152 salcianu 1.12                visited_nodes.add(node);
153 salcianu 1.12                
154 salcianu 1.12                Object[] next = nav.next(node);
155 salcianu 1.12                for(int i = 0 ; i < next.length ; i++)
156 salcianu 1.12                    DFS_first(next[i]);
157 salcianu 1.12                
158 salcianu 1.12                nodes_vector.add(node);
159 salcianu 1.12            }
160 salcianu 1.1.2.1 
161 salcianu 1.1.2.1 
162 salcianu 1.12            private final void reverse_dfs(Navigator/*<Vertex>*/ navigator) {
163 salcianu 1.12                // "in reverse" navigator
164 salcianu 1.12                nav = new ReverseNavigator/*<Vertex>*/(navigator);
165 salcianu 1.12    
166 salcianu 1.12                // Explore the vertices in the decreasing order of their
167 salcianu 1.12                // "finished" time.  For each unvisited vertex, grab all
168 salcianu 1.12                // vertices reachable on the reverse navigator.  No
169 salcianu 1.12                // inter-SCC edges yet.  Put SCCs in scc_vector.
170 salcianu 1.12                reachable_nodes = visited_nodes;
171 salcianu 1.12                visited_nodes = new HashSet();
172 salcianu 1.12                for(int i = nodes_vector.size() - 1; i >= 0; i--){
173 salcianu 1.12                    Object node = nodes_vector.elementAt(i);
174 salcianu 1.12                    // explore nodes that are still unanalyzed
175 salcianu 1.12                    if(!visited_nodes.contains(node)) {
176 salcianu 1.12                        current_scc = new SCComponent();
177 salcianu 1.12                        scc_vector.add(current_scc);
178 salcianu 1.12                        DFS_second(node);
179 salcianu 1.1.2.1                 }
180 salcianu 1.1.2.1             }
181 salcianu 1.1.2.1         }
182 salcianu 1.1.2.1 
183 salcianu 1.12            // DFS for the second step: the "backward" navigation.
184 salcianu 1.12            private final void DFS_second(Object node) {
185 salcianu 1.12                // only nodes reachable from root nodes count: we make
186 salcianu 1.12                // sure that navigator.prev does not take us to strange
187 salcianu 1.12                // places!
188 salcianu 1.12                if(visited_nodes.contains(node) ||
189 salcianu 1.12                   !reachable_nodes.contains(node)) return;
190 salcianu 1.12                
191 salcianu 1.12                visited_nodes.add(node);
192 salcianu 1.12    
193 salcianu 1.12                v2scc.put(node, current_scc);
194 salcianu 1.12                current_scc.nodes.add(node);
195 salcianu 1.12                
196 salcianu 1.12                Object[] next = nav.next(node);
197 salcianu 1.12                for(int i = 0 ; i < next.length ; i++)
198 salcianu 1.12                    DFS_second(next[i]);
199 salcianu 1.9             }
200 salcianu 1.12        
201 salcianu 1.9     
202 salcianu 1.12            // Build the final SCC.  This requires converting some sets to
203 salcianu 1.12            // arrays (and sorting them in the deterministic case).
204 salcianu 1.12            private final void build_final_sccs(Navigator/*<Vertex>*/ navigator) {
205 salcianu 1.12                nextRel        = new RelationImpl();
206 salcianu 1.12                prevRel        = new RelationImpl();
207 salcianu 1.12                scc2exits      = new RelationImpl();
208 salcianu 1.12                scc2entries    = new RelationImpl();
209 salcianu 1.9     
210 salcianu 1.12                // Put inter-SCCs edges.
211 salcianu 1.12                collect_edges(navigator);
212 salcianu 1.12                
213 salcianu 1.12                for(Iterator it = scc_vector.iterator(); it.hasNext(); ) {
214 salcianu 1.12                    SCComponent scc = (SCComponent) it.next();
215 salcianu 1.9     
216 salcianu 1.15                    // We make a compromise between speed of membership
217 salcianu 1.15                    // testing, and memory consumption (having too many
218 salcianu 1.15                    // HashSets is BAD ...)
219 salcianu 1.15                    scc.nodes = 
220 salcianu 1.15                        (scc.nodes.size() > 10) ?
221 salcianu 1.15                        ((Set) new HashSet(scc.nodes)) :
222 salcianu 1.15                        ((Set) new ArraySet(scc.nodes.toArray()));
223 salcianu 1.15    
224 salcianu 1.12                    // add the edges
225 salcianu 1.12                    scc.next = toArraySCC(nextRel.getValues(scc));
226 salcianu 1.12                    scc.prev = toArraySCC(prevRel.getValues(scc));
227 salcianu 1.12                    // add the entries / exits
228 salcianu 1.12                    scc.entries = toArrayVertex(scc2entries.getValues(scc));
229 salcianu 1.12                    scc.exits   = toArrayVertex(scc2exits.getValues(scc));
230 salcianu 1.12                }
231 salcianu 1.12            }
232 salcianu 1.9     
233 salcianu 1.12            // relation vertex -> successor vertices
234 salcianu 1.12            private Relation/*<SCComponent,SCComponent>*/ nextRel;
235 salcianu 1.12            // scc -> set of exit vertices
236 salcianu 1.12            private Relation/*<SCComponent,Vertex>*/ scc2exits;
237 salcianu 1.12            // relation vertex -> predecessor vertices
238 salcianu 1.12            private Relation/*<SCComponent,SCComponent>*/ prevRel;
239 salcianu 1.12            // scc -> set of entry vertices
240 salcianu 1.12            private Relation/*<SCComponent,Vertex>*/ scc2entries;
241 salcianu 1.12    
242 salcianu 1.12    
243 salcianu 1.12            // Collect inter-SCCs edges in nextRel/prevRel: there is an
244 salcianu 1.12            // edge from scc1 to scc2 iff there is at least one pair of
245 salcianu 1.12            // nodes n1 in scc1 and n2 in scc2 such that there exists an
246 salcianu 1.12            // edge from n1 to n2.
247 salcianu 1.12            private final void collect_edges
248 salcianu 1.12                (final Navigator/*<Vertex>*/ navigator) {
249 salcianu 1.12                for(Iterator itscc = scc_vector.iterator(); itscc.hasNext(); ) {
250 salcianu 1.12                    SCComponent scc1 = (SCComponent) itscc.next();
251 salcianu 1.12    
252 salcianu 1.12                    for(Iterator itv = scc1.nodes.iterator(); itv.hasNext(); ) {
253 salcianu 1.12                        Object v1 = itv.next();
254 salcianu 1.12    
255 salcianu 1.12                        Object[] edges = navigator.next(v1);
256 salcianu 1.12                        for(int j = 0; j < edges.length; j++) {
257 salcianu 1.12                            Object v2 = edges[j];
258 salcianu 1.12    
259 salcianu 1.12                            SCComponent scc2 = (SCComponent) v2scc.get(v2);
260 salcianu 1.12                            if(scc1 == scc2) {
261 salcianu 1.12                                scc1.loop = true;
262 salcianu 1.12                            }
263 salcianu 1.12                            else {
264 salcianu 1.12                                nextRel.add(scc1, scc2);
265 salcianu 1.12                                prevRel.add(scc2, scc1);
266 salcianu 1.12                                scc2exits.add(scc1, v1);
267 salcianu 1.12                                scc2entries.add(scc2, v2);
268 salcianu 1.12                            }
269 salcianu 1.12                        }
270 salcianu 1.9                     }
271 salcianu 1.12                }
272 salcianu 1.9             }
273 salcianu 1.9     
274 salcianu 1.12            private final SCComponent[] toArraySCC
275 salcianu 1.12                (final Collection/*<SCComponent>*/ c) {
276 salcianu 1.12                return (SCComponent[]) c.toArray(new SCComponent[c.size()]);
277 salcianu 1.12            }
278 salcianu 1.12    
279 salcianu 1.12            private final Object[]/*Vertex[]*/ toArrayVertex
280 salcianu 1.12                (final Collection/*<Vertex>*/ c) {
281 salcianu 1.12                return /*(Vertex[])*/ c.toArray(new Object[c.size()]);
282 salcianu 1.12            }
283 salcianu 1.12    
284 salcianu 1.12            // Compute set of root SCCs.
285 salcianu 1.13            private final Set<SCComponent/*<Vertex>*/> 
286 salcianu 1.13                get_root_sccs(Collection/*<Vertex>*/ roots) {
287 salcianu 1.12                Set<SCComponent/*<Vertex>*/> root_sccs = new HashSet();
288 salcianu 1.13                for(Object root_vertex : roots) {
289 salcianu 1.13                    SCComponent scc = (SCComponent) v2scc.get(root_vertex);
290 salcianu 1.12                    if(scc.prevLength() == 0)
291 salcianu 1.12                        root_sccs.add(scc);
292 salcianu 1.12                }
293 salcianu 1.12                return root_sccs;
294 salcianu 1.1.2.1         }
295 salcianu 1.1.2.1     }
296 salcianu 1.1.2.1 
297 salcianu 1.1.2.1 
298 salcianu 1.1.2.1     // HERE STARTS THE REAL (i.e. NON STATIC) CLASS
299 salcianu 1.1.2.1 
300 salcianu 1.12        // The only way to produce SCCs is through SCComponent.buildSSC !
301 salcianu 1.12        private SCComponent() { id = count++; }
302 salcianu 1.1.2.1     private static int count = 0;
303 salcianu 1.1.2.1 
304 salcianu 1.1.2.1     /** Returns the numeric ID of <code>this</code> <code>SCComponent</code>.
305 salcianu 1.1.2.1         Just for debug purposes ... */
306 salcianu 1.1.2.1     public int getId(){ return id; }
307 salcianu 1.12        private int id;
308 salcianu 1.1.2.1 
309 salcianu 1.1.2.1     // The nodes of this SCC (Strongly Connected Component).
310 salcianu 1.15        Collection nodes = new ArrayList();
311 salcianu 1.1.2.4     Object[] nodes_array;
312 salcianu 1.1.2.1     // The successors.
313 salcianu 1.1.2.1     private SCComponent[] next;
314 salcianu 1.1.2.1     // The predecessors.
315 salcianu 1.1.2.1     private SCComponent[] prev;
316 salcianu 1.1.2.1 
317 salcianu 1.12        // entries/exists into this SCC
318 salcianu 1.9         private Object[] entries;
319 salcianu 1.9         private Object[] exits;
320 salcianu 1.9     
321 salcianu 1.1.2.5     /** Checks whether <code>this</code> strongly connected component
322 salcianu 1.1.2.5         corresponds to a loop, <i>ie</i> it has at least one arc to
323 salcianu 1.1.2.5         itself. */
324 salcianu 1.1.2.4     public final boolean isLoop() { return loop; }
325 salcianu 1.12        // is there any arc to itself?
326 salcianu 1.12        private boolean loop;
327 salcianu 1.1.2.1 
328 salcianu 1.1.2.1 
329 cananian 1.10        public int compareTo(SCComponent/*<Vertex>*/ scc2) {
330 salcianu 1.1.2.1         int id2 = scc2.id;
331 salcianu 1.1.2.1         if(id  < id2) return -1;
332 salcianu 1.1.2.1         if(id == id2) return 0;
333 salcianu 1.1.2.1         return 1;
334 salcianu 1.1.2.1     }
335 salcianu 1.1.2.1     
336 salcianu 1.1.2.1     /** Returns the number of successors. */
337 salcianu 1.1.2.4     public final int nextLength() { return next.length; }
338 salcianu 1.1.2.1 
339 salcianu 1.1.2.1     /** Returns the <code>i</code>th successor. */
340 salcianu 1.1.2.4     public final SCComponent next(int i) { return next[i]; }
341 salcianu 1.1.2.1 
342 salcianu 1.1.2.1     /** Returns the number of predecessors. */
343 salcianu 1.1.2.4     public final int prevLength() { return prev.length; }
344 salcianu 1.1.2.1 
345 salcianu 1.1.2.1     /** Returns the <code>i</code>th predecessor. */
346 salcianu 1.1.2.4     public final SCComponent prev(int i) { return prev[i]; }
347 salcianu 1.1.2.1 
348 salcianu 1.1.2.4     /** Returns the nodes of <code>this</code> strongly connected component
349 salcianu 1.1.2.4         (set version). */
350 salcianu 1.15        public final Set nodes() { return (Set) nodes; }
351 salcianu 1.1.2.4 
352 salcianu 1.1.2.4     /** Returns the nodes of <code>this</code> strongly connected component;
353 salcianu 1.15            array version - good for iterating over the elements of the SCC. 
354 salcianu 1.15            @deprecated use <code>nodes</code> instead */
355 salcianu 1.15        public final Object[] nodesArray() { 
356 salcianu 1.15            if(nodes_array == null) {
357 salcianu 1.15                nodes_array = nodes.toArray(new Object[nodes.size()]);
358 salcianu 1.15            }
359 salcianu 1.15            return nodes_array;
360 salcianu 1.15        }
361 salcianu 1.1.2.1 
362 salcianu 1.1.2.1     /** Returns the number of nodes in <code>this</code> strongly connected
363 salcianu 1.1.2.1         component. */
364 salcianu 1.15        public final int size() { return nodes.size(); }
365 salcianu 1.1.2.1 
366 salcianu 1.1.2.1     /** Checks whether <code>node</code> belongs to <code>this</code> \
367 salcianu 1.1.2.1         strongly connected component. */
368 salcianu 1.12        public final boolean contains(Object node) { return nodes.contains(node); }
369 salcianu 1.9     
370 salcianu 1.9         /** Returns the entry nodes of <code>this</code> strongly
371 salcianu 1.9             connected component.  These are the nodes taht are reachable
372 salcianu 1.9             from outside the component. */
373 salcianu 1.9         public final Object[] entries() { return entries; }
374 salcianu 1.12    
375 salcianu 1.9         /** Returns the exit nodes of <code>this</code> strongly connected
376 salcianu 1.9             component.  These are the nodes that have arcs toward nodes
377 salcianu 1.9             outside the component. */
378 salcianu 1.9         public final Object[] exits()   { return exits; }
379 salcianu 1.9     
380 salcianu 1.1.2.1 
381 salcianu 1.1.2.1     /** Pretty print debug function. */
382 salcianu 1.1.2.4     public final String toString() {
383 salcianu 1.1.2.1         StringBuffer buffer = new StringBuffer();
384 salcianu 1.1.2.4         buffer.append("SCC" + id + " (size " + size() + ") {\n");
385 salcianu 1.1.2.4         for(int i = 0; i < nodes_array.length; i++) {
386 salcianu 1.1.2.4             buffer.append(nodes_array[i]);
387 salcianu 1.1.2.1             buffer.append("\n");
388 salcianu 1.1.2.1         }
389 salcianu 1.1.2.1         buffer.append("}\n");
390 salcianu 1.12            // string representation of the "prev" links.
391 salcianu 1.1.2.1         int nb_prev = prevLength();
392 salcianu 1.12            if(nb_prev > 0) {
393 salcianu 1.1.2.1             buffer.append("Prev:");
394 salcianu 1.12                for(int i = 0; i < nb_prev ; i++)
395 salcianu 1.12                    buffer.append(" SCC" + prev(i).getId());
396 salcianu 1.1.2.1             buffer.append("\n");
397 salcianu 1.1.2.1         }
398 salcianu 1.12            // string representation of the "next" links.
399 salcianu 1.1.2.1         int nb_next = nextLength();
400 salcianu 1.12            if(nb_next > 0) {
401 salcianu 1.1.2.1             buffer.append("Next:");
402 salcianu 1.12                for(int i = 0; i < nb_next ; i++)
403 salcianu 1.12                    buffer.append(" SCC" + next(i).getId());
404 salcianu 1.1.2.1             buffer.append("\n");
405 salcianu 1.1.2.1         }
406 salcianu 1.1.2.1         return buffer.toString();
407 salcianu 1.1.2.1     }
408 cananian 1.2     }