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