Source code: com/port80/eclipse/xml/editors/XMLOutlinePage.java
1 package com.port80.eclipse.xml.editors;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.List;
6
7 import org.eclipse.jface.action.Action;
8 import org.eclipse.jface.action.IMenuListener;
9 import org.eclipse.jface.action.IMenuManager;
10 import org.eclipse.jface.action.IToolBarManager;
11 import org.eclipse.jface.action.MenuManager;
12 import org.eclipse.jface.action.Separator;
13 import org.eclipse.jface.dialogs.MessageDialog;
14 import org.eclipse.jface.preference.IPreferenceStore;
15 import org.eclipse.jface.text.DefaultPositionUpdater;
16 import org.eclipse.jface.text.IDocument;
17 import org.eclipse.jface.text.ITextSelection;
18 import org.eclipse.jface.text.Position;
19 import org.eclipse.jface.text.source.SourceViewer;
20 import org.eclipse.jface.util.IPropertyChangeListener;
21 import org.eclipse.jface.util.PropertyChangeEvent;
22 import org.eclipse.jface.viewers.DoubleClickEvent;
23 import org.eclipse.jface.viewers.IDoubleClickListener;
24 import org.eclipse.jface.viewers.ISelection;
25 import org.eclipse.jface.viewers.ISelectionChangedListener;
26 import org.eclipse.jface.viewers.IStructuredSelection;
27 import org.eclipse.jface.viewers.SelectionChangedEvent;
28 import org.eclipse.jface.viewers.StructuredSelection;
29 import org.eclipse.jface.viewers.TreeViewer;
30 import org.eclipse.jface.viewers.Viewer;
31 import org.eclipse.swt.SWT;
32 import org.eclipse.swt.dnd.Clipboard;
33 import org.eclipse.swt.events.KeyEvent;
34 import org.eclipse.swt.events.KeyListener;
35 import org.eclipse.swt.widgets.Composite;
36 import org.eclipse.swt.widgets.Control;
37 import org.eclipse.swt.widgets.Menu;
38 import org.eclipse.ui.IActionBars;
39 import org.eclipse.ui.IPartListener;
40 import org.eclipse.ui.ISelectionListener;
41 import org.eclipse.ui.IWorkbenchPart;
42 import org.eclipse.ui.internal.dialogs.ViewSorter;
43 import org.eclipse.ui.part.IPageSite;
44 import org.eclipse.ui.part.Page;
45 import org.eclipse.ui.texteditor.ITextEditor;
46 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
47 import org.w3c.dom.Node;
48
49 import com.port80.eclipse.editors.EditorsPlugin;
50 import com.port80.eclipse.editors.IConstants;
51 import com.port80.eclipse.editors.util.NamedPosition;
52 import com.port80.eclipse.util.IncrementalFindTreeTarget;
53 import com.port80.eclipse.util.UtilPluginImages;
54 import com.port80.eclipse.xml.parser.XElementNS;
55
56 /**
57 * ContentOutlinePage for XMLEditor to use the Eclipse Outline view to
58 * present an outline of terminal and non-terminals in the document.
59 *
60 * @author chrisl
61 */
62 public class XMLOutlinePage
63 extends Page
64 implements IContentOutlinePage, ISelectionChangedListener, KeyListener, ISelectionListener, IPropertyChangeListener {
65
66 ////////////////////////////////////////////////////////////////////////
67
68 private static final String NAME = "XMLOutlinePage";
69 private static final String VERSION = "v0.1";
70 private static final String CONTEXT_MENU_ID = "XMLOutlinePage";
71 private static final boolean DEBUG = true;
72 //
73 public static final String ID = "com.port80.eclipse.xml.editors.XMLOutlinePage";
74 public static final int SHOW_ALL = 0x01;
75
76 private static int fDefaultShowLevel = SHOW_ALL;
77
78 ////////////////////////////////////////////////////////////////////////
79
80 private Clipboard fClipboard;
81 private XMLEditor fEditor;
82 private SourceViewer fEditorViewer;
83 private Node fInput;
84 //
85 private TreeViewer fViewer;
86 private XMLOutlineContentProvider fProvider;
87 private IPartListener fPartListener;
88 private List fInTextOrder;
89 private ViewSorter fSorter;
90 private DefaultPositionUpdater fPositionUpdater;
91 //
92 private Action fActionHeader;
93 private Action fActionSort;
94 private Action fActionRefresh;
95 private Action fActionGoto;
96 private Action fActionAbout;
97 private Action fActionExpandAll;
98 private Action fActionCollapseAll;
99 private Action fActionExpand2;
100 private Action fActionCollapse2;
101 private List fSelectionListeners;
102 //
103 private IncrementalFindTreeTarget findTarget;
104 private int fShowLevel;
105
106 ////////////////////////////////////////////////////////////////////////
107
108 public XMLOutlinePage(XMLEditor editor) {
109 super();
110 fEditor = editor;
111 //
112 fEditorViewer = editor.getViewer();
113 fShowLevel = fDefaultShowLevel;
114 fSelectionListeners = new ArrayList();
115 fInTextOrder = new ArrayList();
116 fSorter = new NameSorter();
117 fProvider = new XMLOutlineContentProvider();
118 }
119
120 ////////////////////////////////////////////////////////////////////////
121
122 /**
123 * @see org.eclipse.ui.part.IPageBookViewPage#init(IPageSite)
124 */
125 public void init(IPageSite site) {
126 super.init(site);
127 }
128
129 /**
130 * @see IWorkbenchPart#dispose()
131 */
132 public void dispose() {
133 EditorsPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
134 disableSyncSelection();
135 fEditor.outlinePageDisposed();
136 super.dispose();
137 fClipboard = null;
138 fEditor = null;
139 fEditorViewer = null;
140 fInput = null;
141 fViewer = null;
142 }
143
144 /**
145 * This is a callback that will allow us to create the viewer and initialize it.
146 */
147 public void createControl(Composite parent) {
148 fViewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
149 fClipboard = new Clipboard(parent.getDisplay());
150 fViewer.setContentProvider(fProvider);
151 fViewer.setLabelProvider(fProvider);
152 fViewer.setInput(fProvider.getRoot());
153 getSite().setSelectionProvider(fViewer);
154 makeActions();
155 hookContextMenu();
156 hookDoubleClickAction();
157 hookKeyListener();
158 // hookPartListener();
159 contributeToActionBars();
160 IPreferenceStore prefs = EditorsPlugin.getDefault().getPreferenceStore();
161 if (prefs.getBoolean(IConstants.PREF_XML_SYNC_SELECTION)) {
162 enableSyncSelection();
163 }
164 prefs.addPropertyChangeListener(this);
165 if (fEditor.getModel() == null)
166 fEditor.createModel();
167 setInput(fEditor.getModel());
168 }
169
170 ////////////////////////////////////////////////////////////////////////
171
172 public void setInput(Node input) {
173 if (fInput != null && input == fInput)
174 return;
175 if (!isValidInput(input)) {
176 setInput(null);
177 return;
178 }
179 //fViewer.collapseAll();
180 fInput = input;
181 refresh();
182 // fViewer.reveal(fProvider.getTop());
183 }
184
185 public ITextEditor getEditor() {
186 return fEditor;
187 }
188
189 Node getInput() {
190 return fInput;
191 }
192
193 private void refresh() {
194 IStructuredSelection selection = (IStructuredSelection) fViewer.getSelection();
195 TreeObject a = null;
196 if (selection != null && !selection.isEmpty()) {
197 a = (TreeObject) selection.getFirstElement();
198 }
199 fViewer.setSelection(StructuredSelection.EMPTY);
200 fInTextOrder.clear();
201 if (fInput != null) {
202 TreeFolder top = new TreeFolder(fEditor.getEditorInput().getName());
203 IDocument doc = fEditorViewer.getDocument();
204 int tabwidth = fEditor.getConfiguration().getTabWidth(fEditorViewer);
205 addPositions(fInput, doc, top, tabwidth);
206 //
207 fProvider.setInput(top);
208 } else {
209 fProvider.setInput(new TreeFolder("Outline not available"));
210 }
211 // Added folders are collapsed by default, expand interesting ones.
212 // NOTE: Need to refresh() before expandTree(), otherwise, collapsing to object do not work.
213 fViewer.setInput(fProvider.getRoot());
214 fViewer.refresh();
215 fProvider.expandTree(fViewer);
216 if (fEditorViewer != null
217 && a != null
218 && a.getElement() != null
219 && (a.getElement() instanceof NamedPosition)) {
220 a = findElement(a.getElement());
221 if (a != null)
222 fViewer.setSelection(new StructuredSelection(a), true);
223 }
224 }
225
226 public TreeViewer getViewer() {
227 return fViewer;
228 }
229
230 /**
231 * @see org.eclipse.ui.part.IPage#getControl()
232 */
233 public Control getControl() {
234 return fViewer.getControl();
235 }
236
237 /**
238 * @see org.eclipse.ui.views.contentoutline.ContentOutlinePage#getTreeViewer()
239 */
240 protected TreeViewer getTreeViewer() {
241 return fViewer;
242 }
243
244 public TreeObject findElement(Object a) {
245 return fProvider.findElement(a);
246 }
247
248 public void setShowLevel(int level) {
249 this.fShowLevel = level;
250 fDefaultShowLevel = fShowLevel;
251 }
252
253 /**
254 * Passing the focus request to the viewer's control.
255 */
256 public void setFocus() {
257 fViewer.getControl().setFocus();
258 }
259
260 ////////////////////////////////////////////////////////////////////////
261
262 private void makeActions() {
263 // fActionShowInEditor = new ShowInEditorAction();
264 //
265 fActionHeader = new Action() {
266 public void run() {
267 }
268 };
269 fActionHeader.setText("XML Outline Menu:");
270 fActionHeader.setEnabled(false);
271 //
272 fActionSort = new Action() {
273 public void run() {
274 setToolTipText(
275 isChecked() ? "Sort in document location order" : "Sort in lexical order");
276 sortAction(isChecked());
277 }
278 };
279 fActionSort.setChecked(false);
280 fActionSort.setText("Sort in lexical order");
281 fActionSort.setToolTipText("Sort in lexical order");
282 UtilPluginImages.setLocalImageDescriptors(fActionSort, UtilPluginImages.IMG_SORT);
283 //
284 fActionGoto = new Action() {
285 public void run() {
286 gotoAction();
287 }
288 };
289 fActionGoto.setText("Goto");
290 fActionGoto.setToolTipText("Goto item");
291 UtilPluginImages.setLocalImageDescriptors(fActionGoto, UtilPluginImages.IMG_GOTO);
292 //
293 fActionRefresh = new Action() {
294 public void run() {
295 refreshAction();
296 }
297 };
298 fActionRefresh.setText("Refresh");
299 fActionRefresh.setToolTipText("Refresh");
300 UtilPluginImages.setLocalImageDescriptors(fActionRefresh, UtilPluginImages.IMG_REFRESH);
301 //
302 fActionAbout = new Action() {
303 public void run() {
304 showMessage(NAME + " " + VERSION);
305 }
306 };
307 fActionAbout.setText("About");
308 fActionAbout.setToolTipText("About MethodView");
309 fActionAbout.setImageDescriptor(UtilPluginImages.DESC_INFO);
310 //
311 fActionExpandAll = new Action() {
312 public void run() {
313 IStructuredSelection selection = (IStructuredSelection) getSelection();
314 if (selection != null)
315 for (Iterator it = selection.iterator(); it.hasNext();)
316 expandAllAction(it.next());
317 }
318 };
319 fActionExpandAll.setText("Expand All");
320 fActionExpandAll.setToolTipText("Expand all");
321 UtilPluginImages.setLocalImageDescriptors(fActionExpandAll, UtilPluginImages.IMG_REFRESH);
322 //
323 fActionCollapseAll = new Action() {
324 public void run() {
325 IStructuredSelection selection = (IStructuredSelection) getSelection();
326 if (selection != null)
327 for (Iterator it = selection.iterator(); it.hasNext();)
328 collapseAllAction(it.next());
329 }
330 };
331 fActionCollapseAll.setText("Collapse All");
332 fActionCollapseAll.setToolTipText("Collapse all");
333 UtilPluginImages.setLocalImageDescriptors(fActionCollapseAll, UtilPluginImages.IMG_REFRESH);
334 //
335 fActionExpand2 = new Action() {
336 public void run() {
337 IStructuredSelection selection = (IStructuredSelection) getSelection();
338 if (selection != null)
339 for (Iterator it = selection.iterator(); it.hasNext();)
340 expand2Action(it.next());
341 }
342 };
343 fActionExpand2.setText("Expand 2");
344 fActionExpand2.setToolTipText("Expand two levels");
345 UtilPluginImages.setLocalImageDescriptors(fActionExpand2, UtilPluginImages.IMG_REFRESH);
346 //
347 fActionCollapse2 = new Action() {
348 public void run() {
349 IStructuredSelection selection = (IStructuredSelection) getSelection();
350 if (selection != null)
351 for (Iterator it = selection.iterator(); it.hasNext();)
352 collapse2Action(it.next());
353 }
354 };
355 fActionCollapse2.setText("Collapse 2");
356 fActionCollapse2.setToolTipText("Collapse two levels");
357 UtilPluginImages.setLocalImageDescriptors(fActionCollapse2, UtilPluginImages.IMG_REFRESH);
358 }
359
360 private void contributeToActionBars() {
361 IActionBars bars = getSite().getActionBars();
362 fillLocalPullDown(bars.getMenuManager());
363 fillLocalToolBar(bars.getToolBarManager());
364 }
365
366 private void fillLocalPullDown(IMenuManager manager) {
367 manager.add(fActionAbout);
368 }
369
370 private void fillLocalToolBar(IToolBarManager manager) {
371 manager.add(fActionRefresh);
372 manager.add(fActionSort);
373 manager.add(new Separator());
374 //JdtPlugin.getNavHistory().addNavigationActions(manager);
375 manager.add(new Separator());
376 }
377
378 private void hookContextMenu() {
379 MenuManager menuMgr = new MenuManager(CONTEXT_MENU_ID);
380 menuMgr.setRemoveAllWhenShown(true);
381 menuMgr.addMenuListener(new IMenuListener() {
382 public void menuAboutToShow(IMenuManager manager) {
383 XMLOutlinePage.this.fillContextMenu(manager);
384 }
385 });
386 Menu menu = menuMgr.createContextMenu(fViewer.getControl());
387 fViewer.getControl().setMenu(menu);
388 // Register menu so that other plugin can contribute to it.
389 getSite().registerContextMenu(CONTEXT_MENU_ID, menuMgr, fViewer);
390 }
391
392 void fillContextMenu(IMenuManager manager) {
393 manager.add(fActionHeader);
394 manager.add(new Separator());
395 manager.add(fActionExpandAll);
396 manager.add(fActionCollapseAll);
397 manager.add(fActionExpand2);
398 manager.add(fActionCollapse2);
399 // Other plug-ins can contribute there actions here
400 manager.add(new Separator("Additions"));
401 }
402
403 private void hookDoubleClickAction() {
404 fViewer.addDoubleClickListener(new IDoubleClickListener() {
405 public void doubleClick(DoubleClickEvent event) {
406 gotoAction();
407 }
408 });
409 }
410
411 /** Temporary hack to do the incremental search. Should move to StructuredViewer later. */
412 private void hookKeyListener() {
413 fViewer.getControl().addKeyListener(this);
414 }
415
416 void showMessage(String message) {
417 MessageDialog.openInformation(fViewer.getControl().getShell(), NAME, message);
418 }
419
420 // Actions /////////////////////////////////////////////////////////////
421 //
422
423 void expandAllAction(Object a) {
424 fViewer.expandToLevel(a, TreeViewer.ALL_LEVELS);
425 }
426
427 void collapseAllAction(Object a) {
428 fViewer.collapseToLevel(a, TreeViewer.ALL_LEVELS);
429 }
430
431 void expand2Action(Object a) {
432 fViewer.expandToLevel(a, 2);
433 }
434
435 void collapse2Action(Object a) {
436 fViewer.collapseToLevel(a, 2);
437 }
438
439 void refreshAction() {
440 EditorsPlugin.deleteProblemMarkers(fEditor.getEditorInput(), IConstants.FormatActionProblemMarker);
441 fEditor.createModel();
442 fInput=fEditor.getModel();
443 refresh();
444 }
445
446 void sortAction(boolean on) {
447 if (fViewer == null)
448 return;
449 fViewer.setSorter(on ? fSorter : null);
450 }
451
452 void gotoAction() {
453 IStructuredSelection selection = (IStructuredSelection) fViewer.getSelection();
454 if (selection == null || selection.isEmpty())
455 return;
456 Object a = selection.getFirstElement();
457 if (!(a instanceof TreeObject))
458 return;
459 Object element = ((TreeObject) a).getElement();
460 if (element == null || !(element instanceof Position))
461 return;
462 Position position = (Position) element;
463 int len = position.length;
464 if (a instanceof TreeFolder)
465 len = 0;
466 fEditorViewer.setSelectedRange(position.offset, len);
467 fEditorViewer.revealRange(position.offset, len);
468 getSite().getActionBars().getStatusLineManager().setMessage(a.toString());
469 }
470
471 // ISelectionChangeListener /////////////////////////////////////////
472
473 /**
474 * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(SelectionChangedEvent)
475 */
476 public void selectionChanged(SelectionChangedEvent event) {
477 }
478
479 /**
480 * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(ISelectionChangedListener)
481 */
482 public void addSelectionChangedListener(ISelectionChangedListener listener) {
483 fSelectionListeners.add(listener);
484 }
485
486 /**
487 * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(ISelectionChangedListener)
488 */
489 public void removeSelectionChangedListener(ISelectionChangedListener listener) {
490 fSelectionListeners.remove(listener);
491 }
492
493 /**
494 * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(ISelection)
495 */
496 public void setSelection(ISelection selection) {
497 if (fViewer != null) {
498 fViewer.setSelection(selection);
499 }
500 }
501
502 public ISelection getSelection() {
503 if (fViewer == null)
504 return StructuredSelection.EMPTY;
505 return (IStructuredSelection) fViewer.getSelection();
506 }
507
508 /**
509 * Fires a selection changed event.
510 *
511 * @param selction the new selection
512 */
513 protected void fireSelectionChanged(ISelection selection) {
514 // create an event
515 SelectionChangedEvent event = new SelectionChangedEvent(this, selection);
516 // fire the event
517 for (int i = 0; i < fSelectionListeners.size(); ++i) {
518 ((ISelectionChangedListener) fSelectionListeners.get(i)).selectionChanged(event);
519 }
520 }
521
522 // ISelectionListener //////////////////////////////////////////////////
523
524 /**
525 * @see ISelectionListener#selectionChanged(IWorkbenchPart, ISelection)
526 */
527 public void selectionChanged(IWorkbenchPart part, ISelection selection) {
528 if (part != fEditor)
529 return;
530 //if (viewIsOnTop()) {
531 setSelectionFromEditor(selection, true);
532 //}
533 }
534
535 // IPropertyChangeListener /////////////////////////////////////////////
536
537 /**
538 * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
539 */
540 public void propertyChange(PropertyChangeEvent event) {
541 if (IConstants.PREF_XML_SYNC_SELECTION.equals(event.getProperty())) {
542 Boolean enable = (Boolean) event.getNewValue();
543 if (enable.booleanValue())
544 enableSyncSelection();
545 else
546 disableSyncSelection();
547 }
548 }
549
550 private void enableSyncSelection() {
551 getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(this);
552 }
553
554 private void disableSyncSelection() {
555 getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(this);
556 }
557
558 // KeyListener ///////////////////////////////////////////////////////
559
560 public void keyPressed(KeyEvent event) {
561 if (DEBUG)
562 System.out.println(
563 NAME
564 + ".keyPressed(): mask="
565 + event.stateMask
566 + ", character="
567 + event.character
568 + ", keycode="
569 + event.keyCode);
570 // if (event.stateMask == (SWT.CTRL | SWT.ALT | SWT.SHIFT) && event.character == 0x73) {
571 if (event.stateMask == 0 && event.character == 0x73) {
572 // 'C-s' is trapped by eclipse. For now, use 'M-C-s' instead.
573 beginSession(true);
574 } else if (event.stateMask == 0 && event.character == 0x72) {
575 // 'C-r' keyPressed() for 'M-C-r' are trapped by eclipse. For now, use 'M-C-r' keyReleased() instead.
576 beginSession(false);
577 }
578 }
579 public void keyReleased(KeyEvent event) {
580 if (DEBUG)
581 System.out.println(
582 NAME
583 + ".keyReleased(): mask="
584 + event.stateMask
585 + ", character="
586 + event.character
587 + ", keycode="
588 + event.keyCode);
589 // if (event.stateMask == (SWT.CTRL | SWT.ALT | SWT.SHIFT) && event.character == 0x72) {
590 }
591
592 public void beginSession(boolean forward) {
593 findTarget =
594 new IncrementalFindTreeTarget(fViewer, getSite().getActionBars().getStatusLineManager(), this);
595 String defaultString = null;
596 findTarget.beginSession(forward, defaultString);
597 }
598
599 // Update View on editor activation ///////////////////////////
600
601 public void setSelectionFromEditor(ISelection selection, boolean reveal) {
602 if (selection == null)
603 return;
604 if (!(selection instanceof ITextSelection))
605 return;
606 if (((ITextSelection) selection).getLength() == 0)
607 return;
608 int offset = ((ITextSelection) selection).getOffset();
609 TreeObject a = findElementAtOffset(offset);
610 if (false)
611 System.err.println(NAME + ".setSelectionFromEditor(): offset=" + offset + ", a=" + a);
612 if (a == null)
613 fViewer.setSelection(StructuredSelection.EMPTY);
614 else
615 fViewer.setSelection(new StructuredSelection(a), reveal);
616 }
617
618 /**
619 * Answers if the given <code>element</code> is a valid
620 * element for this part. For MethodView only IType is valid.
621 *
622 * @param element the object to test
623 * @return <true> if the given element is a valid element
624 */
625 protected boolean isValidInput(Object element) {
626 return (element == null || element instanceof Node);
627 }
628
629 /** Return an error message for the given Java element.*/
630 private String errorMessage(String method, Node node) {
631 String ret = NAME + method;
632 if (node != null)
633 return (ret + ": node=" + node.toString());
634 else
635 return ret;
636 }
637
638 private void addPositions(Node node, IDocument doc, TreeFolder parent, int tabwidth) {
639 TreeFolder child = parent;
640 if (node instanceof XElementNS) {
641 if (node.getNodeType() == Node.ELEMENT_NODE) {
642 XElementNS element = (XElementNS) node;
643 NamedPosition position = (NamedPosition) element.getUserData();
644 if (position != null) {
645 child = new TreeFolder(position);
646 parent.addChild(child);
647 fInTextOrder.add(child);
648 }
649 }
650 }
651 for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling())
652 addPositions(n, doc, child, tabwidth);
653 }
654
655 /**
656 * @enter Tree element must be sorted in text order in source file.
657 */
658 private TreeObject findElementAtOffset(int offset) {
659 if (fInTextOrder == null || fInTextOrder.size() == 0)
660 return null;
661 TreeObject child;
662 TreeObject ret = null;
663 Position p;
664 for (int i = 0; i < fInTextOrder.size(); ++i) {
665 child = (TreeObject) fInTextOrder.get(i);
666 p = (Position) child.getElement();
667 if (p != null && p.offset <= offset) {
668 if (p.offset + p.length > offset)
669 ret = child;
670 } else
671 break;
672 }
673 return ret;
674 }
675
676 ////////////////////////////////////////////////////////////////////////
677
678 static class NameSorter extends ViewSorter {
679 public NameSorter() {
680 super(null);
681 }
682 /**
683 * @see org.eclipse.ui.internal.dialogs.ViewSorter#compare(Viewer, Object, Object)
684 */
685 public int compare(Viewer viewer, Object e1, Object e2) {
686 TreeObject a = (TreeObject) e1;
687 TreeObject b = (TreeObject) e2;
688 int akind = a.fKind;
689 int bkind = b.fKind;
690 if (akind > bkind)
691 return 1;
692 if (bkind > akind)
693 return -1;
694 return a.toString().compareTo(b.toString());
695 }
696 }
697
698 ////////////////////////////////////////////////////////////////////////
699
700 }