package tufts.vue; import static tufts.Util.reverse; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JTextField; import javax.swing.SwingUtilities; import com.google.common.collect.Multimap; import edu.tufts.vue.layout.ClusterLayout; import edu.tufts.vue.layout.ListRandomLayout; import edu.tufts.vue.layout.ScrollingTabularLayout; import edu.tufts.vue.layout.TabularLayout; import edu.tufts.vue.mbs.AnalyzerResult; import edu.tufts.vue.mbs.OpenCalaisAnalyzer; import edu.tufts.vue.metadata.MetadataList; import tufts.vue.ds.Field; import tufts.vue.gui.GUI; import twitter4j.Tweet; import twitter4j.Twitter; import twitter4j.Query; import twitter4j.QueryResult; import twitter4j.TwitterException; public class VueTwitterPanel extends JPanel implements ItemListener, ActionListener { private final static String FROM_LAYER="From"; //private final static String TO_LAYER="To"; private final static String GEO_LAYER="Geo"; private final static String ANALYSIS_LAYER="Analysis"; private final static String TWEETS_LAYER="Tweets"; private static final long serialVersionUID = 1L; private static final Color TweetNodeColor = VueResources.getColor("twitter.tweet.color", Color.gray); private static final float TweetNodeStrokeWidth = VueResources.getInt("twitter.tweet.stroke.width", 0); private static final Color TweetNodeStrokeColor = VueResources.getColor("twitter.tweet.stroke.color", Color.black); private static final Font TweetNodeFont = VueResources.getFont("twitter.tweet.font"); private static final Color ClusterNodeTextColor = VueResources.getColor("node.dataValue.text.color", Color.black); private static final Font ClusterNodeFont = VueResources.getFont("node.dataValue.font"); private static final Color[] ClusterNodeDataColors = VueResources.getColorArray("node.dataValue.color.cycle"); private static int NextColor = 0; private JTextField mSearchFieldEditor = null; private PropertyPanel mPropPanel = null; private JButton followButton = null; private final JRadioButton scatterCheckbox = new JRadioButton(VueResources.getString("twitter.scatter")); private final JRadioButton fromCheckbox = new JRadioButton(VueResources.getString("twitter.from")); // private final JRadioButton toCheckbox =new JRadioButton(VueResources.getString("twitter.to")); // private JRadioButton geographicLocationCheckbox = null; private final JRadioButton analysisCheckbox = new JRadioButton(VueResources.getString("twitter.analysis")); private TwitterThread twitterThread = null; private final ButtonGroup group = new ButtonGroup(); final private HashMap<String,LWMap.Layer> layerList = new HashMap<String,LWMap.Layer>(); private boolean layersInit = false; public VueTwitterPanel() { twitterThread = new TwitterThread(); JPanel innerPanel = new JPanel(); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); //BoxLayout boxLayout = new BoxLayout(innerPanel,BoxLayout.Y_AXIS); innerPanel.setLayout(gridbag); //mTitleEditor = new JTextField(); mSearchFieldEditor = new JTextField(); // geographicLocationCheckbox = new JRadioButton(VueResources.getString("twitter.location")); group.add(scatterCheckbox); // group.add(geographicLocationCheckbox); group.add(fromCheckbox); // group.add(toCheckbox); group.add(analysisCheckbox); scatterCheckbox.setSelected(true); scatterCheckbox.addItemListener(this); fromCheckbox.addItemListener(this); // toCheckbox.addItemListener(this); analysisCheckbox.addItemListener(this); mPropPanel = new PropertyPanel(); mPropPanel.addProperty(VueResources.getString("twitter.searchField"), mSearchFieldEditor); //added through metadata JLabel titleLabel = new JLabel(VueResources.getString("twitter.title")); followButton = new JButton(VueResources.getString("twitter.button.activate")); followButton.addActionListener(this); c.weightx = 1.0; c.gridwidth = GridBagConstraints.REMAINDER; c.anchor = GridBagConstraints.NORTHWEST; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(titleLabel,c); innerPanel.add(titleLabel); c.weightx = 1.0; c.gridwidth = GridBagConstraints.REMAINDER; c.anchor = GridBagConstraints.NORTHWEST; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(mPropPanel,c); innerPanel.add(mPropPanel); c.weightx = 1.0; c.gridwidth = GridBagConstraints.REMAINDER; c.anchor = GridBagConstraints.NORTHWEST; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(scatterCheckbox,c); innerPanel.add(scatterCheckbox); c.weightx = 1.0; c.gridwidth = GridBagConstraints.REMAINDER; c.anchor = GridBagConstraints.NORTHWEST; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(fromCheckbox,c); innerPanel.add(fromCheckbox); c.weightx = 1.0; c.gridwidth = GridBagConstraints.REMAINDER; c.anchor = GridBagConstraints.NORTHWEST; c.fill = GridBagConstraints.HORIZONTAL; // gridbag.setConstraints(toCheckbox,c); // innerPanel.add(toCheckbox); c.weightx = 1.0; c.gridwidth = GridBagConstraints.REMAINDER; c.anchor = GridBagConstraints.NORTHWEST; c.fill = GridBagConstraints.HORIZONTAL; // gridbag.setConstraints(geographicLocationCheckbox,c); // innerPanel.add(geographicLocationCheckbox); c.weightx = 1.0; c.gridwidth = GridBagConstraints.REMAINDER; c.anchor = GridBagConstraints.NORTHWEST; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(analysisCheckbox,c); innerPanel.add(analysisCheckbox); c.gridy=12; c.weightx = 1.0; c.gridwidth = 2; c.anchor = GridBagConstraints.NORTHWEST; c.fill = GridBagConstraints.NONE; gridbag.setConstraints(followButton,c); innerPanel.add(followButton); new PropertiesEditor(true); JPanel metadataPanel = new JPanel(new BorderLayout()); mSearchFieldEditor.setFont(GUI.LabelFace); c.weighty = 1.0; c.gridwidth = GridBagConstraints.REMAINDER; c.fill = GridBagConstraints.BOTH; gridbag.setConstraints(metadataPanel,c); innerPanel.add(metadataPanel); setLayout(new BorderLayout()); add(innerPanel,BorderLayout.CENTER); setBorder(BorderFactory.createEmptyBorder(10,10,0,6)); } public void itemStateChanged(ItemEvent e) { // TODO Auto-generated method stub if (e.getStateChange() == ItemEvent.SELECTED) twitterThread.layout(null); } private Thread t = null; @SuppressWarnings("deprecation") public void actionPerformed(ActionEvent e) { if (e.getSource().equals(followButton)) { initLayers(); if (followButton.getText().equals(VueResources.getString("twitter.button.activate"))) { t = new Thread(twitterThread); t.start(); followButton.setText(VueResources.getString("twitter.button.deactivate")); } else { t.stop(); followButton.setText(VueResources.getString("twitter.button.activate")); } } } private void initLayers() { LWMap map = VUE.getActiveMap(); if (!layersInit) { map.addLayer(FROM_LAYER); // map.addLayer(TO_LAYER); //map.addLayer(GEO_LAYER); map.addLayer(ANALYSIS_LAYER); map.addLayer(TWEETS_LAYER); for (LWComponent layer : reverse(map.getChildren())) { if (layer instanceof LWMap.Layer) { layerList.put(layer.getLabel(), (LWMap.Layer)layer); if (!layer.getLabel().equals(TWEETS_LAYER)) ((LWMap.Layer)layer).setVisible(false); } } layersInit=true; } } class TwitterThread implements Runnable { private static final long UPDATE_INTERVAL = 5000; private HashMap<String,LWNode> fromUserClusterPoints = new HashMap<String,LWNode>(); //private HashMap<String,LWNode> geoClusterPoints = new HashMap<String,LWNode>(); private HashMap<String,LWNode> toUserClusterPoints = new HashMap<String,LWNode>(); private HashMap<String,LWNode> analysisClusterPoints = new HashMap<String,LWNode>(); private Twitter twitter; private ListRandomLayout randomLayout = new ListRandomLayout(); private TabularLayout tableLayout = new TabularLayout(); private ClusterLayout clusterLayout = new ClusterLayout(); private ScrollingTabularLayout scrollTableLayout = new ScrollingTabularLayout(); private LWNode handleFromNode(Tweet tweet) { LWNode fromNode = fromUserClusterPoints.get(tweet.getFromUser()); if (fromNode == null) { fromNode = new LWNode(tweet.getFromUser()); fromNode.setStyle(getClusterStyleNode()); fromUserClusterPoints.put(tweet.getFromUser(), fromNode); final LWNode finalFromNode = fromNode; javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { LWMap.Layer fromLayer = layerList.get(FROM_LAYER); fromLayer.addChild(finalFromNode); } }); } return fromNode; } /* private LWNode handleToNode(Tweet tweet) { if (tweet.getToUser() != null) { LWNode toNode = toUserClusterPoints.get(tweet.getToUser()); if (toNode == null) { toNode = new LWNode(tweet.getToUser()); toNode.setStyle(getClusterStyleNode()); toUserClusterPoints.put(tweet.getToUser(), toNode); final LWNode finalToNode = toNode; javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { LWMap.Layer toLayer = layerList.get(TO_LAYER); toLayer.addChild(finalToNode); } }); } return toNode; } else return null; } */ private OpenCalaisAnalyzer oca = new OpenCalaisAnalyzer(); private LWNode handleAnalysisNode(Tweet tweet) { String toAnalyze = tweet.getText(); Multimap<String,AnalyzerResult> results = oca.analyzeString(toAnalyze); //Set<String> keyset = results.keySet(); //for (String key: keyset) //{ // System.out.println("KEY : " + key); //} String topic = "Uncategorized"; Collection<AnalyzerResult> res = results.get("Topic"); for (AnalyzerResult r: res) { topic = r.getValue(); break; } if (topic.equals("Uncategorized")) { Collection<AnalyzerResult> vals = results.values(); double highRel = 0; for (AnalyzerResult value: vals) { if (value.getRelevance() > highRel); { topic = value.getValue(); highRel = value.getRelevance(); } } } LWNode topicNode = analysisClusterPoints.get(topic); if (topicNode == null) { topicNode = new LWNode(topic); topicNode.setStyle(getClusterStyleNode()); analysisClusterPoints.put(topic, topicNode); final LWNode finalTopicNode = topicNode; javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { LWMap.Layer analysisLayer = layerList.get(ANALYSIS_LAYER); analysisLayer.addChild(finalTopicNode); } }); } return topicNode; } public void run() { twitter = new Twitter(); long lastTweet = 0; while (true) { try { Query q = new Query(mSearchFieldEditor.getText()); if (lastTweet > 0) q.setSinceId(lastTweet); QueryResult results = twitter.search(q); lastTweet = results.getMaxId(); List<Tweet> tweets = results.getTweets(); Collections.reverse(tweets); for (Tweet tweet: tweets) { String msg = tweet.getFromUser() + " : " + tweet.getText(); final LWNode node = new LWNode(tufts.Util.formatLines(msg,50)); node.setStyle(getTweetStyleNode()); MetadataList mlist = new MetadataList(); mlist.add("sender", tweet.getFromUser()); mlist.add("timestamp", tweet.getCreatedAt().toString()); mlist.add("profileImage", tweet.getProfileImageUrl()); if (tweet.getGeoLocation() !=null) mlist.add("geolocation", tweet.getGeoLocation().toString()); if (msg.indexOf("?") > -1) mlist.add("Type of Sentence", "question"); else if (msg.indexOf("!") > -1) mlist.add("Type of Sentence", "exclamation"); else mlist.add("Type of Sentence", "statement"); node.setMetadataList(mlist); LWNode fromNode = handleFromNode(tweet); //LWNode toNode = handleToNode(tweet); final LWNode analysisNode = handleAnalysisNode(tweet); final LWLink fromLink = new LWLink(fromNode,node); /* if (toNode !=null) { final LWLink toLink = new LWLink(node,toNode); final LWMap.Layer toLayer = layerList.get(TO_LAYER); javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { toLayer.addChild(toLink); } }); } */ javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { final LWLink analysisLink = new LWLink(analysisNode,node); final LWMap.Layer analysisLayer = layerList.get(ANALYSIS_LAYER); LWMap.Layer tweetsLayer = layerList.get(TWEETS_LAYER); tweetsLayer.addChild(node); LWMap.Layer fromLayer = layerList.get(FROM_LAYER); fromLayer.addChild(fromLink); layout(node); if (analysisNode !=null) { analysisLayer.addChild(analysisLink); } } }); } } catch (TwitterException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } try { Thread.sleep(UPDATE_INTERVAL); } catch (InterruptedException e ) { } } } //end of run() public LWComponent getClusterStyleNode() { final LWComponent style; style = new LWNode(); // creates a rectangular node style.setFillColor(ClusterNodeDataColors[NextColor]); if (++NextColor >= ClusterNodeDataColors.length) NextColor = 0; style.setFont(ClusterNodeFont); style.setTextColor(ClusterNodeTextColor); return style; } private LWNode getTweetStyleNode() { final LWNode style = new LWNode(); style.setFont(TweetNodeFont); style.setTextColor(Color.black); style.setFillColor(TweetNodeColor); style.setStrokeWidth(TweetNodeStrokeWidth); style.setStrokeColor(TweetNodeStrokeColor); return style; } public void layout(final LWComponent node) { JRadioButton button = getSelection(group); /*if (button.equals(toCheckbox)) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { //LWMap.Layer geoLayer = layerList.get(GEO_LAYER); // LWMap.Layer toLayer = layerList.get(TO_LAYER); LWMap.Layer fromLayer = layerList.get(FROM_LAYER); LWMap.Layer analysisLayer = layerList.get(ANALYSIS_LAYER); //geoLayer.setVisible(false); toLayer.setVisible(false); fromLayer.setVisible(false); analysisLayer.setVisible(true); LWMap.Layer tweetsLayer = layerList.get(TWEETS_LAYER); final LWSelection select = VUE.getSelection(); select.clear(); final List<LWComponent> l = new ArrayList<LWComponent>(); Iterable<LWNode> nodeIterator = toLayer.getChildrenOfType(LWNode.class); for (LWNode c : nodeIterator) { l.add(c); //System.out.println(c.getLabel()); } select.addAll(l); try { //tableLayout.layout(select); nodeIterator = toLayer.getChildrenOfType(LWNode.class); for (final LWNode c : nodeIterator) { SwingUtilities.invokeLater(new Runnable() { public void run() { //Actions.ClusterAction.clusterNodesAbout(c, c.getLinked()); clusterLayout.layout(select); } }); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } SwingUtilities.invokeLater(new Runnable() { public void run() { // ZoomTool.setZoomFit(); } }); } }); } else */if (button.equals(fromCheckbox)) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { //LWMap.Layer geoLayer = layerList.get(GEO_LAYER); //LWMap.Layer toLayer = layerList.get(TO_LAYER); LWMap.Layer fromLayer = layerList.get(FROM_LAYER); LWMap.Layer analysisLayer = layerList.get(ANALYSIS_LAYER); //geoLayer.setVisible(false); //toLayer.setVisible(false); fromLayer.setVisible(true); analysisLayer.setVisible(false); LWMap.Layer tweetsLayer = layerList.get(TWEETS_LAYER); final LWSelection select = VUE.getSelection(); select.clear(); final List<LWComponent> l = new ArrayList<LWComponent>(); Iterable<LWNode> nodeIterator = fromLayer.getChildrenOfType(LWNode.class); for (LWNode c : nodeIterator) { l.add(c); //System.out.println(c.getLabel()); } select.addAll(l); try { //tableLayout.layout(select); nodeIterator = fromLayer.getChildrenOfType(LWNode.class); for (final LWNode c : nodeIterator) { SwingUtilities.invokeLater(new Runnable() { public void run() { //Actions.ClusterAction.clusterNodesAbout(c, c.getLinked()); clusterLayout.layout(select); } }); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } SwingUtilities.invokeLater(new Runnable() { public void run() { // ZoomTool.setZoomFit(); } }); } }); } else if (button.equals(analysisCheckbox)) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { //LWMap.Layer geoLayer = layerList.get(GEO_LAYER); //LWMap.Layer toLayer = layerList.get(TO_LAYER); LWMap.Layer fromLayer = layerList.get(FROM_LAYER); LWMap.Layer analysisLayer = layerList.get(ANALYSIS_LAYER); //geoLayer.setVisible(false); //toLayer.setVisible(false); fromLayer.setVisible(false); analysisLayer.setVisible(true); LWMap.Layer tweetsLayer = layerList.get(TWEETS_LAYER); final LWSelection select = VUE.getSelection(); select.clear(); final List<LWComponent> l = new ArrayList<LWComponent>(); Iterable<LWNode> nodeIterator = analysisLayer.getChildrenOfType(LWNode.class); for (LWNode c : nodeIterator) { l.add(c); //System.out.println(c.getLabel()); } select.addAll(l); try { //tableLayout.layout(select); nodeIterator = analysisLayer.getChildrenOfType(LWNode.class); for (final LWNode c : nodeIterator) { SwingUtilities.invokeLater(new Runnable() { public void run() { //Actions.ClusterAction.clusterNodesAbout(c, c.getLinked()); clusterLayout.layout(select); } }); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } SwingUtilities.invokeLater(new Runnable() { public void run() { ZoomTool.setZoomFit(); } }); } }); } else { //scatter javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { //LWMap.Layer geoLayer = layerList.get(GEO_LAYER); //LWMap.Layer toLayer = layerList.get(TO_LAYER); LWMap.Layer fromLayer = layerList.get(FROM_LAYER); LWMap.Layer analysisLayer = layerList.get(ANALYSIS_LAYER); //geoLayer.setVisible(false); //toLayer.setVisible(false); fromLayer.setVisible(false); analysisLayer.setVisible(false); LWMap.Layer tweetsLayer = layerList.get(TWEETS_LAYER); LWSelection select = VUE.getSelection(); select.clear(); // select.addAll(); // if (node == null) select.addAll(tweetsLayer.getAllDescendents()); // else // select.add(node); try { scrollTableLayout.layout(select); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } SwingUtilities.invokeLater(new Runnable() { public void run() { VUE.getActiveViewer().repaint(); // ZoomTool.setZoomFit(); } }); } }); } } public JRadioButton getSelection(ButtonGroup group) { for (Enumeration e=group.getElements(); e.hasMoreElements(); ) { JRadioButton b = (JRadioButton)e.nextElement(); if (b.getModel() == group.getSelection()) { return b; } } return null; } } }