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