View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2010 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  
47  import com.eviware.soapui.SoapUI;
48  import com.eviware.soapui.impl.wsdl.MutableTestPropertyHolder;
49  import com.eviware.soapui.impl.wsdl.teststeps.AMFRequestTestStep;
50  import com.eviware.soapui.impl.wsdl.teststeps.JdbcRequestTestStep;
51  import com.eviware.soapui.model.TestPropertyHolder;
52  import com.eviware.soapui.model.propertyexpansion.PropertyExpansion;
53  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionImpl;
54  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionUtils;
55  import com.eviware.soapui.model.support.TestPropertyUtils;
56  import com.eviware.soapui.model.testsuite.TestProperty;
57  import com.eviware.soapui.model.testsuite.TestPropertyListener;
58  import com.eviware.soapui.model.tree.nodes.PropertyTreeNode.PropertyModelItem;
59  import com.eviware.soapui.support.StringUtils;
60  import com.eviware.soapui.support.UISupport;
61  import com.eviware.soapui.support.components.JXToolBar;
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  	protected final TestPropertyHolder holder;
72  	protected PropertyHolderTableModel propertiesModel;
73  	protected RemovePropertyAction removePropertyAction;
74  	protected AddPropertyAction addPropertyAction;
75  	protected InternalTestPropertyListener testPropertyListener;
76  	protected JTable propertiesTable;
77  	protected JXToolBar toolbar;
78  	protected LoadPropertiesAction loadPropertiesAction;
79  	protected MovePropertyUpAction movePropertyUpAction;
80  	protected 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 DefaultPropertyTableHolderModel( holder );
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 			if( !( holder instanceof AMFRequestTestStep || holder instanceof JdbcRequestTestStep ) )
183 			{
184 				toolbar.addRelatedGap();
185 				toolbar.add( UISupport.createToolbarButton( new SortPropertiesAction() ) );
186 				toolbar.addRelatedGap();
187 			}
188 		}
189 
190 		JButton clearPropertiesButton = UISupport.createToolbarButton( new ClearPropertiesAction() );
191 		toolbar.add( clearPropertiesButton );
192 		JButton loadPropertiesButton = UISupport.createToolbarButton( loadPropertiesAction );
193 		toolbar.add( loadPropertiesButton );
194 		toolbar.add( UISupport.createToolbarButton( new SavePropertiesAction() ) );
195 
196 		return toolbar;
197 	}
198 
199 	public JXToolBar getToolbar()
200 	{
201 		return toolbar;
202 	}
203 
204 	public JTable getPropertiesTable()
205 	{
206 		return propertiesTable;
207 	}
208 
209 	public void release()
210 	{
211 		if( propertiesTable.isEditing() )
212 			propertiesTable.getCellEditor().stopCellEditing();
213 
214 		holder.removeTestPropertyListener( testPropertyListener );
215 	}
216 
217 	public void setEnabled( boolean enabled )
218 	{
219 		addPropertyAction.setEnabled( enabled );
220 		removePropertyAction.setEnabled( enabled );
221 		propertiesTable.setEnabled( enabled );
222 		loadPropertiesAction.setEnabled( enabled );
223 
224 		super.setEnabled( enabled );
225 	}
226 
227 	private final class InternalTestPropertyListener implements TestPropertyListener
228 	{
229 		private boolean enabled = true;
230 
231 		@SuppressWarnings( "unused" )
232 		public boolean isEnabled()
233 		{
234 			return enabled;
235 		}
236 
237 		@SuppressWarnings( "unused" )
238 		public void setEnabled( boolean enabled )
239 		{
240 			this.enabled = enabled;
241 		}
242 
243 		public void propertyAdded( String name )
244 		{
245 			if( enabled )
246 				propertiesModel.fireTableDataChanged();
247 		}
248 
249 		public void propertyRemoved( String name )
250 		{
251 			if( enabled )
252 				propertiesModel.fireTableDataChanged();
253 		}
254 
255 		public void propertyRenamed( String oldName, String newName )
256 		{
257 			if( enabled )
258 				propertiesModel.fireTableDataChanged();
259 		}
260 
261 		public void propertyValueChanged( String name, String oldValue, String newValue )
262 		{
263 			if( enabled )
264 				propertiesModel.fireTableDataChanged();
265 		}
266 
267 		public void propertyMoved( String name, int oldIndex, int newIndex )
268 		{
269 			if( enabled )
270 				propertiesModel.fireTableDataChanged();
271 		}
272 	}
273 
274 	private class AddPropertyAction extends AbstractAction
275 	{
276 		public AddPropertyAction()
277 		{
278 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/add_property.gif" ) );
279 			putValue( Action.SHORT_DESCRIPTION, "Adds a property to the property list" );
280 		}
281 
282 		public void actionPerformed( ActionEvent e )
283 		{
284 			String name = UISupport.prompt( "Specify unique property name", "Add Property", "" );
285 			if( StringUtils.hasContent( name ) )
286 			{
287 				if( holder.hasProperty( name ) )
288 				{
289 					UISupport.showErrorMessage( "Property name [" + name + "] already exists.." );
290 					return;
291 				}
292 
293 				( ( MutableTestPropertyHolder )holder ).addProperty( name );
294 				final int row = holder.getPropertyNames().length - 1;
295 				propertiesModel.fireTableRowsInserted( row, row );
296 				SwingUtilities.invokeLater( new Runnable()
297 				{
298 					public void run()
299 					{
300 						requestFocusInWindow();
301 						scrollRectToVisible( propertiesTable.getCellRect( row, 1, true ) );
302 						SwingUtilities.invokeLater( new Runnable()
303 						{
304 							public void run()
305 							{
306 								propertiesTable.editCellAt( row, 1 );
307 								Component editorComponent = propertiesTable.getEditorComponent();
308 								if( editorComponent != null )
309 									editorComponent.requestFocusInWindow();
310 							}
311 						} );
312 					}
313 				} );
314 
315 			}
316 		}
317 	}
318 
319 	protected class RemovePropertyAction extends AbstractAction
320 	{
321 		public RemovePropertyAction()
322 		{
323 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/remove_property.gif" ) );
324 			putValue( Action.SHORT_DESCRIPTION, "Removes the selected property from the property list" );
325 			setEnabled( false );
326 		}
327 
328 		public void actionPerformed( ActionEvent e )
329 		{
330 			int row = propertiesTable.getSelectedRow();
331 			if( row == -1 )
332 				return;
333 
334 			UISupport.stopCellEditing( propertiesTable );
335 
336 			String propertyName = propertiesModel.getValueAt( row, 0 ).toString();
337 			if( UISupport.confirm( "Remove property [" + propertyName + "]?", "Remove Property" ) )
338 			{
339 				( ( MutableTestPropertyHolder )holder ).removeProperty( propertyName );
340 				propertiesModel.fireTableRowsDeleted( row, row );
341 			}
342 		}
343 	}
344 
345 	protected class ClearPropertiesAction extends AbstractAction
346 	{
347 		public ClearPropertiesAction()
348 		{
349 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clear_properties.gif" ) );
350 			putValue( Action.SHORT_DESCRIPTION, "Clears all current property values" );
351 		}
352 
353 		public void actionPerformed( ActionEvent e )
354 		{
355 			if( UISupport.confirm( "Clear all property values?", "Clear Properties" ) )
356 			{
357 				for( String name : holder.getPropertyNames() )
358 				{
359 					holder.getProperty( name ).setValue( null );
360 				}
361 			}
362 		}
363 	}
364 
365 	protected class MovePropertyUpAction extends AbstractAction
366 	{
367 		public MovePropertyUpAction()
368 		{
369 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/up_arrow.gif" ) );
370 			putValue( Action.SHORT_DESCRIPTION, "Moves selected property up one row" );
371 			setEnabled( false );
372 		}
373 
374 		public void actionPerformed( ActionEvent e )
375 		{
376 			int ix = propertiesTable.getSelectedRow();
377 			if( ix != -1 )
378 			{
379 				( ( MutableTestPropertyHolder )holder ).moveProperty( holder.getPropertyAt( ix ).getName(), ix - 1 );
380 				propertiesTable.setRowSelectionInterval( ix - 1, ix - 1 );
381 			}
382 		}
383 	}
384 
385 	protected class MovePropertyDownAction extends AbstractAction
386 	{
387 		public MovePropertyDownAction()
388 		{
389 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/down_arrow.gif" ) );
390 			putValue( Action.SHORT_DESCRIPTION, "Moves selected property down one row" );
391 			setEnabled( false );
392 		}
393 
394 		public void actionPerformed( ActionEvent e )
395 		{
396 			int ix = propertiesTable.getSelectedRow();
397 			if( ix != -1 )
398 			{
399 				( ( MutableTestPropertyHolder )holder ).moveProperty( holder.getPropertyAt( ix ).getName(), ix + 1 );
400 
401 				propertiesTable.setRowSelectionInterval( ix + 1, ix + 1 );
402 			}
403 		}
404 	}
405 
406 	protected class LoadPropertiesAction extends AbstractAction
407 	{
408 		private XFormDialog dialog;
409 
410 		public LoadPropertiesAction()
411 		{
412 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/load_properties.gif" ) );
413 			putValue( Action.SHORT_DESCRIPTION, "Loads property values from an external file" );
414 		}
415 
416 		public void actionPerformed( ActionEvent e )
417 		{
418 			if( dialog == null )
419 				dialog = ADialogBuilder.buildDialog( LoadOptionsForm.class );
420 
421 			dialog.getFormField( LoadOptionsForm.DELETEREMAINING )
422 					.setEnabled( holder instanceof MutableTestPropertyHolder );
423 			dialog.getFormField( LoadOptionsForm.CREATEMISSING ).setEnabled( holder instanceof MutableTestPropertyHolder );
424 
425 			if( dialog.show() )
426 			{
427 				try
428 				{
429 					BufferedReader reader = new BufferedReader( new FileReader( dialog.getValue( LoadOptionsForm.FILE ) ) );
430 
431 					String line = reader.readLine();
432 					int count = 0;
433 
434 					Set<String> names = new HashSet<String>( Arrays.asList( holder.getPropertyNames() ) );
435 
436 					while( line != null )
437 					{
438 						if( line.trim().length() > 0 && !( line.charAt( 0 ) == '#' ) )
439 						{
440 							int ix = line.indexOf( '=' );
441 							if( ix > 0 )
442 							{
443 								String name = line.substring( 0, ix ).trim();
444 								String value = line.length() > ix ? line.substring( ix + 1 ) : "";
445 
446 								// read multiline value
447 								if( value.endsWith( "//" ) )
448 								{
449 									value = value.substring( 0, value.length() - 1 );
450 
451 									String ln = reader.readLine();
452 									while( ln != null && ln.endsWith( "//" ) )
453 									{
454 										value += ln.substring( 0, ln.length() - 1 );
455 										ln = reader.readLine();
456 									}
457 
458 									if( ln != null )
459 										value += ln;
460 									if( ln == null )
461 										break;
462 								}
463 
464 								if( holder.hasProperty( name ) )
465 								{
466 									count++ ;
467 									holder.setPropertyValue( name, value );
468 								}
469 								else if( dialog.getBooleanValue( LoadOptionsForm.CREATEMISSING )
470 										&& holder instanceof MutableTestPropertyHolder )
471 								{
472 									TestProperty prop = ( ( MutableTestPropertyHolder )holder ).addProperty( name );
473 									if ( !prop.isReadOnly() ) 
474 										prop.setValue( value );
475 									count++ ;
476 								}
477 
478 								names.remove( name );
479 							}
480 						}
481 
482 						line = reader.readLine();
483 					}
484 
485 					if( dialog.getBooleanValue( LoadOptionsForm.DELETEREMAINING )
486 							&& holder instanceof MutableTestPropertyHolder )
487 					{
488 						for( String name : names )
489 						{
490 							( ( MutableTestPropertyHolder )holder ).removeProperty( name );
491 						}
492 					}
493 
494 					reader.close();
495 					UISupport.showInfoMessage( "Added/Updated " + count + " properties from file" );
496 				}
497 				catch( Exception ex )
498 				{
499 					UISupport.showErrorMessage( ex );
500 				}
501 			}
502 		}
503 	}
504 
505 	private class SavePropertiesAction extends AbstractAction
506 	{
507 		public SavePropertiesAction()
508 		{
509 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/set_properties_target.gif" ) );
510 			putValue( Action.SHORT_DESCRIPTION, "Saves current property-values to a file" );
511 		}
512 
513 		public void actionPerformed( ActionEvent e )
514 		{
515 			if( holder.getPropertyCount() == 0 )
516 			{
517 				UISupport.showErrorMessage( "No properties to save!" );
518 				return;
519 			}
520 
521 			File file = UISupport.getFileDialogs().saveAs( this, "Save Properties" );
522 			if( file != null )
523 			{
524 				try
525 				{
526 					int cnt = TestPropertyUtils.saveTo( holder, file.getAbsolutePath() );
527 					UISupport.showInfoMessage( "Saved " + cnt + " propert" + ( ( cnt == 1 ) ? "y" : "ies" ) + " to file" );
528 				}
529 				catch( IOException e1 )
530 				{
531 					UISupport.showErrorMessage( e1 );
532 				}
533 			}
534 		}
535 	}
536 
537 	private class SortPropertiesAction extends AbstractAction
538 	{
539 		public SortPropertiesAction()
540 		{
541 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/arrow_down.png" ) );
542 			putValue( Action.SHORT_DESCRIPTION, "Sorts properties alphabetically" );
543 		}
544 
545 		public void actionPerformed( ActionEvent e )
546 		{
547 			if( holder.getPropertyCount() == 0 )
548 			{
549 				UISupport.showErrorMessage( "No properties to sort!" );
550 				return;
551 			}
552 
553 			try
554 			{
555 				UISupport.setHourglassCursor();
556 				TestPropertyUtils.sortProperties( ( MutableTestPropertyHolder )holder );
557 			}
558 			finally
559 			{
560 				UISupport.resetCursor();
561 			}
562 
563 		}
564 	}
565 
566 	@AForm( name = "Load Properties", description = "Set load options below" )
567 	private static interface LoadOptionsForm
568 	{
569 		@AField( name = "File", description = "The Properties file to load", type = AFieldType.FILE )
570 		public static final String FILE = "File";
571 
572 		@AField( name = "Create Missing", description = "Creates Missing Properties", type = AFieldType.BOOLEAN )
573 		public static final String CREATEMISSING = "Create Missing";
574 
575 		@AField( name = "Delete Remaining", description = "Deletes properties not in file", type = AFieldType.BOOLEAN )
576 		public static final String DELETEREMAINING = "Delete Remaining";
577 	}
578 
579 	public TestPropertyHolder getHolder()
580 	{
581 		return holder;
582 	}
583 
584 	public PropertyHolderTableModel getPropertiesModel()
585 	{
586 		return propertiesModel;
587 	}
588 
589 	public final class PropertyHolderTablePropertyExpansionDropTarget implements DropTargetListener
590 	{
591 		public PropertyHolderTablePropertyExpansionDropTarget()
592 		{
593 		}
594 
595 		public void dragEnter( DropTargetDragEvent dtde )
596 		{
597 			if( !isAcceptable( dtde.getTransferable(), dtde.getLocation() ) )
598 				dtde.rejectDrag();
599 		}
600 
601 		public void dragExit( DropTargetEvent dtde )
602 		{
603 		}
604 
605 		public void dragOver( DropTargetDragEvent dtde )
606 		{
607 			if( !isAcceptable( dtde.getTransferable(), dtde.getLocation() ) )
608 			{
609 				dtde.rejectDrag();
610 			}
611 			else
612 			{
613 				dtde.acceptDrag( dtde.getDropAction() );
614 			}
615 		}
616 
617 		public void drop( DropTargetDropEvent dtde )
618 		{
619 			if( !isAcceptable( dtde.getTransferable(), dtde.getLocation() ) )
620 			{
621 				dtde.rejectDrop();
622 			}
623 			else
624 			{
625 				try
626 				{
627 					Transferable transferable = dtde.getTransferable();
628 					Object transferData = transferable.getTransferData( transferable.getTransferDataFlavors()[0] );
629 					if( transferData instanceof PropertyModelItem )
630 					{
631 						dtde.acceptDrop( dtde.getDropAction() );
632 						PropertyModelItem modelItem = ( PropertyModelItem )transferData;
633 
634 						String xpath = modelItem.getXPath();
635 						if( xpath == null && XmlUtils.seemsToBeXml( modelItem.getProperty().getValue() ) )
636 						{
637 							xpath = UISupport.selectXPath( "Create PropertyExpansion", "Select XPath below", modelItem
638 									.getProperty().getValue(), null );
639 
640 							if( xpath != null )
641 								xpath = PropertyExpansionUtils.shortenXPathForPropertyExpansion( xpath, modelItem.getProperty()
642 										.getValue() );
643 						}
644 
645 						PropertyExpansion propertyExpansion = new PropertyExpansionImpl( modelItem.getProperty(), xpath );
646 
647 						Point point = dtde.getLocation();
648 						int column = getPropertiesTable().columnAtPoint( point );
649 						int row = getPropertiesTable().rowAtPoint( point );
650 
651 						if( row == -1 )
652 						{
653 							if( holder instanceof MutableTestPropertyHolder )
654 							{
655 								MutableTestPropertyHolder mtph = ( MutableTestPropertyHolder )holder;
656 								String name = UISupport.prompt( "Specify unique name of property", "Add Property", modelItem
657 										.getProperty().getName() );
658 								while( name != null && mtph.hasProperty( name ) )
659 								{
660 									name = UISupport.prompt( "Specify unique name of property", "Add Property", modelItem
661 											.getProperty().getName() );
662 								}
663 
664 								if( name != null )
665 									mtph.addProperty( name ).setValue( propertyExpansion.toString() );
666 							}
667 						}
668 						else
669 						{
670 							getPropertiesTable().setValueAt( propertyExpansion.toString(), row, column );
671 						}
672 
673 						dtde.dropComplete( true );
674 					}
675 				}
676 				catch( Exception e )
677 				{
678 					SoapUI.logError( e );
679 				}
680 			}
681 		}
682 
683 		public void dropActionChanged( DropTargetDragEvent dtde )
684 		{
685 		}
686 
687 		public boolean isAcceptable( Transferable transferable, Point point )
688 		{
689 			int row = getPropertiesTable().rowAtPoint( point );
690 			if( row >= 0 )
691 			{
692 				int column = getPropertiesTable().columnAtPoint( point );
693 				if( column != 1 )
694 					return false;
695 
696 				if( !getPropertiesTable().isCellEditable( row, column ) )
697 					return false;
698 			}
699 			else if( !( getHolder() instanceof MutableTestPropertyHolder ) )
700 			{
701 				return false;
702 			}
703 
704 			DataFlavor[] flavors = transferable.getTransferDataFlavors();
705 			for( int i = 0; i < flavors.length; i++ )
706 			{
707 				DataFlavor flavor = flavors[i];
708 				if( flavor.isMimeTypeEqual( DataFlavor.javaJVMLocalObjectMimeType ) )
709 				{
710 					try
711 					{
712 						Object modelItem = transferable.getTransferData( flavor );
713 						if( modelItem instanceof PropertyModelItem
714 								&& ( ( PropertyModelItem )modelItem ).getProperty().getModelItem() != getHolder()
715 										.getModelItem() )
716 						{
717 							return PropertyExpansionUtils.canExpandProperty( getHolder().getModelItem(),
718 									( ( PropertyModelItem )modelItem ).getProperty() );
719 						}
720 					}
721 					catch( Exception ex )
722 					{
723 						SoapUI.logError( ex );
724 					}
725 				}
726 			}
727 
728 			return false;
729 		}
730 	}
731 }