#181 Infinite recursion between LazyMap and AggregateLayout

Likely Bug
open
nobody
5
2015-01-29
2012-07-28
Anonymous
No

Class: AggregateLayout and AbstractLayout (I think)
Synopsis: Suspected Infinite recursion between LazyMap and AggregateLayout
Description: bulk of message below
Repeat by: none confirmed
Stack Trace: see below
Workaround: see below for a nasty workaround with lots of side-effects

I have encountered a rather tricky problem when using AggregateLayout with CircleLayouts underneath. I think I have found the bug in JUNG, but I don't know how to fix it.

(I have tried to create a standalone testcase, but I have failed to do so because it's to do with stack sizes. I'll continue trying to do that, but hopefully it won't be necessary to isolate the problem with a testcase.)

I have about 100 vertices (my own 'Note' class) and I have a user interface that allows users to group the vertices together. When the user groups the vertices, I create a CircleLayout (from the subgraph containing the selected vertices) and add it to the AggregateLayout. This works very well until I wish to update the memberships of the CircleLayout. If I either use CircleLayout.setGraph() or CircleLayout.getGraph() and edit the Graph directly, then I get the recursive StackOverflowError shown below.

(My workaround is to delete the CircleLayout and create a new one, but I lose the offsets that the user might have introduced by moving the CircleLayout vertices. Also, it causes big problems if the user deletes vertices from the overall graph structure. Definitely not a good workaround.)

If LazyMap.get were calling ChainedTransformer.transform then the stack trace would make sense. LazyMap (defined on line 50 of AbstractLayout) is calling an anonymous inner Transformer class which is not obviously recursive. But the stacktrace shows that LazyMap's factory method has been reset to be AggregateLayout itself! Line 76, 90, 148 of AbstractLayout could be the offender.

I think this is a bug in JUNG, as this kind of recursion should not be happening.

Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
at my.project.Note.hashCode(Note.java)
at java.util.HashMap.getEntry(HashMap.java:344)
at java.util.HashMap.containsKey(HashMap.java:335)
at org.apache.commons.collections15.map.LazyMap.get(LazyMap.java:157)
at edu.uci.ics.jung.algorithms.layout.AbstractLayout.getCoordinates(AbstractLayout.java:170)
at edu.uci.ics.jung.algorithms.layout.AbstractLayout.transform(AbstractLayout.java:174)
at edu.uci.ics.jung.algorithms.layout.AbstractLayout.transform(AbstractLayout.java:38)
at edu.uci.ics.jung.algorithms.layout.AggregateLayout.transform(AggregateLayout.java:243)
at edu.uci.ics.jung.algorithms.layout.AggregateLayout.transform(AggregateLayout.java:35)
at org.apache.commons.collections15.functors.ChainedTransformer.transform(ChainedTransformer.java:127)
at org.apache.commons.collections15.map.LazyMap.get(LazyMap.java:158)
at edu.uci.ics.jung.algorithms.layout.AbstractLayout.getCoordinates(AbstractLayout.java:170)
at edu.uci.ics.jung.algorithms.layout.AbstractLayout.transform(AbstractLayout.java:174)
at edu.uci.ics.jung.algorithms.layout.AbstractLayout.transform(AbstractLayout.java:38)
at edu.uci.ics.jung.algorithms.layout.AggregateLayout.transform(AggregateLayout.java:243)
at edu.uci.ics.jung.algorithms.layout.AggregateLayout.transform(AggregateLayout.java:35)
at org.apache.commons.collections15.functors.ChainedTransformer.transform(ChainedTransformer.java:127)

Thin this repeats, filling up the console:

at org.apache.commons.collections15.map.LazyMap.get(LazyMap.java:158)
at edu.uci.ics.jung.algorithms.layout.AbstractLayout.getCoordinates(AbstractLayout.java:170)
at edu.uci.ics.jung.algorithms.layout.AbstractLayout.transform(AbstractLayout.java:174)
at edu.uci.ics.jung.algorithms.layout.AbstractLayout.transform(AbstractLayout.java:38)
at edu.uci.ics.jung.algorithms.layout.AggregateLayout.transform(AggregateLayout.java:243)
at edu.uci.ics.jung.algorithms.layout.AggregateLayout.transform(AggregateLayout.java:35)
at org.apache.commons.collections15.functors.ChainedTransformer.transform(ChainedTransformer.java:127)

Discussion

  • Comment has been marked as spam. 
    Undo

    You can see all pending comments posted by this user  here

    Anonymous - 2012-07-28

    I think I've worked out what this was! I was setting the initialiser of the CircleLayouts to be the AggregateLayout. This was then creating a crazy chain.

    I'm not so sure if this is a bug in JUNG or poor use of the API by me, but certainly worth noting. Incidentally, this is based on the precedent set in SubLayoutDemo line 201, so perhaps the demo is risking a chain reaction!

     
  • Comment has been marked as spam. 
    Undo

    You can see all pending comments posted by this user  here

    Anonymous - 2012-07-28

    Standalone test case:

    package jung;

    import edu.uci.ics.jung.algorithms.layout.AggregateLayout;
    import edu.uci.ics.jung.algorithms.layout.CircleLayout;
    import edu.uci.ics.jung.algorithms.layout.FRLayout;
    import edu.uci.ics.jung.algorithms.layout.Layout;
    import edu.uci.ics.jung.graph.Graph;
    import edu.uci.ics.jung.graph.UndirectedSparseGraph;
    import edu.uci.ics.jung.visualization.VisualizationViewer;
    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.geom.Point2D;
    import java.util.logging.Logger;
    import javax.swing.JFrame;
    import javax.swing.JPanel;

    // GRR... can't reproduce bug
    public class AggregateLayoutBug extends JPanel {

    private static final Logger log = Logger.getLogger(AggregateLayoutBug.class.getName());

    public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new AggregateLayoutBug());
    frame.pack();
    frame.setVisible(true);
    }

    public AggregateLayoutBug() {
    super(new BorderLayout());

    Graph<Integer, String> graph = new UndirectedSparseGraph<Integer, String>();

    for (int i = 0; i < 1000; i++) {
    graph.addVertex(i);
    }

    Layout<Integer, String> delegateLayout = new FRLayout<Integer, String>(graph);
    AggregateLayout<Integer, String> graphLayout = new AggregateLayout<Integer, String>(delegateLayout);
    final VisualizationViewer<Integer, String> graphVisualiser = new VisualizationViewer<Integer, String>(graphLayout);

    UndirectedSparseGraph<Integer, String> subGraph = new UndirectedSparseGraph<Integer, String>();
    for (int i = 0; i < 100; i++) {
    subGraph.addVertex(i);
    }

    final CircleLayout<Integer, String> subLayout = new CircleLayout<Integer, String>(subGraph);
    subLayout.setSize(new Dimension(100, 100));
    subLayout.setInitializer(graphLayout);

    // this is probably what the user meant to do!
    // subLayout.setInitializer(graphLayout.getDelegate());

    graphLayout.put(subLayout, new Point2D.Double(100, 100));

    add(graphVisualiser, BorderLayout.CENTER);
    }
    }

     
  • Comment has been marked as spam. 
    Undo

    You can see all pending comments posted by this user  here

    Anonymous - 2012-07-28

    Suggestion: in setInitializer() add an instanceof check which checks if the parameter is AggregateLayout, and if so, call its getDelegate().

     

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:





No, thanks