View Javadoc

    1   /*
    2    *  soapUI, copyright (C) 2004-2007 eviware.com
    3    *
    4    *  soapUI is free software; you can redistribute it and/or modify it under the
    5    *  terms of version 2.1 of the GNU Lesser General Public License as published by
    6    *  the Free Software Foundation.
    7    *
    8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
    9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    10   *  See the GNU Lesser General Public License for more details at gnu.org.
    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
    51  /***
    52   * Table for displaying property name/value pairs
    53   *
    54   * @author Ole.Matzura
    55   */
    56
    57  public class JPropertiesTable<T> extends JPanel
    58  {
    59  	public final static Object[] BOOLEAN_OPTIONS = new Object[] {Boolean.TRUE, Boolean.FALSE };
    60
    61  	private PropertiesTableModel<T> tableModel;
    62  	private JTable table;
    63
    64  	public JPropertiesTable( String title )
    65  	{
    66  		this( title, null );
    67  	}
    68
    69  	public JPropertiesTable( String title, T propertyObject )
    70  	{
    71  		super( new BorderLayout());
    72
    73  		tableModel = new PropertiesTableModel<T>( propertyObject );
    74  		table = new PTable( tableModel );
    75
    76  		table.getColumnModel().getColumn(0).setHeaderValue( "Property" );
    77        table.getColumnModel().getColumn(1).setHeaderValue( "Value" );
    78        table.getColumnModel().getColumn(0).setCellRenderer( new PropertiesTableCellRenderer() );
    79        table.getColumnModel().getColumn(1).setCellRenderer( new PropertiesTableCellRenderer() );
    80
    81        add( new JScrollPane( table ), BorderLayout.CENTER );
    82        if( title != null )
    83        {
    84  	      TitledBorder titledBorder = BorderFactory.createTitledBorder( BorderFactory.createEmptyBorder(), title );
    85  	      titledBorder.setTitleFont( titledBorder.getTitleFont().deriveFont( Font.PLAIN, 11 ));
    86  			setBorder( titledBorder);
    87        }
    88
    89  		table.setBackground( Color.WHITE );
    90  		setPreferredSize( table.getPreferredSize());
    91  	}
    92
    93  	@Override
    94  	public void removeNotify()
    95  	{
    96  		getTableModel().release();
    97  		super.removeNotify();
    98  	}
    99
    100 	@Override
    101 	public void addNotify()
    102 	{
    103 		getTableModel().attach();
    104 		super.addNotify();
    105 	}
    106
    107 	public void setPropertyObject( T propertyObject )
    108 	{
    109 		if( table.isEditing() )
    110 			table.getCellEditor().stopCellEditing();
    111
    112 		tableModel.setPropertyObject( propertyObject );
    113 	}
    114
    115 	public PropertiesTableModel getTableModel()
    116 	{
    117 		return tableModel;
    118 	}
    119
    120 	public PropertyDescriptor addProperty( String caption, String name)
    121 	{
    122 		return addProperty( caption, name, false  );
    123 	}
    124
    125 	public PropertyDescriptor addProperty( String caption, String name, boolean editable )
    126 	{
    127 		return addProperty( caption, name, editable, null );
    128 	}
    129
    130 	public PropertyDescriptor addProperty( String caption, String name, boolean editable, PropertyFormatter formatter )
    131 	{
    132 		return tableModel.addProperty( caption, name, editable, formatter  );
    133 	}
    134
    135 	public static final class PropertiesTableModel<T> extends AbstractTableModel implements PropertyChangeListener
    136    {
    137 		private List<PropertyDescriptor> properties = new ArrayList<PropertyDescriptor>();
    138 		private T propertyObject;
    139 		private boolean attached;
    140
    141 		public PropertiesTableModel(T propertyObject)
    142 		{
    143 			this.propertyObject = propertyObject;
    144 		}
    145
    146 		public void attach()
    147 		{
    148 			if( !attached && propertyObject instanceof PropertyChangeNotifier )
    149 			{
    150 				((PropertyChangeNotifier)propertyObject).addPropertyChangeListener( this );
    151 				attached = true;
    152 			}
    153 		}
    154
    155 		public void setPropertyObject(T propertyObject)
    156 		{
    157 			release();
    158 			this.propertyObject = propertyObject;
    159 			attach();
    160 			fireTableDataChanged();
    161 		}
    162
    163 		public PropertyDescriptor addProperty( String caption, String name, boolean editable, PropertyFormatter formatter )
    164 		{
    165 			PropertyDescriptor propertyDescriptor = new PropertyDescriptor( caption, name, editable, formatter );
    166 			properties.add( propertyDescriptor);
    167 			return propertyDescriptor;
    168 		}
    169
    170 		public PropertyDescriptor addProperty( String caption, String name, Object [] options )
    171 		{
    172 			PropertyDescriptor propertyDescriptor = new PropertyDescriptor( caption, name, options );
    173 			properties.add( propertyDescriptor);
    174 			return propertyDescriptor;
    175 		}
    176
    177 		public int getRowCount()
    178 		{
    179 			return properties.size();
    180 		}
    181
    182 		public int getColumnCount()
    183 		{
    184 			return 2;
    185 		}
    186
    187 		public boolean isCellEditable(int rowIndex, int columnIndex)
    188 		{
    189 			if( columnIndex == 0 || propertyObject == null ) return false;
    190 			return properties.get( rowIndex ).isEditable() &&
    191 				PropertyUtils.isWriteable( propertyObject, properties.get( rowIndex ).getName() );
    192 		}
    193
    194 		public void setValueAt(Object aValue, int rowIndex, int columnIndex)
    195 		{
    196 			try
    197 			{
    198 				if( propertyObject != null && columnIndex == 1 && properties.get( rowIndex ).isEditable() )
    199 				{
    200 					BeanUtils.setProperty( propertyObject, properties.get( rowIndex ).getName(), aValue );
    201 				}
    202 			}
    203 			catch (IllegalAccessException e)
    204 			{
    205 				SoapUI.logError( e );
    206 			}
    207 			catch (InvocationTargetException e)
    208 			{
    209 				SoapUI.logError( e );
    210 			}
    211 		}
    212
    213 		public Object getValueAt(int rowIndex, int columnIndex)
    214 		{
    215 			if( propertyObject == null )
    216 				return null;
    217
    218 			try
    219 			{
    220 				PropertyDescriptor propertyDescriptor = properties.get(rowIndex);
    221 				switch (columnIndex)
    222 				{
    223 					case 0:
    224 						return propertyDescriptor.getCaption();
    225 					case 1:
    226 					{
    227 						Object value = PropertyUtils.getSimpleProperty(propertyObject,propertyDescriptor.getName());
    228 						return propertyDescriptor.getFormatter().format( propertyDescriptor.getName(), value );
    229 					}
    230 				}
    231 			}
    232 			catch (IllegalAccessException e)
    233 			{
    234 				SoapUI.logError( e );
    235 			}
    236 			catch (InvocationTargetException e)
    237 			{
    238 				SoapUI.logError( e );
    239 			}
    240 			catch (NoSuchMethodException e)
    241 			{
    242 				SoapUI.logError( e );
    243 			}
    244
    245 			return null;
    246 		}
    247
    248 		public PropertyDescriptor getPropertyDescriptorAt( int row )
    249 		{
    250 			return properties.get( row );
    251 		}
    252
    253 		public void propertyChange(PropertyChangeEvent evt)
    254 		{
    255 			fireTableDataChanged();
    256 		}
    257
    258 		public void release()
    259 		{
    260 			if( propertyObject instanceof PropertyChangeNotifier && attached )
    261 			{
    262 				((PropertyChangeNotifier)propertyObject).removePropertyChangeListener( this );
    263 				attached = false;
    264 			}
    265 		}}
    266
    267 	public static class PropertyDescriptor
    268 	{
    269 		private final String caption;
    270 		private final String name;
    271 		private boolean editable;
    272 		private PropertyFormatter formatter ;
    273 		private Object [] options;
    274 		private DefaultCellEditor cellEditor;
    275
    276 		public PropertyDescriptor(String caption, String name, boolean editable, PropertyFormatter formatter)
    277 		{
    278 			this.caption = caption;
    279 			this.name = name;
    280 			this.editable = editable;
    281 			this.formatter = formatter;
    282
    283 			JTextField textField = new JTextField();
    284 			textField.setBorder(BorderFactory.createEmptyBorder());
    285 			cellEditor = new DefaultCellEditor( textField );
    286 		}
    287
    288 		public PropertyDescriptor(String caption, String name, Object[] options)
    289 		{
    290 			this.caption = caption;
    291 			this.name = name;
    292 			this.options = options;
    293 			editable = true;
    294
    295 			JComboBox comboBox = new JComboBox( options );
    296
    297 			if( options.length > 0 && options[0] == null )
    298 			{
    299 				comboBox.setEditable( true );
    300 				comboBox.removeItemAt( 0 );
    301 			}
    302
    303 			comboBox.setBorder(null);
    304 			cellEditor = new DefaultCellEditor( comboBox );
    305 		}
    306
    307 		public void setFormatter( PropertyFormatter formatter )
    308 		{
    309 			this.formatter = formatter;
    310 		}
    311
    312 		public PropertyFormatter getFormatter()
    313 		{
    314 			return formatter == null ? DefaultFormatter.getInstance() : formatter;
    315 		}
    316
    317 		public String getCaption()
    318 		{
    319 			return caption;
    320 		}
    321
    322 		public boolean isEditable()
    323 		{
    324 			return editable;
    325 		}
    326
    327 		public Object[] getOptions()
    328 		{
    329 			return options;
    330 		}
    331
    332 		public boolean hasOptions()
    333 		{
    334 			return options != null;
    335 		}
    336
    337 		public String getName()
    338 		{
    339 			return name;
    340 		}
    341
    342 		public TableCellEditor getCellEditor()
    343 		{
    344 			return cellEditor;
    345 		}
    346 	}
    347
    348 	private static class PropertiesTableCellRenderer extends DefaultTableCellRenderer
    349 	{
    350 		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
    351 		{
    352 			Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
    353 					row, column);
    354
    355 			if( component instanceof JComponent )
    356 			{
    357 				if(  value != null && value.toString().trim().length() > 0 )
    358 				{
    359 					((JComponent)component).setToolTipText( value.toString() );
    360 				}
    361 				else
    362 				{
    363 					((JComponent)component).setToolTipText( null );
    364 				}
    365 			}
    366
    367 			return component;
    368 		}
    369 	}
    370 	/*
    371 	private class PropertiesTableCellEditor extends AbstractCellEditor implements TableCellEditor
    372 	{
    373 		private JTextField textField;
    374 		private JComboBox comboBox;
    375 		private JComponent current;
    376
    377 		public PropertiesTableCellEditor()
    378 		{
    379 			textField = new JTextField();
    380 			comboBox = new JComboBox();
    381 			comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
    382 		}
    383
    384 		public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
    385 		{
    386 			PropertyDescriptor descriptor = tableModel.getPropertyDescriptorAt( row );
    387
    388 			if( descriptor.hasOptions())
    389 			{
    390 				comboBox.setModel( new DefaultComboBoxModel( descriptor.getOptions() ));
    391 				comboBox.setSelectedItem( value );
    392 				current = comboBox;
    393 			}
    394 			else
    395 			{
    396 				textField.setText( value == null ? "" : value.toString() );
    397 				current = textField;
    398 			}
    399
    400 			current.setBorder( null );
    401 			current.setBackground( Color.WHITE );
    402
    403 			return current;
    404 		}
    405
    406 		public Object getCellEditorValue()
    407 		{
    408 			return current == comboBox ? comboBox.getSelectedItem() : textField.getText();
    409 		}
    410 	}
    411 	*/
    412
    413 	/***
    414 	 * Formatter used for displaying property values
    415 	 *
    416 	 * @author Ole.Matzura
    417 	 */
    418
    419 	public interface PropertyFormatter
    420 	{
    421 		public Object format( String propertyName, Object value );
    422 	}
    423
    424 	private static class DefaultFormatter implements PropertyFormatter
    425 	{
    426 		private static PropertyFormatter instance;
    427
    428 		public static PropertyFormatter getInstance()
    429 		{
    430 			if( instance == null )
    431 				instance = new DefaultFormatter();
    432
    433 			return instance;
    434 		}
    435
    436 		public Object format(String propertyName, Object value)
    437 		{
    438 			return value;
    439 		}
    440 	}
    441
    442 	public void addProperty(String caption, String name, Object [] options)
    443 	{
    444 		tableModel.addProperty( caption, name, options );
    445 	}
    446
    447 	private class PTable extends JTable
    448 	{
    449 		public PTable( TableModel tableModel )
    450 		{
    451 			super( tableModel );
    452
    453 		//	setAutoStartEditOnKeyStroke( true );
    454
    455 			getActionMap().put( TransferHandler.getCopyAction().getValue(Action.NAME), new AbstractAction()
    456 			{
    457 				public void actionPerformed(ActionEvent e)
    458 				{
    459 					int row = getSelectedRow();
    460 					if( row == -1 )
    461 						return;
    462
    463 					StringSelection selection = new StringSelection( getValueAt( row, 1 ).toString() );
    464 					Toolkit.getDefaultToolkit().getSystemClipboard().setContents( selection, selection );
    465 				}});
    466
    467 			putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
    468 		/*
    469 			addFocusListener( new FocusAdapter() {
    470
    471 				public void focusLost(FocusEvent e)
    472 				{
    473 					if( isEditing() && getCellEditor() != null )
    474 						getCellEditor().stopCellEditing();
    475 				}}  );*/
    476 		}
    477
    478 		public TableCellEditor getCellEditor(int row, int column)
    479 		{
    480 			if( column == 0 )
    481 				return super.getCellEditor(row, column);
    482 			else
    483 				return tableModel.getPropertyDescriptorAt( row ).getCellEditor();
    484 		}
    485 	}
    486 }