All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gate.gui.FeaturesSchemaEditor Maven / Gradle / Ivy

Go to download

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 fValues = new ArrayList();
          if(feature.isSchemaFeature()){
            Set permValues = schema.getFeatureSchema(feature.name).
              getPermittedValues();
            if(permValues != null) fValues.addAll(permValues);
          }
          if(!fValues.contains(feature.value)) fValues.add(feature.value);
          Collections.sort(fValues, defaultComparator);
          for(Iterator valIter = fValues.iterator(); 
              valIter.hasNext(); 
              comboModel.addElement(valIter.next()));
          combo.getEditor().getEditorComponent().setBackground(feature.isCorrect() ?
              defaultBackground :
              (feature.isRequired() ? REQUIRED_WRONG : OPTIONAL_WRONG));
          combo.setSelectedItem(feature.value);
          break;
        default: ;
      }
      
    }

    JLabel requiredIconLabel;
    JLabel optionalIconLabel;
    JLabel nonSchemaIconLabel;
    JComboBox editorCombo;
    JComboBox rendererCombo;
    JButton deleteButton;
    ObjectComparator defaultComparator;
    Color defaultBackground;
  }
  
  /* 
   * Resource implementation 
   */
  /** Accessor for features. */
  @Override
  public FeatureMap getFeatures(){
    return features;
  }//getFeatures()

  /** Mutator for features*/
  @Override
  public void setFeatures(FeatureMap features){
    this.features = features;
  }// setFeatures()


  /**
   * Used by the main GUI to tell this VR what handle created it. The VRs can
   * use this information e.g. to add items to the popup for the resource.
   */
  @Override
  public void setHandle(Handle handle){
    this.handle = handle;
  }

  //Parameters utility methods
  /**
   * Gets the value of a parameter of this resource.
   * @param paramaterName the name of the parameter
   * @return the current value of the parameter
   */
  @Override
  public Object getParameterValue(String paramaterName)
                throws ResourceInstantiationException{
    return AbstractResource.getParameterValue(this, paramaterName);
  }

  /**
   * Sets the value for a specified parameter.
   *
   * @param paramaterName the name for the parameteer
   * @param parameterValue the value the parameter will receive
   */
  @Override
  public void setParameterValue(String paramaterName, Object parameterValue)
              throws ResourceInstantiationException{
    // get the beaninfo for the resource bean, excluding data about Object
    BeanInfo resBeanInf = null;
    try {
      resBeanInf = Introspector.getBeanInfo(this.getClass(), Object.class);
    } catch(Exception e) {
      throw new ResourceInstantiationException(
        "Couldn't get bean info for resource " + this.getClass().getName()
        + Strings.getNl() + "Introspector exception was: " + e
      );
    }
    AbstractResource.setParameterValue(this, resBeanInf, paramaterName, parameterValue);
  }

  /**
   * Sets the values for more parameters in one step.
   *
   * @param parameters a feature map that has paramete names as keys and
   * parameter values as values.
   */
  @Override
  public void setParameterValues(FeatureMap parameters)
              throws ResourceInstantiationException{
    AbstractResource.setParameterValues(this, parameters);
  }

  // Properties for the resource
  protected FeatureMap features;
  
  /**
   * The handle for this visual resource
   */
  protected Handle handle;
  
  
}