com.google.code.facebook.graph.sna.applet.FacebookGraphApplet.java Source code

Java tutorial

Introduction

Here is the source code for com.google.code.facebook.graph.sna.applet.FacebookGraphApplet.java

Source

/*
 * Copyright (c) 2003, the JUNG Project and the Regents of the University of
 * California All rights reserved.
 * 
 * This software is open-source under the BSD license; see either "license.txt"
 * or http://jung.sourceforge.net/license.txt for a description.
 * 
 */
package com.google.code.facebook.graph.sna.applet;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.BufferedImage;
import java.io.ObjectInputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JRadioButton;

import org.apache.commons.collections15.Transformer;

import com.google.code.facebook.graph.model.Entity;
import com.google.code.facebook.graph.model.GraphNode;
import com.google.code.facebook.graph.model.enumeration.ConnectionEnum;
import com.google.code.facebook.graph.model.enumeration.FieldEnum;
import com.google.code.facebook.graph.model.enumeration.ObjectType;
import com.google.code.facebook.graph.sna.service.GraphAdapter;
import com.google.code.facebook.graph.sna.service.jung.EdgeAdapter;
import com.google.code.facebook.graph.sna.service.jung.JungGraphAdapter;

import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.LayeredIcon;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ScalingControl;
import edu.uci.ics.jung.visualization.decorators.DefaultVertexIconTransformer;
import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer;
import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer;
import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer;
import edu.uci.ics.jung.visualization.decorators.VertexIconShapeTransformer;
import edu.uci.ics.jung.visualization.picking.PickedState;
import edu.uci.ics.jung.visualization.renderers.Checkmark;
import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer;
import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer;
import edu.uci.ics.jung.visualization.transform.LayoutLensSupport;
import edu.uci.ics.jung.visualization.transform.LensSupport;
import edu.uci.ics.jung.visualization.transform.shape.MagnifyImageLensSupport;

/**
 * Demonstrates the use of images to represent graph vertices.
 * The images are added to the DefaultGraphLabelRenderer and can
 * either be offset from the vertex, or centered on the vertex.
 * Additionally, the relative positioning of the label and
 * image is controlled by subclassing the DefaultGraphLabelRenderer
 * and setting the appropriate properties on its JLabel superclass
 *  FancyGraphLabelRenderer
 * 
 * The images used in this demo (courtesy of slashdot.org) are
 * rectangular but with a transparent background. When vertices
 * are represented by these images, it looks better if the actual
 * shape of the opaque part of the image is computed so that the
 * edge arrowheads follow the visual shape of the image. This demo
 * uses the FourPassImageShaper class to compute the Shape from
 * an image with transparent background.
 * 
 * @author Tom Nelson
 * 
 */
public class FacebookGraphApplet extends JApplet {

    /**
    * 
    */
    private static final long serialVersionUID = 5432239991020505763L;

    private static final Set<ObjectType> DISPLAYABLE_ENTITIES = EnumSet.of(ObjectType.EVENT, ObjectType.GROUP,
            ObjectType.PAGE, ObjectType.USER);

    /**
      * the graph
      */
    Graph<Entity<FieldEnum, ConnectionEnum>, EdgeAdapter<ConnectionEnum>> graph;

    /**
     * the visual component and renderer for the graph
     */
    VisualizationViewer<Entity<FieldEnum, ConnectionEnum>, EdgeAdapter<ConnectionEnum>> vv;

    LensSupport viewSupport;
    LensSupport modelSupport;
    LensSupport magnifyLayoutSupport;
    LensSupport magnifyViewSupport;

