package org.obo.annotation.view;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.util.Arrays;
import java.util.Comparator;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.table.TableCellEditor;
import org.apache.log4j.Logger;
import org.bbop.framework.AbstractGUIComponent;
import org.obo.annotation.base.OBOUtil;
import org.obo.annotation.base.OBOUtil.Differentium;
import org.obo.annotation.base.TermSet;
import org.obo.app.swing.AutocompleteField;
import org.obo.app.swing.BugWorkaroundTable;
import org.obo.app.swing.TablePopupListener;
import org.obo.datamodel.Link;
import org.obo.datamodel.LinkedObject;
import org.obo.datamodel.OBOClass;
import org.obo.datamodel.OBOObject;
import org.obo.datamodel.OBOProperty;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.gui.AdvancedTableFormat;
import ca.odell.glazedlists.gui.WritableTableFormat;
import ca.odell.glazedlists.swing.EventSelectionModel;
import ca.odell.glazedlists.swing.EventTableModel;
public class PostCompositionEditor extends AbstractGUIComponent {
private OBOClass genus;
private EventList<Differentium> diffs = new BasicEventList<Differentium>();
private EventSelectionModel<Differentium> selectionModel = new EventSelectionModel<Differentium>(diffs);
private final TermSet termSet;
private final TermSet relationsSet;
private final TermSet differentiaTermSet;
private final OntologyCoordinator coordinator;
private AutocompleteField<OBOObject> genusBox;
private JButton addDifferentiaButton;
private JButton deleteDifferentiaButton;
private JTable diffTable;
private DifferentiaTableFormat tableFormat;
private TablePopupListener popupListener;
public PostCompositionEditor(String id, TermSet genusTerms, TermSet relations, TermSet differentiaTerms, OntologyCoordinator coordinator) {
super(id);
this.termSet = genusTerms;
this.relationsSet = relations;
this.differentiaTermSet = differentiaTerms;
this.coordinator = coordinator;
}
public PostCompositionEditor(String id, TermSet terms, TermSet relations, OntologyCoordinator coordinator) {
this(id, terms, relations, terms, coordinator);
}
public PostCompositionEditor(TermSet terms, TermSet relations, TermSet differentiaTerms, OntologyCoordinator coordinator) {
this("", terms, relations, differentiaTerms, coordinator);
this.initializeInterface();
}
public PostCompositionEditor(TermSet terms, TermSet relations, OntologyCoordinator coordinator) {
this("", terms, relations, coordinator);
this.initializeInterface();
}
@Override
public void init() {
super.init();
this.initializeInterface();
}
public void addDifferentia() {
this.diffs.add(new Differentium());
}
public void deleteSelectedDifferentia() {
this.selectionModel.getSelected().clear();
}
private void updateGenus() {
this.genus = (OBOClass)(this.genusBox.getValue());
}
public OBOClass getTerm() {
for (Differentium diff : this.diffs) {
if (!diff.isComplete()) {
this.diffs.remove(diff);
}
}
if (this.diffs.isEmpty()) {
return this.genus;
} else {
return OBOUtil.createPostComposition(this.genus, this.diffs);
}
}
public void setTerm(OBOClass aTerm) {
this.diffs.clear();
if ((aTerm != null) && (OBOUtil.isPostCompTerm(aTerm))) {
this.genus = OBOUtil.getGenusTerm(aTerm);
for (Link link : OBOUtil.getAllDifferentia(aTerm)) {
final Differentium diff = new Differentium();
diff.setRelation(link.getType());
final LinkedObject parent = link.getParent();
if (parent instanceof OBOClass) {
diff.setTerm((OBOClass)parent);
} else {
log().error("Differentia is not an OBOClass: " + parent);
}
this.diffs.add(diff);
}
} else {
this.genus = aTerm;
}
this.genusBox.setValue((OBOObject)this.genus);
if (this.diffs.isEmpty()) {
this.diffs.add(new Differentium());
}
}
public int runPostCompositionDialog(Component parentComponent) {
if (this.genusBox == null) {
this.init();
}
this.setPreferredSize(new Dimension(300, 200));
return JOptionPane.showConfirmDialog(parentComponent, this, "Post-composition Editor", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
}
private void runPostCompositionForTermAtPoint(Point p) {
final int column = this.diffTable.getTableHeader().columnAtPoint(p);
final int row = this.diffTable.rowAtPoint(p);
if (!this.tableFormat.getColumnClass(column).equals(OBOObject.class)) return;
final Differentium differentia = this.diffs.get(row);
final OBOClass term = (OBOClass)(this.tableFormat.getColumnValue(differentia, column));
final PostCompositionEditor pce = new PostCompositionEditor(this.termSet, this.relationsSet, this.differentiaTermSet, this.coordinator);
pce.setTerm(term);
final int result = pce.runPostCompositionDialog(this);
if (result == JOptionPane.OK_OPTION) {
this.tableFormat.setColumnValue(differentia, pce.getTerm(), column);
}
}
private void initializeInterface() {
this.setLayout(new GridBagLayout());
final GridBagConstraints labelConstraints = new GridBagConstraints();
this.add(new JLabel("Genus:"), labelConstraints);
final GridBagConstraints comboConstraints = new GridBagConstraints();
comboConstraints.gridx = 1;
comboConstraints.fill = GridBagConstraints.HORIZONTAL;
comboConstraints.weightx = 1.0;
this.genusBox = TermAutocompleteFieldFactory.createAutocompleteBox(this.termSet, this.coordinator);
this.genusBox.setAction(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
updateGenus();
}
});
this.add(this.genusBox, comboConstraints);
final GridBagConstraints postComposeGenusConstraints = new GridBagConstraints();
postComposeGenusConstraints.gridx = 2;
this.tableFormat = new DifferentiaTableFormat();
final EventTableModel<Differentium> model = new EventTableModel<Differentium>(this.diffs, this.tableFormat);
this.diffTable = new BugWorkaroundTable(model);
this.diffTable.setSelectionModel(this.selectionModel);
this.diffTable.setDefaultRenderer(OBOObject.class, new TermRenderer("None"));
this.diffTable.putClientProperty("Quaqua.Table.style", "striped");
this.diffTable.getColumnModel().getColumn(0).setCellEditor(this.tableFormat.getColumnEditor(0));
this.diffTable.getColumnModel().getColumn(1).setCellEditor(this.tableFormat.getColumnEditor(1));
final GridBagConstraints tableConstraints = new GridBagConstraints();
tableConstraints.gridy = 1;
tableConstraints.gridwidth = 3;
tableConstraints.fill = GridBagConstraints.BOTH;
tableConstraints.weighty = 1.0;
this.add(new JScrollPane(diffTable), tableConstraints);
final GridBagConstraints toolbarConstraints = new GridBagConstraints();
toolbarConstraints.gridy = 2;
toolbarConstraints.gridwidth = 2;
toolbarConstraints.fill = GridBagConstraints.HORIZONTAL;
toolbarConstraints.weightx = 1.0;
this.add(this.createToolBar(), toolbarConstraints);
this.popupListener = new TablePopupListener(this.createTablePopupMenu(), this.diffTable);
this.popupListener.setPopupColumns(Arrays.asList(new Integer[] {1}));
this.diffTable.addMouseListener(this.popupListener);
}
private JToolBar createToolBar() {
final JToolBar toolBar = new JToolBar();
this.addDifferentiaButton = new JButton(new AbstractAction(null, new ImageIcon(this.getClass().getResource("/org/phenoscape/view/images/list-add.png"))) {
@Override
public void actionPerformed(ActionEvent e) {
addDifferentia();
}
});
this.addDifferentiaButton.setToolTipText("Add Differentia");
toolBar.add(this.addDifferentiaButton);
this.deleteDifferentiaButton = new JButton(new AbstractAction(null, new ImageIcon(this.getClass().getResource("/org/phenoscape/view/images/list-remove.png"))) {
@Override
public void actionPerformed(ActionEvent e) {
deleteSelectedDifferentia();
}
});
this.deleteDifferentiaButton.setToolTipText("Delete Differentia");
toolBar.add(this.deleteDifferentiaButton);
toolBar.setFloatable(false);
return toolBar;
}
private JPopupMenu createTablePopupMenu() {
final JPopupMenu menu = new JPopupMenu();
menu.add(new AbstractAction("Create Post-composed Term") {
@Override
public void actionPerformed(ActionEvent e) {
runPostCompositionForTermAtPoint(popupListener.getLocation());
}
});
return menu;
}
private class DifferentiaTableFormat implements WritableTableFormat<Differentium>, AdvancedTableFormat<Differentium> {
@Override
public boolean isEditable(Differentium diff, int column) {
return true;
}
public TableCellEditor getColumnEditor(int column) {
switch (column) {
case 0: return TermAutocompleteFieldFactory.createAutocompleteEditor(relationsSet, coordinator);
case 1: return TermAutocompleteFieldFactory.createAutocompleteEditor(differentiaTermSet, coordinator);
default: return null;
}
}
@Override
public Differentium setColumnValue(Differentium diff, Object editedValue, int column) {
switch(column) {
case 0: diff.setRelation((OBOProperty)editedValue); break;
case 1: diff.setTerm((OBOClass)editedValue); break;
}
return diff;
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public String getColumnName(int column) {
switch(column) {
case 0: return "Relationship";
case 1: return "Differentia";
}
return null;
}
@Override
public Object getColumnValue(Differentium diff, int column) {
switch(column) {
case 0: return diff.getRelation();
case 1: return diff.getTerm();
}
return null;
}
@Override
public Class<?> getColumnClass(int column) {
return OBOObject.class;
}
@Override
public Comparator<?> getColumnComparator(int column) {
return GlazedLists.comparableComparator();
}
}
private Logger log() {
return Logger.getLogger(this.getClass());
}
}