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