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