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 }