package tufts.vue;
/*
* AnalyzerAction.java
*
* Created on October 8, 2008, 1:01 PM
*
* Copyright 2003-2010 Tufts University Licensed under the
* Educational Community License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.osedu.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import edu.tufts.vue.mbs.AlchemyAnalyzer;
import edu.tufts.vue.layout.*;
import edu.tufts.vue.mbs.AnalyzerResult;
import edu.tufts.vue.mbs.LWComponentAnalyzer;
import edu.tufts.vue.mbs.OpenCalaisAnalyzer;
import edu.tufts.vue.mbs.YahooAnalyzer;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.*;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import javax.swing.Action;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.json.JSONArray;
import org.json.JSONObject;
import org.osid.repository.Asset;
import org.osid.repository.AssetIterator;
import org.osid.repository.RepositoryException;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import tufts.Util;
import tufts.vue.Actions.LWCAction;
import tufts.vue.gui.GUI;
import tufts.vue.gui.Widget;
import tufts.vue.VueUtil;
import edu.tufts.vue.ontology.OntType;
import edu.tufts.vue.metadata.MetadataList;
import edu.tufts.vue.metadata.VueMetadataElement;
import edu.tufts.vue.preferences.implementations.ShowAgainDialog;
import edu.tufts.vue.ui.DefaultQueryEditor;
// contains layout actions. based on ArrangeAction. The default layout is random layout
public class AnalyzerAction extends Actions.LWCAction {
private boolean firstTime = true;
private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(AnalyzerAction.class);
private LWComponentAnalyzer analyzer = null;
private AnalyzerAction(LWComponentAnalyzer analyzer, String name,KeyStroke keyStroke) {
super(name,keyStroke);
this.analyzer = analyzer;
}
// boolean mayModifySelection() { return true; }
boolean mayModifySelection() { return true; }
boolean enabledFor(LWSelection s) {
return (s.size() == 1
&& s.first() instanceof LWNode); // todo: a have capability check (free-layout? !isLaidOut() ?)
}
private final static int maxLabelLineLength =30;
private static final Color[] ValueNodeDataColors = VueResources.getColorArray("node.dataValue.color.cycle");
public static void addResultsToMap(List<Resource> resourceList,LWComponent centerComponent, int colorSelection)
{
//*** MAP BASED SEARCHING ***
//Equivalent of the are you feeling lucky feature where
//we are going to grab a bunch of results and add them right to the map.
final int RESULTS_TO_ADD=10;
int size = resourceList.size();
// VUE.getSelection().clear();
// VUE.getSelection().add(mCenterComponent);
LWMap active = VUE.getActiveMap();
java.util.List<LWComponent> comps = new ArrayList<LWComponent>();
for (int p=0; p < RESULTS_TO_ADD && p<size; p++)
{
//System.out.println(resourceList.get(p).toString());
Resource r = (Resource)resourceList.get(p);
tufts.vue.LWNode node = new tufts.vue.LWNode(r.getTitle());
node.setFillColor(ValueNodeDataColors[colorSelection%10]);
node.setResource(r);
String label = node.getLabel();
label = Util.formatLines(label, maxLabelLineLength);
node.setLabel(label);
comps.add(node);
node.layout();
node.setLocation(centerComponent.getLocation());
LWLink link = new LWLink(centerComponent,node);
comps.add(link);
link.layout();
}
active.addChildren(comps);
// VUE.getSelection().setTo(comps,"search results");
LayoutAction.search.act(comps);
VUE.getSelection().setTo(centerComponent);
LWSelection selection = VUE.getSelection();
LWContainer.bringToFront(selection);
}
public void act(LWComponent c)
{
//if (VUE.getDRBrowser().getDataSourceViewer().mMapBasedSearchThreads.size() > 0)
//{
//System.out.println("SEARCH ALREADY RUNNING!!!!!!!!!!!");
// return;
//}
//Set the wait cursor here clear it when there's no threads left in the mbs.
GUI.activateWaitCursor();
//figure out if anything is checked
int selectionCount = DefaultQueryEditor.getSelectedRepositoryCount();
if (selectionCount == 0)
{
final ShowAgainDialog sad = new ShowAgainDialog(VUE.getApplicationFrame(),"noResourceSelected",VueResources.local("noResourceSelected.title"),"OK",null);
JPanel panel = new JPanel(new GridLayout(1,1));
String label = Util.formatLines(VueResources.local("noResourceSelected.message"),30);
JLabel vLabel = new JLabel(label);
if(Util.isMacPlatform()){
panel.setPreferredSize(new Dimension(425,25));
panel.setSize(new Dimension(425,25));
panel.setMinimumSize(new Dimension(425,25));
}else{
panel.setPreferredSize(new Dimension(425,25));
}
panel.add(vLabel);
sad.setContentPanel(panel);
VueUtil.centerOnScreen(sad);
if (sad.showAgain())
{
sad.setVisible(true);
sad.setVisible(false);
sad.dispose();
GUI.clearWaitCursor();
return;
}
}
// List<AnalyzerResult> list = analyzer.analyze(c);
// Iterator<AnalyzerResult> i = list.iterator();
VUE.getActiveViewer().getSelection().clear();
//System.out.println("BLAH");
boolean hasResults = false;
String query = "";
final int MAX_TERMS=1;
int termCount = 0;
/* while (i.hasNext() && termCount < MAX_TERMS)
{
hasResults = true;
AnalyzerResult l = i.next();
if (l !=null && l.getValue() !=null)
{ //System.out.println(l.getRelevance() + " : " + l.getValue());
query += l.getValue().trim() + " ";
termCount++;
}*/
/*
* MK - For testing purposes I was adding Nodes of the search terms to the map.
* if (l.getValue() !=null && l.getValue().trim() != " " && !label.startsWith("Topic"))
{
LWNode node = new LWNode(label);
VUE.getActiveMap().add(node);
//VUE.getActiveViewer().getSelection().add(node);
LWLink link = new LWLink(c,node);
VUE.getActiveMap().add(link);
VUE.getActiveViewer().getSelection().add(link);
LayoutAction.random.act(VUE.getActiveViewer().getSelection());
}*/
// }
//if (query.equals(""))
query = c.getLabel();
//if (!hasResults)
//{
// VueUtil.alert(VueResources.getString("dialog.analyzeerror.message"), VueResources.getString("dialog.analyzeerror.title"));
//just use the label from the node, it didn't go right.
//}
VUE.getDRBrowser().getDataSourceViewer().queryEditor.setCriteria(query);
VUE.getDRBrowser().getDataSourceViewer().mapBasedSearch(c);
if (firstTime)
{
GUI.makeVisibleOnScreen(VUE.getDRBrowser());
firstTime = false;
}
return;
}
// random layout. scatters nodes at random
// public static final AnalyzerAction yahoo = new AnalyzerAction(new YahooAnalyzer(),VueResources.getString("analyzeaction.yahoocontent"),null);
public static final AnalyzerAction calais = new AnalyzerAction(new OpenCalaisAnalyzer(),VueResources.getString("analyzeaction.usingopencalais"),null);
public static final AutoTaggerAction calaisAutoTagger = new AutoTaggerAction(new OpenCalaisAnalyzer(),VueResources.getString("analyzeaction.usingopencalais"),null);
public static final SemanticMapAction calaisMapAction = new SemanticMapAction(new OpenCalaisAnalyzer(),VueResources.getString("analyzeaction.usingopencalais"),null);
private static final LWComponentAnalyzer alchemyAnalyzer = new AlchemyAnalyzerAPIKeyGuarder();
private static final AnalyzerAction alchemy = new AnalyzerAction(alchemyAnalyzer,VueResources.getString("analyzeaction.usingalchemyapi"),null);
private static final AutoTaggerAction alchemyAutoTagger = new AutoTaggerAction(alchemyAnalyzer,VueResources.getString("analyzeaction.usingalchemyapi"),null);
private static final SemanticMapAction alchemyMapAction = new SemanticMapAction(alchemyAnalyzer,VueResources.getString("analyzeaction.usingalchemyapi"),null);
public static final Action[] KEYWORDS_MENU_ACTIONS;
public static final Action[] RESOURCES_ACTIONS;
public static final Action[] WEB_ACTIONS;
static {
KEYWORDS_MENU_ACTIONS = new Action[2];
KEYWORDS_MENU_ACTIONS[0] = alchemyAutoTagger;
KEYWORDS_MENU_ACTIONS[1] = calaisAutoTagger;
RESOURCES_ACTIONS = new Action[2];
RESOURCES_ACTIONS[0] = alchemy;
RESOURCES_ACTIONS[1] = calais;
WEB_ACTIONS = new Action[2];
WEB_ACTIONS[0] = alchemyMapAction;
WEB_ACTIONS[1] = calaisMapAction;
}
public static final Action luckyImageAction =
new LWCAction(VueResources.getString("analyzeaction.luckyimage")) {
public void act(LWComponent c) {
JSONArray _ja;
try {
HttpClient client = new HttpClient();
GetMethod method = new GetMethod("https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=093a05d30b0be54c8b18d227fec80b6b&tag_mode=any&sort=relevance&per_page=1&format=json&nojsoncallback=1&text=" + URLEncoder.encode(c.getLabel()));
method.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
client.getParams().setParameter(HttpMethodParams.USER_AGENT,
"Visual Understanding Environment http://vue.tufts.edu");
// Send GET request
int statusCode = client.executeMethod(method);
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: " + method.getStatusLine());
}
InputStream rstream = null;
// Get the response body
rstream = method.getResponseBodyAsStream();
// Process the response from Yahoo! Web Services
BufferedReader br = new BufferedReader(new InputStreamReader(rstream));
String jsonString = "";
String line;
while ((line = br.readLine()) != null) {
jsonString += line;
}
br.close();
// Construct a JSONObject from a source JSON text string.
// A JSONObject is an unordered collection of name/value pairs. Its external
// form is a string wrapped in curly braces with colons between the names
// and values, and commas between the values and names.
// jsonString = jsonString.substring(14);
// jsonString = jsonString.substring(0,jsonString.length()-1);
JSONObject jo = new JSONObject(jsonString);
// A JSONArray is an ordered sequence of values. Its external form is a
// string wrapped in square brackets with commas between the values.
// Get the JSONObject value associated with the search result key.
jo = jo.getJSONObject("photos");
//System.out.println(jo.toString());
// Get the JSONArray value associated with the Result key
_ja = jo.getJSONArray("photo");
// Get the number of search results in this set
int resultCount = _ja.length();
//System.out.println("results: " + resultCount);
java.util.Vector vector = new java.util.Vector();
for (int i=0; i < resultCount; i++) {
JSONObject resultObject = _ja.getJSONObject(i);
String id = null;
String owner = null;
String secret = null;
String server = null;
Integer farm = null;
String title = null;
id = (String)resultObject.getString("id");
owner = (String)resultObject.get("owner");
secret = (String)resultObject.getString("secret");
server = (String)resultObject.get("server");
farm = (Integer)resultObject.get("farm");
title = (String)resultObject.get("title");
String big = "http://farm" + farm + ".static.flickr.com/" + server +"/" + id + "_" + secret +".jpg";
c.setResource(URLResource.create(new URL(big)));
}
} catch (Throwable ex) {
ex.printStackTrace();
}
return;
}
};
public static final Action[] ANALYZER_ACTIONS = {
calais,
};
public static void buildSubMenu(JMenu analyzeNodeMenu) {
// analyzeNodeMenu.add(AnalyzerAction.yahoo);
// JMenu calaisMenu = new JMenu("Node Analysis");
analyzeNodeMenu.add(calais);
analyzeNodeMenu.add(calaisAutoTagger);
analyzeNodeMenu.add(calaisMapAction);
analyzeNodeMenu.add(luckyImageAction);
}
static class SemanticMapAction extends Actions.LWCAction {
// private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(AnalyzerAction.class);
private LWComponentAnalyzer analyzer = null;
private SemanticMapAction(LWComponentAnalyzer analyzer, String name,KeyStroke keyStroke) {
super(name,keyStroke);
this.analyzer = analyzer;
}
// boolean mayModifySelection() { return true; }
public void act(LWComponent c)
{
try
{
GUI.activateWaitCursor();
//bags of components to add to map
final java.util.List<LWComponent> subCatcomps = new ArrayList<LWComponent>();
java.util.List<LWComponent> categoryComps = new ArrayList<LWComponent>();
//Analyze the resource and get a multimap of types and values
Multimap<String,AnalyzerResult> list = null;
try{
list = analyzer.analyzeResource(c);
}
catch(Exception e)
{
VueUtil.alert(VueResources.getString("dialog.semanticmaperror.message"), VueResources.getString("dialog.analyzeerror.title"));
e.printStackTrace();
return;
}
if (list.isEmpty())
{
VueUtil.alert(VueResources.getString("dialog.semanticmaperror.noresults"), VueResources.getString("dialog.analyzeerror.title"));
return;
}
Color joinNodeColor = new Color(8,119,192);
Color leafNodeColor = new Color(157,219,83);
Iterator<String> categoryIterator = list.keySet().iterator();
boolean hasResults = false;
HashMap<LWNode,Collection> categoryChildren = new HashMap<LWNode,Collection>();
while (categoryIterator.hasNext())
{
String key = categoryIterator.next();
//Make a node for the key connect it to the central node.
//TODO
Collection al = list.get(key);
tufts.vue.LWNode node = new tufts.vue.LWNode(key);
node.setLocation(c.getLocation());
tufts.vue.LWLink link = new tufts.vue.LWLink(c,node);
if (al.size() > 0)
{
// node.setFillColor(new Color())
node.setFillColor(joinNodeColor);
link.mStrokeStyle.setTo(LWComponent.StrokeStyle.DASHED);
categoryComps.add(node);
categoryComps.add(link);
categoryChildren.put(node, al);
}
}
final LWMap active = VUE.getActiveMap();
active.addChildren(categoryComps);
LayoutAction.circle.act(categoryComps);
int layerIx = 0;
while( categoryChildren != null ) {
HashMap<LWNode,Collection> newCategoryChildren = null;
HashMap<String,LWNode> newTypeHash = null;
Iterator nodeIterator = categoryChildren.keySet().iterator();
while (nodeIterator.hasNext())
{
subCatcomps.clear();
LWNode joinNode = (LWNode) nodeIterator.next();
Collection subCategories = categoryChildren.get(joinNode);
Iterator subCatIt = subCategories.iterator();
while (subCatIt.hasNext())
{
AnalyzerResult res = (AnalyzerResult) subCatIt.next();
if( res.getSubtypes() == null || res.getSubtypes().size() <= layerIx ) {
tufts.vue.LWNode node = new tufts.vue.LWNode(res.getValue());
MetadataList mlist = new MetadataList();
double rel = res.getRelevance();
if (rel < 0.25)
mlist.add("relevance", "Low");
else if (rel > 0.25 && rel < 0.50)
mlist.add("relevance", "Medium");
else if (rel > 0.50 && rel < 0.75)
mlist.add("relevance", "High");
else
mlist.add("relevance", "Essential");
mlist.add("relevance score",(new Double(rel)).toString());
node.setMetadataList(mlist);
if( res.getOntologies() != null ) {
Iterator ontIt = res.getOntologies().iterator();
while( ontIt.hasNext()) {
OntType ontType = new OntType();
ontType.setLabel((String)ontIt.next());
ontType.setBase("");
VueMetadataElement vme = new VueMetadataElement();
vme.setObject(ontType);
node.getMetadataList().getMetadata().add(vme);
}
}
tufts.vue.LWLink link = new tufts.vue.LWLink(joinNode,node);
node.setLocation(joinNode.getLocation());
node.setFillColor(leafNodeColor);
Font f = node.getFont();
Font derive = f.deriveFont(((float)(10+res.getCount())));
node.setFont(derive);
link.setStrokeColor(getColorFromRelevance(res.getRelevance()));
subCatcomps.add(node);
subCatcomps.add(link);
}
else {
if( newCategoryChildren == null ) {
newCategoryChildren = new HashMap<LWNode,Collection>();
newTypeHash = new HashMap<String,LWNode>();
}
String subtypeStr = (String)res.getSubtypes().get(layerIx);
LWNode subtypeNode = newTypeHash.get(subtypeStr);
if( subtypeNode == null ) {
subtypeNode = new LWNode(subtypeStr);
subtypeNode.setLocation(joinNode.getLocation());
tufts.vue.LWLink link = new tufts.vue.LWLink(joinNode,subtypeNode);
subCatcomps.add(subtypeNode);
subCatcomps.add(link);
newTypeHash.put(subtypeStr,subtypeNode);
newCategoryChildren.put(subtypeNode,new ArrayList());
}
Collection subtypeCollection = newCategoryChildren.get(subtypeNode);
subtypeCollection.add(res);
}
}
active.addChildren(subCatcomps);
LayoutAction.search.act(subCatcomps);
}
layerIx++;
categoryChildren = newCategoryChildren;
}
}
finally
{
GUI.clearWaitCursor();
}
}
public Color getColorFromRelevance(double relevance)
{
/*if (relevance == 1.0)
relevance=relevance-.01;
double component = 255 - (255 * relevance);
if (component < 0)
component = 0;
java.awt.Color c =null;
try
{
c= new java.awt.Color((int)component,(int)component,(int)component);
}
catch(IllegalArgumentException iae)
{
return Color.black;
}*/
return Color.gray;
}
}
static class AutoTaggerAction extends Actions.LWCAction {
// private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(AnalyzerAction.class);
private LWComponentAnalyzer analyzer = null;
private AutoTaggerAction(LWComponentAnalyzer analyzer, String name,KeyStroke keyStroke) {
super(name,keyStroke);
this.analyzer = analyzer;
}
// boolean mayModifySelection() { return true; }
public void act(LWComponent c)
{
List<AnalyzerResult> list = analyzer.analyze(c,true);
Iterator<AnalyzerResult> i = list.iterator();
VUE.getActiveViewer().getSelection().clear();
//System.out.println("BLAH");
boolean hasResults = false;
while (i.hasNext())
{
AnalyzerResult l = i.next();
if (c.getMetadataList().size() ==1 &&
c.getMetadataList().get(0).getValue().equals(""))
c.getMetadataList().remove(0);
if (l.getType()==null || (l.getType() !=null && l.getType().equals("NA")))
{
if (l.getValue()!=null && l.getValue().trim() != "" && l.getValue().length() > 1)
{
hasResults = true;
c.getMetadataList().add("none", l.getValue());
}
}
else
{
if (l.getValue()!=null && l.getValue().trim() != "" && l.getValue().length() > 1)
{
hasResults = true;
c.getMetadataList().add(l.getType(), l.getValue());
}
}
}
if (!hasResults)
{
VueUtil.alert(VueResources.getString("dialog.analyzeerror.message"), VueResources.getString("dialog.analyzeerror.title"));
}
VUE.getActiveViewer().selectionAdd(c);
return;
}
}
static private class AlchemyAnalyzerAPIKeyGuarder extends AlchemyAnalyzer
{
@Override
public List<AnalyzerResult> analyze(LWComponent c, boolean tryFallback) {
return (IsAlchemyAPIKeySet() || keyRequest()) ? super.analyze(c, tryFallback) : new java.util.ArrayList<AnalyzerResult>();
}
@Override
public List<AnalyzerResult> analyze(LWComponent c) {
return (IsAlchemyAPIKeySet() || keyRequest()) ? super.analyze(c) : new java.util.ArrayList<AnalyzerResult>();
}
@Override
public Multimap<String, AnalyzerResult> analyzeResource(LWComponent c) throws Exception {
if( !IsAlchemyAPIKeySet() )
keyRequest();
return super.analyzeResource(c);
}
private boolean keyRequest() {
String key = (String)edu.tufts.vue.preferences.implementations.AlchemyAPIPreference.getInstance().getValue();
if( null == key || "".equals(key) ) {
String title = VueResources.local("dialog.alchemyapikey.title"),
lable = VueResources.local("dialog.alchemyapikey.label");
key = (String)VueUtil.input(VUE.getApplicationFrame(), lable, title,
JOptionPane.PLAIN_MESSAGE, null, null);
if (null == key || key.length() <= 0)
return false;
}
SetAlchemyAPIKey(key);
return true;
}
}
}