    /**
     * create an instance of a simple graph with controls to
     * demo the zoom features.
     * 
     */
    @Override
    public void init() {
        super.init();
        // create a simple graph for the demo
        fetchUserGraph();
        final Collection<? extends Entity<FieldEnum, ConnectionEnum>> vertices = graph.getVertices();

        // a Map for the labels
        Map<Entity<FieldEnum, ConnectionEnum>, String> map = new HashMap<Entity<FieldEnum, ConnectionEnum>, String>();
        Iterator<? extends Entity<FieldEnum, ConnectionEnum>> iterator = vertices.iterator();
        while (iterator.hasNext()) {
            Entity<FieldEnum, ConnectionEnum> entity = iterator.next();
            map.put(entity, entity.getLabel());
        }

        FRLayout<Entity<FieldEnum, ConnectionEnum>, EdgeAdapter<ConnectionEnum>> layout = new FRLayout<Entity<FieldEnum, ConnectionEnum>, EdgeAdapter<ConnectionEnum>>(
                (Graph<Entity<FieldEnum, ConnectionEnum>, EdgeAdapter<ConnectionEnum>>) graph);
        layout.setMaxIterations(100);
        vv = new VisualizationViewer<Entity<FieldEnum, ConnectionEnum>, EdgeAdapter<ConnectionEnum>>(layout,
                new Dimension(2000, 2000));

        Transformer<Entity<FieldEnum, ConnectionEnum>, Paint> vpf = new PickableVertexPaintTransformer<Entity<FieldEnum, ConnectionEnum>>(
                vv.getPickedVertexState(), Color.white, Color.yellow);
        vv.getRenderContext().setVertexFillPaintTransformer(vpf);
        vv.getRenderContext().setEdgeDrawPaintTransformer(
                new PickableEdgePaintTransformer<EdgeAdapter<ConnectionEnum>>(vv.getPickedEdgeState(), Color.black,
                        Color.cyan));

        vv.setBackground(Color.white);

        // a Map for the Icons
        final Map<Entity<FieldEnum, ConnectionEnum>, Icon> iconMap = new HashMap<Entity<FieldEnum, ConnectionEnum>, Icon>();
        new Thread(new Runnable() {
            public void run() {
                Iterator<? extends Entity<FieldEnum, ConnectionEnum>> iterator = vertices.iterator();
                while (iterator.hasNext()) {
                    try {
                        Entity<FieldEnum, ConnectionEnum> entity = iterator.next();
                        //                     Icon icon = 
                        //                        new LayeredIcon(new ImageIcon("http://facebookgraph.appspot.com/proxy?url=" + entity.getPicture(PictureType.SQUARE), "Profile Picture").getImage());
                        //                       iconMap.put(entity, icon);

                        Image image = getImageFromEntity(entity);
                        if (image != null && image.getWidth(null) > 0 && image.getHeight(null) > 0) {
                            Icon icon = new LayeredIcon(image);
                            iconMap.put(entity, icon);
                            if ((iconMap.size() % 5) == 0) {
                                vv.repaint();
                            }
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
                vv.repaint();
            }

        }).start();

        final Transformer<Entity<FieldEnum, ConnectionEnum>, String> vertexStringerImpl = new VertexStringerImpl<Entity<FieldEnum, ConnectionEnum>>(
                map);
        Transformer<EdgeAdapter<ConnectionEnum>, String> edgeStringerImpl = new Transformer<EdgeAdapter<ConnectionEnum>, String>() {
            public String transform(EdgeAdapter<ConnectionEnum> edge) {
                return edge.toString();
            }
        };
        vv.getRenderContext().setEdgeLabelTransformer(edgeStringerImpl);
        vv.getRenderContext().setVertexLabelTransformer(vertexStringerImpl);
        vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan));
        vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan));

        // features on and off. For a real application, use VertexIconAndShapeFunction instead.
        final VertexIconShapeTransformer<Entity<FieldEnum, ConnectionEnum>> vertexImageShapeFunction = new VertexIconShapeTransformer<Entity<FieldEnum, ConnectionEnum>>(
                new EllipseVertexShapeTransformer<Entity<FieldEnum, ConnectionEnum>>());

        final DefaultVertexIconTransformer<Entity<FieldEnum, ConnectionEnum>> vertexIconFunction = new DefaultVertexIconTransformer<Entity<FieldEnum, ConnectionEnum>>();

        vertexImageShapeFunction.setIconMap(iconMap);
        vertexIconFunction.setIconMap(iconMap);

        vv.getRenderContext().setVertexShapeTransformer(vertexImageShapeFunction);
        vv.getRenderContext().setVertexIconTransformer(vertexIconFunction);

        // Get the pickedState and add a listener that will decorate the
        // Vertex images with a checkmark icon when they are picked
        PickedState<Entity<FieldEnum, ConnectionEnum>> ps = vv.getPickedVertexState();
        ps.addItemListener(new PickWithIconListener(vertexIconFunction));

        // add a listener for ToolTips
        vv.setVertexToolTipTransformer(new Transformer<Entity<FieldEnum, ConnectionEnum>, String>() {
            @Override
            public String transform(Entity<FieldEnum, ConnectionEnum> entity) {
                return entity.getDescription();
            }
        });

        Container content = getContentPane();
        final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
        content.add(panel);

        final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
        vv.setGraphMouse(graphMouse);

        final ScalingControl scaler = new CrossoverScalingControl();

