View Javadoc

1   /*
2    *  soapUI, copyright (C) 2006 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of the GNU Lesser General Public License as published by the Free Software Foundation; 
6    *  either version 2.1 of the License, or (at your option) any later version.
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.Component;
17  import java.awt.Dimension;
18  import java.awt.event.ActionEvent;
19  import java.awt.event.MouseAdapter;
20  import java.awt.event.MouseEvent;
21  import java.io.File;
22  import java.io.StringWriter;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.Comparator;
26  import java.util.Enumeration;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  
32  import javax.swing.AbstractAction;
33  import javax.swing.Action;
34  import javax.swing.BorderFactory;
35  import javax.swing.JButton;
36  import javax.swing.JLabel;
37  import javax.swing.JPanel;
38  import javax.swing.JProgressBar;
39  import javax.swing.JScrollPane;
40  import javax.swing.JSplitPane;
41  import javax.swing.JTabbedPane;
42  import javax.swing.JTree;
43  import javax.swing.event.TreeSelectionEvent;
44  import javax.swing.event.TreeSelectionListener;
45  import javax.swing.tree.DefaultMutableTreeNode;
46  import javax.swing.tree.DefaultTreeModel;
47  import javax.swing.tree.TreePath;
48  
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.syntax.jedit.JEditTextArea;
55  import org.w3c.dom.Element;
56  
57  import com.eviware.soapui.impl.wsdl.WsdlInterface;
58  import com.eviware.soapui.impl.wsdl.actions.iface.ExportDefinitionAction;
59  import com.eviware.soapui.impl.wsdl.actions.iface.UpdateInterfaceAction;
60  import com.eviware.soapui.impl.wsdl.actions.support.ShowOnlineHelpAction;
61  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
62  import com.eviware.soapui.impl.wsdl.support.xsd.SchemaUtils;
63  import com.eviware.soapui.model.ModelItem;
64  import com.eviware.soapui.support.UISupport;
65  import com.eviware.soapui.support.components.JEditorStatusBar;
66  import com.eviware.soapui.support.components.JXToolBar;
67  import com.eviware.soapui.support.components.ProgressDialog;
68  import com.eviware.soapui.support.types.StringList;
69  import com.eviware.soapui.support.xml.JXEditTextArea;
70  import com.eviware.soapui.support.xml.XmlUtils;
71  import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
72  import com.eviware.x.dialogs.Worker;
73  import com.eviware.x.dialogs.XProgressDialog;
74  import com.eviware.x.dialogs.XProgressMonitor;
75  import com.jgoodies.forms.builder.ButtonBarBuilder;
76  
77  /***
78   * DesktopPanel for WsdlInterface. Loads all referenced wsdls/xsds for the specified WsdlInterface
79   * and displays these in seperate tabs
80   * 
81   * @author Ole.Matzura
82   */
83  
84  public class WsdlInterfaceDesktopPanel extends ModelItemDesktopPanel<WsdlInterface>
85  {
86  	private final static Logger logger = Logger.getLogger( WsdlInterfaceDesktopPanel.class );
87     private JTabbedPane tabbedPane;
88     private List<JEditTextArea> editors = new ArrayList<JEditTextArea>();
89  	private JTree tree;
90  	private Map<String,DefaultMutableTreeNode> groupNodes = new HashMap<String,DefaultMutableTreeNode>();
91  	private Map<String,TreePath> pathMap = new HashMap<String,TreePath>();
92  	private List<TreePath> navigationHistory = new ArrayList<TreePath>();
93  	private StringList targetNamespaces = new StringList();
94  	public int historyIndex;
95  	public boolean navigating;
96  	private JEditorStatusBar statusBar;
97  
98     public WsdlInterfaceDesktopPanel(WsdlInterface iface)
99     {
100    	super( iface );
101 
102       tabbedPane = new JTabbedPane();
103       tabbedPane.setTabLayoutPolicy( JTabbedPane.SCROLL_TAB_LAYOUT );
104 
105       DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode( iface.getName() );
106 		DefaultTreeModel treeModel = new DefaultTreeModel( rootNode );
107 		tree = new JTree( treeModel );
108 		tree.setBorder( BorderFactory.createEmptyBorder( 2, 2, 2, 2) );
109    	tree.setExpandsSelectedPaths( true );
110    	tree.addTreeSelectionListener( new InternalTreeSelectionListener() );
111    	tree.addMouseListener( new MouseAdapter() {
112 
113 			@Override
114 			public void mouseClicked( MouseEvent arg0 )
115 			{
116 				if( arg0.getClickCount() > 1 )
117 				{
118 					TreePath selectionPath = tree.getSelectionPath();
119 					if( selectionPath != null )
120 					{
121 						DefaultMutableTreeNode treeNode = ( DefaultMutableTreeNode ) selectionPath.getLastPathComponent();
122 						Object userObject = treeNode.getUserObject();
123 						if( userObject instanceof InspectItem )
124 						{
125 							InspectItem item = ( InspectItem ) userObject;
126 							if( item !=  null && item.selector != null )
127 							{
128 								item.selector.selectNode( item );
129 							}
130 						}
131 					}
132 				}
133 			}}  );
134    	
135    	JScrollPane scrollPane = new JScrollPane( tree );
136 		JSplitPane split = UISupport.createHorizontalSplit( scrollPane, 
137 		      	      	  UISupport.createTabPanel( tabbedPane, true ));
138    	
139 		add( split, BorderLayout.CENTER );
140 		
141 		split.setDividerLocation( 250  );
142 		split.setResizeWeight( 0.3 );
143       
144       
145       try
146 		{
147         	 if( iface.getWsdlContext().loadIfNecessary())
148         	 {
149 	          XProgressDialog progressDialog = UISupport.getDialogs().createProgressDialog(
150 	       			"Loading Defintion", 3, "Initializing definition..", true );
151 	          Loader loader = new Loader( iface );
152 	          
153 	          if( progressDialog != null )
154 	          	progressDialog.setCancelLabel( "Run in background" );
155 	          
156 				 progressDialog.run( loader );
157 				 treeModel.nodeStructureChanged( rootNode );
158         	 }
159 		}
160 		catch (Exception e)
161 		{
162 			e.printStackTrace();
163 		}
164       
165 		add( buildToolbar(), BorderLayout.PAGE_START );
166 		statusBar = new JEditorStatusBar( );
167 		add( statusBar, BorderLayout.PAGE_END );
168       setPreferredSize( new Dimension( 600, 500 ));
169    }
170 
171 	private Component buildToolbar()
172 	{
173 		JXToolBar toolbar = UISupport.createToolbar();
174 		
175 		toolbar.addFixed( new JButton( new BackwardAction() ) );
176 		toolbar.addFixed( new JButton( new ForwardAction() ) );
177 		toolbar.addUnrelatedGap();
178 		JButton button = new JButton( new UpdateInterfaceAction( getModelItem() ));
179 		button.setText( null );
180 		toolbar.addFixed( button);
181 		button = new JButton( new ExportDefinitionAction( getModelItem() ));
182 		button.setText( null );
183 		toolbar.addFixed( button);
184 		toolbar.addGlue();
185 		button = new JButton( new ShowOnlineHelpAction( HelpUrls.INTERFACE_HELP_URL ));
186 		button.setText( null );
187 		toolbar.addFixed( button);
188 		
189 		return toolbar;
190 	}
191 
192 	private final class InternalTreeSelectionListener implements TreeSelectionListener
193 	{
194 		public void valueChanged( TreeSelectionEvent e )
195 		{
196 			TreePath newLeadSelectionPath = e.getNewLeadSelectionPath();
197 			if( newLeadSelectionPath != null )
198 			{	
199 				if( !navigating )
200 				{
201 					// if we have moved back in history.. reverse before adding
202 					while( historyIndex < navigationHistory.size()-1 )
203 					{
204 						TreePath path = navigationHistory.remove( navigationHistory.size()-1 );
205 						navigationHistory.add( historyIndex++, path );
206 					}
207 					
208 					navigationHistory.add( newLeadSelectionPath );
209 					historyIndex = navigationHistory.size()-1;
210 				}
211 				
212 				DefaultMutableTreeNode tn = ( DefaultMutableTreeNode ) newLeadSelectionPath.getLastPathComponent();
213 				if( tn.getUserObject() instanceof InspectItem )
214 				{
215 					InspectItem item = ( InspectItem ) tn.getUserObject();
216 					
217 					tabbedPane.setSelectedIndex(  item.getTabIndex() );
218 					statusBar.setInfo( item.getDescription() );
219 					
220 					JEditTextArea editor = editors.get(  item.getTabIndex() );
221 					int lineNumber = item.getLineNumber();
222 					if( lineNumber > 0 && editor.getLineStartOffset( lineNumber ) >= 0 )
223 					{
224 						editor.setCaretPosition( editor.getLineStartOffset( lineNumber ) );
225 					}
226 					else
227 					{
228 						editor.setCaretPosition(  0  );
229 					}
230 				}
231 				
232 				tree.scrollPathToVisible( newLeadSelectionPath );
233 				tree.expandPath( newLeadSelectionPath );
234 			}
235 		}
236 	}
237 
238 	private class Loader implements Worker
239    {
240       private ProgressDialog progressDialog;
241 		private final WsdlInterface iface;
242 		private JProgressBar progressBar;
243 
244       public Loader( WsdlInterface iface )
245       {
246 			this.iface = iface;
247       }
248       
249 		public Object construct( XProgressMonitor monitor )
250       {
251       	try
252          {
253          	Map<String, XmlObject> schemas = iface.getWsdlContext().getDefinitionParts();
254          	int tabCount = tabbedPane.getTabCount();
255          	
256             for (Iterator<String> iter = schemas.keySet().iterator(); iter.hasNext();)
257             {
258    				String url = iter.next();
259 					addTab( url, schemas.get( url ) );
260             }
261             
262             while( tabCount-- > 0 )
263             	tabbedPane.remove( 0 );
264             
265             return null;
266          }
267          catch (Exception e)
268          {
269          	logger.error( "Failed to load WSDL; " + e.getClass().getSimpleName() + "; " + e.getMessage() ); 
270             add( new JLabel( "Failed to load WSDL; " + e.toString() ), BorderLayout.NORTH );
271          	
272             e.printStackTrace();
273             
274             return e;
275          }
276       }
277       
278       private void addTab(String url, XmlObject xmlObject) throws Exception
279    	{
280       	int ix = url.startsWith( "file:" ) ? url.lastIndexOf( File.separatorChar ) : url.lastIndexOf( '/' );
281       	String title = url.substring( ix+1);
282       	
283       	if( progressBar != null )
284       		progressBar.setString( title );
285       	else if( progressDialog != null )
286       		progressDialog.setProgress( 1, title );
287       	
288    		JPanel panel = new JPanel( new BorderLayout() );
289    		JLabel label = new JLabel( url );
290    		label.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ));
291    		panel.add( label, BorderLayout.NORTH );
292    		
293    		JXEditTextArea inputArea = JXEditTextArea.createXmlEditor();
294    		StringWriter writer = new StringWriter();
295    		XmlUtils.serializePretty( xmlObject, writer );
296          String xmlString = writer.toString();
297 
298          // reparse so linenumbers are correct
299          xmlObject = XmlObject.Factory.parse( xmlString, new XmlOptions().setLoadLineNumbers() );
300          
301    		inputArea.setText( xmlString );
302          inputArea.setEditable( false );
303          inputArea.getPainter().setLineHighlightEnabled( true );
304          
305          panel.add( new JScrollPane( inputArea ), BorderLayout.CENTER );
306 			tabbedPane.addTab( title, panel );
307 
308 			if( tree != null )
309 			{
310 				initInspectionTree( xmlObject, inputArea );
311 			}
312 		}
313 
314 		private void initInspectionTree( XmlObject xmlObject, JXEditTextArea inputArea )
315 		{
316 			DefaultMutableTreeNode treeRoot = ((DefaultMutableTreeNode)tree.getModel().getRoot());
317 			
318 			targetNamespaces.add( SchemaUtils.getTargetNamespace( xmlObject ));
319 			
320 			int tabCount = tabbedPane.getTabCount()-1;
321 			mapTreeItems( xmlObject, treeRoot, false, tabCount, "Complex Types", 
322 						"declare namespace xs='http://www.w3.org/2001/XMLSchema';//xs:complexType[@name!='']", "@name", true, null );
323 
324 			mapTreeItems( xmlObject, treeRoot, false, tabCount, "Anonymous Complex Types", 
325 						"declare namespace xs='http://www.w3.org/2001/XMLSchema';//xs:complexType[not(exists(@name))]", 
326 						"parent::node()/@name", true, null );
327 
328 			mapTreeItems( xmlObject, treeRoot, false, tabCount, "Global Elements", 
329 						"declare namespace xs='http://www.w3.org/2001/XMLSchema';//xs:schema/xs:element[@name!='']", "@name", 
330 						true, new GlobalElementSelector() );
331 
332 			mapTreeItems( xmlObject, treeRoot, false, tabCount, "Schemas", 
333 						"declare namespace xs='http://www.w3.org/2001/XMLSchema';//xs:schema", "@targetNamespace", true, null );
334 
335 			List<DefaultMutableTreeNode> messages = mapTreeItems( xmlObject, treeRoot, false, tabCount, "Messages", 
336 						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';//wsdl:message", "@name", true, null );
337 			
338 			
339 			for( DefaultMutableTreeNode treeNode : messages )
340 			{
341 				mapTreeItems( ((InspectItem)treeNode.getUserObject()).item, treeNode, false, 
342 							tabCount, null, "declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:part", 
343 							"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';concat('part: name=[', @name, '] type=[', @type, '] element=[', @element, ']' )", 
344 							true, new PartSelector() );
345 			}
346 			
347 			List<DefaultMutableTreeNode> portTypes = mapTreeItems( xmlObject, treeRoot, false, tabCount, "PortTypes", 
348 						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';//wsdl:portType", "@name", true, null );
349 			
350 			
351 			for( DefaultMutableTreeNode treeNode : portTypes )
352 			{
353 				List<DefaultMutableTreeNode> operationNodes = mapTreeItems( ((InspectItem)treeNode.getUserObject()).item, treeNode, false, 
354 							tabCount, null, "declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:operation", "@name", true, null );
355 				
356 				for( DefaultMutableTreeNode treeNode2 : operationNodes )
357 				{
358 					mapTreeItems( ((InspectItem)treeNode2.getUserObject()).item, treeNode2, false, tabCount, null, 
359 								"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:*", "concat( @name, ' [', local-name(), '], message=[', @message, ']' )", false,
360 								new MessageSelector());
361 				}
362 			}
363 			
364 			List<DefaultMutableTreeNode> bindings = mapTreeItems( xmlObject, treeRoot, false, tabCount, "Bindings", 
365 						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';//wsdl:binding", 
366 						"declare namespace wsdlsoap='http://schemas.xmlsoap.org/wsdl/soap/';concat( @name, ' [style=', wsdlsoap:binding/@style, ']' )", true, null );
367 			
368 			for( DefaultMutableTreeNode treeNode : bindings )
369 			{
370 				List<DefaultMutableTreeNode> operationNodes = mapTreeItems( ((InspectItem)treeNode.getUserObject()).item, treeNode, false, 
371 							tabCount, null, "declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:operation", 
372 							"declare namespace wsdlsoap='http://schemas.xmlsoap.org/wsdl/soap/';concat( @name, ' [soapAction=', wsdlsoap:operation/@soapAction, ']' )", 
373 							true, null );
374 				
375 				for( DefaultMutableTreeNode treeNode2 : operationNodes )
376 				{
377 					mapTreeItems( ((InspectItem)treeNode2.getUserObject()).item, treeNode2, false, tabCount, null, 
378 								"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:*", "concat( @name, ' [', local-name(), ']' )", false,
379 								new BindingOperationSelector() );
380 				}
381 			}
382 			
383 			List<DefaultMutableTreeNode> services = mapTreeItems( xmlObject, treeRoot, false, tabCount, "Services", 
384 						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';//wsdl:service", "@name", true, null );
385 			
386 			for( DefaultMutableTreeNode treeNode : services )
387 			{
388 				mapTreeItems( ((InspectItem)treeNode.getUserObject()).item, treeNode, false, tabCount, null, 
389 							"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:port", "concat( 'port: name=[', @name, '] binding=[', @binding, ']' )", true, 
390 							new PortSelector() );
391 			}
392 			
393 			tree.expandRow( 0 );
394 			editors.add(  inputArea );
395 		}
396 
397       public void finished()
398       {
399       	if( progressDialog != null )
400       		progressDialog.setVisible( false );
401       }
402 
403 		public boolean onCancel()
404 		{
405 			progressBar = new JProgressBar(0, 1);
406 			progressBar.setSize( new Dimension( 120, 20 ));
407 		   progressBar.setStringPainted(true);
408 		   progressBar.setString("Loading Definition.." );
409 		   progressBar.setIndeterminate(true);
410 			
411 		   ButtonBarBuilder builder = ButtonBarBuilder.createLeftToRightBuilder();	
412          builder.addGlue();
413 			builder.addFixed( progressBar );
414          builder.addGlue();
415          builder.setBorder( BorderFactory.createEmptyBorder( 10, 10, 10, 10 ));
416          
417 			tabbedPane.addTab( "Loading.. ", builder.getPanel()  );
418 			return true;
419 		}
420    }
421 	 
422 	public boolean dependsOn(ModelItem modelItem)
423 	{
424 		return modelItem == getModelItem() || modelItem == getModelItem().getProject();
425 	}
426 
427 	public List<DefaultMutableTreeNode> mapTreeItems( XmlObject xmlObject, DefaultMutableTreeNode treeRoot, boolean createEmpty, 
428 				int tabIndex, String groupName, String query, String nameQuery, boolean sort, NodeSelector selector )
429 	{
430 		XmlObject[] items = xmlObject.selectPath( query );
431 		List<DefaultMutableTreeNode> treeNodes = new ArrayList<DefaultMutableTreeNode>();
432 		List<DefaultMutableTreeNode> resultNodes = new ArrayList<DefaultMutableTreeNode>();
433 		
434 		DefaultMutableTreeNode root = treeRoot;
435 		if( groupName != null )
436 		{
437 			String groupKey = new TreePath( root.getPath() ).toString() + "/" + groupName;
438 			root = groupNodes.get( groupKey );
439 			if( root == null && (items.length > 0 || createEmpty))
440 			{
441 				root = new DefaultMutableTreeNode( groupName );
442 				treeRoot.add(  root );
443 				groupNodes.put( groupKey, root );
444 			}
445 			else if( root != null )
446 			{
447 				Enumeration children = root.children();
448 				while( children.hasMoreElements() )
449 					treeNodes.add( ( DefaultMutableTreeNode ) children.nextElement() );
450 			}
451 		}
452 		
453 		if( items.length == 0 )
454 			return resultNodes;
455 		
456 		for( XmlObject item : items )
457 		{
458 			XmlObject[] selectPath = item.selectPath(  nameQuery );
459 			if( selectPath.length > 0 )
460 			{
461 				DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode( new InspectItem( item, selectPath[0], tabIndex, selector ));
462 				treeNodes.add( treeNode);
463 				resultNodes.add( treeNode );
464 			}
465 		}
466 		
467 		if( sort )
468 		{
469 			Collections.sort( treeNodes, new Comparator<DefaultMutableTreeNode>() {
470 	
471 				public int compare( DefaultMutableTreeNode o1, DefaultMutableTreeNode o2 )
472 				{
473 					return o1.toString().compareTo( o2.toString() );
474 				}} );
475 		}
476 		
477 		root.removeAllChildren();
478 		
479 		for( DefaultMutableTreeNode treeNode : treeNodes )
480 		{
481 			root.add( treeNode );
482 			
483 			String path = "/" + getTreeNodeName( treeNode );
484 			TreePath treePath = new TreePath( treeNode.getPath());
485 			while( treeNode.getParent() != null )
486 			{
487 				treeNode = ( DefaultMutableTreeNode ) treeNode.getParent();
488 				path = "/" + getTreeNodeName( treeNode ) + path;
489 			}
490 			
491 			pathMap.put( path, treePath );
492 		}
493 		
494 		return resultNodes;
495 	}
496 	
497 	private String getTreeNodeName( DefaultMutableTreeNode treeNode )
498 	{
499 		Object userObject = treeNode.getUserObject();
500 		if( userObject instanceof InspectItem )
501 			return (( InspectItem ) userObject ).getName();
502 		else
503 			return treeNode.toString();
504 	}
505 
506 	private final class InspectItem
507 	{
508 		private final XmlObject item;
509 		private String name;
510 		private final int tabIndex;
511 		private XmlLineNumber lineNumber;
512 		private final NodeSelector selector;
513 
514 		public InspectItem( XmlObject item, XmlObject nameObj,  int tabIndex, NodeSelector selector )
515 		{
516 			this.item = item;
517 			this.selector = selector;
518 			this.name = XmlUtils.getNodeValue( nameObj.getDomNode() );
519 			if( name == null )
520 				name = nameObj.toString();
521 			this.tabIndex = tabIndex;
522 			
523 			ArrayList list = new ArrayList();
524 			XmlCursor cursor = item.newCursor();
525 			cursor.getAllBookmarkRefs( list );
526 			
527 			for( Object o : list )
528 				if( o instanceof XmlLineNumber )
529 					lineNumber = (XmlLineNumber) o;
530 			
531 			cursor.dispose();
532 		}
533 		
534 		public String getDescription()
535 		{
536 			return getName() + "@" + targetNamespaces.get( tabIndex );
537 		}
538 
539 		public String getName()
540 		{
541 			int ix = name.indexOf( ' ' );
542 			return ix == -1 ? name : name.substring( 0, ix );
543 		}
544 
545 		public int getTabIndex()
546 		{
547 			return tabIndex;
548 		}
549 
550 		public int getLineNumber() 
551 		{
552 			return lineNumber == null ? -1 : lineNumber.getLine()-1;
553 		}
554 		
555 		@Override
556 		public String toString()
557 		{
558 			return name;
559 		}
560 
561 		public NodeSelector getSelector()
562 		{
563 			return selector;
564 		}
565 
566 		public Element getElement()
567 		{
568 			return ( Element ) item.getDomNode();
569 		}
570 	}
571 
572 	public String getDescription()
573 	{
574 		return "Interface [" + getModelItem().getName() + "]";
575 	}
576 
577 	public boolean onClose( boolean canCancel )
578 	{
579 		return release();
580 	}
581 	
582 	private void simpleSelect( InspectItem item, String attribute, String targetGroup )
583 	{
584 		Element elm = item.getElement();
585 		String type = elm.getAttribute( attribute );
586 		if( type.length() > 0 )
587 		{
588 			int ix = type.indexOf( ':' );
589 			if( ix != -1 )
590 				type = type.substring( ix+1 );
591 			
592 			
593 			TreePath treePath = pathMap.get( "/" + getModelItem().getName() + "/" + targetGroup + "/" + type );
594 			if( treePath != null )
595 			{
596 				tree.setSelectionPath( treePath );
597 			}
598 		}
599 	}
600 
601 	public interface NodeSelector
602 	{
603 		public void selectNode( InspectItem item );
604 	}
605 	
606 	public class PartSelector implements NodeSelector
607 	{
608 		public void selectNode( InspectItem item )
609 		{
610 			Element elm = item.getElement();
611 			String type = elm.getAttribute( "type" );
612 			String element = elm.getAttribute( "element" );
613 			if( type.length() > 0 )
614 			{
615 				simpleSelect( item, "type", "Complex Types" );
616 			}
617 			else if( element.length() > 0 )
618 			{
619 				simpleSelect( item, "element", "Global Elements" );
620 			}
621 		}}
622 	
623 	public class MessageSelector implements NodeSelector
624 	{
625 		public void selectNode( InspectItem item )
626 		{
627 			simpleSelect( item, "message", "Messages" );
628 		}}
629 	
630 	public class GlobalElementSelector implements NodeSelector
631 	{
632 		public void selectNode( InspectItem item )
633 		{
634 			simpleSelect( item, "type", "Complex Types" );
635 		}}
636 	
637 	public class PortSelector implements NodeSelector
638 	{
639 		public void selectNode( InspectItem item )
640 		{
641 			simpleSelect( item, "binding", "Bindings" );
642 		}}
643 	
644 	public class BindingOperationSelector implements NodeSelector
645 	{
646 		public void selectNode( InspectItem item )
647 		{
648 			Element elm = item.getElement();
649 			String name = elm.getAttribute( "name" );
650 			
651 			Element operationElm = ( Element ) elm.getParentNode();
652 			Element bindingElm = ( Element ) operationElm.getParentNode();
653 			
654 			String type = bindingElm.getAttribute( "type" );
655 			
656 			if( type.length() > 0 )
657 			{
658 				int ix = type.indexOf( ':' );
659 				if( ix != -1 )
660 					type = type.substring( ix+1 );
661 				
662 				TreePath treePath = pathMap.get( "/" + getModelItem().getName() + "/PortTypes/" + type + "/" + 
663 							operationElm.getAttribute( "name" ) + "/" + name );
664 				if( treePath != null )
665 				{
666 					tree.setSelectionPath( treePath );
667 				}
668 			}
669 		}}
670 	
671 	private class BackwardAction extends AbstractAction
672 	{
673 		public BackwardAction()
674 		{
675 			super( "<");
676 			putValue( Action.SHORT_DESCRIPTION, "Navigate to previous selection" );
677 		}
678 		
679 		public void actionPerformed( ActionEvent arg0 )
680 		{
681 			if( historyIndex > 0 )
682 			{
683 				historyIndex--;
684 				navigating = true;
685 				tree.setSelectionPath( navigationHistory.get( historyIndex ) );
686 				navigating = false;
687 			}
688 		}}
689 	
690 	private class ForwardAction extends AbstractAction
691 	{
692 		public ForwardAction()
693 		{
694 			super( ">");
695 			putValue( Action.SHORT_DESCRIPTION, "Navigate to next selection" );
696 		}
697 		
698 		public void actionPerformed( ActionEvent arg0 )
699 		{
700 			if( historyIndex < navigationHistory.size()-1 )
701 			{
702 				historyIndex++;
703 				navigating = true;
704 				tree.setSelectionPath( navigationHistory.get( historyIndex ) );
705 				navigating = false;
706 			}
707 			
708 		}}
709 	
710 }