View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2008 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.rest.panels.service;
14  
15  import com.eviware.soapui.SoapUI;
16  import com.eviware.soapui.impl.rest.RestResource;
17  import com.eviware.soapui.impl.rest.RestService;
18  import com.eviware.soapui.impl.rest.actions.service.CreateWadlDocumentationAction;
19  import com.eviware.soapui.impl.rest.actions.service.ExportWadlAction;
20  import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
21  import com.eviware.soapui.impl.support.definition.InterfaceDefinitionPart;
22  import com.eviware.soapui.impl.wadl.WadlDefinitionContext;
23  import com.eviware.soapui.impl.wsdl.panels.iface.WsdlInterfaceDesktopPanel;
24  import com.eviware.soapui.impl.wsdl.panels.teststeps.support.LineNumbersPanel;
25  import com.eviware.soapui.impl.wsdl.support.Constants;
26  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
27  import com.eviware.soapui.impl.wsdl.support.xsd.SchemaUtils;
28  import com.eviware.soapui.model.ModelItem;
29  import com.eviware.soapui.support.UISupport;
30  import com.eviware.soapui.support.action.swing.SwingActionDelegate;
31  import com.eviware.soapui.support.components.JEditorStatusBar;
32  import com.eviware.soapui.support.components.JXToolBar;
33  import com.eviware.soapui.support.components.MetricsPanel;
34  import com.eviware.soapui.support.components.MetricsPanel.MetricType;
35  import com.eviware.soapui.support.components.MetricsPanel.MetricsSection;
36  import com.eviware.soapui.support.components.ProgressDialog;
37  import com.eviware.soapui.support.types.StringList;
38  import com.eviware.soapui.support.xml.JXEditTextArea;
39  import com.eviware.soapui.support.xml.XmlUtils;
40  import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
41  import com.eviware.x.dialogs.Worker;
42  import com.eviware.x.dialogs.XProgressDialog;
43  import com.eviware.x.dialogs.XProgressMonitor;
44  import com.jgoodies.forms.builder.ButtonBarBuilder;
45  import org.apache.log4j.Logger;
46  import org.apache.xmlbeans.XmlCursor;
47  import org.apache.xmlbeans.XmlLineNumber;
48  import org.apache.xmlbeans.XmlObject;
49  import org.apache.xmlbeans.XmlOptions;
50  import org.jdesktop.swingx.JXTable;
51  import org.syntax.jedit.JEditTextArea;
52  import org.w3c.dom.Element;
53  
54  import javax.swing.*;
55  import javax.swing.event.TreeSelectionEvent;
56  import javax.swing.event.TreeSelectionListener;
57  import javax.swing.table.AbstractTableModel;
58  import javax.swing.tree.DefaultMutableTreeNode;
59  import javax.swing.tree.DefaultTreeModel;
60  import javax.swing.tree.TreePath;
61  import java.awt.*;
62  import java.awt.event.ActionEvent;
63  import java.awt.event.MouseAdapter;
64  import java.awt.event.MouseEvent;
65  import java.io.File;
66  import java.io.StringWriter;
67  import java.util.*;
68  import java.util.List;
69  
70  public class RestServiceDesktopPanel extends ModelItemDesktopPanel<RestService>
71  {
72     private final static Logger logger = Logger.getLogger( WsdlInterfaceDesktopPanel.class );
73     private JTabbedPane partTabs;
74     private List<JEditTextArea> editors = new ArrayList<JEditTextArea>();
75     private JTree tree;
76     private Map<String, DefaultMutableTreeNode> groupNodes = new HashMap<String, DefaultMutableTreeNode>();
77     private Map<String, TreePath> pathMap = new HashMap<String, TreePath>();
78     private List<TreePath> navigationHistory = new ArrayList<TreePath>();
79     private StringList targetNamespaces = new StringList();
80     private int historyIndex;
81     private boolean navigating;
82     private JEditorStatusBar statusBar;
83     private DefaultMutableTreeNode rootNode;
84     private DefaultTreeModel treeModel;
85     private final RestService restService;
86     private MetricsPanel metrics;
87     private boolean updatingService;
88     private ResourcesTableModel operationsTableModel;
89  
90     public RestServiceDesktopPanel( RestService service )
91     {
92        super( service );
93        this.restService = service;
94  
95        try
96        {
97           buildUI();
98        }
99        catch( Exception e )
100       {
101          UISupport.showErrorMessage( e );
102          SwingUtilities.invokeLater( new Runnable()
103          {
104             public void run()
105             {
106                SoapUI.getDesktop().closeDesktopPanel( RestServiceDesktopPanel.this );
107             }
108          } );
109       }
110    }
111 
112    private void buildUI()
113    {
114       JTabbedPane tabs = new JTabbedPane();
115       tabs.addTab( "Overview", buildServiceOverviewTab() );
116       tabs.addTab( "Service Endpoints", buildEndpointsTab() );
117       tabs.addTab( "WADL Content", buildWadlContentTab() );
118 
119       add( UISupport.createTabPanel( tabs, true ), BorderLayout.CENTER );
120    }
121 
122    private Component buildServiceOverviewTab()
123    {
124       metrics = new MetricsPanel();
125       MetricsSection section = metrics.addSection( "WSDL Definition" );
126 
127       try
128       {
129          section.addMetric( "WADL URL", MetricType.URL ).set( restService.getWadlUrl() +
130             (restService.isGenerated() ? " - generated" : "") );
131          
132          // section.addMetric( "Namespace" ).set(
133          // iface.getBindingName().getNamespaceURI() );
134          // section.addMetric( "Binding" ).set(
135          // iface.getBindingName().getLocalPart() );
136          // section.addMetric( "SOAP Version" ).set(
137          // iface.getSoapVersion().toString() );
138          // section.addMetric( "Style" ).set( iface.getStyle() );
139          // section.addMetric( "WS-A version" ).set( iface.getWsaVersion());
140       }
141       catch( Exception e )
142       {
143          UISupport.showErrorMessage( e );
144       }
145 
146       section.finish();
147 
148       metrics.addSection( "Definition Parts" );
149       section = metrics.addSection( "Resources" );
150       operationsTableModel = new ResourcesTableModel();
151       JXTable table = section.addTable( operationsTableModel );
152       table.getColumn( 1 ).setPreferredWidth( 60 );
153       section.finish();
154 
155       return new JScrollPane( metrics );
156    }
157 
158    private Component buildEndpointsTab()
159    {
160       return restService.getProject().getEndpointStrategy().getConfigurationPanel( restService );
161    }
162 
163    private JComponent buildWadlContentTab()
164    {
165       partTabs = new JTabbedPane();
166       partTabs.setTabLayoutPolicy( JTabbedPane.SCROLL_TAB_LAYOUT );
167 
168       rootNode = new DefaultMutableTreeNode( restService.getName() );
169       treeModel = new DefaultTreeModel( rootNode );
170       tree = new JTree( treeModel );
171       tree.setBorder( BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) );
172       tree.setExpandsSelectedPaths( true );
173       tree.addTreeSelectionListener( new InternalTreeSelectionListener() );
174       tree.addMouseListener( new MouseAdapter()
175       {
176          @Override
177          public void mouseClicked( MouseEvent arg0 )
178          {
179             if( arg0.getClickCount() > 1 )
180             {
181                TreePath selectionPath = tree.getSelectionPath();
182                if( selectionPath != null )
183                {
184                   DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) selectionPath.getLastPathComponent();
185                   Object userObject = treeNode.getUserObject();
186                   if( userObject instanceof InspectItem )
187                   {
188                      InspectItem item = (InspectItem) userObject;
189                      if( item != null && item.selector != null )
190                      {
191                         item.selector.selectNode( item );
192                      }
193                   }
194                }
195             }
196          }
197       } );
198 
199       JScrollPane scrollPane = new JScrollPane( tree );
200       UISupport.addPreviewCorner( scrollPane, true );
201       JSplitPane split = UISupport.createHorizontalSplit( scrollPane, UISupport.createTabPanel( partTabs, true ) );
202 
203       split.setDividerLocation( 250 );
204       split.setResizeWeight( 0.3 );
205 
206       initTreeModel( restService );
207 
208       JPanel panel = new JPanel( new BorderLayout() );
209 
210       panel.add( split, BorderLayout.CENTER );
211       panel.add( buildWadlTabToolbar(), BorderLayout.PAGE_START );
212       statusBar = new JEditorStatusBar();
213       panel.add( statusBar, BorderLayout.PAGE_END );
214       setPreferredSize( new Dimension( 600, 500 ) );
215 
216       return panel;
217    }
218 
219    private void initTreeModel( RestService iface )
220    {
221       try
222       {
223          if( iface.getWadlContext().loadIfNecessary() )
224          {
225             XProgressDialog progressDialog = UISupport.getDialogs().createProgressDialog( "Loading Defintion", 3,
226                     "Initializing definition..", true );
227             Loader loader = new Loader( iface );
228 
229             progressDialog.run( loader );
230             loader = null;
231             treeModel.nodeStructureChanged( rootNode );
232          }
233       }
234       catch( Exception e )
235       {
236          SoapUI.logError( e );
237       }
238    }
239 
240    private Component buildWadlTabToolbar()
241    {
242       JXToolBar toolbar = UISupport.createToolbar();
243 
244       toolbar.addFixed( UISupport.createToolbarButton( new BackwardAction() ) );
245       toolbar.addFixed( UISupport.createToolbarButton( new ForwardAction() ) );
246       toolbar.addUnrelatedGap();
247 //      JButton button = UISupport.createToolbarButton( SwingActionDelegate.createDelegate(
248 //              UpdateInterfaceAction.SOAPUI_ACTION_ID, getModelItem(), null, "/updateDefinition.gif" ) );
249 //      button.setText( null );
250 //      toolbar.addFixed( button );
251       JButton button = UISupport.createToolbarButton( SwingActionDelegate.createDelegate(
252               ExportWadlAction.SOAPUI_ACTION_ID, getModelItem(), null, "/exportDefinition.gif" ) );
253       button.setText( null );
254       toolbar.addFixed( button );
255       toolbar.addFixed( UISupport.createToolbarButton(
256               SwingActionDelegate.createDelegate( CreateWadlDocumentationAction.SOAPUI_ACTION_ID, restService, null, "/export.gif" ) ) );
257 
258       toolbar.addFixed( button );
259 
260       if( restService.isGenerated() )
261       {
262          toolbar.addUnrelatedGap();
263          toolbar.addFixed( UISupport.createToolbarButton( new RecreateWadlAction() ) );
264       }
265 
266       toolbar.addGlue();
267       button = UISupport.createToolbarButton( new ShowOnlineHelpAction( HelpUrls.INTERFACE_HELP_URL ) );
268       button.setText( null );
269 
270       return toolbar;
271    }
272 
273    private final class InternalTreeSelectionListener implements TreeSelectionListener
274    {
275       public void valueChanged( TreeSelectionEvent e )
276       {
277          TreePath newLeadSelectionPath = e.getNewLeadSelectionPath();
278          if( newLeadSelectionPath != null )
279          {
280             if( !navigating )
281             {
282                // if we have moved back in history.. reverse before adding
283                while( historyIndex < navigationHistory.size() - 1 )
284                {
285                   TreePath path = navigationHistory.remove( navigationHistory.size() - 1 );
286                   navigationHistory.add( historyIndex++, path );
287                }
288 
289                navigationHistory.add( newLeadSelectionPath );
290                historyIndex = navigationHistory.size() - 1;
291             }
292 
293             DefaultMutableTreeNode tn = (DefaultMutableTreeNode) newLeadSelectionPath.getLastPathComponent();
294             if( tn.getUserObject() instanceof InspectItem )
295             {
296                InspectItem item = (InspectItem) tn.getUserObject();
297 
298                partTabs.setSelectedIndex( item.getTabIndex() );
299                statusBar.setInfo( item.getDescription() );
300 
301                JEditTextArea editor = editors.get( item.getTabIndex() );
302                int lineNumber = item.getLineNumber();
303                if( lineNumber > 0 && editor.getLineStartOffset( lineNumber ) >= 0 )
304                {
305                   editor.setCaretPosition( editor.getLineStartOffset( lineNumber ) );
306                }
307                else
308                {
309                   editor.setCaretPosition( 0 );
310                }
311             }
312 
313             tree.scrollPathToVisible( newLeadSelectionPath );
314             tree.expandPath( newLeadSelectionPath );
315          }
316       }
317    }
318 
319    private static final String DEFINITION_PARTS_SECTION = "Definition Parts";
320 
321    private class Loader implements Worker
322    {
323       private ProgressDialog progressDialog;
324       private final RestService iface;
325       private JProgressBar progressBar;
326 
327       public Loader( RestService iface )
328       {
329          this.iface = iface;
330       }
331 
332       public Object construct( XProgressMonitor monitor )
333       {
334          MetricsSection section = metrics.getSection( DEFINITION_PARTS_SECTION );
335          section.clear();
336 
337          try
338          {
339             WadlDefinitionContext wadlContext = iface.getWadlContext();
340             if( iface.isGenerated() )
341                wadlContext.regenerateWadl();
342             List<InterfaceDefinitionPart> parts = wadlContext.getDefinitionParts();
343 
344             int tabCount = partTabs.getTabCount();
345 
346             for( InterfaceDefinitionPart part : parts )
347             {
348                addTab( part.getUrl(), part.getContent() );
349             }
350 
351             while( tabCount-- > 0 )
352                partTabs.remove( 0 );
353 
354             return null;
355          }
356          catch( Exception e )
357          {
358             logger.error( "Failed to load WSDL; " + e.getClass().getSimpleName() + "; " + e.getMessage() );
359             add( new JLabel( "Failed to load WSDL; " + e.toString() ), BorderLayout.NORTH );
360 
361             SoapUI.logError( e );
362 
363             return e;
364          }
365          finally
366          {
367             section.finish();
368          }
369       }
370 
371       private void addTab( String url, String content ) throws Exception
372       {
373          int ix = url.startsWith( "file:" ) ? url.lastIndexOf( File.separatorChar ) : url.lastIndexOf( '/' );
374          if( ix == -1 )
375             ix = url.lastIndexOf( '/' );
376 
377          String title = url.substring( ix + 1 );
378 
379          metrics.getSection( DEFINITION_PARTS_SECTION ).addMetric( title, MetricType.URL ).set( url );
380 
381          if( progressBar != null )
382             progressBar.setString( title );
383          else if( progressDialog != null )
384             progressDialog.setProgress( 1, title );
385 
386          JPanel panel = new JPanel( new BorderLayout() );
387          JLabel label = new JLabel( url );
388          label.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
389          panel.add( label, BorderLayout.NORTH );
390 
391          JXEditTextArea inputArea = JXEditTextArea.createXmlEditor( false );
392          StringWriter writer = new StringWriter();
393          XmlUtils.serializePretty( XmlObject.Factory.parse( content ), writer );
394          String xmlString = writer.toString();
395 
396          // reparse so linenumbers are correct
397          XmlObject xmlObject = XmlObject.Factory.parse( xmlString, new XmlOptions().setLoadLineNumbers() );
398 
399          inputArea.setText( xmlString );
400          inputArea.setEditable( false );
401          inputArea.getPainter().setLineHighlightEnabled( true );
402 
403          JPanel p = new JPanel( new BorderLayout() );
404          p.add( inputArea, BorderLayout.CENTER );
405          p.add( new LineNumbersPanel( inputArea ), BorderLayout.WEST );
406 
407          JScrollPane scrollPane = new JScrollPane( p );
408          UISupport.addPreviewCorner( scrollPane, true );
409          panel.add( scrollPane, BorderLayout.CENTER );
410          partTabs.addTab( title, panel );
411 
412          if( tree != null )
413          {
414             initInspectionTree( xmlObject, inputArea );
415          }
416       }
417 
418       private void initInspectionTree( XmlObject xmlObject, JXEditTextArea inputArea )
419       {
420          DefaultMutableTreeNode treeRoot = rootNode;
421 
422          targetNamespaces.add( SchemaUtils.getTargetNamespace( xmlObject ) );
423 
424          int tabCount = partTabs.getTabCount() - 1;
425          String xmlNsDeclaration = "declare namespace xs='" + Constants.XSD_NS + "';";
426          String wadlNsDeclaration = "declare namespace wadl='" + Constants.WADL10_NS + "';";
427 
428          mapTreeItems( xmlObject, treeRoot, false, tabCount, "Complex Types",
429                  xmlNsDeclaration + "//xs:complexType[@name!='']", "@name", true,
430                  null );
431 
432          mapTreeItems( xmlObject, treeRoot, false, tabCount, "Simple Types",
433                  xmlNsDeclaration + "//xs:simpleType[@name!='']", "@name", true,
434                  null );
435 
436          mapTreeItems( xmlObject, treeRoot, false, tabCount, "Anonymous Complex Types",
437                  xmlNsDeclaration + "//xs:complexType[not(exists(@name))]",
438                  "parent::node()/@name", true, null );
439 
440          mapTreeItems( xmlObject, treeRoot, false, tabCount, "Global Elements",
441                  xmlNsDeclaration + "//xs:schema/xs:element[@name!='']", "@name",
442                  true, new GlobalElementSelector() );
443 
444          mapTreeItems( xmlObject, treeRoot, false, tabCount, "Schemas",
445                  xmlNsDeclaration + "//xs:schema", "@targetNamespace", true, null );
446 
447          List<DefaultMutableTreeNode> resources = mapTreeItems( xmlObject, treeRoot, false, tabCount, "Resources",
448                  wadlNsDeclaration + "//wadl:resource", "concat( @path, ' [', *:doc[1]/@title, ']' )", true, null );
449 
450 //			for (DefaultMutableTreeNode treeNode : resources)
451 //			{
452 //				mapTreeItems(
453 //						((InspectItem) treeNode.getUserObject()).item,
454 //						treeNode,
455 //						false,
456 //						tabCount,
457 //						null,
458 //						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:part",
459 //						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';concat('part: name=[', @name, '] type=[', @type, '] element=[', @element, ']' )",
460 //						true, new PartSelector());
461 //			}
462 
463          List<DefaultMutableTreeNode> methods = mapTreeItems( xmlObject, treeRoot, false, tabCount, "Methods",
464                  wadlNsDeclaration + "//wadl:method[exists(@name)]", "concat( @name, ' [', *:doc[1]/@title, ']' )", true, null );
465 
466 //			for (DefaultMutableTreeNode treeNode : methods)
467 //			{
468 //				List<DefaultMutableTreeNode> operationNodes = mapTreeItems(((InspectItem) treeNode.getUserObject()).item,
469 //						treeNode, false, tabCount, null,
470 //						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:operation", "@name", true, null);
471 //
472 //				for (DefaultMutableTreeNode treeNode2 : operationNodes)
473 //				{
474 //					mapTreeItems(((InspectItem) treeNode2.getUserObject()).item, treeNode2, false, tabCount, null,
475 //							"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:*",
476 //							"concat( @name, ' [', local-name(), '], message=[', @message, ']' )", false, new MessageSelector());
477 //				}
478 //			}
479 
480          List<DefaultMutableTreeNode> representations = mapTreeItems( xmlObject, treeRoot, false, tabCount, "Representations",
481                  wadlNsDeclaration + "//wadl:representation", "concat( @mediaType, ' [', *:doc[1]/@title, ']' )", true, null );
482 
483 //         List<DefaultMutableTreeNode> bindings = mapTreeItems(
484 //					xmlObject,
485 //					treeRoot,
486 //					false,
487 //					tabCount,
488 //					"Bindings",
489 //					"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';//wsdl:binding",
490 //					"declare namespace wsdlsoap='http://schemas.xmlsoap.org/wsdl/soap/';concat( @name, ' [style=', wsdlsoap:binding[1]/@style, ']' )",
491 //					true, null);
492 //
493 //			for (DefaultMutableTreeNode treeNode : bindings)
494 //			{
495 //				List<DefaultMutableTreeNode> operationNodes = mapTreeItems(
496 //						((InspectItem) treeNode.getUserObject()).item,
497 //						treeNode,
498 //						false,
499 //						tabCount,
500 //						null,
501 //						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:operation",
502 //						"declare namespace wsdlsoap='http://schemas.xmlsoap.org/wsdl/soap/';concat( @name, ' [soapAction=', wsdlsoap:operation/@soapAction, ']' )",
503 //						true, null);
504 //
505 //				for (DefaultMutableTreeNode treeNode2 : operationNodes)
506 //				{
507 //					mapTreeItems(((InspectItem) treeNode2.getUserObject()).item, treeNode2, false, tabCount, null,
508 //							"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:*",
509 //							"concat( @name, ' [', local-name(), ']' )", false, new BindingOperationSelector());
510 //				}
511 //			}
512 //
513 //			List<DefaultMutableTreeNode> services = mapTreeItems(xmlObject, treeRoot, false, tabCount, "Services",
514 //					"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';//wsdl:service", "@name", true, null);
515 //
516 //			for (DefaultMutableTreeNode treeNode : services)
517 //			{
518 //				mapTreeItems(((InspectItem) treeNode.getUserObject()).item, treeNode, false, tabCount, null,
519 //						"declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/';wsdl:port",
520 //						"concat( 'port: name=[', @name, '] binding=[', @binding, ']' )", true, new PortSelector());
521 //			}
522 
523          tree.expandRow( 0 );
524          editors.add( inputArea );
525       }
526 
527       public void finished()
528       {
529          if( progressDialog != null )
530             progressDialog.setVisible( false );
531 
532          progressDialog = null;
533       }
534 
535       public boolean onCancel()
536       {
537          progressBar = new JProgressBar( 0, 1 );
538          progressBar.setSize( new Dimension( 120, 20 ) );
539          progressBar.setStringPainted( true );
540          progressBar.setString( "Loading Definition.." );
541          progressBar.setIndeterminate( true );
542 
543          ButtonBarBuilder builder = ButtonBarBuilder.createLeftToRightBuilder();
544          builder.addGlue();
545          builder.addFixed( progressBar );
546          builder.addGlue();
547          builder.setBorder( BorderFactory.createEmptyBorder( 10, 10, 10, 10 ) );
548 
549          partTabs.addTab( "Loading.. ", builder.getPanel() );
550          return true;
551       }
552    }
553 
554    public boolean dependsOn( ModelItem modelItem )
555    {
556       return getModelItem().dependsOn( modelItem );
557    }
558 
559    public List<DefaultMutableTreeNode> mapTreeItems( XmlObject xmlObject, DefaultMutableTreeNode treeRoot,
560                                                      boolean createEmpty, int tabIndex, String groupName, String query, String nameQuery, boolean sort,
561                                                      NodeSelector selector )
562    {
563       List<DefaultMutableTreeNode> resultNodes = new ArrayList<DefaultMutableTreeNode>();
564 
565       try
566       {
567          XmlObject[] items = xmlObject.selectPath( query );
568          List<DefaultMutableTreeNode> treeNodes = new ArrayList<DefaultMutableTreeNode>();
569 
570          DefaultMutableTreeNode root = treeRoot;
571          if( groupName != null )
572          {
573             String groupKey = new TreePath( root.getPath() ).toString() + "/" + groupName;
574             root = groupNodes.get( groupKey );
575             if( root == null && (items.length > 0 || createEmpty) )
576             {
577                root = new DefaultMutableTreeNode( groupName );
578                treeRoot.add( root );
579                groupNodes.put( groupKey, root );
580             }
581             else if( root != null )
582             {
583                Enumeration<?> children = root.children();
584                while( children.hasMoreElements() )
585                   treeNodes.add( (DefaultMutableTreeNode) children.nextElement() );
586             }
587          }
588 
589          if( items.length == 0 )
590             return resultNodes;
591 
592          for( XmlObject item : items )
593          {
594             XmlObject[] selectPath = item.selectPath( nameQuery );
595             if( selectPath.length > 0 )
596             {
597                DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode( new InspectItem( item, selectPath[0],
598                        tabIndex, selector ) );
599                treeNodes.add( treeNode );
600                resultNodes.add( treeNode );
601             }
602          }
603 
604          if( sort )
605          {
606             Collections.sort( treeNodes, new Comparator<DefaultMutableTreeNode>()
607             {
608 
609                public int compare( DefaultMutableTreeNode o1, DefaultMutableTreeNode o2 )
610                {
611                   return o1.toString().compareTo( o2.toString() );
612                }
613             } );
614          }
615 
616          root.removeAllChildren();
617 
618          for( DefaultMutableTreeNode treeNode : treeNodes )
619          {
620             root.add( treeNode );
621 
622             String path = "/" + getTreeNodeName( treeNode );
623             TreePath treePath = new TreePath( treeNode.getPath() );
624             while( treeNode.getParent() != null )
625             {
626                treeNode = (DefaultMutableTreeNode) treeNode.getParent();
627                path = "/" + getTreeNodeName( treeNode ) + path;
628             }
629 
630             pathMap.put( path, treePath );
631          }
632       }
633       catch( Throwable e )
634       {
635          SoapUI.log( "Failed to map items for query [" + query + "]:[" + nameQuery + "]" );
636          SoapUI.logError( e );
637       }
638 
639       return resultNodes;
640    }
641 
642    private String getTreeNodeName( DefaultMutableTreeNode treeNode )
643    {
644       Object userObject = treeNode.getUserObject();
645       if( userObject instanceof InspectItem )
646          return ((InspectItem) userObject).getName();
647       else
648          return treeNode.toString();
649    }
650 
651    private final class InspectItem
652    {
653       private final XmlObject item;
654       private String name;
655       private final int tabIndex;
656       private XmlLineNumber lineNumber;
657       private final NodeSelector selector;
658 
659       public InspectItem( XmlObject item, XmlObject nameObj, int tabIndex, NodeSelector selector )
660       {
661          this.item = item;
662          this.selector = selector;
663          this.name = XmlUtils.getNodeValue( nameObj.getDomNode() );
664          if( name == null )
665             name = nameObj.toString();
666          this.tabIndex = tabIndex;
667 
668          ArrayList<?> list = new ArrayList<Object>();
669          XmlCursor cursor = item.newCursor();
670          cursor.getAllBookmarkRefs( list );
671 
672          for( Object o : list )
673             if( o instanceof XmlLineNumber )
674                lineNumber = (XmlLineNumber) o;
675 
676          cursor.dispose();
677       }
678 
679       public String getDescription()
680       {
681          return getName() + "@" + targetNamespaces.get( tabIndex );
682       }
683 
684       public String getName()
685       {
686          int ix = name.indexOf( ' ' );
687          return ix == -1 ? name : name.substring( 0, ix );
688       }
689 
690       public int getTabIndex()
691       {
692          return tabIndex;
693       }
694 
695       public int getLineNumber()
696       {
697          return lineNumber == null ? -1 : lineNumber.getLine() - 1;
698       }
699 
700       @Override
701       public String toString()
702       {
703          return name;
704       }
705 
706       public NodeSelector getSelector()
707       {
708          return selector;
709       }
710 
711       public Element getElement()
712       {
713          return (Element) item.getDomNode();
714       }
715    }
716 
717    public boolean onClose( boolean canCancel )
718    {
719 
720       return release();
721    }
722 
723    private void simpleSelect( InspectItem item, String attribute, String targetGroup )
724    {
725       Element elm = item.getElement();
726       String type = elm.getAttribute( attribute );
727       if( type.length() > 0 )
728       {
729          int ix = type.indexOf( ':' );
730          if( ix != -1 )
731             type = type.substring( ix + 1 );
732 
733          TreePath treePath = pathMap.get( "/" + getModelItem().getName() + "/" + targetGroup + "/" + type );
734          if( treePath != null )
735          {
736             tree.setSelectionPath( treePath );
737          }
738       }
739    }
740 
741    protected interface NodeSelector
742    {
743       public void selectNode( InspectItem item );
744    }
745 
746    public class PartSelector implements NodeSelector
747    {
748       public void selectNode( InspectItem item )
749       {
750          Element elm = item.getElement();
751          String type = elm.getAttribute( "type" );
752          String element = elm.getAttribute( "element" );
753          if( type.length() > 0 )
754          {
755             simpleSelect( item, "type", "Complex Types" );
756          }
757          else if( element.length() > 0 )
758          {
759             simpleSelect( item, "element", "Global Elements" );
760          }
761       }
762    }
763 
764    public class MessageSelector implements NodeSelector
765    {
766       public void selectNode( InspectItem item )
767       {
768          simpleSelect( item, "message", "Messages" );
769       }
770    }
771 
772    public class GlobalElementSelector implements NodeSelector
773    {
774       public void selectNode( InspectItem item )
775       {
776          simpleSelect( item, "type", "Complex Types" );
777       }
778    }
779 
780    public class PortSelector implements NodeSelector
781    {
782       public void selectNode( InspectItem item )
783       {
784          simpleSelect( item, "binding", "Bindings" );
785       }
786    }
787 
788    public class BindingOperationSelector implements NodeSelector
789    {
790       public void selectNode( InspectItem item )
791       {
792          Element elm = item.getElement();
793          String name = elm.getAttribute( "name" );
794 
795          Element operationElm = (Element) elm.getParentNode();
796          Element bindingElm = (Element) operationElm.getParentNode();
797 
798          String type = bindingElm.getAttribute( "type" );
799 
800          if( type.length() > 0 )
801          {
802             int ix = type.indexOf( ':' );
803             if( ix != -1 )
804                type = type.substring( ix + 1 );
805 
806             TreePath treePath = pathMap.get( "/" + getModelItem().getName() + "/PortTypes/" + type + "/"
807                     + operationElm.getAttribute( "name" ) + "/" + name );
808             if( treePath != null )
809             {
810                tree.setSelectionPath( treePath );
811             }
812          }
813       }
814    }
815 
816    private class BackwardAction extends AbstractAction
817    {
818       public BackwardAction()
819       {
820          putValue( SMALL_ICON, UISupport.createImageIcon( "/arrow_left.png" ) );
821          putValue( Action.SHORT_DESCRIPTION, "Navigate to previous selection" );
822       }
823 
824       public void actionPerformed( ActionEvent arg0 )
825       {
826          if( historyIndex > 0 )
827          {
828             historyIndex--;
829             navigating = true;
830             tree.setSelectionPath( navigationHistory.get( historyIndex ) );
831             navigating = false;
832          }
833       }
834    }
835 
836    private class ForwardAction extends AbstractAction
837    {
838       public ForwardAction()
839       {
840          putValue( SMALL_ICON, UISupport.createImageIcon( "/arrow_right.png" ) );
841          putValue( Action.SHORT_DESCRIPTION, "Navigate to next selection" );
842       }
843 
844       public void actionPerformed( ActionEvent arg0 )
845       {
846          if( historyIndex < navigationHistory.size() - 1 )
847          {
848             historyIndex++;
849             navigating = true;
850             tree.setSelectionPath( navigationHistory.get( historyIndex ) );
851             navigating = false;
852          }
853       }
854    }
855 
856    private class RecreateWadlAction extends AbstractAction
857    {
858       public RecreateWadlAction()
859       {
860          putValue( SMALL_ICON, UISupport.createImageIcon( "/updateDefinition.gif" ) );
861          putValue( Action.SHORT_DESCRIPTION, "Recreate WADL" );
862       }
863 
864       public void actionPerformed( ActionEvent arg0 )
865       {
866          partTabs.removeAll();
867          tree.setSelectionRow( -1 );
868          rootNode.removeAllChildren();
869          editors.clear();
870          groupNodes.clear();
871          pathMap.clear();
872          targetNamespaces.clear();
873          initTreeModel( restService );
874          operationsTableModel.fireTableDataChanged();
875          updatingService = false;
876       }
877    }
878 
879 
880    private class ResourcesTableModel extends AbstractTableModel
881    {
882       public int getColumnCount()
883       {
884          return 2;
885       }
886 
887       public int getRowCount()
888       {
889          return restService.getOperationCount();
890       }
891 
892       @Override
893       public String getColumnName( int column )
894       {
895          switch( column )
896          {
897             case 0:
898                return "Name";
899             case 1:
900                return "Path";
901          }
902 
903          return null;
904       }
905 
906       public Object getValueAt( int rowIndex, int columnIndex )
907       {
908          if( updatingService )
909             return "<updating>";
910 
911          RestResource operation = restService.getOperationAt( rowIndex );
912 
913          switch( columnIndex )
914          {
915             case 0:
916                return operation.getName();
917             case 1:
918                return operation.getPath();
919          }
920 
921          return null;
922       }
923    }
924 
925 }