/* * 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. */ /* * TextOpenAction.java * * Very rough initial data import demos and first Zotero Prototype * data import * * Created on October 23, 2003, 12:40 PM */ package tufts.vue.action; /** * * @author akumar03 * @author dhelle01 */ import java.io.*; import java.net.*; import java.util.zip.*; import java.util.*; import java.awt.event.*; import javax.swing.*; import tufts.vue.*; public class TextOpenAction extends VueAction { private static boolean DEBUG_LOCAL = false; //todo: add constants for row length, starting position, y gaps and x gaps //also make these all be read from VueResources.properties file for ease of modification //after compilation public static final int NODE_LABEL_TRUNCATE_LENGTH = 8; public static final int CIRCLE_LAYOUT = 0; public static final int RANDOM_LAYOUT = 1; public static final int GRAVITY_LAYOUT = 2; public static final int STAGGERED_LAYOUT = 3; public static int layout = STAGGERED_LAYOUT; public static final int MAP_SIZE = 500; public static final int MAX_SIZE =5000; public static boolean ZOTERO_PROTOTYPE = false; public TextOpenAction(String label) { super(label, null, ":general/Open"); } public TextOpenAction() { this("Import Text file..."); } public static boolean isZoteroProtypeEnabled() { return ZOTERO_PROTOTYPE; } public static void setZoteroPrototypeEnabled(boolean enabled) { ZOTERO_PROTOTYPE = enabled; } // workaround for rapid-succession Ctrl-O's which pop multiple open dialogs private static final Object LOCK = new Object(); private static boolean openUnderway = false; public void actionPerformed(ActionEvent e) { synchronized (LOCK) { if (openUnderway) return; openUnderway = true; } try { File file = ActionUtil.openFile("Open Map", "text"); displayMap(file); System.out.println("Action["+e.getActionCommand()+"] completed."); } finally { openUnderway = false; } } public static void displayMap(File file) { if (file != null) { VUE.activateWaitCursor(); try { LWMap loadedMap = loadMap(file.getAbsolutePath()); VUE.displayMap(loadedMap); }catch(Throwable t) { t.printStackTrace(); } finally { VUE.clearWaitCursor(); } } } // todo: have only one root loadMap that hanldes files & urls -- actually, make it ALL url's public static LWMap loadMap(String fileName) throws Exception{ Map<String,LWNode> nodeMap = new HashMap<String,LWNode>(); Map<String,Integer> repeatMap = new HashMap<String,Integer>(); Map<String,LWLink> linkMap = new HashMap<String, LWLink>(); String mapName = fileName.substring(fileName.lastIndexOf("/")+1,fileName.length()); if(mapName.lastIndexOf(".")>0) mapName = mapName.substring(0,mapName.lastIndexOf(".")); if(mapName.length() == 0) mapName = "Text Import"; LWMap map = new LWMap(mapName); BufferedReader reader = new BufferedReader(new FileReader(fileName)); String line; String links = null; // for Zotero prototype if(!ZOTERO_PROTOTYPE) // in zotero import each row is a node { reader.readLine(); // skip the first line } int count = 0; // for staggered layout float y = 20; float x = 0; int toggle = 0; List<String[]> linksList = new ArrayList<String[]>(); // also for Zotero Prototype while((line=reader.readLine()) != null && count <MAX_SIZE) { if(DEBUG_LOCAL) { System.out.println(line+" words: "+line.split(",").length); } if(ZOTERO_PROTOTYPE) { String[] parts = line.split("\""); //todo: sanity check on number of parts line = parts[0]; if(parts.length > 1) links = parts[1]; else links = null; if(DEBUG_LOCAL) { if(links!=null) { System.out.println("links -- " + links); } } } String[] words = line.split(","); LWNode node1; LWNode node2 = null; if(ZOTERO_PROTOTYPE) { node1 = new LWNode(words[1]); try { // todo: can throw its own stack trace // so, avoid for non standard (rdf, zotero notes etc.) uri; Resource resource = map.getResourceFactory().get(words[2]); node1.setResource(resource); } catch(Exception e) { System.out.println("Exception setting resource: " + e); } String id = words[0]; if(links != null) { String[] linksTo = links.split(","); String[] arr = new String[linksTo.length+1]; arr[0] = id; System.arraycopy(linksTo,0,arr,1,linksTo.length); linksList.add(arr); } // this makes the id editable.. // have to be careful about maintaining id for updates // todo: put in content info instead. //edu.tufts.vue.metadata.VueMetadataElement vme = new edu.tufts.vue.metadata.VueMetadataElement(); ////vme.setType(edu.tufts.vue.metadata.VueMetadataElement.CATEGORY); //String[] obj = {"http://vue.tufts.edu/custom.rdfs#z_id",id}; //vme.setObject(obj); //node1.getMetadataList().getMetadata().add(vme); //vme.setType(edu.tufts.vue.metadata.VueMetadataElement.CATEGORY); // put in content info instead of keywords: node1.getResource().setProperty("Zotero.id",id); // just for debug purposes -- many more references than I expected // and they are not bidirectional.. this seems to not just be // the "related" relation from zotero, which is probably what we want? //edu.tufts.vue.metadata.VueMetadataElement vme2 = new edu.tufts.vue.metadata.VueMetadataElement(); //vme2.setType(edu.tufts.vue.metadata.VueMetadataElement.CATEGORY); //String[] obj2 = {"http://vue.tufts.edu/custom.rdfs#z_links",links}; //vme2.setObject(obj2); //if(links!=null) //node1.getMetadataList().getMetadata().add(vme2); nodeMap.put(words[0],node1); if(DEBUG_LOCAL) { System.out.println("Node map addition " + words[0] + "," + node1); } //repeatMap.put(words[0], new Integer(1)); map.add(node1); node1.layout(); } else if(words.length == 4) { if(!nodeMap.containsKey(words[0])) { node1 = new LWNode(words[0]); Resource resource = map.getResourceFactory().get(words[1]); node1.setResource(resource); nodeMap.put(words[0],node1); repeatMap.put(words[0], new Integer(1)); map.add(node1); } else { node1 = nodeMap.get(words[0]); int nc= repeatMap.get(words[0]).intValue(); repeatMap.put(words[0],new Integer(nc+1)); } if(!nodeMap.containsKey(words[2])) { node2 = new LWNode(words[2]); Resource resource = map.getResourceFactory().get( words[3] ); node2.setResource(resource); map.add(node2); nodeMap.put(words[2],node2); repeatMap.put(words[2], new Integer(1)); } else { node2 = nodeMap.get(words[2]); int nc= repeatMap.get(words[2]).intValue(); repeatMap.put(words[2],new Integer(nc+1)); } }else { try { if(!nodeMap.containsKey(words[0])) { node1 = new LWNode(words[0]); nodeMap.put(words[0],node1); repeatMap.put(words[0], new Integer(1)); map.add(node1); } else { node1 = nodeMap.get(words[0]); int nc= repeatMap.get(words[0]).intValue(); repeatMap.put(words[0],new Integer(nc+1)); } if(!nodeMap.containsKey(words[1])) { node2 = new LWNode(words[1]); map.add(node2); nodeMap.put(words[1],node2); repeatMap.put(words[1], new Integer(1)); } else { node2 = nodeMap.get(words[1]); int nc= repeatMap.get(words[1]).intValue(); repeatMap.put(words[1],new Integer(nc+1)); } } catch(Exception e) { System.out.println("Exception in text import: " + e); node1 = new LWNode("exception"); node2 = new LWNode("exception 2"); } } if(ZOTERO_PROTOTYPE) { // actually just avoiding else clause code // zotero links are calculated after loop // using linkslist and nodemap } else { System.out.println("COUNT: "+count+" Node1 :"+ node1.getLabel()+" Node2 : "+ (node2.getLabel())); String linkKey = node1.getLabel()+node2.getLabel(); if(!linkMap.containsKey(linkKey)) { LWLink link = new LWLink(node1,node2); linkMap.put(linkKey,link); map.add(link); } } if(layout == STAGGERED_LAYOUT) { //x += 150; x += 300; if(toggle == 0) { toggle++; y = y + 50; } else if(toggle == 1) { toggle = 0; y = y - 50; } if(count % 5 == 0) { y += 100; x = 400; toggle = 0; } node1.setLocation(x,y); if(node2 != null) node2.setLocation(x+150,y); } if(layout == CIRCLE_LAYOUT ) { double angle = Math.random()*Math.PI*4; node1.setLocation(MAP_SIZE*(1+Math.cos(angle)),MAP_SIZE*(1+Math.sin(angle))); angle = Math.random()*Math.PI*4; node2.setLocation(MAP_SIZE*(1+Math.cos(angle)),MAP_SIZE*(1+Math.sin(angle))); } else if (layout == RANDOM_LAYOUT) { node1.setLocation(MAP_SIZE*Math.random(),MAP_SIZE*Math.random()); node2.setLocation(MAP_SIZE*Math.random(),MAP_SIZE*Math.random()); } else if(layout == GRAVITY_LAYOUT) { double angle = Math.random()*Math.PI*4; int nc1= repeatMap.get(node1.getLabel()).intValue(); // nc1 = nc1*nc1; double fact1 = Math.sqrt(nc1); node1.setLocation(MAP_SIZE*(1+Math.cos(angle)/fact1),MAP_SIZE*(1+Math.sin(angle)/fact1)); angle = Math.random()*Math.PI*4; int nc2= repeatMap.get(node1.getLabel()).intValue(); //nc2=nc2*nc2; double fact2 = Math.sqrt(nc2); node2.setLocation(MAP_SIZE*(1+Math.cos(angle)/fact2),MAP_SIZE*(1+Math.sin(angle)/fact2)); } count++; } if(ZOTERO_PROTOTYPE) { Iterator<String[]> nodes = linksList.iterator(); while(nodes.hasNext()) { String[] arr = nodes.next(); LWNode node = nodeMap.get(arr[0]); if(DEBUG_LOCAL) { System.out.println("TextOpenAction: linkslist arr length " + arr.length); } for(int i=1;i<arr.length;i++) { String linkKey = arr[0] + "|" + arr[i]; if(DEBUG_LOCAL) { System.out.println("TextOpenAction: linkKey " + linkKey); } if(!linkMap.containsKey(linkKey)) { if(DEBUG_LOCAL) { System.out.println("TextOpenAction: accepted -- " + linkKey); } LWNode node2 = nodeMap.get(arr[i]); LWLink link = new LWLink(node,node2); linkMap.put(linkKey,link); map.add(link); } } } } return map; } public static void main(String args[]) throws Exception { String file = args.length == 0 ? "C:\\anoop\\vue\\maps\\Short.rdf" : args[0]; System.err.println("Attempting to read map from " + file); DEBUG.Enabled = true; VUE.parseArgs(args); LWMap map; map = loadMap(file); System.out.println("Loaded map: " + map); } }