1
15 package gate.gui;
16
17 import java.awt.*;
18 import java.awt.Component;
19 import java.awt.Rectangle;
20 import java.awt.event.ActionEvent;
21 import java.awt.event.ActionListener;
22 import java.util.*;
23 import java.util.ArrayList;
24 import java.util.List;
25 import javax.swing.*;
26 import javax.swing.JLabel;
27 import javax.swing.JTable;
28 import javax.swing.table.AbstractTableModel;
29 import javax.swing.table.TableCellRenderer;
30 import gate.*;
31 import gate.FeatureMap;
32 import gate.Resource;
33 import gate.creole.*;
34 import gate.creole.AnnotationSchema;
35 import gate.creole.FeatureSchema;
36 import gate.event.FeatureMapListener;
37 import gate.swing.XJTable;
38 import gate.util.*;
39 import gate.util.GateRuntimeException;
40
41
43 public class FeaturesSchemaEditor extends AbstractVisualResource
44 implements ResizableVisualResource, FeatureMapListener{
45 public FeaturesSchemaEditor(){
46 setBackground(UIManager.getDefaults().getColor("Table.background"));
47 }
48
49 public void setTargetFeatures(FeatureMap features){
50 if(features != null) features.removeFeatureMapListener(this);
51 this.targetFeatures = features;
52 populate();
53 if(features != null) features.addFeatureMapListener(this);
54 }
55
56
57
60 public void setTarget(Object target){
61 this.target = (FeatureBearer)target;
62 setTargetFeatures(this.target.getFeatures());
63 }
64
65 public void setSchema(AnnotationSchema schema){
66 this.schema = schema;
67 featuresModel.fireTableRowsUpdated(0, featureList.size() - 1);
68 }
69
70 public XJTable getTable(){
71 return mainTable;
72 }
73
74
77 public void featureMapUpdated(){
78 populate();
79 }
80
81
82
83 public Resource init() throws ResourceInstantiationException {
84 featureList = new ArrayList();
85 emptyFeature = new Feature("", null);
86 featureList.add(emptyFeature);
87 initGUI();
88 return this;
89 }
91 protected void initGUI(){
92 featuresModel = new FeaturesTableModel();
93 mainTable = new XJTable();
94 mainTable.setModel(featuresModel);
95 mainTable.setTableHeader(null);
96 mainTable.setSortable(false);
97 mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
98 mainTable.setShowVerticalLines(false);
99 mainTable.setBackground(getBackground());
100 mainTable.setIntercellSpacing(new Dimension(2,2));
101 featureEditorRenderer = new FeatureEditorRenderer();
102 mainTable.getColumnModel().getColumn(ICON_COL).
103 setCellRenderer(featureEditorRenderer);
104 mainTable.getColumnModel().getColumn(NAME_COL).
105 setCellRenderer(featureEditorRenderer);
106 mainTable.getColumnModel().getColumn(NAME_COL).
107 setCellEditor(featureEditorRenderer);
108 mainTable.getColumnModel().getColumn(VALUE_COL).
109 setCellRenderer(featureEditorRenderer);
110 mainTable.getColumnModel().getColumn(VALUE_COL).
111 setCellEditor(featureEditorRenderer);
112 mainTable.getColumnModel().getColumn(DELETE_COL).
113 setCellRenderer(featureEditorRenderer);
114 mainTable.getColumnModel().getColumn(DELETE_COL).
115 setCellEditor(featureEditorRenderer);
116 scroller = new JScrollPane(mainTable);
117 scroller.setBackground(getBackground());
118 scroller.getViewport().setBackground(getBackground());
119 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
120 add(scroller);
121 }
122
123
127 protected void populate(){
128 featureList.clear();
129 Set fNames = new HashSet();
131
132 if(targetFeatures != null){
133 fNames.addAll(targetFeatures.keySet());
135 if(schema != null && schema.getFeatureSchemaSet() != null){
136 Iterator fSchemaIter = schema.getFeatureSchemaSet().iterator();
137 while(fSchemaIter.hasNext()){
138 FeatureSchema fSchema = (FeatureSchema)fSchemaIter.next();
139 fNames.add(fSchema.getFeatureName());
141 }
142 }
143 List featureNames = new ArrayList(fNames);
144 Collections.sort(featureNames);
145 Iterator namIter = featureNames.iterator();
146 while(namIter.hasNext()){
147 String name = (String)namIter.next();
148 Object value = targetFeatures.get(name);
149 featureList.add(new Feature(name, value));
150 }
151 }
152 featureList.add(emptyFeature);
153 featuresModel.fireTableDataChanged();
154 mainTable.setSize(mainTable.getPreferredScrollableViewportSize());
156 }
157
158 FeatureMap targetFeatures;
159 FeatureBearer target;
160 Feature emptyFeature;
161 AnnotationSchema schema;
162 FeaturesTableModel featuresModel;
163 List featureList;
164 FeatureEditorRenderer featureEditorRenderer;
165 XJTable mainTable;
166 JScrollPane scroller;
167
168 private static final int COLUMNS = 4;
169 private static final int ICON_COL = 0;
170 private static final int NAME_COL = 1;
171 private static final int VALUE_COL = 2;
172 private static final int DELETE_COL = 3;
173
174 private static final Color REQUIRED_WRONG = Color.RED;
175 private static final Color OPTIONAL_WRONG = Color.ORANGE;
176
177 protected class Feature{
178 String name;
179 Object value;
180
181 public Feature(String name, Object value){
182 this.name = name;
183 this.value = value;
184 }
185 boolean isSchemaFeature(){
186 return schema != null && schema.getFeatureSchema(name) != null;
187 }
188 boolean isCorrect(){
189 if(schema == null) return true;
190 FeatureSchema fSchema = schema.getFeatureSchema(name);
191 return fSchema == null || fSchema.getPermissibleValues() == null||
192 fSchema.getPermissibleValues().contains(value);
193 }
194 boolean isRequired(){
195 if(schema == null) return false;
196 FeatureSchema fSchema = schema.getFeatureSchema(name);
197 return fSchema != null && fSchema.isRequired();
198 }
199 Object getDefaultValue(){
200 if(schema == null) return null;
201 FeatureSchema fSchema = schema.getFeatureSchema(name);
202 return fSchema == null ? null : fSchema.getFeatureValue();
203 }
204 }
205
206
207 protected class FeaturesTableModel extends AbstractTableModel{
208 public int getRowCount(){
209 return featureList.size();
210 }
211
212 public int getColumnCount(){
213 return COLUMNS;
214 }
215
216 public Object getValueAt(int row, int column){
217 Feature feature = (Feature)featureList.get(row);
218 switch(column){
219 case NAME_COL:
220 return feature.name;
221 case VALUE_COL:
222 return feature.value;
223 default:
224 return null;
225 }
226 }
227
228 public boolean isCellEditable(int rowIndex, int columnIndex){
229 return columnIndex == VALUE_COL || columnIndex == NAME_COL ||
230 columnIndex == DELETE_COL;
231 }
232
233 public void setValueAt(Object aValue, int rowIndex, int columnIndex){
234 Feature feature = (Feature)featureList.get(rowIndex);
235 if(targetFeatures == null){
236 targetFeatures = Factory.newFeatureMap();
237 target.setFeatures(targetFeatures);
238 setTargetFeatures(targetFeatures);
239 }
240 switch(columnIndex){
241 case VALUE_COL:
242 feature.value = aValue;
243 if(feature.name != null && feature.name.length() > 0){
244 targetFeatures.put(feature.name, aValue);
245 fireTableRowsUpdated(rowIndex, rowIndex);
246 mainTable.setSize(mainTable.getPreferredScrollableViewportSize());
247 }
248 break;
249 case NAME_COL:
250 targetFeatures.remove(feature.name);
251 feature.name = (String)aValue;
252 targetFeatures.put(feature.name, feature.value);
253 if(feature == emptyFeature) emptyFeature = new Feature("", null);
254 populate();
255 break;
256 case DELETE_COL:
257 break;
259 default:
260 throw new GateRuntimeException("Non editable cell!");
261 }
262
263 }
264
265 public String getColumnName(int column){
266 switch(column){
267 case NAME_COL:
268 return "Name";
269 case VALUE_COL:
270 return "Value";
271 case DELETE_COL:
272 return "";
273 default:
274 return null;
275 }
276 }
277 }
278
279
280 protected class FeatureEditorRenderer extends DefaultCellEditor implements TableCellRenderer{
281 public FeatureEditorRenderer(){
282 super(new JComboBox());
283 editorCombo = (JComboBox)editorComponent;
284 editorCombo.setModel(new DefaultComboBoxModel());
285 editorCombo.setBackground(mainTable.getBackground());
286 editorCombo.setEditable(true);
287 editorCombo.addActionListener(new ActionListener(){
288 public void actionPerformed(ActionEvent evt){
289 stopCellEditing();
290 }
291 });
292
293 rendererCombo = new JComboBox();
294 rendererCombo.setModel(new DefaultComboBoxModel());
295 rendererCombo.setBackground(mainTable.getBackground());
296 rendererCombo.setEditable(true);
297 rendererCombo.setOpaque(false);
298
299
300 requiredIconLabel = new JLabel(){
301 public void repaint(long tm, int x, int y, int width, int height){}
302 public void repaint(Rectangle r){}
303 public void validate(){}
304 public void revalidate(){}
305 protected void firePropertyChange(String propertyName,
306 Object oldValue,
307 Object newValue){}
308
309 };
310 requiredIconLabel.setIcon(MainFrame.getIcon("r.gif"));
311 requiredIconLabel.setOpaque(false);
312 requiredIconLabel.setToolTipText("Required feature");
313
314 optionalIconLabel = new JLabel(){
315 public void repaint(long tm, int x, int y, int width, int height){}
316 public void repaint(Rectangle r){}
317 public void validate(){}
318 public void revalidate(){}
319 protected void firePropertyChange(String propertyName,
320 Object oldValue,
321 Object newValue){}
322
323 };
324 optionalIconLabel.setIcon(MainFrame.getIcon("o.gif"));
325 optionalIconLabel.setOpaque(false);
326 optionalIconLabel.setToolTipText("Optional feature");
327
328 nonSchemaIconLabel = new JLabel(MainFrame.getIcon("c.gif")){
329 public void repaint(long tm, int x, int y, int width, int height){}
330 public void repaint(Rectangle r){}
331 public void validate(){}
332 public void revalidate(){}
333 protected void firePropertyChange(String propertyName,
334 Object oldValue,
335 Object newValue){}
336
337 };
338 nonSchemaIconLabel.setToolTipText("Custom feature");
339 nonSchemaIconLabel.setOpaque(false);
340
341 deleteButton = new JButton(MainFrame.getIcon("delete.gif"));
342 deleteButton.setMargin(new Insets(0,0,0,0));
343 deleteButton.setBorderPainted(false);
344 deleteButton.setContentAreaFilled(false);
345 deleteButton.setOpaque(false);
346 deleteButton.setToolTipText("Delete");
347 deleteButton.addActionListener(new ActionListener(){
348 public void actionPerformed(ActionEvent evt){
349 int row = mainTable.getEditingRow();
350 if(row < 0) return;
351 Feature feature = (Feature)featureList.get(row);
352 if(feature == emptyFeature){
353 feature.value = null;
354 featuresModel.fireTableRowsUpdated(row, row);
355 }else{
356 featureList.remove(row);
357 targetFeatures.remove(feature.name);
358 populate();
359 }
360 }
361 });
362 }
363
364 public Component getTableCellRendererComponent(JTable table, Object value,
365 boolean isSelected, boolean hasFocus, int row, int column){
366 Feature feature = (Feature)featureList.get(row);
367 switch(column){
368 case ICON_COL:
369 return feature.isSchemaFeature() ?
370 (feature.isRequired() ?
371 requiredIconLabel :
372 optionalIconLabel) :
373 nonSchemaIconLabel;
374 case NAME_COL:
375 rendererCombo.setPreferredSize(null);
376 prepareCombo(rendererCombo, row, column);
377 Dimension dim = rendererCombo.getPreferredSize();
378 return rendererCombo;
380 case VALUE_COL:
381 prepareCombo(rendererCombo, row, column);
382 return rendererCombo;
383 case DELETE_COL: return deleteButton;
384 default: return null;
385 }
386 }
387
388 public Component getTableCellEditorComponent(JTable table, Object value,
389 boolean isSelected, int row, int column){
390 switch(column){
391 case NAME_COL:
392 prepareCombo(editorCombo, row, column);
393 return editorCombo;
394 case VALUE_COL:
395 prepareCombo(editorCombo, row, column);
396 return editorCombo;
397 case DELETE_COL: return deleteButton;
398 default: return null;
399 }
400
401 }
402
403 protected void prepareCombo(JComboBox combo, int row, int column){
404 Feature feature = (Feature)featureList.get(row);
405 DefaultComboBoxModel comboModel = (DefaultComboBoxModel)combo.getModel();
406 comboModel.removeAllElements();
407 switch(column){
408 case NAME_COL:
409 List fNames = new ArrayList();
410 if(schema != null && schema.getFeatureSchemaSet() != null){
411 Iterator fSchemaIter = schema.getFeatureSchemaSet().iterator();
412 while(fSchemaIter.hasNext())
413 fNames.add(((FeatureSchema)fSchemaIter.next()).getFeatureName());
414 }
415 if(!fNames.contains(feature.name))fNames.add(feature.name);
416 Collections.sort(fNames);
417 for(Iterator nameIter = fNames.iterator();
418 nameIter.hasNext();
419 comboModel.addElement(nameIter.next()));
420 combo.getEditor().getEditorComponent().setBackground(FeaturesSchemaEditor.this.getBackground());
421 combo.setSelectedItem(feature.name);
422 break;
423 case VALUE_COL:
424 List fValues = new ArrayList();
425 if(feature.isSchemaFeature()){
426 Set permValues = schema.getFeatureSchema(feature.name).
427 getPermissibleValues();
428 if(permValues != null) fValues.addAll(permValues);
429 }
430 if(!fValues.contains(feature.value)) fValues.add(feature.value);
431 for(Iterator valIter = fValues.iterator();
432 valIter.hasNext();
433 comboModel.addElement(valIter.next()));
434 combo.getEditor().getEditorComponent().setBackground(feature.isCorrect() ?
435 FeaturesSchemaEditor.this.getBackground() :
436 (feature.isRequired() ? REQUIRED_WRONG : OPTIONAL_WRONG));
437 combo.setSelectedItem(feature.value);
438 break;
439 default: ;
440 }
441
442 }
443
444 JLabel requiredIconLabel;
445 JLabel optionalIconLabel;
446 JLabel nonSchemaIconLabel;
447 JComboBox editorCombo;
448 JComboBox rendererCombo;
449 JButton deleteButton;
450 }
451 }