gate.gui.FeaturesSchemaEditor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gate-core Show documentation
Show all versions of gate-core Show documentation
GATE - general achitecture for text engineering - is open source
software capable of solving almost any text processing problem. This
artifact enables you to embed the core GATE Embedded with its essential
dependencies. You will able to use the GATE Embedded API and load and
store GATE XML documents. This artifact is the perfect dependency for
CREOLE plugins or for applications that need to customize the GATE
dependencies due to confict with their own dependencies or for lower
footprint.
The newest version!
/*
* Copyright (c) 1998-2005, The University of Sheffield.
*
* This file is part of GATE (see http://gate.ac.uk/), and is free software,
* licenced under the GNU Library General Public License, Version 2, June 1991
* (in the distribution as file licence.html, and also available at
* http://gate.ac.uk/gate/licence.html).
*
* FeaturesSchemaEditor.java
*
* Valentin Tablan, May 18, 2004
*
* $Id: FeaturesSchemaEditor.java 18884 2015-08-25 17:23:21Z markagreenwood $
*/
package gate.gui;
import gate.Factory;
import gate.FeatureMap;
import gate.Resource;
import gate.creole.AbstractResource;
import gate.creole.AnnotationSchema;
import gate.creole.FeatureSchema;
import gate.creole.ResourceInstantiationException;
import gate.creole.metadata.CreoleResource;
import gate.creole.metadata.GuiType;
import gate.event.FeatureMapListener;
import gate.swing.XJTable;
import gate.util.FeatureBearer;
import gate.util.GateRuntimeException;
import gate.util.ObjectComparator;
import gate.util.Strings;
import java.awt.AWTKeyStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
/**
*/
@SuppressWarnings({"serial","rawtypes","unchecked"})
@CreoleResource(name = "Resource Features", guiType = GuiType.SMALL,
resourceDisplayed = "gate.util.FeatureBearer")
public class FeaturesSchemaEditor extends XJTable
implements ResizableVisualResource, FeatureMapListener{
public FeaturesSchemaEditor(){
// setBackground(UIManager.getDefaults().getColor("Table.background"));
instance = this;
}
public void setTargetFeatures(FeatureMap features){
if(targetFeatures != null) targetFeatures.removeFeatureMapListener(this);
this.targetFeatures = features;
populate();
if(targetFeatures != null) targetFeatures.addFeatureMapListener(this);
}
@Override
public void cleanup() {
if(targetFeatures != null){
targetFeatures.removeFeatureMapListener(this);
targetFeatures = null;
}
target = null;
schema = null;
}
/* (non-Javadoc)
* @see gate.VisualResource#setTarget(java.lang.Object)
*/
@Override
public void setTarget(Object target){
this.target = (FeatureBearer)target;
setTargetFeatures(this.target.getFeatures());
}
public void setSchema(AnnotationSchema schema){
this.schema = schema;
featuresModel.fireTableRowsUpdated(0, featureList.size() - 1);
}
/* (non-Javadoc)
* @see gate.event.FeatureMapListener#featureMapUpdated()
* Called each time targetFeatures is changed.
*/
@Override
public void featureMapUpdated(){
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run(){
populate();
}
});
}
/** Initialise this resource, and return it. */
@Override
public Resource init() throws ResourceInstantiationException {
featureList = new ArrayList();
emptyFeature = new Feature("", null);
featureList.add(emptyFeature);
initGUI();
return this;
}//init()
protected void initGUI(){
featuresModel = new FeaturesTableModel();
setModel(featuresModel);
setTableHeader(null);
setSortable(false);
setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
setShowGrid(false);
setBackground(getBackground());
setIntercellSpacing(new Dimension(2,2));
setTabSkipUneditableCell(true);
setEditCellAsSoonAsFocus(true);
featureEditorRenderer = new FeatureEditorRenderer();
getColumnModel().getColumn(ICON_COL).
setCellRenderer(featureEditorRenderer);
getColumnModel().getColumn(NAME_COL).
setCellRenderer(featureEditorRenderer);
getColumnModel().getColumn(NAME_COL).
setCellEditor(featureEditorRenderer);
getColumnModel().getColumn(VALUE_COL).
setCellRenderer(featureEditorRenderer);
getColumnModel().getColumn(VALUE_COL).
setCellEditor(featureEditorRenderer);
getColumnModel().getColumn(DELETE_COL).
setCellRenderer(featureEditorRenderer);
getColumnModel().getColumn(DELETE_COL).
setCellEditor(featureEditorRenderer);
// //the background colour seems to change somewhere when using the GTK+
// //look and feel on Linux, so we copy the value now and set it
// Color tableBG = getBackground();
// //make a copy of the value (as the reference gets changed somewhere)
// tableBG = new Color(tableBG.getRGB());
// setBackground(tableBG);
// allow Tab key to select the next cell in the table
setSurrendersFocusOnKeystroke(true);
setFocusCycleRoot(true);
// remove (shift) control tab as traversal keys
Set keySet = new HashSet(
getFocusTraversalKeys(
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
keySet.remove(KeyStroke.getKeyStroke("control TAB"));
setFocusTraversalKeys(
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keySet);
keySet = new HashSet(
getFocusTraversalKeys(
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
keySet.remove(KeyStroke.getKeyStroke("shift control TAB"));
setFocusTraversalKeys(
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, keySet);
// add (shift) control tab to go the container of this component
keySet.clear();
keySet.add(KeyStroke.getKeyStroke("control TAB"));
setFocusTraversalKeys(
KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, keySet);
keySet.clear();
keySet.add(KeyStroke.getKeyStroke("shift control TAB"));
setFocusTraversalKeys(
KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS, keySet);
}
/**
* Called internally whenever the data represented changes.
* Get feature names from targetFeatures and schema then sort them
* and add them to featureList.
* Fire a table data changed event for the feature table whith featureList
* used as data model.
*/
protected void populate(){
featureList.clear();
//get all the existing features
Set fNames = new HashSet();
if(targetFeatures != null){
//add all the schema features
fNames.addAll(targetFeatures.keySet());
if(schema != null && schema.getFeatureSchemaSet() != null){
for(FeatureSchema featureSchema : schema.getFeatureSchemaSet()) {
// if(featureSchema.isRequired())
fNames.add(featureSchema.getFeatureName());
}
}
List featureNames = new ArrayList(fNames);
Collections.sort(featureNames);
for(Object featureName : featureNames) {
String name = (String) featureName;
Object value = targetFeatures.get(name);
featureList.add(new Feature(name, value));
}
}
if (!featureList.contains(emptyFeature)) {
featureList.add(emptyFeature);
}
featuresModel.fireTableDataChanged();
// mainTable.setSize(mainTable.getPreferredScrollableViewportSize());
}
FeatureMap targetFeatures;
FeatureBearer target;
Feature emptyFeature;
AnnotationSchema schema;
FeaturesTableModel featuresModel;
List featureList;
FeatureEditorRenderer featureEditorRenderer;
FeaturesSchemaEditor instance;
private static final int COLUMNS = 4;
private static final int ICON_COL = 0;
private static final int NAME_COL = 1;
private static final int VALUE_COL = 2;
private static final int DELETE_COL = 3;
private static final Color REQUIRED_WRONG = Color.RED;
private static final Color OPTIONAL_WRONG = Color.ORANGE;
protected class Feature{
String name;
Object value;
public Feature(String name, Object value){
this.name = name;
this.value = value;
}
boolean isSchemaFeature(){
return schema != null && schema.getFeatureSchema(name) != null;
}
boolean isCorrect(){
if(schema == null) return true;
FeatureSchema fSchema = schema.getFeatureSchema(name);
return fSchema == null || fSchema.getPermittedValues() == null||
fSchema.getPermittedValues().contains(value);
}
boolean isRequired(){
if(schema == null) return false;
FeatureSchema fSchema = schema.getFeatureSchema(name);
return fSchema != null && fSchema.isRequired();
}
Object getDefaultValue(){
if(schema == null) return null;
FeatureSchema fSchema = schema.getFeatureSchema(name);
return fSchema == null ? null : fSchema.getFeatureValue();
}
}
protected class FeaturesTableModel extends AbstractTableModel{
@Override
public int getRowCount(){
return featureList.size();
}
@Override
public int getColumnCount(){
return COLUMNS;
}
@Override
public Object getValueAt(int row, int column){
Feature feature = featureList.get(row);
switch(column){
case NAME_COL:
return feature.name;
case VALUE_COL:
return feature.value;
default:
return null;
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex){
return columnIndex == VALUE_COL || columnIndex == NAME_COL ||
columnIndex == DELETE_COL;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex){
Feature feature = featureList.get(rowIndex);
if (feature == null) { return; }
if(targetFeatures == null){
targetFeatures = Factory.newFeatureMap();
target.setFeatures(targetFeatures);
setTargetFeatures(targetFeatures);
}
switch(columnIndex){
case VALUE_COL:
if (feature.value != null
&& feature.value.toString().equals(aValue)) { return; }
feature.value = aValue;
if(feature.name != null && feature.name.length() > 0){
targetFeatures.removeFeatureMapListener(instance);
targetFeatures.put(feature.name, aValue);
targetFeatures.addFeatureMapListener(instance);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// edit the last row that is empty
FeaturesSchemaEditor.this.editCellAt(FeaturesSchemaEditor.this.getRowCount() - 1, NAME_COL);
}
});
}
break;
case NAME_COL:
if (feature.name.equals(aValue)) {
return;
}
targetFeatures.remove(feature.name);
feature.name = (String)aValue;
targetFeatures.put(feature.name, feature.value);
if(feature == emptyFeature) emptyFeature = new Feature("", null);
populate();
int newRow;
for (newRow = 0; newRow < FeaturesSchemaEditor.this.getRowCount(); newRow++) {
if (FeaturesSchemaEditor.this.getValueAt(newRow, NAME_COL).equals(feature.name)) {
break; // find the previously selected row in the new table
}
}
final int newRowF = newRow;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// edit the cell containing the value associated with this name
FeaturesSchemaEditor.this.editCellAt(newRowF, VALUE_COL);
}
});
break;
case DELETE_COL:
//nothing
break;
default:
throw new GateRuntimeException("Non editable cell!");
}
}
@Override
public String getColumnName(int column){
switch(column){
case NAME_COL:
return "Name";
case VALUE_COL:
return "Value";
case DELETE_COL:
return "";
default:
return null;
}
}
}
protected class FeatureEditorRenderer extends DefaultCellEditor
implements TableCellRenderer {
@Override
public boolean stopCellEditing() {
// this is a fix for a bug in Java 8 where tabbing out of the
// combo box doesn't store the value like it does in java 7
editorCombo.setSelectedItem(editorCombo.getEditor().getItem());
return super.stopCellEditing();
}
public FeatureEditorRenderer(){
super(new JComboBox());
defaultComparator = new ObjectComparator();
editorCombo = (JComboBox)editorComponent;
editorCombo.setModel(new DefaultComboBoxModel());
editorCombo.setEditable(true);
editorCombo.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent evt){
stopCellEditing();
}
});
defaultBackground = editorCombo.getEditor().getEditorComponent()
.getBackground();
rendererCombo = new JComboBox(){
@Override
public Dimension getMaximumSize() {
return getPreferredSize();
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
};
rendererCombo.setModel(new DefaultComboBoxModel());
rendererCombo.setEditable(true);
rendererCombo.setOpaque(false);
requiredIconLabel = new JLabel(){
@Override
public void repaint(long tm, int x, int y, int width, int height){}
@Override
public void repaint(Rectangle r){}
@Override
public void validate(){}
@Override
public void revalidate(){}
@Override
protected void firePropertyChange(String propertyName,
Object oldValue,
Object newValue){}
@Override
public Dimension getMaximumSize() {
return getPreferredSize();
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
};
requiredIconLabel.setIcon(MainFrame.getIcon("r"));
requiredIconLabel.setOpaque(false);
requiredIconLabel.setToolTipText("Required feature");
optionalIconLabel = new JLabel(){
@Override
public void repaint(long tm, int x, int y, int width, int height){}
@Override
public void repaint(Rectangle r){}
@Override
public void validate(){}
@Override
public void revalidate(){}
@Override
protected void firePropertyChange(String propertyName,
Object oldValue,
Object newValue){}
@Override
public Dimension getMaximumSize() {
return getPreferredSize();
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
};
optionalIconLabel.setIcon(MainFrame.getIcon("o"));
optionalIconLabel.setOpaque(false);
optionalIconLabel.setToolTipText("Optional feature");
nonSchemaIconLabel = new JLabel(MainFrame.getIcon("c")){
@Override
public void repaint(long tm, int x, int y, int width, int height){}
@Override
public void repaint(Rectangle r){}
@Override
public void validate(){}
@Override
public void revalidate(){}
@Override
protected void firePropertyChange(String propertyName,
Object oldValue,
Object newValue){}
@Override
public Dimension getMaximumSize() {
return getPreferredSize();
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
};
nonSchemaIconLabel.setToolTipText("Custom feature");
nonSchemaIconLabel.setOpaque(false);
deleteButton = new JButton(MainFrame.getIcon("delete"));
deleteButton.setMargin(new Insets(0,0,0,0));
deleteButton.setToolTipText("Delete");
deleteButton.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent evt){
int row = FeaturesSchemaEditor.this.getEditingRow();
if(row < 0) return;
Feature feature = featureList.get(row);
if(feature == emptyFeature){
feature.value = null;
}else{
featureList.remove(row);
targetFeatures.remove(feature.name);
featuresModel.fireTableRowsDeleted(row, row);
// mainTable.setSize(mainTable.getPreferredScrollableViewportSize());
}
}
});
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column){
Feature feature = featureList.get(row);
switch(column){
case ICON_COL:
return feature.isSchemaFeature() ?
(feature.isRequired() ?
requiredIconLabel :
optionalIconLabel) :
nonSchemaIconLabel;
case NAME_COL:
prepareCombo(rendererCombo, row, column);
rendererCombo.getPreferredSize();
return rendererCombo;
case VALUE_COL:
prepareCombo(rendererCombo, row, column);
return rendererCombo;
case DELETE_COL: return deleteButton;
default: return null;
}
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column){
switch(column){
case NAME_COL:
prepareCombo(editorCombo, row, column);
return editorCombo;
case VALUE_COL:
prepareCombo(editorCombo, row, column);
return editorCombo;
case DELETE_COL: return deleteButton;
default: return null;
}
}
protected void prepareCombo(JComboBox combo, int row, int column){
Feature feature = featureList.get(row);
DefaultComboBoxModel comboModel = (DefaultComboBoxModel)combo.getModel();
comboModel.removeAllElements();
switch(column){
case NAME_COL:
List fNames = new ArrayList();
if(schema != null && schema.getFeatureSchemaSet() != null){
Iterator fSchemaIter = schema.getFeatureSchemaSet().iterator();
while(fSchemaIter.hasNext())
fNames.add(fSchemaIter.next().getFeatureName());
}
if(!fNames.contains(feature.name))fNames.add(feature.name);
Collections.sort(fNames);
for(Iterator nameIter = fNames.iterator();
nameIter.hasNext();
comboModel.addElement(nameIter.next()));
combo.getEditor().getEditorComponent().setBackground(defaultBackground);
combo.setSelectedItem(feature.name);
break;
case VALUE_COL:
List