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.impl.wsdl.panels.teststeps.support;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Component;
17  import java.awt.Point;
18  import java.awt.datatransfer.DataFlavor;
19  import java.awt.datatransfer.Transferable;
20  import java.awt.dnd.DnDConstants;
21  import java.awt.dnd.DropTarget;
22  import java.awt.dnd.DropTargetDragEvent;
23  import java.awt.dnd.DropTargetDropEvent;
24  import java.awt.dnd.DropTargetEvent;
25  import java.awt.dnd.DropTargetListener;
26  import java.awt.event.ActionEvent;
27  import java.io.File;
28  import java.io.FileInputStream;
29  import java.util.Properties;
30  
31  import javax.swing.AbstractAction;
32  import javax.swing.Action;
33  import javax.swing.JButton;
34  import javax.swing.JPanel;
35  import javax.swing.JScrollPane;
36  import javax.swing.JTable;
37  import javax.swing.ListSelectionModel;
38  import javax.swing.SwingUtilities;
39  import javax.swing.TransferHandler;
40  import javax.swing.event.ListSelectionEvent;
41  import javax.swing.event.ListSelectionListener;
42  import javax.swing.table.AbstractTableModel;
43  
44  import com.eviware.soapui.SoapUI;
45  import com.eviware.soapui.impl.wsdl.MutableTestPropertyHolder;
46  import com.eviware.soapui.model.TestPropertyHolder;
47  import com.eviware.soapui.model.propertyexpansion.PropertyExpansion;
48  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionImpl;
49  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionUtils;
50  import com.eviware.soapui.model.testsuite.TestProperty;
51  import com.eviware.soapui.model.testsuite.TestPropertyListener;
52  import com.eviware.soapui.model.tree.nodes.PropertyTreeNode.PropertyModelItem;
53  import com.eviware.soapui.support.StringUtils;
54  import com.eviware.soapui.support.UISupport;
55  import com.eviware.soapui.support.components.JXToolBar;
56  import com.eviware.soapui.support.types.StringList;
57  import com.eviware.soapui.support.xml.XmlUtils;
58  
59  public class PropertyHolderTable extends JPanel
60  {
61  	private final TestPropertyHolder holder;
62  	private PropertiesModel propertiesModel;
63  	private RemovePropertyAction removePropertyAction;
64  	private AddPropertyAction addPropertyAction;
65  	private InternalTestPropertyListener testPropertyListener;
66  	private JTable propertiesTable;
67  	private JXToolBar toolbar;
68  	private LoadPropertiesAction loadPropertiesAction;
69  
70  	public PropertyHolderTable( TestPropertyHolder holder )
71  	{
72  		super( new BorderLayout() );
73  		this.holder = holder;
74  		
75  		removePropertyAction = new RemovePropertyAction();
76  		addPropertyAction = new AddPropertyAction();
77  		loadPropertiesAction = new LoadPropertiesAction();
78  		
79  		testPropertyListener = new InternalTestPropertyListener();
80  		holder.addTestPropertyListener( testPropertyListener );
81  		
82  		JScrollPane scrollPane = new JScrollPane( buildPropertiesTable() );
83  		
84  		if( getHolder().getModelItem() != null )
85  		{
86  			DropTarget dropTarget = new DropTarget( scrollPane, new PropertyHolderTablePropertyExpansionDropTarget() );
87  			dropTarget.setDefaultActions( DnDConstants.ACTION_COPY_OR_MOVE );
88  		}
89  		
90  		add( scrollPane, BorderLayout.CENTER );
91  		add( buildToolbar(), BorderLayout.NORTH );
92  	}
93  
94  	protected JTable buildPropertiesTable()
95  	{
96  		propertiesModel = new PropertiesModel();
97  		propertiesTable = new PropertiesHolderJTable();
98  		propertiesTable.setSurrendersFocusOnKeystroke( true );
99  
100 		propertiesTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
101 		propertiesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener()
102 		{
103 			public void valueChanged(ListSelectionEvent e)
104 			{
105 				removePropertyAction.setEnabled(propertiesTable.getSelectedRow() != -1);
106 			}
107 		});
108 		
109 		propertiesTable.setDragEnabled( true );
110 		propertiesTable.setTransferHandler( new TransferHandler( "testProperty"));
111 		
112 		if( getHolder().getModelItem() != null )
113 		{
114 			DropTarget dropTarget = new DropTarget( propertiesTable, new PropertyHolderTablePropertyExpansionDropTarget() );
115 			dropTarget.setDefaultActions( DnDConstants.ACTION_COPY_OR_MOVE );
116 		}
117 		
118 		return propertiesTable;
119 	}
120 	
121 	public class PropertiesHolderJTable extends JTable
122 	{
123 		public PropertiesHolderJTable()
124 		{
125 			super( propertiesModel );
126 			setSelectionMode( ListSelectionModel.SINGLE_SELECTION  );
127 		}
128 		
129 		public PropertyModelItem getTestProperty()
130 		{
131 			int index = getSelectedRow();
132 			if( index == -1 )
133 				return null;
134 			TestProperty property = propertiesModel.getPropertyAtRow( index );
135 			return new PropertyModelItem( property, true );
136 		}
137 	}
138 	
139 	private Component buildToolbar()
140 	{
141 		toolbar = UISupport.createSmallToolbar();
142 		
143 		if( holder instanceof MutableTestPropertyHolder )
144 		{
145 			JButton addPropertyButton = UISupport.createToolbarButton(addPropertyAction);
146 			toolbar.add(addPropertyButton);
147 			JButton removePropertyButton = UISupport.createToolbarButton(removePropertyAction);
148 			toolbar.add(removePropertyButton);
149 		}
150 		
151 		JButton clearPropertiesButton = UISupport.createToolbarButton(new ClearPropertiesAction());
152 		toolbar.add(clearPropertiesButton);
153 		JButton loadPropertiesButton = UISupport.createToolbarButton(loadPropertiesAction);
154 		toolbar.add(loadPropertiesButton);
155 
156 		
157 		return toolbar;
158 	}
159 
160 	public JXToolBar getToolbar()
161 	{
162 		return toolbar;
163 	}
164 	
165 	public JTable getPropertiesTable()
166 	{
167 		return propertiesTable;
168 	}
169 
170 	public void release()
171 	{
172 		if( propertiesTable.isEditing() )
173 			propertiesTable.getCellEditor().stopCellEditing();
174 		
175 		holder.removeTestPropertyListener( testPropertyListener );
176 	}
177 	
178 	public void setEnabled( boolean enabled )
179 	{
180 		addPropertyAction.setEnabled( enabled );
181 		removePropertyAction.setEnabled( enabled );
182 		propertiesTable.setEnabled( enabled );
183 		loadPropertiesAction.setEnabled( enabled );
184 		
185 		super.setEnabled( enabled );
186 	}
187 	
188 	private final class InternalTestPropertyListener implements TestPropertyListener
189 	{
190 		private boolean enabled = true;
191 
192 		public boolean isEnabled()
193 		{
194 			return enabled;
195 		}
196 
197 		public void setEnabled(boolean enabled)
198 		{
199 			this.enabled = enabled;
200 		}
201 
202 		public void propertyAdded(String name)
203 		{
204 			if (enabled)
205 				propertiesModel.fireTableDataChanged();
206 		}
207 
208 		public void propertyRemoved(String name)
209 		{
210 			if (enabled)
211 				propertiesModel.fireTableDataChanged();
212 		}
213 
214 		public void propertyRenamed(String oldName, String newName)
215 		{
216 			if (enabled)
217 				propertiesModel.fireTableDataChanged();
218 		}
219 
220 		public void propertyValueChanged(String name, String oldValue, String newValue)
221 		{
222 			if (enabled)
223 				propertiesModel.fireTableDataChanged();
224 		}
225 	}
226 
227 	private class PropertiesModel extends AbstractTableModel
228 	{
229 		private StringList names = new StringList();
230 
231 		public PropertiesModel()
232 		{
233 			names = new StringList( holder.getPropertyNames() );
234 		}
235 
236 		public int getRowCount()
237 		{
238 			return names.size();
239 		}
240 
241 		public int getColumnCount()
242 		{
243 			return 2;
244 		}
245 		
246 		@Override
247 		public void fireTableDataChanged()
248 		{
249 			names = new StringList( holder.getPropertyNames() );
250 			super.fireTableDataChanged();
251 		}
252 
253 		public String getColumnName(int columnIndex)
254 		{
255 			switch (columnIndex)
256 			{
257 			case 0:
258 				return "Name";
259 			case 1:
260 				return "Value";
261 			}
262 
263 			return null;
264 		}
265 
266 		public boolean isCellEditable(int rowIndex, int columnIndex)
267 		{
268 			if( columnIndex == 0 )
269 			{
270 				return holder instanceof MutableTestPropertyHolder;
271 			}
272 			
273 			return !holder.getProperty( names.get( rowIndex ) ).isReadOnly();
274 		}
275 
276 		public void setValueAt(Object aValue, int rowIndex, int columnIndex)
277 		{
278 			TestProperty property = holder.getProperty(names.get( rowIndex ));
279 			switch (columnIndex)
280 			{
281 			case 0:
282 			{
283 				if( holder instanceof MutableTestPropertyHolder)
284 				{
285 					TestProperty prop = holder.getProperty( aValue.toString() );
286 					if (prop != null && prop != property)
287 					{
288 						UISupport.showErrorMessage("Property name exists!");
289 						return;
290 					}
291 					((MutableTestPropertyHolder)holder).renameProperty( property.getName(), aValue.toString() );
292 				}
293 				break;
294 			}
295 			case 1:
296 			{
297 				property.setValue(aValue.toString());
298 				break;
299 			}
300 			}
301 		}
302 		
303 		public TestProperty getPropertyAtRow( int rowIndex )
304 		{
305 			return holder.getProperty(names.get( rowIndex ));
306 		}
307 
308 		public Object getValueAt(int rowIndex, int columnIndex)
309 		{
310 			TestProperty property = holder.getProperty(names.get( rowIndex ));
311 
312 			switch (columnIndex)
313 			{
314 			case 0:
315 				return property.getName();
316 			case 1:
317 				return property.getValue();
318 			}
319 
320 			return null;
321 		}
322 	}
323 
324 	private class AddPropertyAction extends AbstractAction
325 	{
326 		public AddPropertyAction()
327 		{
328 			putValue(Action.SMALL_ICON, UISupport.createImageIcon("/add_property.gif"));
329 			putValue(Action.SHORT_DESCRIPTION, "Adds a property to the property list");
330 		}
331 
332 		public void actionPerformed(ActionEvent e)
333 		{
334 			String name = UISupport.prompt("Specify unique property name", "Add Property", "");
335 			if ( StringUtils.hasContent( name ))
336 			{
337 				if( holder.hasProperty( name ))
338 				{
339 					UISupport.showErrorMessage( "Property name [" + name + "] already exists.." );
340 					return;
341 				}
342 				
343 				((MutableTestPropertyHolder)holder).addProperty(name);
344 				final int row = holder.getPropertyNames().length - 1;
345 				propertiesModel.fireTableRowsInserted(row, row);
346 				SwingUtilities.invokeLater( new Runnable()
347 				{
348 					public void run()
349 					{
350 						requestFocusInWindow();
351 						scrollRectToVisible( propertiesTable.getCellRect( row,1,true ) );
352 						SwingUtilities.invokeLater( new Runnable()
353 						{
354 							public void run()
355 							{
356 								propertiesTable.editCellAt(row, 1);
357 								propertiesTable.getEditorComponent().requestFocusInWindow();
358 							}
359 						} );
360 					}
361 				} );
362 				
363 			}
364 		}
365 	}
366 
367 	private class RemovePropertyAction extends AbstractAction
368 	{
369 		public RemovePropertyAction()
370 		{
371 			putValue(Action.SMALL_ICON, UISupport.createImageIcon("/remove_property.gif"));
372 			putValue(Action.SHORT_DESCRIPTION, "Removes the selected property from the property list");
373 			setEnabled(false);
374 		}
375 
376 		public void actionPerformed(ActionEvent e)
377 		{
378 			int row = propertiesTable.getSelectedRow();
379 			if (row == -1)
380 				return;
381 
382 			UISupport.stopCellEditing(propertiesTable);
383 
384 			String propertyName = propertiesModel.getValueAt(row, 0).toString();
385 			if (UISupport.confirm("Remove property [" + propertyName + "]?", "Remove Property"))
386 			{
387 				((MutableTestPropertyHolder)holder).removeProperty( propertyName );
388 				propertiesModel.fireTableRowsDeleted(row, row);
389 			}
390 		}
391 	}
392 	
393 	private class ClearPropertiesAction extends AbstractAction
394 	{
395 		public ClearPropertiesAction()
396 		{
397 			putValue(Action.SMALL_ICON, UISupport.createImageIcon("/clear_properties.gif"));
398 			putValue(Action.SHORT_DESCRIPTION, "Clears all current property values");
399 		}
400 
401 		public void actionPerformed(ActionEvent e)
402 		{
403 			if( UISupport.confirm("Clear all property values?", "Clear Properties"))
404 			{
405 				for( String name : holder.getPropertyNames() )
406 				{
407 					holder.getProperty( name ).setValue( null );
408 				}
409 			}
410 		}
411 	}
412 	
413 	private class LoadPropertiesAction extends AbstractAction
414 	{
415 		public LoadPropertiesAction()
416 		{
417 			putValue(Action.SMALL_ICON, UISupport.createImageIcon("/load_properties.gif"));
418 			putValue(Action.SHORT_DESCRIPTION, "Loads property values from an external file");
419 		}
420 
421 		public void actionPerformed(ActionEvent e)
422 		{
423 			File file = UISupport.getFileDialogs().open(this, "Set properties source", null, null, null);
424 			if (file != null)
425 			{
426 				try
427 				{
428 					boolean createMissing =
429 						holder instanceof MutableTestPropertyHolder &&
430 						UISupport.confirm("Create missing properties?", "Set Properties Source");
431 					
432 					int cnt = 0;
433 					
434 					Properties props = new Properties();
435 					props.load( new FileInputStream( file ) );
436 					for( Object obj : props.keySet() )
437 					{
438 						String name = obj.toString();
439 						if( holder.hasProperty( name ))
440 						{
441 							holder.setPropertyValue( name, props.getProperty( name ) );
442 						}
443 						else if( createMissing )
444 						{
445 							((MutableTestPropertyHolder)holder).addProperty( name ).setValue( props.getProperty( name ) );
446 						}
447 					}
448 					
449 					UISupport.showInfoMessage("Loaded " + cnt + " properties from [" + file.getAbsolutePath() + "]");
450 				}
451 				catch (Exception e1)
452 				{
453 					UISupport.showErrorMessage("Failed to load properties from [" + file.getAbsolutePath() + "]; " + e1);
454 				}
455 			}
456 		}
457 	}
458 
459 	public TestPropertyHolder getHolder()
460 	{
461 		return holder;
462 	}
463 
464 	public PropertiesModel getPropertiesModel()
465 	{
466 		return propertiesModel;
467 	}
468 	
469 	public final class PropertyHolderTablePropertyExpansionDropTarget implements DropTargetListener
470 	{
471 		public PropertyHolderTablePropertyExpansionDropTarget()
472 		{
473 		}
474 		
475 		public void dragEnter( DropTargetDragEvent dtde )
476 		{
477 			if( !isAcceptable( dtde.getTransferable(), dtde.getLocation() ) )
478 				dtde.rejectDrag();
479 		}
480 
481 		public void dragExit( DropTargetEvent dtde )
482 		{
483 		}
484 
485 		public void dragOver( DropTargetDragEvent dtde )
486 		{
487 			if( !isAcceptable( dtde.getTransferable(), dtde.getLocation() ) )
488 			{
489 				dtde.rejectDrag();
490 			}
491 			else
492 			{
493 				dtde.acceptDrag( dtde.getDropAction() );
494 			}
495 		}
496 
497 		public void drop( DropTargetDropEvent dtde )
498 		{
499 			if( !isAcceptable( dtde.getTransferable(), dtde.getLocation() ) )
500 			{
501 				dtde.rejectDrop();
502 			}
503 			else
504 			{
505 				try
506 				{
507 					Transferable transferable = dtde.getTransferable();
508 					Object transferData = transferable.getTransferData( transferable.getTransferDataFlavors()[0] );
509 					if( transferData instanceof PropertyModelItem )
510 					{
511 						dtde.acceptDrop( dtde.getDropAction() );
512 						PropertyModelItem modelItem =  ( PropertyModelItem ) transferData;
513 						
514 						String xpath = modelItem.getXPath();
515 						if( xpath == null && XmlUtils.seemsToBeXml( modelItem.getProperty().getValue()  ))
516 						{
517 							xpath = UISupport.selectXPath( "Create PropertyExpansion", "Select XPath below", 
518 										modelItem.getProperty().getValue(), null );
519 							
520 							if( xpath != null )
521 								xpath = XmlUtils.removeXPathNamespaceDeclarations( xpath );
522 						}
523 
524 						PropertyExpansion propertyExpansion = new PropertyExpansionImpl( modelItem.getProperty(), xpath );
525 						
526 						Point point = dtde.getLocation();
527 						int column = getPropertiesTable().columnAtPoint( point );
528 						int row = getPropertiesTable().rowAtPoint( point );
529 						
530 						if( row == -1 )
531 						{
532 							if( holder instanceof MutableTestPropertyHolder )
533 							{
534 								MutableTestPropertyHolder mtph = ( MutableTestPropertyHolder ) holder;
535 								String name = UISupport.prompt( "Specify unique name of property", "Add Property", modelItem.getProperty().getName() );
536 								while( name != null && mtph.hasProperty( name ) )
537 								{
538 									name = UISupport.prompt( "Specify unique name of property", "Add Property", modelItem.getProperty().getName() );
539 								}
540 								
541 								if( name != null )
542 									mtph.addProperty( name ).setValue( propertyExpansion.toString() );
543 							}
544 						}
545 						else
546 						{
547 							getPropertiesTable().setValueAt( propertyExpansion.toString(), row, column );
548 						}
549 
550 						dtde.dropComplete( true );
551 					}
552 				}
553 				catch( Exception e )
554 				{
555 					SoapUI.logError( e );
556 				}
557 			}
558 		}
559 
560 		public void dropActionChanged( DropTargetDragEvent dtde )
561 		{
562 		}
563 		
564 		public boolean isAcceptable( Transferable transferable, Point point )
565 		{
566 			int row = getPropertiesTable().rowAtPoint( point );
567 			if( row >= 0 )
568 			{
569 				int column = getPropertiesTable().columnAtPoint( point );
570 				if( column != 1 )
571 					return false;
572 
573 				if( !getPropertiesTable().isCellEditable( row, column ))
574 					return false;
575 			}
576 			else if( !(getHolder() instanceof MutableTestPropertyHolder ))
577 			{
578 				return false;
579 			}
580 			
581 			DataFlavor[] flavors = transferable.getTransferDataFlavors();
582 			for( int i = 0; i < flavors.length; i++ )
583 			{
584 				DataFlavor flavor = flavors[i];
585 				if( flavor.isMimeTypeEqual( DataFlavor.javaJVMLocalObjectMimeType ) )
586 				{
587 					try
588 					{
589 						Object modelItem = transferable.getTransferData( flavor );
590 						if( modelItem instanceof PropertyModelItem && 
591 									((PropertyModelItem)modelItem).getProperty().getModelItem() != getHolder().getModelItem() )
592 						{
593 							return PropertyExpansionUtils.canExpandProperty( getHolder().getModelItem(), 
594 										((PropertyModelItem)modelItem).getProperty() );
595 						}
596 					}
597 					catch( Exception ex )
598 					{
599 						SoapUI.logError( ex );
600 					}
601 				}
602 			}
603 			
604 			return false;
605 		}
606 	}
607 }