1   /*  SchemaAnnotationEditor.java
2    *
3    *  Copyright (c) 1998-2001, The University of Sheffield.
4    *
5    *  This file is part of GATE (see http://gate.ac.uk/), and is free
6    *  software, licenced under the GNU Library General Public License,
7    *  Version 2, June 1991 (in the distribution as file licence.html,
8    *  and also available at http://gate.ac.uk/gate/licence.html).
9    *
10   *  Cristian URSU,  12/July/2001
11   *
12   *  $Id: SchemaAnnotationEditor.java,v 1.12 2001/11/05 16:07:48 valyt Exp $
13   *
14   */
15  
16  package gate.gui;
17  
18  import java.awt.Frame;
19  import java.awt.BorderLayout;
20  import java.awt.Component;
21  import java.awt.event.*;
22  import javax.swing.*;
23  import javax.swing.table.*;
24  
25  import java.util.*;
26  import java.lang.reflect.*;
27  
28  import gate.*;
29  import gate.annotation.*;
30  import gate.util.*;
31  import gate.creole.*;
32  
33  /** This class is a viewer which adds/edits features on a GATE annotation.
34    * This viewer is {@link gate.creole.AnnotationSchema} driven.
35    */
36  public class SchemaAnnotationEditor extends AbstractVisualResource
37                                      implements AnnotationVisualResource{
38  
39    /** Default constructor */
40    public SchemaAnnotationEditor(){}
41  
42    // Methods required by AnnotationVisualResource
43  
44    /**
45      * Called by the GUI when this viewer/editor has to initialise itself for a
46      * specific annotation or text span.
47      * @param target the object which will always be a {@link gate.AnnotationSet}
48      */
49    public void setTarget(Object target){
50      currentAnnotSet = (AnnotationSet) target;
51    }// setTarget();
52  
53    /**
54      * Used when the viewer/editor has to display/edit an existing annotation
55      * @param ann the annotation to be displayed or edited. If ann is null then
56      * the method simply returns
57      */
58    public void setAnnotation(Annotation ann){
59      // If ann is null, then simply return.
60      if (ann == null) return;
61  
62      currentAnnot = ann;
63      currentStartOffset = currentAnnot.getStartNode().getOffset();
64      currentEndOffset = currentAnnot.getEndNode().getOffset();
65      currentAnnotFeaturesMap = Factory.newFeatureMap();
66      currentAnnotFeaturesMap.putAll(currentAnnot.getFeatures());
67      currentAnnotSchema = null;
68      CreoleRegister creoleReg = Gate.getCreoleRegister();
69      List currentAnnotationSchemaList =
70                        creoleReg.getLrInstances("gate.creole.AnnotationSchema");
71      // If there is no Annotation schema loaded the editor can only do nothing
72      if (currentAnnotationSchemaList.isEmpty()) return;
73      name2annotSchemaMap = new TreeMap();
74      Iterator annotSchemaIter = currentAnnotationSchemaList.iterator();
75      // currentAnnotationSchemaList is not empty
76      currentAnnotSchema = (AnnotationSchema) currentAnnotationSchemaList.get(0);
77      while (annotSchemaIter.hasNext()){
78        AnnotationSchema annotSch = (AnnotationSchema)annotSchemaIter.next();
79        name2annotSchemaMap.put(annotSch.getAnnotationName(),annotSch);
80        if (currentAnnot.getType().equals(annotSch.getAnnotationName()))
81          currentAnnotSchema = annotSch;
82      }// End while
83  
84      initLocalData();
85      buildGuiComponents();
86      initListeners();
87    }// setAnnotation();
88  
89    /**
90      * Used when the viewer has to create new annotations.
91      * @param startOffset the start offset of the span covered by the new
92      * annotation(s). If is <b>null</b> the method will simply return.
93      * @param endOffset the end offset of the span covered by the new
94      * annotation(s). If is <b>null</b> the method will simply return.
95      */
96    public void setSpan(Long startOffset, Long endOffset, String annotType){
97      // If one of them is null, then simply return.
98      if (startOffset == null || endOffset == null) return;
99      currentStartOffset = startOffset;
100     currentEndOffset = endOffset;
101     currentAnnot = null;
102     currentAnnotFeaturesMap = null;
103     currentAnnotSchema = null;
104     CreoleRegister creoleReg = Gate.getCreoleRegister();
105     List currentAnnotationSchemaList = null;
106     try{
107       currentAnnotationSchemaList =
108               creoleReg.getAllInstances("gate.creole.AnnotationSchema");
109     } catch (GateException e){
110       // This exception shouldn't happen. If it happens then something went
111       // terribly wrong.
112       throw new LuckyException("gate.creole.AnnotationSchema or a class that"+
113       " extends it, is not registered in the creole.xml register.Edit your"+
114       " creole.xml and try again.");
115     }// End try
116     // If there is no Annotation schema loaded, the editor can only do nothing
117     if (currentAnnotationSchemaList.isEmpty()) return;
118     name2annotSchemaMap = new TreeMap();
119     Iterator annotSchemaIter = currentAnnotationSchemaList.iterator();
120     // currentAnnotationSchemaList is not empty (see the above comment)
121     currentAnnotSchema = (AnnotationSchema) currentAnnotationSchemaList.get(0);
122     while (annotSchemaIter.hasNext()){
123       AnnotationSchema annotSch = (AnnotationSchema)annotSchemaIter.next();
124       name2annotSchemaMap.put(annotSch.getAnnotationName(),annotSch);
125     }// End while
126 
127     initLocalData();
128     buildGuiComponents();
129     initListeners();
130   }// setSpan();
131 
132   /**
133    * Called by the GUI when the user has pressed the "OK" button. This should
134    * trigger the saving of the newly created annotation(s)
135    */
136   public void okAction() throws GateException{
137     // Construct the response featutre
138     Iterator iter = tableModel.data.iterator();
139     while (iter.hasNext()){
140       RowData rd = (RowData) iter.next();
141       responseMap.put(rd.getFeatureSchema().getFeatureName(), rd.getValue());
142     }// End while
143     if (currentAnnot == null){
144       currentAnnotSet.add( currentStartOffset,
145                            currentEndOffset,
146                            currentAnnotSchema.getAnnotationName(),
147                            responseMap);
148     }else{
149       if (currentAnnot.getType().equals(currentAnnotSchema.getAnnotationName())){
150         currentAnnot.setFeatures(responseMap);
151       }else{
152         currentAnnotSet.add( currentStartOffset,
153                              currentEndOffset,
154                              currentAnnotSchema.getAnnotationName(),
155                              responseMap);
156         currentAnnotSet.remove(currentAnnot);
157       }// End if
158     }// End if
159   }//okAction();
160 
161   public void cancelAction() throws GateException {
162     //no need for any cleanup, because this editor has been implemented
163     //so that it does not modify the document and annotations, unless
164     //OK is pressed
165     return;
166   }
167 
168   /**
169     * Checks whether this viewer/editor can handle a specific annotation type.
170     * @param annotationType represents the annotation type being questioned.If
171     * it is <b>null</b> then the method will return false.
172     * @return true if the SchemaAnnotationEditor can handle the annotationType
173     * or false otherwise.
174     */
175   public boolean canDisplayAnnotationType(String annotationType){
176     // Returns true only if the there is an AnnotationSchema with the same type
177     // as annotationType.
178     if (annotationType == null) return false;
179     CreoleRegister creoleReg = Gate.getCreoleRegister();
180     List currentAnnotationSchemaList =
181                       creoleReg.getLrInstances("gate.creole.AnnotationSchema");
182     if (currentAnnotationSchemaList.isEmpty()) return false;
183     Iterator iter = currentAnnotationSchemaList.iterator();
184     while (iter.hasNext()){
185       AnnotationSchema annotSchema = (AnnotationSchema) iter.next();
186       if (annotationType.equals(annotSchema.getAnnotationName())) return true;
187     }// End while
188     return false;
189   }// canDisplayAnnotationType();
190 
191   // The Schema Editor functionality
192 
193   // Local data
194   /** The annotation schema present into the system*/
195   List currentAnnotationSchemaList = null;
196   /** The curent annotation set used by the editor*/
197   AnnotationSet currentAnnotSet = null;
198   /** The curent annotation used by the editor*/
199   Annotation currentAnnot = null;
200   /** The start offset of the span covered by the currentAnnot*/
201   Long currentStartOffset = null;
202   /** The end offset of the span covered by the currentAnnot*/
203   Long currentEndOffset = null;
204   /** This is the currentAnnotSchema being used by the editor*/
205   AnnotationSchema currentAnnotSchema = null;
206   /** The current FeatureMap used by the editor*/
207   FeatureMap currentAnnotFeaturesMap = null;
208   /** This field is returned when a featureMap was editted or created*/
209   FeatureMap responseMap = null;
210   /** This field is the table model used to represent features*/
211   FeaturesTableModel tableModel = null;
212   /** A map from feature name to its FeatureSchema definition*/
213   Map name2featureSchemaMap = null;
214   /** A map from annotation type to its AnnotationSchema definition*/
215   Map name2annotSchemaMap = null;
216   /** A list model used to represent the features not assigned to an annot*/
217   DefaultListModel listModel = null;
218 
219   // Gui Components
220   /** Displays the current features of the annotation being editted */
221   JTable  featuresTable = null;
222   /** A JScroll for the featuresTable component */
223   JScrollPane featuresTableScroll = null;
224   /** Displays all possible features of the annotation being
225    *  editted (taken from AnnotationSchema)
226    */
227   JList   featureSchemaList = null;
228   /** A JScroll for the featuresTable component */
229   JScrollPane featuresListScroll = null;
230   /** This button removes current features and add them to possible feature list*/
231   JButton removeFeatButton = null;
232   /** This button does the opposite of the above*/
233   JButton addFeatButton = null;
234   /** Displays all possible annotation schema loaded into the system*/
235   JComboBox annotSchemaComboBox = null;
236   /** This inner class deals with feature editting*/
237   InnerFeaturesEditor featuresEditor = null;
238 
239   /** Init local data needed by the GUI components to initialize*/
240   protected void initLocalData(){
241     // Create the response feature Map
242     responseMap = Factory.newFeatureMap();
243 
244     if (currentAnnotFeaturesMap == null)
245       currentAnnotFeaturesMap = Factory.newFeatureMap();
246 
247     name2featureSchemaMap = new HashMap();
248     // Construct a set of feature names from feature schema
249     Map fSNames2FSMap = new HashMap();
250 
251     listModel = new DefaultListModel();
252     // Init name2featureSchemaMap
253     // If the feature map provided was null, then we are in the creation mode
254     Set featuresSch = currentAnnotSchema.getFeatureSchemaSet();
255     if (featuresSch != null){
256       Iterator iter = featuresSch.iterator();
257       while (iter.hasNext()){
258         FeatureSchema fs = (FeatureSchema) iter.next();
259         // If the currentAnnotFeaturesMap doesn't contain the feature
260         // from FeatureSchema then
261         // add the featureSchema to the list
262         if (fs != null){
263           fSNames2FSMap.put(fs.getFeatureName(),fs);
264           if( !currentAnnotFeaturesMap.containsKey(fs.getFeatureName())){
265               name2featureSchemaMap.put(fs.getFeatureName(),fs);
266               listModel.addElement(fs.getFeatureName());
267           }// end if
268         }// end if
269       }// end while
270     }// end if
271 
272     // Init the table model
273     Set tableData = new HashSet();
274     Iterator iterator = currentAnnotFeaturesMap.keySet().iterator();
275     while (iterator.hasNext()){
276       String key = (String) iterator.next();
277       // If in currentAnnotFeaturesMap there is a key contained into
278       // fSNames2FSMap then
279       // add this feature to the table model together with its corresponding
280       // FeatureSchema
281       if (fSNames2FSMap.keySet().contains(key)){
282         // Add it to the table model
283         Object value = currentAnnotFeaturesMap.get(key);
284         tableData.add(new RowData(value,(FeatureSchema)fSNames2FSMap.get(key)));
285       } else
286         // Add it to the responseFeatureMap
287         // It might be a feature detected by the nameMatcher module, etc.
288         // Those features must be preserved.
289         responseMap.put(key,currentAnnotFeaturesMap.get(key));
290     }// end while
291     tableModel = new FeaturesTableModel(tableData);
292   }// initLocalData();
293 
294   /** This method creates the GUI components and paces them into the layout*/
295   protected void buildGuiComponents(){
296     this.setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
297     // Create the annotationSchema JComboBox box
298 
299     JPanel annotSchBox = new JPanel();
300     annotSchBox.setLayout(new BoxLayout(annotSchBox, BoxLayout.Y_AXIS));
301     annotSchBox.setAlignmentX(Component.LEFT_ALIGNMENT);
302     annotSchBox.add(Box.createVerticalStrut(5));
303     annotSchemaComboBox = new JComboBox(name2annotSchemaMap.keySet().toArray());
304     annotSchemaComboBox.setEditable(false);
305     annotSchemaComboBox.setAlignmentX(Component.LEFT_ALIGNMENT);
306     annotSchemaComboBox.setSelectedItem(currentAnnotSchema.getAnnotationName());
307     JLabel annotSchemaLabel = new JLabel("Select annotation type");
308     annotSchemaLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
309     annotSchBox.add(annotSchemaLabel);
310     annotSchBox.add(annotSchemaComboBox);
311 
312 
313     //Create the main box
314     JPanel componentsBox = new JPanel();
315     componentsBox.setLayout(new BoxLayout(componentsBox, BoxLayout.X_AXIS));
316     componentsBox.setAlignmentX(Component.LEFT_ALIGNMENT);
317 
318     // Create the feature table
319     featuresTable = new JTable();
320     featuresTable.setSelectionMode(
321                   ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
322     featuresTable.setModel(new FeaturesTableModel(new HashSet()));
323     featuresTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
324     featuresEditor = new InnerFeaturesEditor();
325     featuresTable.setDefaultEditor(java.lang.Object.class, featuresEditor);
326     featuresTableScroll = new JScrollPane(featuresTable);
327 
328     Box box = Box.createVerticalBox();
329     JLabel currentFeat = new JLabel("Current features");
330     currentFeat.setAlignmentX(Component.LEFT_ALIGNMENT);
331     box.add(currentFeat);
332     box.add(Box.createVerticalStrut(10));
333     box.add(featuresTableScroll);
334     featuresTableScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
335 
336     componentsBox.add(box);
337     componentsBox.add(Box.createHorizontalStrut(10));
338 
339     // add the remove put buttons
340     Box buttBox = Box.createVerticalBox();
341     removeFeatButton = new JButton(">>");
342     addFeatButton = new JButton("<<");
343 
344     buttBox.add(addFeatButton);
345     buttBox.add(Box.createVerticalStrut(10));
346     buttBox.add(removeFeatButton);
347 
348     componentsBox.add(buttBox);
349 
350     componentsBox.add(Box.createHorizontalStrut(10));
351 
352     // add the Feature Schema list
353     featureSchemaList = new JList();
354     featureSchemaList.setSelectionMode(
355                   ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
356     Set featuresSch = currentAnnotSchema.getFeatureSchemaSet();
357     if(featuresSch != null){
358       featureSchemaList.setVisibleRowCount(featuresSch.size());
359     }
360     featuresListScroll = new JScrollPane(featureSchemaList);
361 
362     box = Box.createVerticalBox();
363     JLabel possibFeaturesLabel = new JLabel("Possible features");
364     possibFeaturesLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
365     box.add(possibFeaturesLabel);
366     box.add(Box.createVerticalStrut(10));
367     featuresListScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
368     box.add(featuresListScroll);
369 
370     componentsBox.add(box);
371     componentsBox.add(Box.createHorizontalStrut(5));
372 
373     box = Box.createVerticalBox();
374     box.add(annotSchBox);
375     box.add(componentsBox);
376     this.add(box);
377     this.initGuiComponents();
378   }//buildGuiComponents();
379 
380   /** Init GUI components with values taken from local data*/
381   protected void initGuiComponents(){
382     featuresTable.setModel(tableModel);
383     featureSchemaList.setModel(listModel);
384   }//initGuiComponents()
385 
386   /** Init all the listeners*/
387   protected void initListeners(){
388 
389     annotSchemaComboBox.addActionListener(new ActionListener() {
390       public void actionPerformed(ActionEvent e) {
391         currentAnnotSchema = (AnnotationSchema) name2annotSchemaMap.get(
392                                  (String)annotSchemaComboBox.getSelectedItem());
393         initLocalData();
394         initGuiComponents();
395       }// actionPerformed();
396     });//addActionListener();
397 
398     // -> removeFeatButton
399     removeFeatButton.addActionListener(new ActionListener() {
400       public void actionPerformed(ActionEvent e) {
401         doRemoveFeatures();
402       }//actionPerformed();
403     });// addActionListener();
404 
405     // <- addFeatButton
406     addFeatButton.addActionListener(new ActionListener() {
407       public void actionPerformed(ActionEvent e) {
408         doAddFeatures();
409       }// actionPerformed();
410     });// addActionListener();
411   }//initListeners()
412 
413   /** This method remove a feature from the table and adds it to the list*/
414   private void doRemoveFeatures(){
415     int[] selectedRows = featuresTable.getSelectedRows();
416 
417     if (selectedRows.length <= 0) return;
418 
419     for (int i = (selectedRows.length - 1); i > -1 ; i--)
420       doRemoveFeature(selectedRows[i]);
421 
422     tableModel.fireTableDataChanged();
423   }// doRemoveFeatures();
424 
425   /** This removes the feature @ rowIndex*/
426   private void doRemoveFeature(int rowIndex){
427     RowData rd =  (RowData) tableModel.data.get(rowIndex);
428 
429     name2featureSchemaMap.put(rd.getFeatureSchema().getFeatureName(),
430                                                       rd.getFeatureSchema());
431 
432     listModel.addElement(rd.getFeatureSchema().getFeatureName());
433     tableModel.data.remove(rowIndex);
434   }// doRemoveFeature();
435 
436   /** This method adds features from the list to the table*/
437   private void doAddFeatures(){
438     Object[] selectedFeaturesName = featureSchemaList.getSelectedValues();
439     for (int i = 0 ; i < selectedFeaturesName.length; i ++){
440       doAddFeature((String) selectedFeaturesName[i]);
441     }// end for
442     tableModel.fireTableDataChanged();
443   }//doAddFeatures();
444 
445   /** This method adds a feature from the list to the table*/
446   private void doAddFeature(String aFeatureName){
447       FeatureSchema fs=(FeatureSchema) name2featureSchemaMap.get(aFeatureName);
448 
449       // Remove the feature schema from the list
450       name2featureSchemaMap.remove(aFeatureName);
451       listModel.removeElement(aFeatureName);
452 
453       Object value = null;
454       if (fs.isDefault() || fs.isFixed())
455         value = fs.getFeatureValue();
456       if (value == null && fs.isEnumeration()){
457         Iterator iter = fs.getPermissibleValues().iterator();
458         if (iter.hasNext()) value = iter.next();
459       }
460       tableModel.data.add(new RowData(value,fs));
461   }// doAddFeature();
462 
463   // Inner classes
464 
465   // TABLE MODEL
466   protected class FeaturesTableModel extends AbstractTableModel{
467 
468     ArrayList data = null;
469 
470     public FeaturesTableModel(Set aData){
471       data = new ArrayList(aData);
472     }// FeaturesTableModel
473 
474     public void fireTableDataChanged(){
475       super.fireTableDataChanged();
476     }// fireTableDataChanged();
477 
478     public int getColumnCount(){return 3;}
479 
480     public Class getColumnClass(int columnIndex){
481       switch(columnIndex){
482         case 0: return String.class;
483         case 1: return Object.class;
484         case 2: return String.class;
485         default: return Object.class;
486       }
487     }//getColumnClass()
488 
489     public String getColumnName(int columnIndex){
490       switch(columnIndex){
491         case 0: return "Name";
492         case 1: return "Value";
493         case 2: return "Type";
494         default: return "?";
495       }
496     }//public String getColumnName(int columnIndex)
497 
498     public boolean isCellEditable( int rowIndex,
499                                    int columnIndex){
500 
501 
502         if(columnIndex == 1){
503           RowData rd = (RowData) data.get(rowIndex);
504           FeatureSchema fs = rd.getFeatureSchema();
505           if (fs.isFixed() || fs.isProhibited()) return false;
506           else return true;
507         }// end if
508         if(columnIndex == 0 || columnIndex == 2) return false;
509         return false;
510     }//isCellEditable
511 
512     public int getRowCount(){
513       return data.size();
514     }//getRowCount()
515 
516     /** Returns the value at row,column from Table Model */
517     public Object getValueAt( int rowIndex,
518                               int columnIndex){
519 
520       RowData rd = (RowData) data.get(rowIndex);
521 
522       switch(columnIndex){
523         case 0: return rd.getFeatureSchema().getFeatureName();
524         case 1: return (rd.getValue() == null)? new String(""): rd.getValue();
525         case 2: {
526                   // Show only the last substring. For example, for
527                   // java.lang.Integer -> Integer
528                   String type = rd.getFeatureSchema().getValueClassName();
529                   if(type == null)
530                       return new String("");
531                   else{
532                     int start = type.lastIndexOf(".");
533                     if ((start > -1) && (start < type.length()))
534                       return type.substring(start+1,type.length());
535                     else return type;
536                   }// End if
537                 }
538 
539         default: return "?";
540       }// End Switch
541     }//getValueAt
542 
543     /** Set the value from the Cell Editor into the table model*/
544     public void setValueAt( Object aValue,
545                             int rowIndex,
546                             int columnIndex){
547 
548       if (data == null || data.isEmpty()) return;
549       RowData rd = (RowData) data.get(rowIndex);
550       switch(columnIndex){
551         case 0:{break;}
552         case 1:{
553           // Try to perform type conversion
554           String className = null;
555           String aValueClassName = null;
556           // Need to create an object belonging to class "className" based on
557           // the string object "aValue"
558           if (aValue == null){
559             rd.setValue("?");
560             return;
561           }// End if
562           // Get the class name the final object must belong
563           className = rd.getFeatureSchema().getValueClassName();
564           // Get the class name that aValue object belongs to.
565           aValueClassName = aValue.getClass().toString();
566           // If there is no class to convert to, let the aValue object as it is
567           // and return.
568           if (className == null){
569               rd.setValue(aValue);
570               return;
571           }// End if
572 
573           // If both classes are the same, then return. There is nothing to
574           // convert to
575           if (className.equals(aValueClassName)){
576             rd.setValue(aValue);
577             return;
578           }// End if
579           // If the class "aValue" object belongs to is not String then return.
580           // This method tries to convert a string to various other types.
581           if (!"class java.lang.String".equals(aValueClassName)){
582             rd.setValue(aValue);
583             return;
584           }// End if
585 
586           // The aValue object belonging to java.lang.String needs to be
587           // converted into onother object belonging to "className"
588           Class  classObj = null;
589           try{
590             // Create a class object from className
591             classObj = Gate.getClassLoader().loadClass(className);
592           }catch (ClassNotFoundException cnfex){
593             try{
594               //now let's try the system classloader
595               classObj = Class.forName(className);
596             }catch (ClassNotFoundException cnfe){
597             rd.setValue(aValue);
598             return;
599             }
600           }// End catch
601           // Get its list of constructors
602           Constructor[] constrArray = classObj.getConstructors();
603           if (constrArray == null){
604             rd.setValue(aValue);
605             return;
606           }// End if
607 
608           // Search for the constructo which takes only one String parameter
609           boolean found = false;
610           Constructor constructor = null;
611           for (int i=0; i<constrArray.length; i++){
612             constructor = constrArray[i];
613             if ( constructor.getParameterTypes().length == 1 &&
614                  "class java.lang.String".equals(
615                                 constructor.getParameterTypes()[0].toString())
616                ){
617                   found = true;
618                   break;
619             }// End if
620           }// End for
621 
622           if (!found){
623             rd.setValue(aValue);
624             return;
625           }// End if
626           try{
627             // Try to create an object with this constructor
628             Object[] paramsArray = new Object[1];
629             paramsArray[0] = aValue;
630             Object newValueObject = constructor.newInstance(paramsArray);
631 
632             rd.setValue(newValueObject);
633 
634           } catch (Exception e){
635             rd.setValue("");
636           }// End catch
637 
638 //          rd.setValue(aValue);
639           break;
640         }// End case
641         case 2:{break;}
642         case 3:{break;}
643         default:{}
644       }// End switch
645     }// setValueAt();
646 
647   }///class FeaturesTableModel extends DefaultTableModel
648 
649   /** Internal class used in the inner FeaturesTableModel class*/
650   class RowData {
651     private Object value = null;
652     private FeatureSchema featSchema = null;
653 
654     /** Constructor*/
655     RowData(Object aValue, FeatureSchema aFeatureSchema){
656       value = aValue;
657       featSchema = aFeatureSchema;
658     }//RowData
659 
660     public void setValue(Object aValue){
661       value = aValue;
662     }// setValue();
663 
664     public Object getValue(){
665       return value;
666     }//getValue()
667 
668     public void setFeatureSchema(FeatureSchema aFeatureSchema){
669       featSchema = aFeatureSchema;
670     }// setFeatureSchema();
671 
672     public FeatureSchema getFeatureSchema(){
673       return featSchema;
674     }//getFeatureSchema()
675 
676   }// RowData
677 
678   // The EDITOR RENDERER
679   /** This inner class deals with the feature type being eddited. What it does
680     * is to decide what GUI component to use (JComboBox, JTextField or JLabel)
681     */
682   class InnerFeaturesEditor extends AbstractCellEditor  implements TableCellEditor{
683     // Fields
684     JComboBox cb = null;
685     JTextField tf = null;
686     int thisRow = 0;
687     int thisColumn = 0;
688     /** Constructor*/
689     public InnerFeaturesEditor(){}
690     /** The method overridden in order to implement behaviour*/
691     public Component getTableCellEditorComponent( JTable table,
692                                                   Object value,
693                                                   boolean isSelected,
694                                                   int row,
695                                                   int column){
696        thisRow = row;
697        thisColumn = column;
698        RowData rd = (RowData) tableModel.data.get(row);
699        if (rd.getFeatureSchema().isEnumeration()){
700           cb = new JComboBox(rd.getFeatureSchema().
701                                             getPermissibleValues().toArray());
702           cb.setSelectedItem(value);
703           cb.addActionListener(new ActionListener(){
704             public void actionPerformed(ActionEvent e){
705               tableModel.setValueAt(cb.getSelectedItem(),thisRow,thisColumn);
706             }// actionPerformed();
707           });//addActionListener();
708           tf = null;
709           return cb;
710        }// End if
711        if ( rd.getFeatureSchema().isDefault() ||
712             rd.getFeatureSchema().isOptional() ||
713             rd.getFeatureSchema().isRequired() ){
714 
715             tf = new JTextField(value.toString());
716             cb = null;
717             return tf;
718        }// End iff
719        return new JLabel(value.toString());
720     }//getTableCellEditorComponent
721     /** @return the object representing the value stored at that cell*/
722     public Object getCellEditorValue(){
723       if (cb != null ) return cb.getSelectedItem();
724       if (tf != null ) return tf.getText();
725       return new String("");
726     }//getCellEditorValue
727   }///InnerFeaturesEditor inner class
728 }// End class SchemaAnnotationEditor