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