/* ShortestPathRelation.java - visualization of semantic relations in Wiktionary parsed database.
*
* Copyright (c) 2009 Andrew Krizhanovsky <andrew.krizhanovsky at gmail.com>
* Distributed under GNU Public License.
*/
package wigraph;
import wikt.sql.TPage;
import wikt.sql.TLang;
import wikt.sql.TPOS;
import wikt.sql.TRelation;
import wikt.sql.TRelationType;
import wikt.constant.Relation;
import wikipedia.sql.Connect;
import wikipedia.util.StringUtil;
import edu.uci.ics.jung.graph.SparseGraph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath;
import edu.uci.ics.jung.visualization.renderers.EdgeLabelRenderer;
import javax.swing.Box;
import java.awt.Dimension;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.io.*;
import java.util.*;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import org.apache.commons.collections15.Factory;
import org.apache.commons.collections15.Transformer;
import edu.uci.ics.jung.algorithms.generators.random.EppsteinPowerLawGenerator;
import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.shortestpath.BFSDistanceLabeler;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.SparseMultigraph;
import edu.uci.ics.jung.graph.util.Pair;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.Renderer;
import edu.uci.ics.jung.graph.util.Graphs;
import edu.uci.ics.jung.graph.ObservableGraph;
import edu.uci.ics.jung.graph.event.GraphEvent;
import edu.uci.ics.jung.graph.event.GraphEventListener;
/** Visualization of semantic relations in Wiktionary parsed database.
*/
public class ShortestPathRelation extends JPanel {
//private static final long serialVersionUID = 7526217664458188502L;
private static Connect ruwikt_parsed_conn;
private static DijkstraShortestPath<String,Integer> alg;
private static Graph g_all_relations;
final VisualizationViewer<String,Number> vv;
final Layout<String,Number> layout;
/** List of words separated by comma */
private JTextField word_set1;
private JTextField word_set2;
private JTextField result_len;
//String[] word_set1 = {"доклад", "рапорт", "донесение", "сообщение", "рассказ"}; // "отчет", , "описание событий"
private static String INITIAL_WORD_SET1 = "доклад,рапорт,донесение,сообщение,рассказ"; // "отчет", , "описание событий"
private static String INITIAL_WORD_SET2 = "обнародование,издание,публикация"; // "опубликование" "оглашение", "печатание",
//String[] word_set2 = {"обнародование", "издание", "публикация"}; // "опубликование" "оглашение", "печатание",
/** Searches shortest path using word sets 1 & 2, redraw picture */
final JButton search_path_btn;
/**
* Starting vertex
*/
private String mFrom;
/**
* Ending vertex
*/
private String mTo;
private Graph<String,Number> mGraph; //SparseGraph<String, Integer> mGraph;
private Set<String> mPred;
public void initialize() {
ruwikt_parsed_conn = new Connect();
ruwikt_parsed_conn.Open(Connect.RUWIKT_HOST,Connect.RUWIKT_PARSED_DB,Connect.RUWIKT_USER,Connect.RUWIKT_PASS);
g_all_relations = LoadRelations.loadGraph(ruwikt_parsed_conn, "relation_pairs.txt", "unique_words.txt");
assert(null != g_all_relations);
System.out.println("Calculation Dijkstra shortest path...");
alg = new DijkstraShortestPath(g_all_relations);
}
/**
* @return the graph for this demo
*/
Graph<String, Number> getGraph(String[] word_set1, String[] word_set2) {
//Graph<String, Number> g = new SparseGraph<String, Number>();
//create a graph
Graph<String,Number> ig = Graphs.<String,Number>synchronizedGraph(new SparseGraph<String,Number>());
ObservableGraph<String,Number> og = new ObservableGraph<String,Number>(ig);
og.addGraphEventListener(new GraphEventListener<String,Number>() {
public void handleGraphEvent(GraphEvent<String, Number> evt) {
System.err.println("got "+evt);
}});
Graph<String, Number> g = og;
// extract all vertices which belong to shortest paths in graph 'g_all_relations'
//String word1 = "WAN"; //String word2 = "network"; //WAN - LAN - network
for(String word1 : word_set1) {
g.addVertex(word1);
for(String word2 : word_set2) {
g.addVertex(word2);
String[] word_path = null;
if(null != TPage.get(ruwikt_parsed_conn, word1) &&
null != TPage.get(ruwikt_parsed_conn, word2))
{
System.out.println("Starting search path ['" + word1 + "', '" + word2 + "']");
word_path = PathSearcher.getShortestPath(g_all_relations, alg, word1, word2);
if(word_path.length > 0) {
int len = word_path.length - 1;
System.out.println("There is a path from '" + word1 + "' to '" + word2 + "', length = " + len);
System.out.println("" + 0 + ": " + word_path[0]);
g.addVertex(word_path[0]);
for(int i=1; i<word_path.length; i++) {
Relation r = TRelation.getRelationType(ruwikt_parsed_conn, word_path[i-1], word_path[i]);
String rel_name = null == r ? "" : r.toString();
System.out.println("" + i + ".: " + rel_name + "["+ word_path[i-1] + ", "+ word_path[i] + "]");
g.addVertex(word_path[i]);
g.addEdge(g.getEdgeCount(), word_path[i-1], word_path[i]);
}
}
}
if(null == word_path || 0 == word_path.length)
System.out.println("There is no path from '" + word1 + "' to '" + word2 + "'.");
}
}
return g;
}
/** Removes vertices without edges. */
private void removeIsolatedVertices(Graph<String, Number> g) {
List<String> vertices_wo_edges = new ArrayList<String>();
for(String v : g.getVertices())
if(g.degree(v) == 0)
vertices_wo_edges.add(v);
for(String v : vertices_wo_edges)
g.removeVertex(v);
}
EdgeLabelRenderer edgeLabelRenderer;
public ShortestPathRelation() {
initialize();
String[] w1 = INITIAL_WORD_SET1.split(",");
String[] w2 = INITIAL_WORD_SET2.split(",");
this.mGraph = getGraph(w1, w2);
removeIsolatedVertices(this.mGraph);
setBackground(Color.WHITE);
// show graph
layout = new FRLayout<String,Number>(mGraph);
vv = new VisualizationViewer<String,Number>(layout);
vv.setBackground(Color.WHITE);
vv.getRenderContext().setVertexDrawPaintTransformer(new MyVertexDrawPaintFunction<String>());
vv.getRenderContext().setVertexFillPaintTransformer(new MyVertexFillPaintFunction<String>());
vv.getRenderContext().setEdgeDrawPaintTransformer(new MyEdgePaintFunction());
vv.getRenderContext().setEdgeStrokeTransformer(new MyEdgeStrokeFunction());
vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<String>());
vv.setGraphMouse(new DefaultModalGraphMouse<String, Number>());
vv.addPostRenderPaintable(new VisualizationViewer.Paintable(){
public boolean useTransform() {
return true;
}
public void paint(Graphics g) {
if(mPred == null) return;
// for all edges, paint edges that are in shortest path
for (Number e : layout.getGraph().getEdges()) {
if(isBlessed(e)) {
String v1 = mGraph.getEndpoints(e).getFirst();
String v2 = mGraph.getEndpoints(e).getSecond();
Point2D p1 = layout.transform(v1);
Point2D p2 = layout.transform(v2);
p1 = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p1);
p2 = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p2);
Renderer<String,Number> renderer = vv.getRenderer();
renderer.renderEdge(
vv.getRenderContext(),
layout,
e);
}
}
}
});
edgeLabelRenderer = vv.getRenderContext().getEdgeLabelRenderer();
Transformer<Number,String> stringer = new Transformer<Number,String>(){
public String transform(Number e) {
Relation r = TRelation.getRelationType(ruwikt_parsed_conn,
mGraph.getEndpoints(e).getFirst(),
mGraph.getEndpoints(e).getSecond());
/*System.out.println("word1=" + mGraph.getEndpoints(e).getFirst() +
"; word2=" + mGraph.getEndpoints(e).getSecond());
if(null != r)
System.out.println("relation=" + r.toString());*/
return null == r ? "" : r.toString();
//return "Edge:"+mGraph.getEndpoints(e).toString();
}
};
vv.getRenderContext().setEdgeLabelTransformer(stringer);
// button to search a shortest path using word sets 1 & 2, redraw picture
search_path_btn = new JButton("Search");
search_path_btn.setToolTipText("Search a shortest path");
search_path_btn.setBackground(Color.decode("#D8C0C0"));
search_path_btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// delete all vertices
List<String> vertices = new ArrayList<String>();
vertices.addAll(mGraph.getVertices());
for(String v : vertices)
mGraph.removeVertex(v);
String[] w1_source = word_set1.getText().split(",");
String[] w2_source = word_set2.getText().split(",");
w1_source = StringUtil.trim(w1_source);
w2_source = StringUtil.trim(w2_source);
// get only words in Wiktionary, i.e. in a graph
String[] w1 = GraphCreator.getOnlyVertexInGraph(g_all_relations, w1_source);
String[] w2 = GraphCreator.getOnlyVertexInGraph(g_all_relations, w2_source);
mGraph = getGraph(w1, w2);
removeIsolatedVertices(mGraph);
layout.setGraph(mGraph);
vv.repaint();
DistanceData dist = PathSearcher.calcPathLenRelatedness(g_all_relations, alg, w1, w2);
result_len.setText("Shortest path len: min=" + dist.min +
", average="+ dist.average +
", max="+ dist.max);
System.out.println("Word set 1 has length " + w1.length);
System.out.println("Word set 2 has length " + w2.length);
System.out.println("Vertices : "+ mGraph.getVertexCount());
System.out.println("Edges : " + mGraph.getEdgeCount());
}
});
setLayout(new BorderLayout());
add(vv, BorderLayout.CENTER);
// set up controls
add(setUpControls(), BorderLayout.SOUTH);
}
boolean isBlessed( Number e ) {
Pair<String> endpoints = mGraph.getEndpoints(e);
String v1= endpoints.getFirst() ;
String v2= endpoints.getSecond() ;
return v1.equals(v2) == false && mPred.contains(v1) && mPred.contains(v2);
}
/**
* @author danyelf
*/
public class MyEdgePaintFunction implements Transformer<Number,Paint> {
public Paint transform(Number e) {
if ( mPred == null || mPred.size() == 0) return Color.BLACK;
if( isBlessed( e )) {
return new Color(0.0f, 0.0f, 1.0f, 0.5f);//Color.BLUE;
} else {
return Color.LIGHT_GRAY;
}
}
}
public class MyEdgeStrokeFunction implements Transformer<Number,Stroke> {
protected final Stroke THIN = new BasicStroke(1);
protected final Stroke THICK = new BasicStroke(1);
public Stroke transform(Number e) {
if ( mPred == null || mPred.size() == 0) return THIN;
if (isBlessed( e ) ) {
return THICK;
} else
return THIN;
}
}
/**
* @author danyelf
*/
public class MyVertexDrawPaintFunction<V> implements Transformer<V,Paint> {
public Paint transform(V v) {
return Color.black;
}
}
public class MyVertexFillPaintFunction<V> implements Transformer<V,Paint> {
public Paint transform( V v ) {
if ( v == mFrom) {
return Color.BLUE;
}
if ( v == mTo ) {
return Color.BLUE;
}
if ( mPred == null ) {
return Color.LIGHT_GRAY;
} else {
if ( mPred.contains(v)) {
return Color.RED;
} else {
return Color.LIGHT_GRAY;
}
}
}
}
/**
*
*/
private JPanel setUpControls() {
JPanel jp = new JPanel();
jp.setBackground(Color.WHITE);
jp.setLayout(new BoxLayout(jp, BoxLayout.PAGE_AXIS));
jp.setBorder(BorderFactory.createLineBorder(Color.black, 3));
// jp_ss - word wist 1 (ss) horizontal panel
JPanel jp_wl1 = new JPanel();
jp_wl1.setLayout(new BoxLayout(jp_wl1, BoxLayout.X_AXIS));
JLabel word1_label = new JLabel("List of words 1 separated by comma");//, Label.RIGHT)
word_set1 = new JTextField(20);
word1_label.setDisplayedMnemonic('W');
word_set1.setFocusAccelerator('W');
word_set1.setText(INITIAL_WORD_SET1);
jp_wl1.add(word1_label);
jp_wl1.add(word_set1);
// jp_ss - word wist 1 (ss) horizontal panel
JPanel jp_wl2 = new JPanel();
jp_wl2.setLayout(new BoxLayout(jp_wl2, BoxLayout.X_AXIS));
JLabel word2_label = new JLabel("List of words 2");//, Label.RIGHT)
word_set2 = new JTextField(20);
word2_label.setDisplayedMnemonic('o');
word_set2.setFocusAccelerator('o');
word_set2.setText(INITIAL_WORD_SET2);
jp_wl2.add(word2_label);
jp_wl2.add(word_set2);
jp_wl2.add(Box.createRigidArea(new Dimension(5,0)));
jp_wl2.add(search_path_btn);
jp.add(jp_wl1);
jp.add(jp_wl2);
result_len = new JTextField(20);
jp.add(result_len);
jp.add(
new JLabel("Select a pair of vertices for which a shortest path will be displayed"));
JPanel jp2 = new JPanel();
jp2.add(new JLabel("vertex from", SwingConstants.LEFT));
jp2.add(getSelectionBox(true));
jp2.setBackground(Color.white);
JPanel jp3 = new JPanel();
jp3.add(new JLabel("vertex to", SwingConstants.LEFT));
jp3.add(getSelectionBox(false));
jp3.setBackground(Color.white);
jp.add( jp2 );
jp.add( jp3 );
final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
vv.setGraphMouse(graphMouse);
JComboBox modeBox = graphMouse.getModeComboBox();
jp.setBorder(BorderFactory.createTitledBorder("Mouse Mode"));
jp.add(modeBox);
return jp;
}
private Component getSelectionBox(final boolean from) {
Set<String> s = new TreeSet<String>();
for (String v : mGraph.getVertices()) {
s.add(v);
}
final JComboBox choices = new JComboBox(s.toArray());
choices.setSelectedIndex(-1);
choices.setBackground(Color.WHITE);
choices.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String v = (String)choices.getSelectedItem();
if (from) {
mFrom = v;
} else {
mTo = v;
}
drawShortest();
repaint();
}
});
return choices;
}
/**
*
*/
protected void drawShortest() {
if (mFrom == null || mTo == null) {
return;
}
BFSDistanceLabeler<String,Number> bdl = new BFSDistanceLabeler<String,Number>();
bdl.labelDistances(mGraph, mFrom);
mPred = new HashSet<String>();
// grab a predecessor
String v = mTo;
Set<String> prd = bdl.getPredecessors(v);
mPred.add( mTo );
while( prd != null && prd.size() > 0) {
v = prd.iterator().next();
mPred.add( v );
if ( v == mFrom ) return;
prd = bdl.getPredecessors(v);
}
}
public static void main(String[] s) {
JFrame jf = new JFrame();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.getContentPane().add(new ShortestPathRelation());
jf.pack();
jf.setVisible(true);
}
static class GraphFactory implements Factory<Graph<String,Number>> {
public Graph<String,Number> create() {
return new SparseMultigraph<String,Number>();
}
}
static class VertexFactory implements Factory<String> {
char a = 'a';
public String create() {
return Character.toString(a++);
}
}
static class EdgeFactory implements Factory<Number> {
int count;
public Number create() {
return count++;
}
}
}