        JButton plus = new JButton("+");
        plus.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                scaler.scale(vv, 1.1f, vv.getCenter());
            }
        });
        JButton minus = new JButton("-");
        minus.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                scaler.scale(vv, 1 / 1.1f, vv.getCenter());
            }
        });

        JComboBox modeBox = graphMouse.getModeComboBox();
        JPanel modePanel = new JPanel();
        modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode"));
        modePanel.add(modeBox);

        JPanel scaleGrid = new JPanel(new GridLayout(1, 0));
        scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom"));
        JPanel controls = new JPanel();
        scaleGrid.add(plus);
        scaleGrid.add(minus);
        controls.add(scaleGrid);

        controls.add(modePanel);
        content.add(controls, BorderLayout.SOUTH);

        this.viewSupport = new MagnifyImageLensSupport<Entity<FieldEnum, ConnectionEnum>, EdgeAdapter<ConnectionEnum>>(
                vv);
        //           new ViewLensSupport<Number,Number>(vv, new HyperbolicShapeTransformer(vv, 
        //              vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), 
        //                new ModalLensGraphMouse());

        this.modelSupport = new LayoutLensSupport<Entity<FieldEnum, ConnectionEnum>, EdgeAdapter<ConnectionEnum>>(
                vv);

        graphMouse.addItemListener(modelSupport.getGraphMouse().getModeListener());
        graphMouse.addItemListener(viewSupport.getGraphMouse().getModeListener());

        ButtonGroup radio = new ButtonGroup();
        JRadioButton none = new JRadioButton("None");
        none.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                if (viewSupport != null) {
                    viewSupport.deactivate();
                }
                if (modelSupport != null) {
                    modelSupport.deactivate();
                }
            }
        });
        none.setSelected(true);

        JRadioButton hyperView = new JRadioButton("View");
        hyperView.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                viewSupport.activate(e.getStateChange() == ItemEvent.SELECTED);
            }
        });

        JRadioButton hyperModel = new JRadioButton("Layout");
        hyperModel.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                modelSupport.activate(e.getStateChange() == ItemEvent.SELECTED);
            }
        });
        radio.add(none);
        radio.add(hyperView);
        radio.add(hyperModel);

        JMenuBar menubar = new JMenuBar();
        JMenu modeMenu = graphMouse.getModeMenu();
        menubar.add(modeMenu);

        JPanel lensPanel = new JPanel(new GridLayout(2, 0));
        lensPanel.setBorder(BorderFactory.createTitledBorder("Lens"));
        lensPanel.add(none);
        lensPanel.add(hyperView);
        lensPanel.add(hyperModel);
        controls.add(lensPanel);
    }

    private Image getImageFromEntity(Entity<FieldEnum, ConnectionEnum> entity) {
        if (DISPLAYABLE_ENTITIES.contains(entity.getType())) {
            try {
                URL url = new URL("http://facebookgraph.appspot.com/proxy?url=" + encodeUrl(entity.getPicture()));
                HttpURLConnection request = (HttpURLConnection) url.openConnection();

                request.connect();

                if (request.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    BufferedImage image = ImageIO.read(request.getInputStream());
                    return Toolkit.getDefaultToolkit().createImage(image.getSource());
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 
     */
    @SuppressWarnings("unchecked")
    private void fetchUserGraph() {
        try {
            String accessToken = getParameter("accessToken");
            URL url = new URL("http://facebookgraph.appspot.com/fetchGraph?accessToken=" + encodeUrl(accessToken));
            HttpURLConnection request = (HttpURLConnection) url.openConnection();

            request.connect();

            if (request.getResponseCode() == HttpURLConnection.HTTP_OK) {
                GraphAdapter<Graph<Entity<FieldEnum, ConnectionEnum>, EdgeAdapter<ConnectionEnum>>, Entity<FieldEnum, ConnectionEnum>, ConnectionEnum> adapter = new JungGraphAdapter<Entity<FieldEnum, ConnectionEnum>, ConnectionEnum>();
                ObjectInputStream is = new ObjectInputStream(new GZIPInputStream(request.getInputStream()));
                graph = adapter.adaptFrom((GraphNode<? extends FieldEnum, ConnectionEnum>) is.readObject());
                is.close();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * A simple implementation of VertexStringer that
     * gets Vertex labels from a Map  
     * 
     * @author Tom Nelson 
     *
     *
     */
    class VertexStringerImpl<V> implements Transformer<V, String> {

        Map<V, String> map = new HashMap<V, String>();

        boolean enabled = true;

        public VertexStringerImpl(Map<V, String> map) {
            this.map = map;
        }

        /**
         * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex)
         */
        public String transform(V v) {
            if (isEnabled()) {
                return map.get(v);
            } else {
                return "";
            }
        }

        /**
         * @return Returns the enabled.
         */
        public boolean isEnabled() {
            return enabled;
        }

        /**
         * @param enabled The enabled to set.
         */
        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }
    }

    private String encodeUrl(String url) {
        try {
            return URLEncoder.encode(url, "UTF-8");
        } catch (Exception e) {
            return url;
        }
    }

    public static class PickWithIconListener implements ItemListener {
        DefaultVertexIconTransformer<Entity<FieldEnum, ConnectionEnum>> imager;
        Icon checked;

        public PickWithIconListener(
                DefaultVertexIconTransformer<Entity<FieldEnum, ConnectionEnum>> vertexIconFunction) {
            this.imager = vertexIconFunction;
            checked = new Checkmark(Color.red);
        }

        @SuppressWarnings("unchecked")
        public void itemStateChanged(ItemEvent e) {
            Icon icon = imager.transform((Entity<FieldEnum, ConnectionEnum>) e.getItem());
            if (icon != null && icon instanceof LayeredIcon) {
                if (e.getStateChange() == ItemEvent.SELECTED) {
                    ((LayeredIcon) icon).add(checked);
                } else {
                    ((LayeredIcon) icon).remove(checked);
                }
            }
        }
    }

    /**
     * a driver for this demo
     */
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        Container content = frame.getContentPane();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        FacebookGraphApplet applet = new FacebookGraphApplet();
        applet.init();

        content.add(applet);
        frame.pack();
        frame.setVisible(true);
    }
}