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