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 ) return false;
209 return properties.get( rowIndex ).isEditable() &&
210 PropertyUtils.isWriteable( propertyObject, properties.get( rowIndex ).getName() );
211 }
212
213 public void setValueAt(Object aValue, int rowIndex, int columnIndex)
214 {
215 try
216 {
217 if( propertyObject != null && columnIndex == 1 && properties.get( rowIndex ).isEditable() )
218 {
219 BeanUtils.setProperty( propertyObject, properties.get( rowIndex ).getName(), aValue );
220 }
221 }
222 catch (IllegalAccessException e)
223 {
224 SoapUI.logError( e );
225 }
226 catch (InvocationTargetException e)
227 {
228 SoapUI.logError( e );
229 }
230 }
231
232 public Object getValueAt(int rowIndex, int columnIndex)
233 {
234 if( propertyObject == null )
235 return null;
236
237 try
238 {
239 PropertyDescriptor propertyDescriptor = properties.get(rowIndex);
240 switch (columnIndex)
241 {
242 case 0:
243 return propertyDescriptor.getCaption();
244 case 1:
245 {
246 Object value = PropertyUtils.getSimpleProperty(propertyObject,propertyDescriptor.getName());
247 return propertyDescriptor.getFormatter().format( propertyDescriptor.getName(), value );
248 }
249 }
250 }
251 catch (IllegalAccessException e)
252 {
253 SoapUI.logError( e );
254 }
255 catch (InvocationTargetException e)
256 {
257 SoapUI.logError( e );
258 }
259 catch (NoSuchMethodException e)
260 {
261 SoapUI.logError( e );
262 }
263
264 return null;
265 }
266
267 public PropertyDescriptor getPropertyDescriptorAt( int row )
268 {
269 return properties.get( row );
270 }
271
272 public void propertyChange(PropertyChangeEvent evt)
273 {
274 fireTableDataChanged();
275 }
276
277 public void release()
278 {
279 if( propertyObject instanceof PropertyChangeNotifier && attached )
280 {
281 ((PropertyChangeNotifier)propertyObject).removePropertyChangeListener( this );
282 attached = false;
283 }
284 }
285
286 public PropertyDescriptor addPropertyShadow(String caption, String name, boolean editable) {
287 PropertyDescriptor propertyDescriptor = new PropertyDescriptor( caption, name, editable );
288 properties.add( propertyDescriptor);
289 return propertyDescriptor;
290 }}
291
292 public static class PropertyDescriptor
293 {
294 private final String caption;
295 private final String name;
296 private boolean editable;
297 private PropertyFormatter formatter ;
298 private Object [] options;
299 private DefaultCellEditor cellEditor;
300 private String description;
301
302 public PropertyDescriptor(String caption, String name, boolean editable, PropertyFormatter formatter)
303 {
304 this.caption = caption;
305 this.name = name;
306 this.editable = editable;
307 this.formatter = formatter;
308
309 JTextField textField = new JTextField();
310 textField.setBorder(BorderFactory.createEmptyBorder());
311 cellEditor = new DefaultCellEditor( textField );
312 }
313
314 public PropertyDescriptor(String caption, String name, Object[] options)
315 {
316 this.caption = caption;
317 this.name = name;
318 this.options = options;
319 editable = true;
320
321 JComboBox comboBox = new JComboBox( options );
322
323 if( options.length > 0 && options[0] == null )
324 {
325 comboBox.setEditable( true );
326 comboBox.removeItemAt( 0 );
327 }
328
329 comboBox.setBorder(null);
330 cellEditor = new DefaultCellEditor( comboBox );
331 }
332
333
334 /***
335 * For password field in table.
336 *
337 * @author robert nemet
338 * @param caption
339 * @param name
340 * @param editable
341 */
342 public PropertyDescriptor(String caption, String name, boolean editable ) {
343
344 this.caption = caption;
345 this.name = name;
346 this.editable = editable;
347
348 JPasswordField textField = new JPasswordField();
349 textField.setBorder(BorderFactory.createEmptyBorder());
350 cellEditor = new DefaultCellEditor( textField );
351 }
352
353 public void setFormatter( PropertyFormatter formatter )
354 {
355 this.formatter = formatter;
356 }
357
358 public PropertyFormatter getFormatter()
359 {
360 return formatter == null ? DefaultFormatter.getInstance() : formatter;
361 }
362
363 public String getCaption()
364 {
365 return caption;
366 }
367
368 public String getDescription()
369 {
370 return description;
371 }
372
373 public void setDescription( String description )
374 {
375 this.description = description;
376 }
377
378 public boolean isEditable()
379 {
380 return editable;
381 }
382
383 public Object[] getOptions()
384 {
385 return options;
386 }
387
388 public boolean hasOptions()
389 {
390 return options != null;
391 }
392
393 public String getName()
394 {
395 return name;
396 }
397
398 public TableCellEditor getCellEditor()
399 {
400 return cellEditor;
401 }
402 }
403
404 private static class PropertiesTableCellRenderer extends DefaultTableCellRenderer
405 {
406 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
407 {
408 Component component;
409 DefaultCellEditor cellEditor = (DefaultCellEditor) table.getCellEditor(row, column);
410 if( cellEditor.getComponent() instanceof JPasswordField && value instanceof String) {
411 if ( value != null && ((String)value).length() > 0 ) {
412 component = super.getTableCellRendererComponent(table, "**************", isSelected, hasFocus,
413 row, column);
414 } else {
415 component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
416 }
417 } else {
418 component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
419 row, column);
420 }
421 if( component instanceof JComponent )
422 {
423 PropertyDescriptor descriptor = ((PropertiesTableModel<?>)table.getModel()).getPropertyDescriptorAt( row );
424
425 if( StringUtils.hasContent( descriptor.getDescription() ))
426 {
427 ((JComponent)component).setToolTipText( descriptor.getDescription() );
428 }
429
430 else if( value != null && StringUtils.hasContent( value.toString() ) && !(cellEditor.getComponent() instanceof JPasswordField))
431 {
432 ((JComponent)component).setToolTipText( value.toString() );
433 }
434 else
435 {
436 ((JComponent)component).setToolTipText( null );
437 }
438 }
439
440 return component;
441 }
442 }
443
444
445
446
447
448
449
450
451
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
484
485
486 /***
487 * Formatter used for displaying property values
488 *
489 * @author Ole.Matzura
490 */
491
492 public interface PropertyFormatter
493 {
494 public Object format( String propertyName, Object value );
495 }
496
497 private static class DefaultFormatter implements PropertyFormatter
498 {
499 private static PropertyFormatter instance;
500
501 public static PropertyFormatter getInstance()
502 {
503 if( instance == null )
504 instance = new DefaultFormatter();
505
506 return instance;
507 }
508
509 public Object format(String propertyName, Object value)
510 {
511 return value;
512 }
513 }
514
515 public PropertyDescriptor addProperty(String caption, String name, Object [] options)
516 {
517 return tableModel.addProperty( caption, name, options );
518 }
519
520 private class PTable extends JTable
521 {
522 public PTable( TableModel tableModel )
523 {
524 super( tableModel );
525
526
527
528 getActionMap().put( TransferHandler.getCopyAction().getValue(Action.NAME), new AbstractAction()
529 {
530 public void actionPerformed(ActionEvent e)
531 {
532 int row = getSelectedRow();
533 if( row == -1 )
534 return;
535
536 StringSelection selection = new StringSelection( getValueAt( row, 1 ).toString() );
537 Toolkit.getDefaultToolkit().getSystemClipboard().setContents( selection, selection );
538 }});
539
540 putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
541
542
543
544
545
546
547
548
549 }
550
551 public TableCellEditor getCellEditor(int row, int column)
552 {
553 if( column == 0 )
554 return super.getCellEditor(row, column);
555 else
556 return tableModel.getPropertyDescriptorAt( row ).getCellEditor();
557 }
558 }
559
560 /***
561 * Value in this field will not be showen. It will be masked...
562 *
563 * @author robert nemet
564 * @param caption
565 * @param name
566 * @param editable
567 * @return
568 */
569 public PropertyDescriptor addPropertyShadow( String caption, String name, boolean editable )
570 {
571 return tableModel.addPropertyShadow( caption, name, editable );
572 }
573 }