package edu.cmu.minorthird.text.gui;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.border.TitledBorder;
import javax.swing.text.SimpleAttributeSet;
import edu.cmu.minorthird.text.Span;
import edu.cmu.minorthird.text.SpanDifference;
import edu.cmu.minorthird.text.TextLabels;
import edu.cmu.minorthird.util.gui.Controllable;
import edu.cmu.minorthird.util.gui.ControlledViewer;
import edu.cmu.minorthird.util.gui.VanillaViewer;
import edu.cmu.minorthird.util.gui.Viewer;
import edu.cmu.minorthird.util.gui.ViewerControls;
import edu.cmu.minorthird.util.gui.ViewerFrame;
/**
* A scheme for marking spans up in a labeling.
*
* @author William Cohen
*/
public class MarkupControls extends ViewerControls{
static final long serialVersionUID=20080305L;
private static Map<String,SimpleAttributeSet> colorMap=
new TreeMap<String,SimpleAttributeSet>(); // map color name ->
// SimpleAttributeSet
static{
colorMap.put("blue",HiliteColors.blue);
colorMap.put("red",HiliteColors.red);
colorMap.put("green",HiliteColors.green);
colorMap.put("yellow",HiliteColors.yellow);
colorMap.put("gray",HiliteColors.gray);
}
private static final int NUM_COLOR_BOXES=5;
protected TextLabels labels; // TextLabels being marked up
protected List<String> types=new ArrayList<String>(); // all types in the
// textLabels
private Map<String,String> typeColorCode=new HashMap<String,String>(); // map
// types
// ->
// color
// name
// (eg
// "red")
private Map<String,Map<String,String>> propColorCode=
new HashMap<String,Map<String,String>>(); // map prop -> (map from
// value->color name)
private JComboBox guessBox,truthBox; // used for spanDifferencesa
private SpanDifference sd; // result of diffing two types
private List<JComboBox> boxList=new ArrayList<JComboBox>(); // all combo
// boxes, for
// reset
public MarkupControls(TextLabels labels){
super();
this.labels=labels;
types=new ArrayList<String>(labels.getTypes());
typeColorCode=new HashMap<String,String>();
initialize(); // initialize again, now that types are defined
}
/**
* Lay out the controls
*/
@Override
protected void initialize(){
if(types==null)
return; // will go back and initialize later
setLayout(new GridBagLayout());
// subpanel will contain all content, and
// will be wrapped in a JScrollPane
JPanel subpanel=new JPanel();
subpanel.setLayout(new GridBagLayout());
//
// configure the 'compare x to y' bit
//
guessBox=new JComboBox();
guessBox.addItem("-compare- ");
truthBox=new JComboBox();
truthBox.addItem("-to-");
for(int i=0;i<types.size();i++){
guessBox.addItem(types.get(i).toString());
truthBox.addItem(types.get(i).toString());
}
ActionListener diffListener=new ActionListener(){
@Override
public void actionPerformed(ActionEvent e){
if(guessBox.getSelectedIndex()==0||truthBox.getSelectedIndex()==0){
sd=null;
}else{
String guess=guessBox.getSelectedItem().toString();
String truth=truthBox.getSelectedItem().toString();
sd=
new SpanDifference(labels.instanceIterator(guess),labels
.instanceIterator(truth),labels.closureIterator(truth));
}
}
};
guessBox.addActionListener(diffListener);
truthBox.addActionListener(diffListener);
JPanel comparePanel=new JPanel();
comparePanel.setBorder(new TitledBorder("Compare types"));
// comparePanel.setLayout(new BoxLayout(comparePanel,BoxLayout.Y_AXIS));
comparePanel.add(guessBox);
comparePanel.add(truthBox);
boxList.add(guessBox);
boxList.add(truthBox);
subpanel.add(comparePanel,gbc(0));
//
// configure the highlight types area
//
JPanel typePanel=new JPanel();
// typePanel.setBorder(new TitledBorder("Highlight types"));
typePanel.setLayout(new GridBagLayout());
for(int i=0;i<NUM_COLOR_BOXES;i++){
JPanel boxPanel=new JPanel();
JComboBox colorBox=makeBox("color",colorMap.keySet());
JComboBox typeBox=makeBox("type",types);
boxPanel.add(colorBox);
boxPanel.add(typeBox);
boxList.add(colorBox);
boxList.add(typeBox);
ActionListener boxListener=new ColorTypeBoxListener(colorBox,typeBox);
colorBox.addActionListener(boxListener);
typeBox.addActionListener(boxListener);
typePanel.add(boxPanel,gbc(i));
}
//
// configure the highlight properties area
//
JPanel propPanel=new JPanel();
// propPanel.setBorder(new TitledBorder("Highlight properties"));
propPanel.setLayout(new GridBagLayout());
for(int i=0;i<NUM_COLOR_BOXES;i++){
JPanel boxPanel=new JPanel();
JComboBox colorBox=makeBox("color",colorMap.keySet());
JComboBox propBox=makeBox("property",propValuePairs());
boxPanel.add(colorBox);
boxPanel.add(propBox);
boxList.add(colorBox);
boxList.add(propBox);
ActionListener boxListener=new ColorPropBoxListener(colorBox,propBox);
colorBox.addActionListener(boxListener);
propBox.addActionListener(boxListener);
propPanel.add(boxPanel,gbc(i));
}
//
// configure a summary window
//
JPanel summaryPanel=new JPanel();
summaryPanel.setLayout(new GridBagLayout());
summaryPanel.add(new JLabel("Documents:"),gbc(0,0));
summaryPanel.add(new JLabel(Integer.toString(labels.getTextBase().size())),
gbc(0,1));
summaryPanel.add(new JLabel("Token Properties:"),gbc(1,0));
summaryPanel.add(new JLabel(Integer.toString(labels.getTokenProperties()
.size())),gbc(1,1));
summaryPanel.add(new JLabel("Span Properties:"),gbc(2,0));
summaryPanel.add(new JLabel(Integer.toString(labels.getSpanProperties()
.size())),gbc(2,1));
summaryPanel.add(new JLabel("Span Types:"),gbc(3,0));
summaryPanel.add(new JLabel(Integer.toString(labels.getTypes().size())),
gbc(3,1));
JTabbedPane tabbedPane=new JTabbedPane();
tabbedPane.add("Summary",summaryPanel);
tabbedPane.add("SpanTypes",typePanel);
tabbedPane.add("SpanProps",propPanel);
subpanel.add(tabbedPane,gbc(1));
JPanel applyPanel=new JPanel();
applyPanel.setBorder(new TitledBorder("Update display"));
applyPanel.add(makeApplyButton());
applyPanel.add(makeResetButton());
subpanel.add(applyPanel,gbc(2));
// finally add the subpanel with a scroll pane and a desire to
// fill some space..
GridBagConstraints gbc=gbc(0);
gbc.fill=GridBagConstraints.BOTH;
add(new JScrollPane(subpanel),gbc);
}
@Override
public int preferredLocation(){
return ViewerControls.RIGHT;
}
@Override
public boolean prefersToBeResized(){
return true;
}
private Set<String> propValuePairs(){
SortedSet<String> accum=new TreeSet<String>();
Set<String> props=labels.getSpanProperties();
for(Iterator<String> i=props.iterator();i.hasNext();){
String prop=i.next();
for(Iterator<Span> j=labels.getSpansWithProperty(prop);j.hasNext();){
Span s=j.next();
String val=labels.getProperty(s,prop);
accum.add(packPropValue(prop,val));
}
}
return accum;
}
private String packPropValue(String p,String v){
return p+"="+v;
}
private String[] unpackPropValue(String pv){
return pv.split("=");
}
private JButton makeResetButton(){
return new JButton(new AbstractAction("-reset controls-"){
static final long serialVersionUID=20080306L;
@Override
public void actionPerformed(ActionEvent e){
for(int i=0;i<boxList.size();i++){
(boxList.get(i)).setSelectedIndex(0);
}
}
});
}
// a drop-down box of something's
private JComboBox makeBox(String tag,Collection<?> c){
final JComboBox box=new JComboBox();
box.addItem("-select "+tag+"-");
for(Iterator<?> j=c.iterator();j.hasNext();){
box.addItem(j.next());
}
return box;
}
private class ColorTypeBoxListener implements ActionListener{
private JComboBox colorBox,typeBox;
private String type=null;
public ColorTypeBoxListener(JComboBox colorBox,JComboBox typeBox){
this.colorBox=colorBox;
this.typeBox=typeBox;
}
@Override
public void actionPerformed(ActionEvent ev){
if(typeBox.getSelectedIndex()>0){
type=(String)typeBox.getSelectedItem();
if(colorBox.getSelectedIndex()!=0){
typeColorCode.put(type,(String)colorBox.getSelectedItem());
}else{
typeColorCode.remove(type);
}
}else{
if(type!=null)
typeColorCode.remove(type);
type=null;
}
}
}
private class ColorPropBoxListener implements ActionListener{
private JComboBox colorBox,propValBox;
private String[] propVal=null;
public ColorPropBoxListener(JComboBox colorBox,JComboBox propValBox){
this.colorBox=colorBox;
this.propValBox=propValBox;
}
@Override
public void actionPerformed(ActionEvent ev){
if(propValBox.getSelectedIndex()>0){
propVal=unpackPropValue((String)propValBox.getSelectedItem());
if(colorBox.getSelectedIndex()!=0){
putPropCode(propVal[0],propVal[1],(String)colorBox.getSelectedItem());
}else{
removePropCode(propVal[0],propVal[1]);
}
}else{
if(propVal!=null)
removePropCode(propVal[0],propVal[1]);
propVal=null;
}
}
}
private void putPropCode(String p,String v,String color){
Map<String,String> m=propColorCode.get(p);
if(m==null)
propColorCode.put(p,(m=new HashMap<String,String>()));
m.put(v,color);
}
private void removePropCode(String p,String v){
Map<String,String> m=propColorCode.get(p);
if(m==null)
propColorCode.put(p,(m=new HashMap<String,String>()));
m.remove(v);
}
// build a drop-down box to select a color
// private JComboBox colorBox(final int i){
// final String type=(String)types.get(i);
// final String color=(String)typeColorCode.get(type);
// final JComboBox box=new JComboBox();
// for(Iterator<String> j=colorMap.keySet().iterator();j.hasNext();){
// box.addItem(j.next());
// }
// if(color!=null)
// box.setSelectedItem(color);
// else
// box.setSelectedIndex(0);
// box.addActionListener(new ActionListener(){
//
// public void actionPerformed(ActionEvent e){
// if(box.getSelectedIndex()!=0)
// typeColorCode.put(type,(String)box.getSelectedItem());
// else
// typeColorCode.remove(type);
// }
// });
// return box;
// }
// build GridBagConstraint with some default values
private GridBagConstraints gbc(int i){
GridBagConstraints gbc=new GridBagConstraints();
gbc.anchor=GridBagConstraints.WEST;
gbc.weightx=gbc.weighty=1.0;
gbc.gridx=0;
gbc.gridy=i;
return gbc;
}
// build GridBagConstraint with some default values
private GridBagConstraints gbc(int row,int col){
GridBagConstraints gbc=new GridBagConstraints();
gbc.ipadx=20;
gbc.anchor=col==1?GridBagConstraints.WEST:GridBagConstraints.EAST;
gbc.weightx=gbc.weighty=1.0;
gbc.gridx=col;
gbc.gridy=row;
return gbc;
}
//
// what's exported to the viewer...
//
/**
* Tell the ControlledViewer what color is associated with a type.
*/
public SimpleAttributeSet getColor(String type){
String s=typeColorCode.get(type);
if(s==null)
return null;
else
return colorMap.get(s);
}
/**
* Tell the ControlledViewer what color is associated with a property/value
* pair
*/
public SimpleAttributeSet getColor(String prop,String value){
Map<String,String> m=propColorCode.get(prop);
if(m==null)
return null;
String s=m.get(value);
if(s==null)
return null;
else
return colorMap.get(s);
}
public Set<String> getColoredProperties(){
return propColorCode.keySet();
}
public Set<String> getColoredValues(String prop){
Map<String,String> m=propColorCode.get(prop);
if(m==null)
return Collections.<String>emptySet();
return m.keySet();
}
/**
* Export a span difference to the controlled Span Viewer.
*/
public SpanDifference getSpanDifference(){
return sd;
}
// for an interactive test, below
private static class TestViewer extends VanillaViewer implements Controllable{
static final long serialVersionUID=20080306L;
public TestViewer(){
super("yow");
}
@Override
public void applyControls(ViewerControls controls){
System.out.println("applied "+controls);
}
}
public static void main(String[] argv){
try{
TextLabels labels=
edu.cmu.minorthird.text.learn.SampleExtractionProblem.trainLabels();
Viewer v=
new ControlledViewer(new TestViewer(),new MarkupControls(labels));
new ViewerFrame("MarkupControls",v);
}catch(Exception e){
e.printStackTrace();
}
}
}