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.iface;
14  
15  import com.eviware.soapui.SoapUI;
16  import com.eviware.soapui.actions.SoapUIPreferencesAction;
17  import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
18  import com.eviware.soapui.impl.support.definition.InterfaceDefinitionPart;
19  import com.eviware.soapui.impl.wsdl.WsdlInterface;
20  import com.eviware.soapui.impl.wsdl.WsdlOperation;
21  import com.eviware.soapui.impl.wsdl.actions.iface.CreateWsdlDocumentationAction;
22  import com.eviware.soapui.impl.wsdl.actions.iface.ExportDefinitionAction;
23  import com.eviware.soapui.impl.wsdl.actions.iface.UpdateInterfaceAction;
24  import com.eviware.soapui.impl.wsdl.actions.iface.tools.wsi.WSIAnalyzeAction;
25  import com.eviware.soapui.impl.wsdl.actions.iface.tools.wsi.WSIReportPanel;
26  import com.eviware.soapui.impl.wsdl.panels.teststeps.support.LineNumbersPanel;
27  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
28  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
29  import com.eviware.soapui.impl.wsdl.support.xsd.SchemaUtils;
30  import com.eviware.soapui.model.ModelItem;
31  import com.eviware.soapui.model.iface.Interface;
32  import com.eviware.soapui.model.support.ProjectListenerAdapter;
33  import com.eviware.soapui.support.UISupport;
34  import com.eviware.soapui.support.action.swing.SwingActionDelegate;
35  import com.eviware.soapui.support.components.JEditorStatusBar;
36  import com.eviware.soapui.support.components.JXToolBar;
37  import com.eviware.soapui.support.components.MetricsPanel;
38  import com.eviware.soapui.support.components.MetricsPanel.MetricType;
39  import com.eviware.soapui.support.components.MetricsPanel.MetricsSection;
40  import com.eviware.soapui.support.components.ProgressDialog;
41  import com.eviware.soapui.support.types.StringList;
42  import com.eviware.soapui.support.xml.JXEditTextArea;
43  import com.eviware.soapui.support.xml.XmlUtils;
44  import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
45  import com.eviware.x.dialogs.Worker;
46  import com.eviware.x.dialogs.XProgressDialog;
47  import com.eviware.x.dialogs.XProgressMonitor;
48  import com.jgoodies.forms.builder.ButtonBarBuilder;
49  import org.apache.log4j.Logger;
50  import org.apache.xmlbeans.XmlCursor;
51  import org.apache.xmlbeans.XmlLineNumber;
52  import org.apache.xmlbeans.XmlObject;
53  import org.apache.xmlbeans.XmlOptions;
54  import org.jdesktop.swingx.JXTable;
55  import org.syntax.jedit.JEditTextArea;
56  import org.w3c.dom.Element;
57  
58  import javax.swing.*;
59  import javax.swing.event.TreeSelectionEvent;
60  import javax.swing.event.TreeSelectionListener;
61  import javax.swing.table.AbstractTableModel;
62  import javax.swing.tree.DefaultMutableTreeNode;
63  import javax.swing.tree.DefaultTreeModel;
64  import javax.swing.tree.TreePath;
65  import javax.wsdl.BindingOperation;
66  import java.awt.*;
67  import java.awt.event.ActionEvent;
68  import java.awt.event.MouseAdapter;
69  import java.awt.event.MouseEvent;
70  import java.io.File;
71  import java.io.StringWriter;
72  import java.util.*;
73  import java.util.List;
74  
75  /***
76   * DesktopPanel for WsdlInterface. Loads all referenced wsdls/xsds for the specified WsdlInterface
77   * and displays these in seperate tabs
78   * 
79   * @author Ole.Matzura
80   */
81  
82  public class WsdlInterfaceDesktopPanel extends ModelItemDesktopPanel<WsdlInterface>
83  {
84  	private final static Logger logger = Logger.getLogger( WsdlInterfaceDesktopPanel.class );
85     private JTabbedPane partTabs;
86     private List<JEditTextArea> editors = new ArrayList<JEditTextArea>();
87  	private JTree tree;
88  	private Map<String,DefaultMutableTreeNode> groupNodes = new HashMap<String,DefaultMutableTreeNode>();
89  	private Map<String,TreePath> pathMap = new HashMap<String,TreePath>();
90  	private List<TreePath> navigationHistory = new ArrayList<TreePath>();
91  	private StringList targetNamespaces = new StringList();
92  	private int historyIndex;
93  	private boolean navigating;
94  	private JEditorStatusBar statusBar;
95  	private DefaultMutableTreeNode rootNode;
96  	private DefaultTreeModel treeModel;
97  	private InternalProjectListener projectListener;
98  	private final WsdlInterface iface;
99  	private JPanel wsiPanel;
100 	private WSIReportPanel reportPanel;
101 	private SaveWsiReportAction saveWsiReportAction;
102 	private MetricsPanel metrics;
103 	private boolean updatingInterface;
104 	private OperationsTableModel operationsTableModel;
105 	
106    public WsdlInterfaceDesktopPanel(WsdlInterface iface)
107    {
108    	super( iface );
109 		this.iface = iface;
110 		
111 		try
112 		{
113 			iface.getWsdlContext().loadIfNecessary();
114 			buildUI();
115 		}
116 		catch( Exception e )
117 		{
118 			UISupport.showErrorMessage( e );
119 			SwingUtilities.invokeLater( new Runnable() {
120 
121 				public void run()
122 				{
123 					SoapUI.getDesktop().closeDesktopPanel( WsdlInterfaceDesktopPanel.this );
124 				}} );
125 		}
126    }
127    
128    private void buildUI()
129 	{
130    	JTabbedPane tabs = new JTabbedPane();
131    	tabs.addTab( "Overview", buildInterfaceOverviewTab() );
132    	tabs.addTab( "Service Endpoints", buildEndpointsTab() );
133    	tabs.addTab( "WSDL Content", buildWsdlContentTab() );
134    	tabs.addTab( "WS-I Compliance", buildWSITab() );
135    	
136    	add( UISupport.createTabPanel( tabs, true ), BorderLayout.CENTER );
137    }
138 
139 	private Component buildWSITab()
140 	{
141 		wsiPanel = new JPanel( new BorderLayout() );
142 		wsiPanel.setBackground( Color.WHITE );
143 		wsiPanel.setOpaque( true );
144 		
145 		wsiPanel.add( builderWsiToolbar(), BorderLayout.NORTH );
146 		
147 		return wsiPanel;
148 	}
149 
150 	private Component builderWsiToolbar()
151 	{
152 		JXToolBar toolbar = UISupport.createToolbar();
153 		
154 		toolbar.addFixed( UISupport.createToolbarButton( new RunWSIAction() ) );
155 		toolbar.addFixed( UISupport.createToolbarButton( new WSIOptionsAction() ));
156 		toolbar.addRelatedGap();
157 		saveWsiReportAction = new SaveWsiReportAction();
158 		toolbar.addFixed( UISupport.createToolbarButton(saveWsiReportAction ));
159 
160 		toolbar.addGlue();
161 		toolbar.addFixed( UISupport.createToolbarButton( new ShowOnlineHelpAction( HelpUrls.WSI_COMPLIANCE_HELP_URL ) ) );
162 		
163 		return toolbar;
164 	}
165 
166 	private Component buildInterfaceOverviewTab()
167 	{
168 		metrics = new MetricsPanel();
169 		JXToolBar toolbar = UISupport.createSmallToolbar();
170 		toolbar.addGlue();
171 		toolbar.addFixed( UISupport.createToolbarButton( new ShowOnlineHelpAction( HelpUrls.INTERFACE_OVERVIEW_HELP_URL ) ) );
172 		metrics.add(toolbar,BorderLayout.NORTH );
173 		MetricsSection section = metrics.addSection( "WSDL Definition" );
174 		
175 		try
176 		{
177 			section.addMetric( "WSDL URL", MetricType.URL ).set( iface.getDefinition() );
178 			section.addMetric( "Namespace" ).set( iface.getBindingName().getNamespaceURI() );
179 			section.addMetric( "Binding" ).set( iface.getBindingName().getLocalPart() );
180 			section.addMetric( "SOAP Version" ).set( iface.getSoapVersion().toString() );
181 			section.addMetric( "Style" ).set( iface.getStyle() );
182 			section.addMetric( "WS-A version" ).set( iface.getWsaVersion());
183 		}
184 		catch( Exception e )
185 		{
186 			UISupport.showErrorMessage( e );
187 		}
188 		
189 		section.finish();
190 		
191 		metrics.addSection( "Definition Parts" );
192 		section = metrics.addSection( "Operations" );
193 		operationsTableModel = new OperationsTableModel();
194 		JXTable table = section.addTable( operationsTableModel );
195 		table.getColumn( 1 ).setPreferredWidth( 60 );
196 		table.getColumn( 2 ).setPreferredWidth( 60 );
197 		section.finish();
198 		
199 		table.packColumn( 3, 10 );
200 		if( table.getColumn( 3 ).getPreferredWidth() < 250 )
201 			table.getColumn( 3 ).setPreferredWidth( 250 );
202 		
203 		return new JScrollPane( metrics );
204 	}
205 
206 	private Component buildEndpointsTab()
207 	{
208 		return iface.getProject().getEndpointStrategy().getConfigurationPanel( iface );
209 	}
210 
211 	private JComponent buildWsdlContentTab()
212    {
213       partTabs = new JTabbedPane();
214       partTabs.setTabLayoutPolicy( JTabbedPane.SCROLL_TAB_LAYOUT );
215 
216       rootNode = new DefaultMutableTreeNode( iface.getName() );
217 		treeModel = new DefaultTreeModel( rootNode );
218 		tree = new JTree( treeModel );
219 		tree.setBorder( BorderFactory.createEmptyBorder( 2, 2, 2, 2) );
220    	tree.setExpandsSelectedPaths( true );
221    	tree.addTreeSelectionListener( new InternalTreeSelectionListener() );
222    	tree.addMouseListener( new MouseAdapter() {
223 
224 			@Override
225 			public void mouseClicked( MouseEvent arg0 )
226 			{
227 				if( arg0.getClickCount() > 1 )
228 				{
229 					TreePath selectionPath = tree.getSelectionPath();
230 					if( selectionPath != null )
231 					{
232 						DefaultMutableTreeNode treeNode = ( DefaultMutableTreeNode ) selectionPath.getLastPathComponent();
233 						Object userObject = treeNode.getUserObject();
234 						if( userObject instanceof InspectItem )
235 						{
236 							InspectItem item = ( InspectItem ) userObject;
237 							if( item !=  null && item.selector != null )
238 							{
239 								item.selector.selectNode( item );
240 							}
241 						}
242 					}
243 				}
244 			}}  );
245    	
246    	JScrollPane scrollPane = new JScrollPane( tree );
247 		JSplitPane split = UISupport.createHorizontalSplit( scrollPane, 
248 		      	      	  UISupport.createTabPanel( partTabs, true ));
249    	
250 		split.setDividerLocation( 250  );
251 		split.setResizeWeight( 0.3 );
252       
253       initTreeModel( iface );
254       
255       JPanel panel = new JPanel( new BorderLayout() );
256       
257       panel.add( split, BorderLayout.CENTER );
258       panel.add( buildWsdlTabToolbar(), BorderLayout.PAGE_START );
259 		statusBar = new JEditorStatusBar( );
260 		panel.add( statusBar, BorderLayout.PAGE_END );
261       setPreferredSize( new Dimension( 600, 500 ));
262       
263       projectListener = new InternalProjectListener();
264 		iface.getProject().addProjectListener( projectListener );
265 		
266 		return panel;
267    }
268 
269 	private void initTreeModel( WsdlInterface iface )
270 	{
271 		try
272 		{
273         	 if( iface.getWsdlContext().loadIfNecessary())
274         	 {
275 	          XProgressDialog progressDialog = UISupport.getDialogs().createProgressDialog(
276 	       			"Loading Defintion", 3, "Initializing definition..", true );
277 	          Loader loader = new Loader( iface );
278 	          
279 	          if( progressDialog != null )
280 	          	progressDialog.setCancelLabel( "Run in background" );
281 	          
282 				 progressDialog.run( loader );
283 				 loader = null;
284 				 treeModel.nodeStructureChanged( rootNode );
285         	 }
286 		}
287 		catch (Exception e)
288 		{
289 			SoapUI.logError( e );
290 		}
291 	}
292 
293 	private Component buildWsdlTabToolbar()
294 	{
295 		JXToolBar toolbar = UISupport.createToolbar();
296 		
297 		toolbar.addFixed( UISupport.createToolbarButton( new BackwardAction() ) );
298 		toolbar.addFixed( UISupport.createToolbarButton( new ForwardAction() ) );
299 		toolbar.addUnrelatedGap();
300 		JButton button = UISupport.createToolbarButton( SwingActionDelegate.createDelegate( UpdateInterfaceAction.SOAPUI_ACTION_ID, getModelItem(),
301 					null, "/updateDefinition.gif" ));
302 		button.setText( null );
303 		toolbar.addFixed( button);
304 		button = UISupport.createToolbarButton( SwingActionDelegate.createDelegate( ExportDefinitionAction.SOAPUI_ACTION_ID, getModelItem(),
305 					null, "/exportDefinition.gif"));
306 		button.setText( null );
307 		toolbar.addFixed(UISupport.createToolbarButton(
308 				SwingActionDelegate.createDelegate( CreateWsdlDocumentationAction.SOAPUI_ACTION_ID, iface, null, "/export.gif")));
309 		toolbar.addFixed( button);
310 		toolbar.addGlue();
311 		button = UISupport.createToolbarButton( new ShowOnlineHelpAction( HelpUrls.WSDL_CONTENT_HELP_URL ));
312 		button.setText( null );
313 		toolbar.addFixed( button);
314 		
315 		return toolbar;
316 	}
317 
318 	private final class InternalProjectListener extends ProjectListenerAdapter
319 	{
320 		@Override
321 		public void interfaceUpdated( Interface iface )
322 		{
323 			if( iface == getModelItem() )
324 			{
325 				updatingInterface = true;
326 				partTabs.removeAll();
327 				tree.setSelectionRow( -1 );
328 				rootNode.removeAllChildren();
329 			   editors.clear();
330 				groupNodes.clear();
331 				pathMap.clear();
332 				targetNamespaces.clear();
333 				initTreeModel( ( WsdlInterface ) iface );
334 				operationsTableModel.fireTableDataChanged();
335 				updatingInterface = false;
336 			}
337 		}
338 	}
339 
340 	private final class InternalTreeSelectionListener implements TreeSelectionListener
341 	{
342 		public void valueChanged( TreeSelectionEvent e )
343 		{
344 			TreePath newLeadSelectionPath = e.getNewLeadSelectionPath();
345 			if( newLeadSelectionPath != null )
346 			{	
347 				if( !navigating )
348 				{
349 					// if we have moved back in history.. reverse before adding
350 					while( historyIndex < navigationHistory.size()-1 )
351 					{
352 						TreePath path = navigationHistory.remove( navigationHistory.size()-1 );
353 						navigationHistory.add( historyIndex++, path );
354 					}
355 					
356 					navigationHistory.add( newLeadSelectionPath );
357 					historyIndex = navigationHistory.size()-1;
358 				}
359 				
360 				DefaultMutableTreeNode tn = ( DefaultMutableTreeNode ) newLeadSelectionPath.getLastPathComponent();
361 				if( tn.getUserObject() instanceof InspectItem )
362 				{
363 					InspectItem item = ( InspectItem ) tn.getUserObject();
364 					
365 					partTabs.setSelectedIndex(  item.getTabIndex() );
366 					statusBar.setInfo( item.getDescription() );
367 					
368 					JEditTextArea editor = editors.get(  item.getTabIndex() );
369 					int lineNumber = item.getLineNumber();
370 					if( lineNumber > 0 && editor.getLineStartOffset( lineNumber ) >= 0 )
371 					{
372 						editor.setCaretPosition( editor.getLineStartOffset( lineNumber ) );
373 					}
374 					else
375 					{
376 						editor.setCaretPosition(  0  );
377 					}
378 				}
379 				
380 				tree.scrollPathToVisible( newLeadSelectionPath );
381 				tree.expandPath( newLeadSelectionPath );
382 			}
383 		}
384 	}
385 
386 	private class Loader implements Worker
387    {
388       private static final String DEFINITION_PARTS_SECTION = "Definition Parts";
389 		private ProgressDialog progressDialog;
390 		private final WsdlInterface iface;
391 		private JProgressBar progressBar;
392 
393       public Loader( WsdlInterface iface )
394       {
395 			this.iface = iface;
396       }
397       
398 		public Object construct( XProgressMonitor monitor )
399       {
400 			MetricsSection section = metrics.getSection( DEFINITION_PARTS_SECTION );
401 			section.clear();
402 			
403       	try
404          {
405          	List<InterfaceDefinitionPart> schemas = iface.getWsdlContext().getDefinitionParts();
406          	int tabCount = partTabs.getTabCount();
407          	
408             for (InterfaceDefinitionPart part : schemas )
409             {
410 					addTab( part.getUrl(), part.getContent() );
411             }
412             
413             while( tabCount-- > 0 )
414             	partTabs.remove( 0 );
415             
416             return null;
417          }
418          catch (Exception e)
419          {
420          	logger.error( "Failed to load WSDL; " + e.getClass().getSimpleName() + "; " + e.getMessage() ); 
421             add( new JLabel( "Failed to load WSDL; " + e.toString() ), BorderLayout.NORTH );
422          	
423             SoapUI.logError( e );
424             
425             return e;
426          }
427          finally
428          {
429          	section.finish();
430          }
431          
432          
433       }
434       
435       private void addTab(String url, String content) throws Exception
436    	{
437       	int ix = url.startsWith( "file:" ) ? url.lastIndexOf( File.separatorChar ) : url.lastIndexOf( '/' );
438       	if( ix == -1 )
439       		ix = url.lastIndexOf( '/' );
440       	
441       	String title = url.substring( ix+1);
442 
443       	metrics.getSection( DEFINITION_PARTS_SECTION ).addMetric( title, MetricType.URL ).set( url );
444       	
445       	if( progressBar != null )
446       		progressBar.setString( title );
447       	else if( progressDialog != null )
448       		progressDialog.setProgress( 1, title );
449       	
450    		JPanel panel = new JPanel( new BorderLayout() );
451    		JLabel label = new JLabel( url );
452    		label.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ));
453    		panel.add( label, BorderLayout.NORTH );
454    		
455    		JXEditTextArea inputArea = JXEditTextArea.createXmlEditor( false );
456    		StringWriter writer = new StringWriter();
457    		XmlUtils.serializePretty( XmlObject.Factory.parse( content ), writer );
458          String xmlString = writer.toString();
459 
460          // reparse so linenumbers are correct
461          XmlObject xmlObject = XmlObject.Factory.parse(xmlString, new XmlOptions().setLoadLineNumbers());
462          
463    		inputArea.setText( xmlString );
464          inputArea.setEditable( false );
465          inputArea.getPainter().setLineHighlightEnabled( true );
466          
467          JPanel p = new JPanel( new BorderLayout() );
468    		p.add( inputArea, BorderLayout.CENTER );
469    		p.add( new LineNumbersPanel( inputArea ), BorderLayout.WEST );
470          
471          panel.add( new JScrollPane( p ), BorderLayout.CENTER );
472 			partTabs.addTab( title, panel );
473 
474 			if( tree != null )
475 			{
476 				initInspectionTree( xmlObject, inputArea );
477 			}
478 		}
479 
480 		private void initInspectionTree( XmlObject xmlObject, JXEditTextArea inputArea )
481 		{
482 			DefaultMutableTreeNode treeRoot = rootNode;
483 			
484 			targetNamespaces.add( SchemaUtils.getTargetNamespace( xmlObject ));
485 			
486 			int tabCount = partTabs.getTabCount()-1;
487 			mapTreeItems( xmlObject, treeRoot, false, tabCount, "Complex Types", 
488 						"declare namespace xs='http://www.w3.org/2001/XMLSchema';//xs:complexType[@name!='']", "@name", true, null );
489 
490 			mapTreeItems( xmlObject, treeRoot, false, tabCount, "Simple Types", 
491 						"declare namespace xs='http://www.w3.org/2001/XMLSchema';//xs:simpleType[@name!='']", "@name", true, null );
492 
493 			mapTreeItems( xmlObject, treeRoot, false, tabCount, "Anonymous Complex Types", 
494 						"declare namespace xs='http://www.w3.org/2001/XMLSchema';//xs:complexType[not(exists(@name))]", 
495 						"parent::node()/@name", true, null );
496 
497 			mapTreeItems( xmlObject, treeRoot, false, tabCount, "Global Elements", 
498 						"declare namespace xs='http://www.w3.org/2001/XMLSchema';//xs:schema/xs:element[@name!='']", "@name", 
499 						true, new GlobalElementSelector() );
500 
501 			mapTreeItems( xmlObject, treeRoot, false, tabCount, "Schemas", 
502 						"declare namespace xs='http://www.w3.org/2001/XMLSchema';//xs:schema", "@targetNamespace", true, null );
503 
504 			List<DefaultMutableTreeNode> messages = mapTreeItems( xmlObject, treeRoot, false, tabCount, "Messages", 
505 						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';//wsdl:message", "@name", true, null );
506 			
507 			
508 			for( DefaultMutableTreeNode treeNode : messages )
509 			{
510 				mapTreeItems( ((InspectItem)treeNode.getUserObject()).item, treeNode, false, 
511 							tabCount, null, "declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:part", 
512 							"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';concat('part: name=[', @name, '] type=[', @type, '] element=[', @element, ']' )", 
513 							true, new PartSelector() );
514 			}
515 			
516 			List<DefaultMutableTreeNode> portTypes = mapTreeItems( xmlObject, treeRoot, false, tabCount, "PortTypes", 
517 						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';//wsdl:portType", "@name", true, null );
518 			
519 			
520 			for( DefaultMutableTreeNode treeNode : portTypes )
521 			{
522 				List<DefaultMutableTreeNode> operationNodes = mapTreeItems( ((InspectItem)treeNode.getUserObject()).item, treeNode, false, 
523 							tabCount, null, "declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:operation", "@name", true, null );
524 				
525 				for( DefaultMutableTreeNode treeNode2 : operationNodes )
526 				{
527 					mapTreeItems( ((InspectItem)treeNode2.getUserObject()).item, treeNode2, false, tabCount, null, 
528 								"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:*", "concat( @name, ' [', local-name(), '], message=[', @message, ']' )", false,
529 								new MessageSelector());
530 				}
531 			}
532 			
533 			List<DefaultMutableTreeNode> bindings = mapTreeItems( xmlObject, treeRoot, false, tabCount, "Bindings", 
534 						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';//wsdl:binding", 
535 						"declare namespace wsdlsoap='http://schemas.xmlsoap.org/wsdl/soap/';concat( @name, ' [style=', wsdlsoap:binding[1]/@style, ']' )", true, null );
536 			
537 			for( DefaultMutableTreeNode treeNode : bindings )
538 			{
539 				List<DefaultMutableTreeNode> operationNodes = mapTreeItems( ((InspectItem)treeNode.getUserObject()).item, treeNode, false, 
540 							tabCount, null, "declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:operation", 
541 							"declare namespace wsdlsoap='http://schemas.xmlsoap.org/wsdl/soap/';concat( @name, ' [soapAction=', wsdlsoap:operation/@soapAction, ']' )", 
542 							true, null );
543 				
544 				for( DefaultMutableTreeNode treeNode2 : operationNodes )
545 				{
546 					mapTreeItems( ((InspectItem)treeNode2.getUserObject()).item, treeNode2, false, tabCount, null, 
547 								"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:*", "concat( @name, ' [', local-name(), ']' )", false,
548 								new BindingOperationSelector() );
549 				}
550 			}
551 			
552 			List<DefaultMutableTreeNode> services = mapTreeItems( xmlObject, treeRoot, false, tabCount, "Services", 
553 						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';//wsdl:service", "@name", true, null );
554 			
555 			for( DefaultMutableTreeNode treeNode : services )
556 			{
557 				mapTreeItems( ((InspectItem)treeNode.getUserObject()).item, treeNode, false, tabCount, null, 
558 							"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:port", "concat( 'port: name=[', @name, '] binding=[', @binding, ']' )", true, 
559 							new PortSelector() );
560 			}
561 			
562 			tree.expandRow( 0 );
563 			editors.add(  inputArea );
564 		}
565 
566       public void finished()
567       {
568       	if( progressDialog != null )
569       		progressDialog.setVisible( false );
570       	
571       	progressDialog = null;
572       }
573 
574 		public boolean onCancel()
575 		{
576 			progressBar = new JProgressBar(0, 1);
577 			progressBar.setSize( new Dimension( 120, 20 ));
578 		   progressBar.setStringPainted(true);
579 		   progressBar.setString("Loading Definition.." );
580 		   progressBar.setIndeterminate(true);
581 			
582 		   ButtonBarBuilder builder = ButtonBarBuilder.createLeftToRightBuilder();	
583          builder.addGlue();
584 			builder.addFixed( progressBar );
585          builder.addGlue();
586          builder.setBorder( BorderFactory.createEmptyBorder( 10, 10, 10, 10 ));
587          
588 			partTabs.addTab( "Loading.. ", builder.getPanel()  );
589 			return true;
590 		}
591    }
592 	 
593 	public boolean dependsOn(ModelItem modelItem)
594 	{
595 		return modelItem == getModelItem() || modelItem == getModelItem().getProject();
596 	}
597 
598 	public List<DefaultMutableTreeNode> mapTreeItems( XmlObject xmlObject, DefaultMutableTreeNode treeRoot, boolean createEmpty, 
599 				int tabIndex, String groupName, String query, String nameQuery, boolean sort, NodeSelector selector )
600 	{
601 		List<DefaultMutableTreeNode> resultNodes = new ArrayList<DefaultMutableTreeNode>();
602 		
603 		try
604 		{
605 			XmlObject[] items = xmlObject.selectPath( query );
606 			List<DefaultMutableTreeNode> treeNodes = new ArrayList<DefaultMutableTreeNode>();
607 			
608 			DefaultMutableTreeNode root = treeRoot;
609 			if( groupName != null )
610 			{
611 				String groupKey = new TreePath( root.getPath() ).toString() + "/" + groupName;
612 				root = groupNodes.get( groupKey );
613 				if( root == null && (items.length > 0 || createEmpty))
614 				{
615 					root = new DefaultMutableTreeNode( groupName );
616 					treeRoot.add(  root );
617 					groupNodes.put( groupKey, root );
618 				}
619 				else if( root != null )
620 				{
621 					Enumeration<?> children = root.children();
622 					while( children.hasMoreElements() )
623 						treeNodes.add( ( DefaultMutableTreeNode ) children.nextElement() );
624 				}
625 			}
626 			
627 			if( items.length == 0 )
628 				return resultNodes;
629 			
630 			for( XmlObject item : items )
631 			{
632 				XmlObject[] selectPath = item.selectPath(  nameQuery );
633 				if( selectPath.length > 0 )
634 				{
635 					DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode( new InspectItem( item, selectPath[0], tabIndex, selector ));
636 					treeNodes.add( treeNode);
637 					resultNodes.add( treeNode );
638 				}
639 			}
640 			
641 			if( sort )
642 			{
643 				Collections.sort( treeNodes, new Comparator<DefaultMutableTreeNode>() {
644 
645 					public int compare( DefaultMutableTreeNode o1, DefaultMutableTreeNode o2 )
646 					{
647 						return o1.toString().compareTo( o2.toString() );
648 					}} );
649 			}
650 			
651 			root.removeAllChildren();
652 			
653 			for( DefaultMutableTreeNode treeNode : treeNodes )
654 			{
655 				root.add( treeNode );
656 				
657 				String path = "/" + getTreeNodeName( treeNode );
658 				TreePath treePath = new TreePath( treeNode.getPath());
659 				while( treeNode.getParent() != null )
660 				{
661 					treeNode = ( DefaultMutableTreeNode ) treeNode.getParent();
662 					path = "/" + getTreeNodeName( treeNode ) + path;
663 				}
664 				
665 				pathMap.put( path, treePath );
666 			}
667 		}
668 		catch( Throwable e )
669 		{
670 			SoapUI.log( "Failed to map items for query [" + query + "]:[" + nameQuery + "]" );
671 			SoapUI.logError( e );
672 		}
673 		
674 		return resultNodes;
675 	}
676 	
677 	private String getTreeNodeName( DefaultMutableTreeNode treeNode )
678 	{
679 		Object userObject = treeNode.getUserObject();
680 		if( userObject instanceof InspectItem )
681 			return (( InspectItem ) userObject ).getName();
682 		else
683 			return treeNode.toString();
684 	}
685 
686 	private final class InspectItem
687 	{
688 		private final XmlObject item;
689 		private String name;
690 		private final int tabIndex;
691 		private XmlLineNumber lineNumber;
692 		private final NodeSelector selector;
693 
694 		public InspectItem( XmlObject item, XmlObject nameObj,  int tabIndex, NodeSelector selector )
695 		{
696 			this.item = item;
697 			this.selector = selector;
698 			this.name = XmlUtils.getNodeValue( nameObj.getDomNode() );
699 			if( name == null )
700 				name = nameObj.toString();
701 			this.tabIndex = tabIndex;
702 			
703 			ArrayList<?> list = new ArrayList<Object>();
704 			XmlCursor cursor = item.newCursor();
705 			cursor.getAllBookmarkRefs( list );
706 			
707 			for( Object o : list )
708 				if( o instanceof XmlLineNumber )
709 					lineNumber = (XmlLineNumber) o;
710 			
711 			cursor.dispose();
712 		}
713 		
714 		public String getDescription()
715 		{
716 			return getName() + "@" + targetNamespaces.get( tabIndex );
717 		}
718 
719 		public String getName()
720 		{
721 			int ix = name.indexOf( ' ' );
722 			return ix == -1 ? name : name.substring( 0, ix );
723 		}
724 
725 		public int getTabIndex()
726 		{
727 			return tabIndex;
728 		}
729 
730 		public int getLineNumber() 
731 		{
732 			return lineNumber == null ? -1 : lineNumber.getLine()-1;
733 		}
734 		
735 		@Override
736 		public String toString()
737 		{
738 			return name;
739 		}
740 
741 		public NodeSelector getSelector()
742 		{
743 			return selector;
744 		}
745 
746 		public Element getElement()
747 		{
748 			return ( Element ) item.getDomNode();
749 		}
750 	}
751 
752 	public boolean onClose( boolean canCancel )
753 	{
754 		if( projectListener != null )
755 			getModelItem().getProject().removeProjectListener( projectListener );
756 		
757 		return release();
758 	}
759 	
760 	private void simpleSelect( InspectItem item, String attribute, String targetGroup )
761 	{
762 		Element elm = item.getElement();
763 		String type = elm.getAttribute( attribute );
764 		if( type.length() > 0 )
765 		{
766 			int ix = type.indexOf( ':' );
767 			if( ix != -1 )
768 				type = type.substring( ix+1 );
769 			
770 			
771 			TreePath treePath = pathMap.get( "/" + getModelItem().getName() + "/" + targetGroup + "/" + type );
772 			if( treePath != null )
773 			{
774 				tree.setSelectionPath( treePath );
775 			}
776 		}
777 	}
778 
779 	protected interface NodeSelector
780 	{
781 		public void selectNode( InspectItem item );
782 	}
783 	
784 	public class PartSelector implements NodeSelector
785 	{
786 		public void selectNode( InspectItem item )
787 		{
788 			Element elm = item.getElement();
789 			String type = elm.getAttribute( "type" );
790 			String element = elm.getAttribute( "element" );
791 			if( type.length() > 0 )
792 			{
793 				simpleSelect( item, "type", "Complex Types" );
794 			}
795 			else if( element.length() > 0 )
796 			{
797 				simpleSelect( item, "element", "Global Elements" );
798 			}
799 		}}
800 	
801 	public class MessageSelector implements NodeSelector
802 	{
803 		public void selectNode( InspectItem item )
804 		{
805 			simpleSelect( item, "message", "Messages" );
806 		}}
807 	
808 	public class GlobalElementSelector implements NodeSelector
809 	{
810 		public void selectNode( InspectItem item )
811 		{
812 			simpleSelect( item, "type", "Complex Types" );
813 		}}
814 	
815 	public class PortSelector implements NodeSelector
816 	{
817 		public void selectNode( InspectItem item )
818 		{
819 			simpleSelect( item, "binding", "Bindings" );
820 		}}
821 	
822 	public class BindingOperationSelector implements NodeSelector
823 	{
824 		public void selectNode( InspectItem item )
825 		{
826 			Element elm = item.getElement();
827 			String name = elm.getAttribute( "name" );
828 			
829 			Element operationElm = ( Element ) elm.getParentNode();
830 			Element bindingElm = ( Element ) operationElm.getParentNode();
831 			
832 			String type = bindingElm.getAttribute( "type" );
833 			
834 			if( type.length() > 0 )
835 			{
836 				int ix = type.indexOf( ':' );
837 				if( ix != -1 )
838 					type = type.substring( ix+1 );
839 				
840 				TreePath treePath = pathMap.get( "/" + getModelItem().getName() + "/PortTypes/" + type + "/" + 
841 							operationElm.getAttribute( "name" ) + "/" + name );
842 				if( treePath != null )
843 				{
844 					tree.setSelectionPath( treePath );
845 				}
846 			}
847 		}}
848 	
849 	private class BackwardAction extends AbstractAction
850 	{
851 		public BackwardAction()
852 		{
853 			putValue( SMALL_ICON, UISupport.createImageIcon( "/arrow_left.png" ));
854 			putValue( Action.SHORT_DESCRIPTION, "Navigate to previous selection" );
855 		}
856 		
857 		public void actionPerformed( ActionEvent arg0 )
858 		{
859 			if( historyIndex > 0 )
860 			{
861 				historyIndex--;
862 				navigating = true;
863 				tree.setSelectionPath( navigationHistory.get( historyIndex ) );
864 				navigating = false;
865 			}
866 		}}
867 	
868 	private class ForwardAction extends AbstractAction
869 	{
870 		public ForwardAction()
871 		{
872 			putValue( SMALL_ICON, UISupport.createImageIcon( "/arrow_right.png" ));
873 			putValue( Action.SHORT_DESCRIPTION, "Navigate to next selection" );
874 		}
875 		
876 		public void actionPerformed( ActionEvent arg0 )
877 		{
878 			if( historyIndex < navigationHistory.size()-1 )
879 			{
880 				historyIndex++;
881 				navigating = true;
882 				tree.setSelectionPath( navigationHistory.get( historyIndex ) );
883 				navigating = false;
884 			}
885 			
886 		}}
887 	
888 	private class RunWSIAction extends AbstractAction
889 	{
890 		public RunWSIAction()
891 		{
892 			putValue( SMALL_ICON, UISupport.createImageIcon( "/run.gif" ));
893 			putValue( Action.SHORT_DESCRIPTION, "Creates a WS-I report for this interface" );
894 		}
895 		
896 		public void actionPerformed( ActionEvent arg0 )
897 		{
898 			WSIAnalyzeAction action = new WSIAnalyzeAction()
899 			{
900 				@Override
901 				protected void showReport( File reportFile, String configFile ) throws Exception
902 				{
903 					reportPanel = new WSIReportPanel( reportFile, configFile, null, false );
904 					reportPanel.setPreferredSize( new Dimension( 600, 400 ));
905 					if( wsiPanel.getComponentCount() > 1 )
906 						wsiPanel.remove( 1 );
907 					
908 					wsiPanel.add( reportPanel, BorderLayout.CENTER );
909 					wsiPanel.revalidate();
910 					saveWsiReportAction.setEnabled( true );
911 				}
912 			};
913 			
914 			action.perform( getModelItem(), null );
915 		}}
916 	
917 	private class WSIOptionsAction extends AbstractAction
918 	{
919 		public WSIOptionsAction()
920 		{
921 			putValue( SMALL_ICON, UISupport.createImageIcon( "/options.gif" ));
922 			putValue( Action.SHORT_DESCRIPTION, "Sets WS-I report creation options" );
923 		}
924 		
925 		public void actionPerformed( ActionEvent arg0 )
926 		{
927 			SoapUIPreferencesAction.getInstance().show( SoapUIPreferencesAction.WS_I_SETTINGS );
928 		}
929 	}
930 
931 	private class SaveWsiReportAction extends AbstractAction
932 	{
933 		public SaveWsiReportAction()
934 		{
935 			putValue( SMALL_ICON, UISupport.createImageIcon( "/export.gif" ));
936 			putValue( Action.SHORT_DESCRIPTION, "Saved the current WS-I report to a file" );
937 			
938 			setEnabled( false );
939 		}
940 		
941 		public void actionPerformed( ActionEvent arg0 )
942 		{
943 			if( reportPanel != null )
944 				reportPanel.getSaveReportAction().actionPerformed( null );
945 		}
946 	}
947 	
948 	private class OperationsTableModel extends AbstractTableModel
949 	{
950 		public int getColumnCount()
951 		{
952 			return 4;
953 		}
954 
955 		public int getRowCount()
956 		{
957 			return iface.getOperationCount();
958 		}
959 
960 		@Override
961 		public String getColumnName( int column )
962 		{
963 			switch( column )
964 			{
965 			case 0 : return "Name";
966 			case 1 : return "Use";
967 			case 2 : return "One-Way";
968 			case 3 : return "Action";
969 			}
970 			
971 			return null;
972 		}
973 
974 		public Object getValueAt( int rowIndex, int columnIndex )
975 		{
976 			if( updatingInterface )
977 				return "<updating>";
978 			
979 			WsdlOperation operation = iface.getOperationAt( rowIndex );
980 			BindingOperation bindingOperation = operation.getBindingOperation();
981 			
982 			switch( columnIndex )
983 			{
984 			case 0 : return operation.getName();
985 			case 1 : 
986 			{
987 				boolean in = WsdlUtils.isInputSoapEncoded( bindingOperation );
988 				boolean out = operation.isUnidirectional() ? false : WsdlUtils.isOutputSoapEncoded( bindingOperation );
989 				
990 				if( out && in )
991 					return "SOAP Encoding";
992 				if( !out && !in )
993 					return "Literal";
994 				
995 				return "Mixed";
996 			}
997 			case 3 : return operation.getAction();
998 			case 2 : return Boolean.valueOf( operation.isUnidirectional() );
999 			}
1000 			
1001 			return null;
1002 		}}
1003 }