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