package edu.cmu.minorthird.text.gui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import org.apache.log4j.Logger;
import edu.cmu.minorthird.text.BasicTextLabels;
import edu.cmu.minorthird.text.BoneheadStemmer;
import edu.cmu.minorthird.text.FancyLoader;
import edu.cmu.minorthird.text.MonotonicTextLabels;
import edu.cmu.minorthird.text.MutableTextLabels;
import edu.cmu.minorthird.text.NestedTextLabels;
import edu.cmu.minorthird.text.TextBase;
import edu.cmu.minorthird.text.TextBaseLoader;
import edu.cmu.minorthird.text.TextLabels;
import edu.cmu.minorthird.text.TextLabelsLoader;
import edu.cmu.minorthird.text.mixup.MixupInterpreter;
import edu.cmu.minorthird.text.mixup.MixupProgram;
/**
* View results of executing a mixup program.
*
* @author William Cohen
*/
public class MixupDebugger extends JComponent{
static final long serialVersionUID=20080306L;
private TextBaseEditor editor;
private Logger log=Logger.getLogger(this.getClass());
public TextBaseEditor getEditor(){
return editor;
}
public MixupDebugger(TextBase base,File groundTruthLabelsFile,
File mixupProgramFile,boolean readOnly,boolean stem) throws IOException{
this(base,new BasicTextLabels(base),groundTruthLabelsFile,mixupProgramFile,
readOnly,stem);
}
public MixupDebugger(TextBase base,TextLabels baseLabels,
File groundTruthLabelsFile,File mixupProgramFile,boolean readOnly,
boolean stem) throws IOException{
super();
if(mixupProgramFile==null){
throw new IllegalArgumentException("mixup program must be specified");
}
MutableTextLabels truthLabels=null;
MonotonicTextLabels programLabels;
MixupProgram program=new MixupProgram();
if(groundTruthLabelsFile!=null&&groundTruthLabelsFile.exists()){
log
.info("loading textLabels from "+groundTruthLabelsFile.getName()+
"...");
truthLabels=new BasicTextLabels(base);
TextLabelsLoader labelsLoader=new TextLabelsLoader();
labelsLoader
.setClosurePolicy(TextLabelsLoader.CLOSE_TYPES_IN_LABELED_DOCS);
labelsLoader.importOps(truthLabels,base,groundTruthLabelsFile);
}else{
if(truthLabels==null)
truthLabels=new BasicTextLabels(base);
}
if(stem)
new BoneheadStemmer().stem(base,truthLabels);
String errorString="no mixup program specified";
try{
program=new MixupProgram(mixupProgramFile);
errorString="Loaded "+mixupProgramFile.getName();
}catch(Exception e){
errorString=e.toString();
}
programLabels=
new NestedTextLabels(new NestedTextLabels(baseLabels),truthLabels);
System.out.println("evaluating program from "+mixupProgramFile.getName()+
"...");
MixupInterpreter interp=new MixupInterpreter(program);
interp.eval(programLabels);
new TextLabelsLoader()
.saveTypesAsOps(programLabels,new File("test.labels"));
StatusMessage statusMsg=new StatusMessage();
JScrollPane errorPane=new JScrollPane(new JTextField(errorString));
editor=
new TextBaseEditor(base,programLabels,truthLabels,statusMsg,readOnly);
JButton saveButton=null;
if(groundTruthLabelsFile!=null){
saveButton=
new JButton(new SaveTruthLabelsAction("Save current to "+
groundTruthLabelsFile.getName(),groundTruthLabelsFile,
truthLabels,statusMsg));
}else{
saveButton=
new JButton(new SaveTruthLabelsAction(
"Save current to [no file specified]",groundTruthLabelsFile,
truthLabels,statusMsg));
saveButton.setEnabled(false);
}
editor.getViewerTracker().setSaveAs(groundTruthLabelsFile);
JButton refreshButton=
new JButton(new RefreshProgramAction("Reload program from "+
mixupProgramFile.getName(),mixupProgramFile,base,baseLabels,
truthLabels,editor.getViewer(),errorPane));
JTextField newTypeField=new JTextField(10);
JButton newTypeButton=
new JButton(new NewTypeAction("New type:",truthLabels,editor
.getViewer().getTruthBox(),newTypeField));
//
// layout stuff
//
setPreferredSize(new Dimension(800,600));
setLayout(new GridBagLayout());
JComponent top=new JPanel(new GridBagLayout());
JComponent bottom=new JPanel(new GridBagLayout());
GridBagConstraints gbc;
int col=0;
int row=0;
// top panel
++row;
gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.HORIZONTAL;
gbc.weightx=1.0;
gbc.weighty=0.0;
gbc.gridx=++col;
gbc.gridy=row;
top.add(refreshButton,gbc);
gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.HORIZONTAL;
gbc.weightx=1.0;
gbc.weighty=0.0;
gbc.gridx=++col;
gbc.gridy=row;
top.add(newTypeButton,gbc);
gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.HORIZONTAL;
gbc.weightx=1.0;
gbc.weighty=0.0;
gbc.gridx=++col;
gbc.gridy=row;
top.add(newTypeField,gbc);
gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.HORIZONTAL;
gbc.weightx=1.0;
gbc.weighty=0.0;
gbc.gridx=++col;
gbc.gridy=row;
top.add(saveButton,gbc);
++row;
gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.BOTH;
gbc.weightx=1.0;
gbc.weighty=1.0;
gbc.gridx=1;
gbc.gridy=row;
gbc.gridwidth=col;
top.add(errorPane,gbc);
// bottom panel
row=col=0;
++row;
gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.BOTH;
gbc.weightx=1.0;
gbc.weighty=1.0;
gbc.gridx=1;
gbc.gridy=row;
bottom.add(editor,gbc);
++row;
gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.HORIZONTAL;
gbc.weightx=1.0;
gbc.weighty=0.0;
gbc.gridx=1;
gbc.gridy=row;
bottom.add(statusMsg,gbc);
// a splitpane to size them
top.setMinimumSize(new Dimension(100,50));
bottom.setMinimumSize(new Dimension(100,100));
JSplitPane splitPane=new JSplitPane(JSplitPane.VERTICAL_SPLIT,top,bottom);
splitPane.setDividerLocation(50);
gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.BOTH;
gbc.weightx=1.0;
gbc.weighty=1.0;
gbc.gridx=1;
gbc.gridy=1;
add(splitPane,gbc);
}
static private class SaveTruthLabelsAction extends AbstractAction{
static final long serialVersionUID=20080306L;
private File saveFile;
private MutableTextLabels labels;
private StatusMessage statusMsg;
public SaveTruthLabelsAction(String msg,File saveFile,
MutableTextLabels labels,StatusMessage statusMsg){
super(msg);
this.saveFile=saveFile;
this.labels=labels;
this.statusMsg=statusMsg;
}
@Override
public void actionPerformed(ActionEvent event){
if(saveFile==null){
statusMsg.display("no save file specified?");
return;
}
statusMsg.display("saving to "+saveFile.getName());
try{
new TextLabelsLoader().saveTypesAsOps(labels,saveFile);
statusMsg.display("saved to "+saveFile.getName());
}catch(Exception e){
statusMsg.display("can't save to "+saveFile.getName()+": "+e);
}
}
}
private static class RefreshProgramAction extends AbstractAction{
static final long serialVersionUID=20080306L;
private File mixupFile;
private TextLabels initProgramLabels;
private TextLabels truthLabels;
//private TextBase base;
private JScrollPane errorPane;
private TextBaseViewer viewer;
public RefreshProgramAction(String msg,File mixupFile,TextBase base,
TextLabels initProgramLabels,TextLabels truthLabels,
TextBaseViewer viewer,JScrollPane errorPane){
super(msg);
this.mixupFile=mixupFile;
//this.base=base;
this.initProgramLabels=initProgramLabels;
this.truthLabels=truthLabels;
this.viewer=viewer;
this.errorPane=errorPane;
}
/*
public TextBase getBase(){
return base;
}
public void setBase(TextBase base){
this.base=base;
}
*/
@Override
public void actionPerformed(ActionEvent event){
MixupProgram program=new MixupProgram();
try{
program=new MixupProgram(mixupFile);
}catch(Exception e){
errorPane.getViewport().setView(new JTextField(e.toString()));
return;
}
// NestedTextLabels programLabels = new NestedTextLabels( truthLabels );
MonotonicTextLabels programLabels=
new NestedTextLabels(new NestedTextLabels(initProgramLabels),
truthLabels);
try{
MixupInterpreter interp=new MixupInterpreter(program);
interp.eval(programLabels);
}catch(Exception e){
errorPane.getViewport().setView(new JTextField(e.toString()));
return;
}
viewer.updateTextLabels(programLabels);
updateTypeBox(programLabels,viewer.getGuessBox());
updateTypeBox(programLabels,viewer.getTruthBox());
updateTypeBox(programLabels,viewer.getDisplayedTypeBox());
errorPane.getViewport().setView(
new JTextField("loaded "+mixupFile.getName()));
}
}
static private void updateTypeBox(TextLabels labels,JComboBox box){
Set<String> oldTypes=new HashSet<String>();
for(int j=0;j<box.getItemCount();j++){
oldTypes.add((String)box.getItemAt(j));
}
for(Iterator<String> i=labels.getTypes().iterator();i.hasNext();){
String t=i.next();
System.out.println("checking type "+t);
if(!oldTypes.contains(t)){
box.addItem(t);
System.out.println("adding type "+t);
}
}
}
private static class NewTypeAction extends AbstractAction{
static final long serialVersionUID=20080306L;
private Set<String> truthTypeSet;
private JComboBox truthBox;
private JTextField newTypeField;
public NewTypeAction(String msg,MutableTextLabels truthLabels,
JComboBox truthBox,JTextField newTypeField){
super(msg);
truthTypeSet=new HashSet<String>();
truthTypeSet.addAll(truthLabels.getTypes());
this.truthBox=truthBox;
this.newTypeField=newTypeField;
}
@Override
public void actionPerformed(ActionEvent e){
String newType=newTypeField.getText().trim();
if(newType.length()>0&&truthTypeSet.add(newType)){
truthBox.addItem(newType);
truthBox.setSelectedItem(newType);
}
}
}
public static MixupDebugger debug(TextBase base,File groundTruthLabelsFile,
File mixupProgramFile){
MonotonicTextLabels baseLabels=new BasicTextLabels(base);
return debug(base,baseLabels,groundTruthLabelsFile,mixupProgramFile);
}
public static MixupDebugger debug(TextBase base,TextLabels baseLabels,
File groundTruthLabelsFile,File mixupProgramFile){
try{
JFrame frame=new JFrame("MixupDebugger");
MixupDebugger debugger=
new MixupDebugger(base,baseLabels,groundTruthLabelsFile,
mixupProgramFile,false,false);
frame.getContentPane().add(debugger,BorderLayout.CENTER);
frame.addWindowListener(new WindowAdapter(){
// public void windowClosing(WindowEvent e) { System.exit(0); }
});
frame.pack();
frame.setVisible(true);
return debugger;
}catch(Exception e){
e.printStackTrace();
return null;
}
}
public static void main(String[] args){
String textBaseId=null;
String fileName=null;
File groundTruthLabelsFile=null;
File mixupProgramFile=null;
// String mainType = null;
boolean readOnly=false,stem=false;
// parse options
if(args.length<4){
System.out
.println("Not enough arguments. Must have at least {-file <dataFile> or -textBase <textBaseFile>} and -mixup <programFile>");
usage();
}
for(int argp=0;argp<args.length;argp++){
if("-textBase".equals(args[argp])){
textBaseId=args[++argp];
}else if("-file".equals(args[argp])){
fileName=args[++argp];
}else if("-truth".equals(args[argp])){
groundTruthLabelsFile=new File(args[++argp]);
}else if("-mixup".equals(args[argp])){
mixupProgramFile=new File(args[++argp]);
}else if("-readOnly".equals(args[argp])){
readOnly=true;
}else if("-stem".equals(args[argp])){
stem=true;
}else{
System.out.println("illegal option "+args[argp]);
}
}
if((textBaseId==null&&fileName==null)||mixupProgramFile==null||
!mixupProgramFile.exists()||!mixupProgramFile.isFile()){
System.out
.println("Either can't data file not specified or can't find mixupProgram");
usage();
return;
}
try{
JFrame frame=new JFrame("TextBaseEditor");
TextBase base;
if(textBaseId!=null)
base=FancyLoader.loadTextLabels(textBaseId).getTextBase();
else{ // this assumes directory of files
File data=new File(fileName);
if(data.isDirectory()){
TextBaseLoader loader=
new TextBaseLoader(TextBaseLoader.DOC_PER_FILE,false);
base=loader.load(data);
}else{
TextBaseLoader loader=
new TextBaseLoader(TextBaseLoader.DOC_PER_LINE,false);
base=loader.load(data);
}
}
MixupDebugger debugger=
new MixupDebugger(base,groundTruthLabelsFile,mixupProgramFile,
readOnly,stem);
frame.getContentPane().add(debugger,BorderLayout.CENTER);
frame.addWindowListener(new WindowAdapter(){
// public void windowClosing(WindowEvent e) { System.exit(0); }
});
frame.pack();
frame.setVisible(true);
}catch(Exception e){
e.printStackTrace();
}
}
private static void usage(){
System.out
.println("usage: MixupDebugger {-file <dataFile> or -textBase <textBaseFile>} -truth <truthLabels> -mixup <programFile> [options]");
System.out.println(" options: -readOnly -stem");
System.exit(0);
}
}