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 }