View Javadoc

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