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