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[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 }