View Javadoc

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