View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2009 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.BufferedReader;
28  import java.io.File;
29  import java.io.FileReader;
30  import java.io.IOException;
31  import java.util.Arrays;
32  import java.util.HashSet;
33  import java.util.Set;
34  
35  import javax.swing.AbstractAction;
36  import javax.swing.Action;
37  import javax.swing.JButton;
38  import javax.swing.JPanel;
39  import javax.swing.JScrollPane;
40  import javax.swing.JTable;
41  import javax.swing.ListSelectionModel;
42  import javax.swing.SwingUtilities;
43  import javax.swing.TransferHandler;
44  import javax.swing.event.ListSelectionEvent;
45  import javax.swing.event.ListSelectionListener;
46  import javax.swing.table.AbstractTableModel;
47  
48  import com.eviware.soapui.SoapUI;
49  import com.eviware.soapui.impl.wsdl.MutableTestPropertyHolder;
50  import com.eviware.soapui.model.TestPropertyHolder;
51  import com.eviware.soapui.model.propertyexpansion.PropertyExpansion;
52  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionImpl;
53  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionUtils;
54  import com.eviware.soapui.model.support.TestPropertyUtils;
55  import com.eviware.soapui.model.testsuite.TestProperty;
56  import com.eviware.soapui.model.testsuite.TestPropertyListener;
57  import com.eviware.soapui.model.tree.nodes.PropertyTreeNode.PropertyModelItem;
58  import com.eviware.soapui.support.StringUtils;
59  import com.eviware.soapui.support.UISupport;
60  import com.eviware.soapui.support.components.JXToolBar;
61  import com.eviware.soapui.support.types.StringList;
62  import com.eviware.soapui.support.xml.XmlUtils;
63  import com.eviware.x.form.XFormDialog;
64  import com.eviware.x.form.support.ADialogBuilder;
65  import com.eviware.x.form.support.AField;
66  import com.eviware.x.form.support.AForm;
67  import com.eviware.x.form.support.AField.AFieldType;
68  
69  public class PropertyHolderTable extends JPanel
70  {
71  	private final TestPropertyHolder holder;
72  	private PropertiesModel propertiesModel;
73  	private RemovePropertyAction removePropertyAction;
74  	private AddPropertyAction addPropertyAction;
75  	private InternalTestPropertyListener testPropertyListener;
76  	private JTable propertiesTable;
77  	private JXToolBar toolbar;
78  	private LoadPropertiesAction loadPropertiesAction;
79  	private MovePropertyUpAction movePropertyUpAction;
80  	private MovePropertyDownAction movePropertyDownAction;
81  
82  	public PropertyHolderTable( TestPropertyHolder holder )
83  	{
84  		super( new BorderLayout() );
85  		this.holder = holder;
86  
87  		loadPropertiesAction = new LoadPropertiesAction();
88  		testPropertyListener = new InternalTestPropertyListener();
89  		holder.addTestPropertyListener( testPropertyListener );
90  
91  		JScrollPane scrollPane = new JScrollPane( buildPropertiesTable() );
92  
93  		if( getHolder().getModelItem() != null )
94  		{
95  			DropTarget dropTarget = new DropTarget( scrollPane, new PropertyHolderTablePropertyExpansionDropTarget() );
96  			dropTarget.setDefaultActions( DnDConstants.ACTION_COPY_OR_MOVE );
97  		}
98  
99  		add( scrollPane, BorderLayout.CENTER );
100 		add( buildToolbar(), BorderLayout.NORTH );
101 	}
102 
103 	protected JTable buildPropertiesTable()
104 	{
105 		propertiesModel = new PropertiesModel();
106 		propertiesTable = new PropertiesHolderJTable();
107 		propertiesTable.setSurrendersFocusOnKeystroke( true );
108 
109 		propertiesTable.putClientProperty( "terminateEditOnFocusLost", Boolean.TRUE );
110 		propertiesTable.getSelectionModel().addListSelectionListener( new ListSelectionListener()
111 		{
112 			public void valueChanged( ListSelectionEvent e )
113 			{
114 				int selectedRow = propertiesTable.getSelectedRow();
115 				if( removePropertyAction != null )
116 					removePropertyAction.setEnabled( selectedRow != -1 );
117 
118 				if( movePropertyUpAction != null )
119 					movePropertyUpAction.setEnabled( selectedRow > 0 );
120 
121 				if( movePropertyDownAction != null )
122 					movePropertyDownAction.setEnabled( selectedRow >= 0 && selectedRow < propertiesTable.getRowCount() - 1 );
123 			}
124 		} );
125 
126 		propertiesTable.setDragEnabled( true );
127 		propertiesTable.setTransferHandler( new TransferHandler( "testProperty" ) );
128 
129 		if( getHolder().getModelItem() != null )
130 		{
131 			DropTarget dropTarget = new DropTarget( propertiesTable, new PropertyHolderTablePropertyExpansionDropTarget() );
132 			dropTarget.setDefaultActions( DnDConstants.ACTION_COPY_OR_MOVE );
133 		}
134 
135 		return propertiesTable;
136 	}
137 
138 	public class PropertiesHolderJTable extends JTable
139 	{
140 		public PropertiesHolderJTable()
141 		{
142 			super( propertiesModel );
143 			setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
144 			// setAutoStartEditOnKeyStroke( true );
145 			setSurrendersFocusOnKeystroke( true );
146 			setRowHeight( 19 );
147 			// setHorizontalScrollEnabled(true);
148 		}
149 
150 		public PropertyModelItem getTestProperty()
151 		{
152 			int index = getSelectedRow();
153 			if( index == -1 )
154 				return null;
155 			TestProperty property = propertiesModel.getPropertyAtRow( index );
156 			return new PropertyModelItem( property, true );
157 		}
158 	}
159 
160 	private Component buildToolbar()
161 	{
162 		toolbar = UISupport.createSmallToolbar();
163 
164 		if( holder instanceof MutableTestPropertyHolder )
165 		{
166 			removePropertyAction = new RemovePropertyAction();
167 			addPropertyAction = new AddPropertyAction();
168 			movePropertyUpAction = new MovePropertyUpAction();
169 			movePropertyDownAction = new MovePropertyDownAction();
170 
171 			JButton addPropertyButton = UISupport.createToolbarButton( addPropertyAction );
172 			toolbar.add( addPropertyButton );
173 			JButton removePropertyButton = UISupport.createToolbarButton( removePropertyAction );
174 			toolbar.add( removePropertyButton );
175 
176 			toolbar.addRelatedGap();
177 			JButton movePropertyUpButton = UISupport.createToolbarButton( movePropertyUpAction );
178 			toolbar.add( movePropertyUpButton );
179 			JButton movePropertyDownButton = UISupport.createToolbarButton( movePropertyDownAction );
180 			toolbar.add( movePropertyDownButton );
181 			
182 			toolbar.addRelatedGap();
183 			toolbar.add( UISupport.createToolbarButton( new SortPropertiesAction() ));
184 			toolbar.addRelatedGap();
185 		}
186 
187 		JButton clearPropertiesButton = UISupport.createToolbarButton( new ClearPropertiesAction() );
188 		toolbar.add( clearPropertiesButton );
189 		JButton loadPropertiesButton = UISupport.createToolbarButton( loadPropertiesAction );
190 		toolbar.add( loadPropertiesButton );
191 		toolbar.add( UISupport.createToolbarButton( new SavePropertiesAction() ) );
192 
193 		return toolbar;
194 	}
195 
196 	public JXToolBar getToolbar()
197 	{
198 		return toolbar;
199 	}
200 
201 	public JTable getPropertiesTable()
202 	{
203 		return propertiesTable;
204 	}
205 
206 	public void release()
207 	{
208 		if( propertiesTable.isEditing() )
209 			propertiesTable.getCellEditor().stopCellEditing();
210 
211 		holder.removeTestPropertyListener( testPropertyListener );
212 	}
213 
214 	public void setEnabled( boolean enabled )
215 	{
216 		addPropertyAction.setEnabled( enabled );
217 		removePropertyAction.setEnabled( enabled );
218 		propertiesTable.setEnabled( enabled );
219 		loadPropertiesAction.setEnabled( enabled );
220 
221 		super.setEnabled( enabled );
222 	}
223 
224 	private final class InternalTestPropertyListener implements TestPropertyListener
225 	{
226 		private boolean enabled = true;
227 
228 		public boolean isEnabled()
229 		{
230 			return enabled;
231 		}
232 
233 		public void setEnabled( boolean enabled )
234 		{
235 			this.enabled = enabled;
236 		}
237 
238 		public void propertyAdded( String name )
239 		{
240 			if( enabled )
241 				propertiesModel.fireTableDataChanged();
242 		}
243 
244 		public void propertyRemoved( String name )
245 		{
246 			if( enabled )
247 				propertiesModel.fireTableDataChanged();
248 		}
249 
250 		public void propertyRenamed( String oldName, String newName )
251 		{
252 			if( enabled )
253 				propertiesModel.fireTableDataChanged();
254 		}
255 
256 		public void propertyValueChanged( String name, String oldValue, String newValue )
257 		{
258 			if( enabled )
259 				propertiesModel.fireTableDataChanged();
260 		}
261 
262 		public void propertyMoved( String name, int oldIndex, int newIndex )
263 		{
264 			if( enabled )
265 				propertiesModel.fireTableDataChanged();
266 		}
267 	}
268 
269 	private class PropertiesModel extends AbstractTableModel
270 	{
271 		private StringList names = new StringList();
272 
273 		public PropertiesModel()
274 		{
275 			names = new StringList( holder.getPropertyNames() );
276 		}
277 
278 		public int getRowCount()
279 		{
280 			return names.size();
281 		}
282 
283 		public int getColumnCount()
284 		{
285 			return 2;
286 		}
287 
288 		@Override
289 		public void fireTableDataChanged()
290 		{
291 			names = new StringList( holder.getPropertyNames() );
292 			super.fireTableDataChanged();
293 		}
294 
295 		public String getColumnName( int columnIndex )
296 		{
297 			switch( columnIndex )
298 			{
299 			case 0 :
300 				return "Name";
301 			case 1 :
302 				return "Value";
303 			}
304 
305 			return null;
306 		}
307 
308 		public boolean isCellEditable( int rowIndex, int columnIndex )
309 		{
310 			if( columnIndex == 0 )
311 			{
312 				return holder instanceof MutableTestPropertyHolder;
313 			}
314 
315 			return !holder.getProperty( names.get( rowIndex ) ).isReadOnly();
316 		}
317 
318 		public void setValueAt( Object aValue, int rowIndex, int columnIndex )
319 		{
320 			TestProperty property = holder.getProperty( names.get( rowIndex ) );
321 			switch( columnIndex )
322 			{
323 			case 0 :
324 			{
325 				if( holder instanceof MutableTestPropertyHolder )
326 				{
327 					TestProperty prop = holder.getProperty( aValue.toString() );
328 					if( prop != null && prop != property )
329 					{
330 						UISupport.showErrorMessage( "Property name exists!" );
331 						return;
332 					}
333 					( ( MutableTestPropertyHolder )holder ).renameProperty( property.getName(), aValue.toString() );
334 				}
335 				break;
336 			}
337 			case 1 :
338 			{
339 				property.setValue( aValue.toString() );
340 				break;
341 			}
342 			}
343 		}
344 
345 		@Override
346 		public Class<?> getColumnClass( int columnIndex )
347 		{
348 			return String.class;
349 		}
350 
351 		public TestProperty getPropertyAtRow( int rowIndex )
352 		{
353 			return holder.getProperty( names.get( rowIndex ) );
354 		}
355 
356 		public Object getValueAt( int rowIndex, int columnIndex )
357 		{
358 			TestProperty property = holder.getProperty( names.get( rowIndex ) );
359 			if( property == null )
360 				return null;
361 
362 			switch( columnIndex )
363 			{
364 			case 0 :
365 				return property.getName();
366 			case 1 :
367 				return property.getValue();
368 			}
369 
370 			return null;
371 		}
372 	}
373 
374 	private class AddPropertyAction extends AbstractAction
375 	{
376 		public AddPropertyAction()
377 		{
378 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/add_property.gif" ) );
379 			putValue( Action.SHORT_DESCRIPTION, "Adds a property to the property list" );
380 		}
381 
382 		public void actionPerformed( ActionEvent e )
383 		{
384 			String name = UISupport.prompt( "Specify unique property name", "Add Property", "" );
385 			if( StringUtils.hasContent( name ) )
386 			{
387 				if( holder.hasProperty( name ) )
388 				{
389 					UISupport.showErrorMessage( "Property name [" + name + "] already exists.." );
390 					return;
391 				}
392 
393 				( ( MutableTestPropertyHolder )holder ).addProperty( name );
394 				final int row = holder.getPropertyNames().length - 1;
395 				propertiesModel.fireTableRowsInserted( row, row );
396 				SwingUtilities.invokeLater( new Runnable()
397 				{
398 					public void run()
399 					{
400 						requestFocusInWindow();
401 						scrollRectToVisible( propertiesTable.getCellRect( row, 1, true ) );
402 						SwingUtilities.invokeLater( new Runnable()
403 						{
404 							public void run()
405 							{
406 								propertiesTable.editCellAt( row, 1 );
407 								Component editorComponent = propertiesTable.getEditorComponent();
408 								if( editorComponent != null )
409 									editorComponent.requestFocusInWindow();
410 							}
411 						} );
412 					}
413 				} );
414 
415 			}
416 		}
417 	}
418 
419 	private class RemovePropertyAction extends AbstractAction
420 	{
421 		public RemovePropertyAction()
422 		{
423 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/remove_property.gif" ) );
424 			putValue( Action.SHORT_DESCRIPTION, "Removes the selected property from the property list" );
425 			setEnabled( false );
426 		}
427 
428 		public void actionPerformed( ActionEvent e )
429 		{
430 			int row = propertiesTable.getSelectedRow();
431 			if( row == -1 )
432 				return;
433 
434 			UISupport.stopCellEditing( propertiesTable );
435 
436 			String propertyName = propertiesModel.getValueAt( row, 0 ).toString();
437 			if( UISupport.confirm( "Remove property [" + propertyName + "]?", "Remove Property" ) )
438 			{
439 				( ( MutableTestPropertyHolder )holder ).removeProperty( propertyName );
440 				propertiesModel.fireTableRowsDeleted( row, row );
441 			}
442 		}
443 	}
444 
445 	private class ClearPropertiesAction extends AbstractAction
446 	{
447 		public ClearPropertiesAction()
448 		{
449 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clear_properties.gif" ) );
450 			putValue( Action.SHORT_DESCRIPTION, "Clears all current property values" );
451 		}
452 
453 		public void actionPerformed( ActionEvent e )
454 		{
455 			if( UISupport.confirm( "Clear all property values?", "Clear Properties" ) )
456 			{
457 				for( String name : holder.getPropertyNames() )
458 				{
459 					holder.getProperty( name ).setValue( null );
460 				}
461 			}
462 		}
463 	}
464 
465 	private class MovePropertyUpAction extends AbstractAction
466 	{
467 		public MovePropertyUpAction()
468 		{
469 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/up_arrow.gif" ) );
470 			putValue( Action.SHORT_DESCRIPTION, "Moves selected property up one row" );
471 			setEnabled( false );
472 		}
473 
474 		public void actionPerformed( ActionEvent e )
475 		{
476 			int ix = propertiesTable.getSelectedRow();
477 			if( ix != -1 )
478 			{
479 				( ( MutableTestPropertyHolder )holder ).moveProperty( holder.getPropertyAt( ix ).getName(), ix - 1 );
480 				propertiesTable.setRowSelectionInterval( ix - 1, ix - 1 );
481 			}
482 		}
483 	}
484 
485 	private class MovePropertyDownAction extends AbstractAction
486 	{
487 		public MovePropertyDownAction()
488 		{
489 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/down_arrow.gif" ) );
490 			putValue( Action.SHORT_DESCRIPTION, "Moves selected property down one row" );
491 			setEnabled( false );
492 		}
493 
494 		public void actionPerformed( ActionEvent e )
495 		{
496 			int ix = propertiesTable.getSelectedRow();
497 			if( ix != -1 )
498 			{
499 				( ( MutableTestPropertyHolder )holder ).moveProperty( holder.getPropertyAt( ix ).getName(), ix + 1 );
500 
501 				propertiesTable.setRowSelectionInterval( ix + 1, ix + 1 );
502 			}
503 		}
504 	}
505 
506 	private class LoadPropertiesAction extends AbstractAction
507 	{
508 		private XFormDialog dialog;
509 
510 		public LoadPropertiesAction()
511 		{
512 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/load_properties.gif" ) );
513 			putValue( Action.SHORT_DESCRIPTION, "Loads property values from an external file" );
514 		}
515 
516 		public void actionPerformed( ActionEvent e )
517 		{
518 			if( dialog == null )
519 				dialog = ADialogBuilder.buildDialog( LoadOptionsForm.class );
520 
521 			dialog.getFormField( LoadOptionsForm.DELETEREMAINING )
522 					.setEnabled( holder instanceof MutableTestPropertyHolder );
523 			dialog.getFormField( LoadOptionsForm.CREATEMISSING ).setEnabled( holder instanceof MutableTestPropertyHolder );
524 
525 			if( dialog.show() )
526 			{
527 				try
528 				{
529 					BufferedReader reader = new BufferedReader( new FileReader( dialog.getValue( LoadOptionsForm.FILE ) ) );
530 
531 					String line = reader.readLine();
532 					int count = 0;
533 
534 					Set<String> names = new HashSet<String>( Arrays.asList( holder.getPropertyNames() ) );
535 
536 					while( line != null )
537 					{
538 						if( line.trim().length() > 0 && !( line.charAt( 0 ) == '#' ) )
539 						{
540 							int ix = line.indexOf( '=' );
541 							if( ix > 0 )
542 							{
543 								String name = line.substring( 0, ix ).trim();
544 								String value = line.length() > ix ? line.substring( ix + 1 ) : "";
545 
546 								// read multiline value
547 								if( value.endsWith( "//" ) )
548 								{
549 									value = value.substring( 0, value.length() - 1 );
550 
551 									String ln = reader.readLine();
552 									while( ln != null && ln.endsWith( "//" ) )
553 									{
554 										value += ln.substring( 0, ln.length() - 1 );
555 										ln = reader.readLine();
556 									}
557 
558 									if( ln != null )
559 										value += ln;
560 									if( ln == null )
561 										break;
562 								}
563 
564 								if( holder.hasProperty( name ) )
565 								{
566 									count++ ;
567 									holder.setPropertyValue( name, value );
568 								}
569 								else if( dialog.getBooleanValue( LoadOptionsForm.CREATEMISSING )
570 										&& holder instanceof MutableTestPropertyHolder )
571 								{
572 									( ( MutableTestPropertyHolder )holder ).addProperty( name ).setValue( value );
573 									count++ ;
574 								}
575 
576 								names.remove( name );
577 							}
578 						}
579 
580 						line = reader.readLine();
581 					}
582 
583 					if( dialog.getBooleanValue( LoadOptionsForm.DELETEREMAINING )
584 							&& holder instanceof MutableTestPropertyHolder )
585 					{
586 						for( String name : names )
587 						{
588 							( ( MutableTestPropertyHolder )holder ).removeProperty( name );
589 						}
590 					}
591 
592 					reader.close();
593 					UISupport.showInfoMessage( "Added/Updated " + count + " properties from file" );
594 				}
595 				catch( Exception ex )
596 				{
597 					UISupport.showErrorMessage( ex );
598 				}
599 			}
600 		}
601 	}
602 
603 	private class SavePropertiesAction extends AbstractAction
604 	{
605 		public SavePropertiesAction()
606 		{
607 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/set_properties_target.gif" ) );
608 			putValue( Action.SHORT_DESCRIPTION, "Saves current property-values to a file" );
609 		}
610 
611 		public void actionPerformed( ActionEvent e )
612 		{
613 			if( holder.getPropertyCount() == 0 )
614 			{
615 				UISupport.showErrorMessage( "No properties to save!" );
616 				return;
617 			}
618 
619 			File file = UISupport.getFileDialogs().saveAs( this, "Save Properties" );
620 			if( file != null )
621 			{
622 				try
623 				{
624 					int cnt = TestPropertyUtils.saveTo( holder, file.getAbsolutePath() );
625 					UISupport.showInfoMessage( "Saved " + cnt + " propert" + ((cnt == 1)?"y":"ies") + " to file" );
626 				}
627 				catch( IOException e1 )
628 				{
629 					UISupport.showErrorMessage( e1 );
630 				}
631 			}
632 		}
633 	}
634 	
635 	private class SortPropertiesAction extends AbstractAction
636 	{
637 		public SortPropertiesAction()
638 		{
639 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/arrow_down.png" ) );
640 			putValue( Action.SHORT_DESCRIPTION, "Sorts properties alphabetically" );
641 		}
642 
643 		public void actionPerformed( ActionEvent e )
644 		{
645 			if( holder.getPropertyCount() == 0 )
646 			{
647 				UISupport.showErrorMessage( "No properties to sort!" );
648 				return;
649 			}
650 
651 			try
652 			{
653 				UISupport.setHourglassCursor();
654 				TestPropertyUtils.sortProperties( ( MutableTestPropertyHolder )holder );
655 			}
656 			finally
657 			{
658 				UISupport.resetCursor();
659 			}
660 			
661 		}
662 	}
663 
664 	@AForm( name = "Load Properties", description = "Set load options below" )
665 	private static interface LoadOptionsForm
666 	{
667 		@AField( name = "File", description = "The Properties file to load", type = AFieldType.FILE )
668 		public static final String FILE = "File";
669 
670 		@AField( name = "Create Missing", description = "Creates Missing Properties", type = AFieldType.BOOLEAN )
671 		public static final String CREATEMISSING = "Create Missing";
672 
673 		@AField( name = "Delete Remaining", description = "Deletes properties not in file", type = AFieldType.BOOLEAN )
674 		public static final String DELETEREMAINING = "Delete Remaining";
675 	}
676 
677 	public TestPropertyHolder getHolder()
678 	{
679 		return holder;
680 	}
681 
682 	public PropertiesModel getPropertiesModel()
683 	{
684 		return propertiesModel;
685 	}
686 
687 	public final class PropertyHolderTablePropertyExpansionDropTarget implements DropTargetListener
688 	{
689 		public PropertyHolderTablePropertyExpansionDropTarget()
690 		{
691 		}
692 
693 		public void dragEnter( DropTargetDragEvent dtde )
694 		{
695 			if( !isAcceptable( dtde.getTransferable(), dtde.getLocation() ) )
696 				dtde.rejectDrag();
697 		}
698 
699 		public void dragExit( DropTargetEvent dtde )
700 		{
701 		}
702 
703 		public void dragOver( DropTargetDragEvent dtde )
704 		{
705 			if( !isAcceptable( dtde.getTransferable(), dtde.getLocation() ) )
706 			{
707 				dtde.rejectDrag();
708 			}
709 			else
710 			{
711 				dtde.acceptDrag( dtde.getDropAction() );
712 			}
713 		}
714 
715 		public void drop( DropTargetDropEvent dtde )
716 		{
717 			if( !isAcceptable( dtde.getTransferable(), dtde.getLocation() ) )
718 			{
719 				dtde.rejectDrop();
720 			}
721 			else
722 			{
723 				try
724 				{
725 					Transferable transferable = dtde.getTransferable();
726 					Object transferData = transferable.getTransferData( transferable.getTransferDataFlavors()[0] );
727 					if( transferData instanceof PropertyModelItem )
728 					{
729 						dtde.acceptDrop( dtde.getDropAction() );
730 						PropertyModelItem modelItem = ( PropertyModelItem )transferData;
731 
732 						String xpath = modelItem.getXPath();
733 						if( xpath == null && XmlUtils.seemsToBeXml( modelItem.getProperty().getValue() ) )
734 						{
735 							xpath = UISupport.selectXPath( "Create PropertyExpansion", "Select XPath below", modelItem
736 									.getProperty().getValue(), null );
737 
738 							if( xpath != null )
739 								xpath = XmlUtils.removeXPathNamespaceDeclarations( xpath );
740 						}
741 
742 						PropertyExpansion propertyExpansion = new PropertyExpansionImpl( modelItem.getProperty(), xpath );
743 
744 						Point point = dtde.getLocation();
745 						int column = getPropertiesTable().columnAtPoint( point );
746 						int row = getPropertiesTable().rowAtPoint( point );
747 
748 						if( row == -1 )
749 						{
750 							if( holder instanceof MutableTestPropertyHolder )
751 							{
752 								MutableTestPropertyHolder mtph = ( MutableTestPropertyHolder )holder;
753 								String name = UISupport.prompt( "Specify unique name of property", "Add Property", modelItem
754 										.getProperty().getName() );
755 								while( name != null && mtph.hasProperty( name ) )
756 								{
757 									name = UISupport.prompt( "Specify unique name of property", "Add Property", modelItem
758 											.getProperty().getName() );
759 								}
760 
761 								if( name != null )
762 									mtph.addProperty( name ).setValue( propertyExpansion.toString() );
763 							}
764 						}
765 						else
766 						{
767 							getPropertiesTable().setValueAt( propertyExpansion.toString(), row, column );
768 						}
769 
770 						dtde.dropComplete( true );
771 					}
772 				}
773 				catch( Exception e )
774 				{
775 					SoapUI.logError( e );
776 				}
777 			}
778 		}
779 
780 		public void dropActionChanged( DropTargetDragEvent dtde )
781 		{
782 		}
783 
784 		public boolean isAcceptable( Transferable transferable, Point point )
785 		{
786 			int row = getPropertiesTable().rowAtPoint( point );
787 			if( row >= 0 )
788 			{
789 				int column = getPropertiesTable().columnAtPoint( point );
790 				if( column != 1 )
791 					return false;
792 
793 				if( !getPropertiesTable().isCellEditable( row, column ) )
794 					return false;
795 			}
796 			else if( !( getHolder() instanceof MutableTestPropertyHolder ) )
797 			{
798 				return false;
799 			}
800 
801 			DataFlavor[] flavors = transferable.getTransferDataFlavors();
802 			for( int i = 0; i < flavors.length; i++ )
803 			{
804 				DataFlavor flavor = flavors[i];
805 				if( flavor.isMimeTypeEqual( DataFlavor.javaJVMLocalObjectMimeType ) )
806 				{
807 					try
808 					{
809 						Object modelItem = transferable.getTransferData( flavor );
810 						if( modelItem instanceof PropertyModelItem
811 								&& ( ( PropertyModelItem )modelItem ).getProperty().getModelItem() != getHolder()
812 										.getModelItem() )
813 						{
814 							return PropertyExpansionUtils.canExpandProperty( getHolder().getModelItem(),
815 									( ( PropertyModelItem )modelItem ).getProperty() );
816 						}
817 					}
818 					catch( Exception ex )
819 					{
820 						SoapUI.logError( ex );
821 					}
822 				}
823 			}
824 
825 			return false;
826 		}
827 	}
828 }