View Javadoc

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