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 defaultComparator = new ObjectComparator();
284 editorCombo = (JComboBox)editorComponent;
285 editorCombo.setModel(new DefaultComboBoxModel());
286 editorCombo.setBackground(mainTable.getBackground());
287 editorCombo.setEditable(true);
288 editorCombo.addActionListener(new ActionListener(){
289 public void actionPerformed(ActionEvent evt){
290 stopCellEditing();
291 }
292 });
293
294 rendererCombo = new JComboBox();
295 rendererCombo.setModel(new DefaultComboBoxModel());
296 rendererCombo.setBackground(mainTable.getBackground());
297 rendererCombo.setEditable(true);
298 rendererCombo.setOpaque(false);
299
300
301 requiredIconLabel = new JLabel(){
302 public void repaint(long tm, int x, int y, int width, int height){}
303 public void repaint(Rectangle r){}
304 public void validate(){}
305 public void revalidate(){}
306 protected void firePropertyChange(String propertyName,
307 Object oldValue,
308 Object newValue){}
309
310 };
311 requiredIconLabel.setIcon(MainFrame.getIcon("r.gif"));
312 requiredIconLabel.setOpaque(false);
313 requiredIconLabel.setToolTipText("Required feature");
314
315 optionalIconLabel = new JLabel(){
316 public void repaint(long tm, int x, int y, int width, int height){}
317 public void repaint(Rectangle r){}
318 public void validate(){}
319 public void revalidate(){}
320 protected void firePropertyChange(String propertyName,
321 Object oldValue,
322 Object newValue){}
323
324 };
325 optionalIconLabel.setIcon(MainFrame.getIcon("o.gif"));
326 optionalIconLabel.setOpaque(false);
327 optionalIconLabel.setToolTipText("Optional feature");
328
329 nonSchemaIconLabel = new JLabel(MainFrame.getIcon("c.gif")){
330 public void repaint(long tm, int x, int y, int width, int height){}
331 public void repaint(Rectangle r){}
332 public void validate(){}
333 public void revalidate(){}
334 protected void firePropertyChange(String propertyName,
335 Object oldValue,
336 Object newValue){}
337
338 };
339 nonSchemaIconLabel.setToolTipText("Custom feature");
340 nonSchemaIconLabel.setOpaque(false);
341
342 deleteButton = new JButton(MainFrame.getIcon("delete.gif"));
343 deleteButton.setMargin(new Insets(0,0,0,0));
344 deleteButton.setBorderPainted(false);
345 deleteButton.setContentAreaFilled(false);
346 deleteButton.setOpaque(false);
347 deleteButton.setToolTipText("Delete");
348 deleteButton.addActionListener(new ActionListener(){
349 public void actionPerformed(ActionEvent evt){
350 int row = mainTable.getEditingRow();
351 if(row < 0) return;
352 Feature feature = (Feature)featureList.get(row);
353 if(feature == emptyFeature){
354 feature.value = null;
355 featuresModel.fireTableRowsUpdated(row, row);
356 }else{
357 featureList.remove(row);
358 targetFeatures.remove(feature.name);
359 populate();
360 }
361 }
362 });
363 }
364
365 public Component getTableCellRendererComponent(JTable table, Object value,
366 boolean isSelected, boolean hasFocus, int row, int column){
367 Feature feature = (Feature)featureList.get(row);
368 switch(column){
369 case ICON_COL:
370 return feature.isSchemaFeature() ?
371 (feature.isRequired() ?
372 requiredIconLabel :
373 optionalIconLabel) :
374 nonSchemaIconLabel;
375 case NAME_COL:
376 rendererCombo.setPreferredSize(null);
377 prepareCombo(rendererCombo, row, column);
378 Dimension dim = rendererCombo.getPreferredSize();
379 return rendererCombo;
381 case VALUE_COL:
382 prepareCombo(rendererCombo, row, column);
383 return rendererCombo;
384 case DELETE_COL: return deleteButton;
385 default: return null;
386 }
387 }
388
389 public Component getTableCellEditorComponent(JTable table, Object value,
390 boolean isSelected, int row, int column){
391 switch(column){
392 case NAME_COL:
393 prepareCombo(editorCombo, row, column);
394 return editorCombo;
395 case VALUE_COL:
396 prepareCombo(editorCombo, row, column);
397 return editorCombo;
398 case DELETE_COL: return deleteButton;
399 default: return null;
400 }
401
402 }
403
404 protected void prepareCombo(JComboBox combo, int row, int column){
405 Feature feature = (Feature)featureList.get(row);
406 DefaultComboBoxModel comboModel = (DefaultComboBoxModel)combo.getModel();
407 comboModel.removeAllElements();
408 switch(column){
409 case NAME_COL:
410 List fNames = new ArrayList();
411 if(schema != null && schema.getFeatureSchemaSet() != null){
412 Iterator fSchemaIter = schema.getFeatureSchemaSet().iterator();
413 while(fSchemaIter.hasNext())
414 fNames.add(((FeatureSchema)fSchemaIter.next()).getFeatureName());
415 }
416 if(!fNames.contains(feature.name))fNames.add(feature.name);
417 Collections.sort(fNames);
418 for(Iterator nameIter = fNames.iterator();
419 nameIter.hasNext();
420 comboModel.addElement(nameIter.next()));
421 combo.getEditor().getEditorComponent().setBackground(FeaturesSchemaEditor.this.getBackground());
422 combo.setSelectedItem(feature.name);
423 break;
424 case VALUE_COL:
425 List fValues = new ArrayList();
426 if(feature.isSchemaFeature()){
427 Set permValues = schema.getFeatureSchema(feature.name).
428 getPermissibleValues();
429 if(permValues != null) fValues.addAll(permValues);
430 }
431 if(!fValues.contains(feature.value)) fValues.add(feature.value);
432 Collections.sort(fValues, defaultComparator);
433 for(Iterator valIter = fValues.iterator();
434 valIter.hasNext();
435 comboModel.addElement(valIter.next()));
436 combo.getEditor().getEditorComponent().setBackground(feature.isCorrect() ?
437 FeaturesSchemaEditor.this.getBackground() :
438 (feature.isRequired() ? REQUIRED_WRONG : OPTIONAL_WRONG));
439 combo.setSelectedItem(feature.value);
440 break;
441 default: ;
442 }
443
444 }
445
446 JLabel requiredIconLabel;
447 JLabel optionalIconLabel;
448 JLabel nonSchemaIconLabel;
449 JComboBox editorCombo;
450 JComboBox rendererCombo;
451 JButton deleteButton;
452 ObjectComparator defaultComparator;
453 }
454 }