1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.support.components;
14
15 import java.awt.BorderLayout;
16 import java.awt.Color;
17 import java.awt.Component;
18 import java.awt.Font;
19 import java.awt.Toolkit;
20 import java.awt.datatransfer.StringSelection;
21 import java.awt.event.ActionEvent;
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24 import java.lang.reflect.InvocationTargetException;
25 import java.util.ArrayList;
26 import java.util.List;
27
28 import javax.swing.AbstractAction;
29 import javax.swing.Action;
30 import javax.swing.BorderFactory;
31 import javax.swing.DefaultCellEditor;
32 import javax.swing.JComboBox;
33 import javax.swing.JComponent;
34 import javax.swing.JPanel;
35 import javax.swing.JScrollPane;
36 import javax.swing.JTable;
37 import javax.swing.JTextField;
38 import javax.swing.TransferHandler;
39 import javax.swing.border.TitledBorder;
40 import javax.swing.table.AbstractTableModel;
41 import javax.swing.table.DefaultTableCellRenderer;
42 import javax.swing.table.TableCellEditor;
43 import javax.swing.table.TableModel;
44
45 import org.apache.commons.beanutils.BeanUtils;
46 import org.apache.commons.beanutils.PropertyUtils;
47
48 import com.eviware.soapui.SoapUI;
49 import com.eviware.soapui.support.PropertyChangeNotifier;
50 import com.eviware.soapui.support.StringUtils;
51
52 /***
53 * Table for displaying property name/value pairs
54 *
55 * @author Ole.Matzura
56 */
57
58 public class JPropertiesTable<T> extends JPanel
59 {
60 public final static Object[] BOOLEAN_OPTIONS = new Object[] {Boolean.TRUE, Boolean.FALSE };
61
62 private PropertiesTableModel<T> tableModel;
63 private JTable table;
64
65 private TitledBorder titledBorder;
66
67 private String title;
68
69 public JPropertiesTable( String title )
70 {
71 this( title, null );
72 }
73
74 public JPropertiesTable( String title, T propertyObject )
75 {
76 super( new BorderLayout());
77 this.title = title;
78
79 tableModel = new PropertiesTableModel<T>( propertyObject );
80 table = new PTable( tableModel );
81
82 table.getColumnModel().getColumn(0).setHeaderValue( "Property" );
83 table.getColumnModel().getColumn(1).setHeaderValue( "Value" );
84 table.getColumnModel().getColumn(0).setCellRenderer( new PropertiesTableCellRenderer() );
85 table.getColumnModel().getColumn(1).setCellRenderer( new PropertiesTableCellRenderer() );
86
87 add( new JScrollPane( table ), BorderLayout.CENTER );
88 titledBorder = BorderFactory.createTitledBorder( BorderFactory.createEmptyBorder(), title );
89 titledBorder.setTitleFont( titledBorder.getTitleFont().deriveFont( Font.PLAIN, 11 ));
90
91 if( title != null )
92 setBorder( titledBorder);
93
94 table.setBackground( Color.WHITE );
95 setPreferredSize( table.getPreferredSize());
96 }
97
98 public void setTitle( String title )
99 {
100 this.title = title;
101 titledBorder.setTitle( title );
102 setBorder( titledBorder );
103 repaint();
104 }
105
106 public String getTitle()
107 {
108 return title;
109 }
110
111 @Override
112 public void removeNotify()
113 {
114 getTableModel().release();
115 super.removeNotify();
116 }
117
118 @Override
119 public void addNotify()
120 {
121 getTableModel().attach();
122 super.addNotify();
123 }
124
125 public void setPropertyObject( T propertyObject )
126 {
127 if( table.isEditing() )
128 table.getCellEditor().stopCellEditing();
129
130 tableModel.setPropertyObject( propertyObject );
131 }
132
133 public PropertiesTableModel getTableModel()
134 {
135 return tableModel;
136 }
137
138 public PropertyDescriptor addProperty( String caption, String name)
139 {
140 return addProperty( caption, name, false );
141 }
142
143 public PropertyDescriptor addProperty( String caption, String name, boolean editable )
144 {
145 return addProperty( caption, name, editable, null );
146 }
147
148 public PropertyDescriptor addProperty( String caption, String name, boolean editable, PropertyFormatter formatter )
149 {
150 return tableModel.addProperty( caption, name, editable, formatter );
151 }
152
153 public static final class PropertiesTableModel<T> extends AbstractTableModel implements PropertyChangeListener
154 {
155 private List<PropertyDescriptor> properties = new ArrayList<PropertyDescriptor>();
156 private T propertyObject;
157 private boolean attached;
158
159 public PropertiesTableModel(T propertyObject)
160 {
161 this.propertyObject = propertyObject;
162 }
163
164 public void attach()
165 {
166 if( !attached && propertyObject instanceof PropertyChangeNotifier )
167 {
168 ((PropertyChangeNotifier)propertyObject).addPropertyChangeListener( this );
169 attached = true;
170 }
171 }
172
173 public void setPropertyObject(T propertyObject)
174 {
175 release();
176 this.propertyObject = propertyObject;
177 attach();
178 fireTableDataChanged();
179 }
180
181 public PropertyDescriptor addProperty( String caption, String name, boolean editable, PropertyFormatter formatter )
182 {
183 PropertyDescriptor propertyDescriptor = new PropertyDescriptor( caption, name, editable, formatter );
184 properties.add( propertyDescriptor);
185 return propertyDescriptor;
186 }
187
188 public PropertyDescriptor addProperty( String caption, String name, Object [] options )
189 {
190 PropertyDescriptor propertyDescriptor = new PropertyDescriptor( caption, name, options );
191 properties.add( propertyDescriptor);
192 return propertyDescriptor;
193 }
194
195 public int getRowCount()
196 {
197 return properties.size();
198 }
199
200 public int getColumnCount()
201 {
202 return 2;
203 }
204
205 public boolean isCellEditable(int rowIndex, int columnIndex)
206 {
207 if( columnIndex == 0 || propertyObject == null ) return false;
208 return properties.get( rowIndex ).isEditable() &&
209 PropertyUtils.isWriteable( propertyObject, properties.get( rowIndex ).getName() );
210 }
211
212 public void setValueAt(Object aValue, int rowIndex, int columnIndex)
213 {
214 try
215 {
216 if( propertyObject != null && columnIndex == 1 && properties.get( rowIndex ).isEditable() )
217 {
218 BeanUtils.setProperty( propertyObject, properties.get( rowIndex ).getName(), aValue );
219 }
220 }
221 catch (IllegalAccessException e)
222 {
223 SoapUI.logError( e );
224 }
225 catch (InvocationTargetException e)
226 {
227 SoapUI.logError( e );
228 }
229 }
230
231 public Object getValueAt(int rowIndex, int columnIndex)
232 {
233 if( propertyObject == null )
234 return null;
235
236 try
237 {
238 PropertyDescriptor propertyDescriptor = properties.get(rowIndex);
239 switch (columnIndex)
240 {
241 case 0:
242 return propertyDescriptor.getCaption();
243 case 1:
244 {
245 Object value = PropertyUtils.getSimpleProperty(propertyObject,propertyDescriptor.getName());
246 return propertyDescriptor.getFormatter().format( propertyDescriptor.getName(), value );
247 }
248 }
249 }
250 catch (IllegalAccessException e)
251 {
252 SoapUI.logError( e );
253 }
254 catch (InvocationTargetException e)
255 {
256 SoapUI.logError( e );
257 }
258 catch (NoSuchMethodException e)
259 {
260 SoapUI.logError( e );
261 }
262
263 return null;
264 }
265
266 public PropertyDescriptor getPropertyDescriptorAt( int row )
267 {
268 return properties.get( row );
269 }
270
271 public void propertyChange(PropertyChangeEvent evt)
272 {
273 fireTableDataChanged();
274 }
275
276 public void release()
277 {
278 if( propertyObject instanceof PropertyChangeNotifier && attached )
279 {
280 ((PropertyChangeNotifier)propertyObject).removePropertyChangeListener( this );
281 attached = false;
282 }
283 }}
284
285 public static class PropertyDescriptor
286 {
287 private final String caption;
288 private final String name;
289 private boolean editable;
290 private PropertyFormatter formatter ;
291 private Object [] options;
292 private DefaultCellEditor cellEditor;
293 private String description;
294
295 public PropertyDescriptor(String caption, String name, boolean editable, PropertyFormatter formatter)
296 {
297 this.caption = caption;
298 this.name = name;
299 this.editable = editable;
300 this.formatter = formatter;
301
302 JTextField textField = new JTextField();
303 textField.setBorder(BorderFactory.createEmptyBorder());
304 cellEditor = new DefaultCellEditor( textField );
305 }
306
307 public PropertyDescriptor(String caption, String name, Object[] options)
308 {
309 this.caption = caption;
310 this.name = name;
311 this.options = options;
312 editable = true;
313
314 JComboBox comboBox = new JComboBox( options );
315
316 if( options.length > 0 && options[0] == null )
317 {
318 comboBox.setEditable( true );
319 comboBox.removeItemAt( 0 );
320 }
321
322 comboBox.setBorder(null);
323 cellEditor = new DefaultCellEditor( comboBox );
324 }
325
326 public void setFormatter( PropertyFormatter formatter )
327 {
328 this.formatter = formatter;
329 }
330
331 public PropertyFormatter getFormatter()
332 {
333 return formatter == null ? DefaultFormatter.getInstance() : formatter;
334 }
335
336 public String getCaption()
337 {
338 return caption;
339 }
340
341 public String getDescription()
342 {
343 return description;
344 }
345
346 public void setDescription( String description )
347 {
348 this.description = description;
349 }
350
351 public boolean isEditable()
352 {
353 return editable;
354 }
355
356 public Object[] getOptions()
357 {
358 return options;
359 }
360
361 public boolean hasOptions()
362 {
363 return options != null;
364 }
365
366 public String getName()
367 {
368 return name;
369 }
370
371 public TableCellEditor getCellEditor()
372 {
373 return cellEditor;
374 }
375 }
376
377 private static class PropertiesTableCellRenderer extends DefaultTableCellRenderer
378 {
379 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
380 {
381 Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
382 row, column);
383
384 if( component instanceof JComponent )
385 {
386 PropertyDescriptor descriptor = ((PropertiesTableModel)table.getModel()).getPropertyDescriptorAt( row );
387
388 if( StringUtils.hasContent( descriptor.getDescription() ))
389 {
390 ((JComponent)component).setToolTipText( descriptor.getDescription() );
391 }
392 else if( value != null && StringUtils.hasContent( value.toString() ))
393 {
394 ((JComponent)component).setToolTipText( value.toString() );
395 }
396 else
397 {
398 ((JComponent)component).setToolTipText( null );
399 }
400 }
401
402 return component;
403 }
404 }
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448 /***
449 * Formatter used for displaying property values
450 *
451 * @author Ole.Matzura
452 */
453
454 public interface PropertyFormatter
455 {
456 public Object format( String propertyName, Object value );
457 }
458
459 private static class DefaultFormatter implements PropertyFormatter
460 {
461 private static PropertyFormatter instance;
462
463 public static PropertyFormatter getInstance()
464 {
465 if( instance == null )
466 instance = new DefaultFormatter();
467
468 return instance;
469 }
470
471 public Object format(String propertyName, Object value)
472 {
473 return value;
474 }
475 }
476
477 public PropertyDescriptor addProperty(String caption, String name, Object [] options)
478 {
479 return tableModel.addProperty( caption, name, options );
480 }
481
482 private class PTable extends JTable
483 {
484 public PTable( TableModel tableModel )
485 {
486 super( tableModel );
487
488
489
490 getActionMap().put( TransferHandler.getCopyAction().getValue(Action.NAME), new AbstractAction()
491 {
492 public void actionPerformed(ActionEvent e)
493 {
494 int row = getSelectedRow();
495 if( row == -1 )
496 return;
497
498 StringSelection selection = new StringSelection( getValueAt( row, 1 ).toString() );
499 Toolkit.getDefaultToolkit().getSystemClipboard().setContents( selection, selection );
500 }});
501
502 putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
503
504
505
506
507
508
509
510
511 }
512
513 public TableCellEditor getCellEditor(int row, int column)
514 {
515 if( column == 0 )
516 return super.getCellEditor(row, column);
517 else
518 return tableModel.getPropertyDescriptorAt( row ).getCellEditor();
519 }
520 }
521 }