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          setRowHeight( 19 );
121 //			setHorizontalScrollEnabled(true);
122       }
123 
124       public PropertyModelItem getTestProperty()
125       {
126          int index = getSelectedRow();
127          if( index == -1 )
128             return null;
129          TestProperty property = propertiesModel.getPropertyAtRow( index );
130          return new PropertyModelItem( property, true );
131       }
132    }
133 
134    private Component buildToolbar()
135    {
136       toolbar = UISupport.createSmallToolbar();
137 
138       if( holder instanceof MutableTestPropertyHolder )
139       {
140          removePropertyAction = new RemovePropertyAction();
141          addPropertyAction = new AddPropertyAction();
142          movePropertyUpAction = new MovePropertyUpAction();
143          movePropertyDownAction = new MovePropertyDownAction();
144 
145          JButton addPropertyButton = UISupport.createToolbarButton( addPropertyAction );
146          toolbar.add( addPropertyButton );
147          JButton removePropertyButton = UISupport.createToolbarButton( removePropertyAction );
148          toolbar.add( removePropertyButton );
149 
150          JButton movePropertyUpButton = UISupport.createToolbarButton( movePropertyUpAction );
151          toolbar.add( movePropertyUpButton );
152          JButton movePropertyDownButton = UISupport.createToolbarButton( movePropertyDownAction );
153          toolbar.add( movePropertyDownButton );
154       }
155 
156       JButton clearPropertiesButton = UISupport.createToolbarButton( new ClearPropertiesAction() );
157       toolbar.add( clearPropertiesButton );
158       JButton loadPropertiesButton = UISupport.createToolbarButton( loadPropertiesAction );
159       toolbar.add( loadPropertiesButton );
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       @Override
314       public Class<?> getColumnClass( int columnIndex )
315       {
316          return String.class;
317       }
318 
319       public TestProperty getPropertyAtRow( int rowIndex )
320       {
321          return holder.getProperty( names.get( rowIndex ) );
322       }
323 
324       public Object getValueAt( int rowIndex, int columnIndex )
325       {
326          TestProperty property = holder.getProperty( names.get( rowIndex ) );
327          if( property == null )
328             return null;
329 
330          switch( columnIndex )
331          {
332             case 0:
333                return property.getName();
334             case 1:
335                return property.getValue();
336          }
337 
338          return null;
339       }
340    }
341 
342    private class AddPropertyAction extends AbstractAction
343    {
344       public AddPropertyAction()
345       {
346          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/add_property.gif" ) );
347          putValue( Action.SHORT_DESCRIPTION, "Adds a property to the property list" );
348       }
349 
350       public void actionPerformed( ActionEvent e )
351       {
352          String name = UISupport.prompt( "Specify unique property name", "Add Property", "" );
353          if( StringUtils.hasContent( name ) )
354          {
355             if( holder.hasProperty( name ) )
356             {
357                UISupport.showErrorMessage( "Property name [" + name + "] already exists.." );
358                return;
359             }
360 
361             ( (MutableTestPropertyHolder) holder ).addProperty( name );
362             final int row = holder.getPropertyNames().length - 1;
363             propertiesModel.fireTableRowsInserted( row, row );
364             SwingUtilities.invokeLater( new Runnable()
365             {
366                public void run()
367                {
368                   requestFocusInWindow();
369                   scrollRectToVisible( propertiesTable.getCellRect( row, 1, true ) );
370                   SwingUtilities.invokeLater( new Runnable()
371                   {
372                      public void run()
373                      {
374                         propertiesTable.editCellAt( row, 1 );
375                         Component editorComponent = propertiesTable.getEditorComponent();
376                         if( editorComponent != null )
377                            editorComponent.requestFocusInWindow();
378                      }
379                   } );
380                }
381             } );
382 
383          }
384       }
385    }
386 
387    private class RemovePropertyAction extends AbstractAction
388    {
389       public RemovePropertyAction()
390       {
391          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/remove_property.gif" ) );
392          putValue( Action.SHORT_DESCRIPTION, "Removes the selected property from the property list" );
393          setEnabled( false );
394       }
395 
396       public void actionPerformed( ActionEvent e )
397       {
398          int row = propertiesTable.getSelectedRow();
399          if( row == -1 )
400             return;
401 
402          UISupport.stopCellEditing( propertiesTable );
403 
404          String propertyName = propertiesModel.getValueAt( row, 0 ).toString();
405          if( UISupport.confirm( "Remove property [" + propertyName + "]?", "Remove Property" ) )
406          {
407             ( (MutableTestPropertyHolder) holder ).removeProperty( propertyName );
408             propertiesModel.fireTableRowsDeleted( row, row );
409          }
410       }
411    }
412 
413    private class ClearPropertiesAction extends AbstractAction
414    {
415       public ClearPropertiesAction()
416       {
417          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clear_properties.gif" ) );
418          putValue( Action.SHORT_DESCRIPTION, "Clears all current property values" );
419       }
420 
421       public void actionPerformed( ActionEvent e )
422       {
423          if( UISupport.confirm( "Clear all property values?", "Clear Properties" ) )
424          {
425             for( String name : holder.getPropertyNames() )
426             {
427                holder.getProperty( name ).setValue( null );
428             }
429          }
430       }
431    }
432 
433    private class MovePropertyUpAction extends AbstractAction
434    {
435       public MovePropertyUpAction()
436       {
437          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/up_arrow.gif" ) );
438          putValue( Action.SHORT_DESCRIPTION, "Moves selected property up one row" );
439          setEnabled( false );
440       }
441 
442       public void actionPerformed( ActionEvent e )
443       {
444          int ix = propertiesTable.getSelectedRow();
445          if( ix != -1 )
446          {
447             ( (MutableTestPropertyHolder) holder ).moveProperty(
448                     holder.getPropertyAt( ix ).getName(), ix - 1 );
449             propertiesTable.setRowSelectionInterval( ix - 1, ix - 1 );
450          }
451       }
452    }
453 
454    private class MovePropertyDownAction extends AbstractAction
455    {
456       public MovePropertyDownAction()
457       {
458          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/down_arrow.gif" ) );
459          putValue( Action.SHORT_DESCRIPTION, "Moves selected property down one row" );
460          setEnabled( false );
461       }
462 
463       public void actionPerformed( ActionEvent e )
464       {
465          int ix = propertiesTable.getSelectedRow();
466          if( ix != -1 )
467          {
468             ( (MutableTestPropertyHolder) holder ).moveProperty(
469                     holder.getPropertyAt( ix ).getName(), ix + 1 );
470 
471             propertiesTable.setRowSelectionInterval( ix + 1, ix + 1 );
472          }
473       }
474    }
475 
476    private class LoadPropertiesAction extends AbstractAction
477    {
478       public LoadPropertiesAction()
479       {
480          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/load_properties.gif" ) );
481          putValue( Action.SHORT_DESCRIPTION, "Loads property values from an external file" );
482       }
483 
484       public void actionPerformed( ActionEvent e )
485       {
486          File file = UISupport.getFileDialogs().open( this, "Set properties source", null, null, null );
487          if( file != null )
488          {
489             try
490             {
491                boolean createMissing =
492                        holder instanceof MutableTestPropertyHolder &&
493                                UISupport.confirm( "Create missing properties?", "Set Properties Source" );
494 
495                Properties props = new Properties();
496                props.load( new FileInputStream( file ) );
497                for( Object obj : props.keySet() )
498                {
499                   String name = obj.toString();
500                   if( holder.hasProperty( name ) )
501                   {
502                      holder.setPropertyValue( name, props.getProperty( name ) );
503                   }
504                   else if( createMissing )
505                   {
506                      ( (MutableTestPropertyHolder) holder ).addProperty( name ).setValue( props.getProperty( name ) );
507                   }
508                }
509 
510                UISupport.showInfoMessage( "Loaded " + props.size() + " properties from [" + file.getAbsolutePath() + "]" );
511             }
512             catch( Exception e1 )
513             {
514                UISupport.showErrorMessage( "Failed to load properties from [" + file.getAbsolutePath() + "]; " + e1 );
515             }
516          }
517       }
518    }
519 
520    public TestPropertyHolder getHolder()
521    {
522       return holder;
523    }
524 
525    public PropertiesModel getPropertiesModel()
526    {
527       return propertiesModel;
528    }
529 
530    public final class PropertyHolderTablePropertyExpansionDropTarget implements DropTargetListener
531    {
532       public PropertyHolderTablePropertyExpansionDropTarget()
533       {
534       }
535 
536       public void dragEnter( DropTargetDragEvent dtde )
537       {
538          if( !isAcceptable( dtde.getTransferable(), dtde.getLocation() ) )
539             dtde.rejectDrag();
540       }
541 
542       public void dragExit( DropTargetEvent dtde )
543       {
544       }
545 
546       public void dragOver( DropTargetDragEvent dtde )
547       {
548          if( !isAcceptable( dtde.getTransferable(), dtde.getLocation() ) )
549          {
550             dtde.rejectDrag();
551          }
552          else
553          {
554             dtde.acceptDrag( dtde.getDropAction() );
555          }
556       }
557 
558       public void drop( DropTargetDropEvent dtde )
559       {
560          if( !isAcceptable( dtde.getTransferable(), dtde.getLocation() ) )
561          {
562             dtde.rejectDrop();
563          }
564          else
565          {
566             try
567             {
568                Transferable transferable = dtde.getTransferable();
569                Object transferData = transferable.getTransferData( transferable.getTransferDataFlavors()[0] );
570                if( transferData instanceof PropertyModelItem )
571                {
572                   dtde.acceptDrop( dtde.getDropAction() );
573                   PropertyModelItem modelItem = (PropertyModelItem) transferData;
574 
575                   String xpath = modelItem.getXPath();
576                   if( xpath == null && XmlUtils.seemsToBeXml( modelItem.getProperty().getValue() ) )
577                   {
578                      xpath = UISupport.selectXPath( "Create PropertyExpansion", "Select XPath below",
579                              modelItem.getProperty().getValue(), null );
580 
581                      if( xpath != null )
582                         xpath = XmlUtils.removeXPathNamespaceDeclarations( xpath );
583                   }
584 
585                   PropertyExpansion propertyExpansion = new PropertyExpansionImpl( modelItem.getProperty(), xpath );
586 
587                   Point point = dtde.getLocation();
588                   int column = getPropertiesTable().columnAtPoint( point );
589                   int row = getPropertiesTable().rowAtPoint( point );
590 
591                   if( row == -1 )
592                   {
593                      if( holder instanceof MutableTestPropertyHolder )
594                      {
595                         MutableTestPropertyHolder mtph = (MutableTestPropertyHolder) holder;
596                         String name = UISupport.prompt( "Specify unique name of property", "Add Property", modelItem.getProperty().getName() );
597                         while( name != null && mtph.hasProperty( name ) )
598                         {
599                            name = UISupport.prompt( "Specify unique name of property", "Add Property", modelItem.getProperty().getName() );
600                         }
601 
602                         if( name != null )
603                            mtph.addProperty( name ).setValue( propertyExpansion.toString() );
604                      }
605                   }
606                   else
607                   {
608                      getPropertiesTable().setValueAt( propertyExpansion.toString(), row, column );
609                   }
610 
611                   dtde.dropComplete( true );
612                }
613             }
614             catch( Exception e )
615             {
616                SoapUI.logError( e );
617             }
618          }
619       }
620 
621       public void dropActionChanged( DropTargetDragEvent dtde )
622       {
623       }
624 
625       public boolean isAcceptable( Transferable transferable, Point point )
626       {
627          int row = getPropertiesTable().rowAtPoint( point );
628          if( row >= 0 )
629          {
630             int column = getPropertiesTable().columnAtPoint( point );
631             if( column != 1 )
632                return false;
633 
634             if( !getPropertiesTable().isCellEditable( row, column ) )
635                return false;
636          }
637          else if( !( getHolder() instanceof MutableTestPropertyHolder ) )
638          {
639             return false;
640          }
641 
642          DataFlavor[] flavors = transferable.getTransferDataFlavors();
643          for( int i = 0; i < flavors.length; i++ )
644          {
645             DataFlavor flavor = flavors[i];
646             if( flavor.isMimeTypeEqual( DataFlavor.javaJVMLocalObjectMimeType ) )
647             {
648                try
649                {
650                   Object modelItem = transferable.getTransferData( flavor );
651                   if( modelItem instanceof PropertyModelItem &&
652                           ( (PropertyModelItem) modelItem ).getProperty().getModelItem() != getHolder().getModelItem() )
653                   {
654                      return PropertyExpansionUtils.canExpandProperty( getHolder().getModelItem(),
655                              ( (PropertyModelItem) modelItem ).getProperty() );
656                   }
657                }
658                catch( Exception ex )
659                {
660                   SoapUI.logError( ex );
661                }
662             }
663          }
664 
665          return false;
666       }
667    }
668 }