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