code: /*--------------------------------------------------------------*
    Copyright (C) 2006-2015 OpenSim Ltd.
  
    This file is distributed WITHOUT ANY WARRANTY. See the file
    'License' for details on this and other legal matters.
  *--------------------------------------------------------------*/
  
  package org.omnetpp.inifile.editor.views;
  
  import java.util.ArrayList;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  import java.util.WeakHashMap;
  
  import org.eclipse.jface.action.IAction;
  import org.eclipse.jface.action.IMenuManager;
  import org.eclipse.jface.action.IToolBarManager;
  import org.eclipse.jface.action.MenuManager;
  import org.eclipse.jface.viewers.DoubleClickEvent;
  import org.eclipse.jface.viewers.IDoubleClickListener;
  import org.eclipse.jface.viewers.ISelection;
  import org.eclipse.jface.viewers.ISelectionChangedListener;
  import org.eclipse.jface.viewers.IStructuredSelection;
  import org.eclipse.jface.viewers.SelectionChangedEvent;
  import org.eclipse.jface.viewers.TreeViewer;
  import org.eclipse.swt.SWT;
  import org.eclipse.swt.graphics.Point;
  import org.eclipse.swt.widgets.Composite;
  import org.eclipse.swt.widgets.Control;
  import org.eclipse.swt.widgets.Item;
  import org.eclipse.ui.IEditorInput;
  import org.eclipse.ui.IEditorPart;
  import org.omnetpp.common.ui.GenericTreeContentProvider;
  import org.omnetpp.common.ui.GenericTreeLabelProvider;
  import org.omnetpp.common.ui.GenericTreeNode;
  import org.omnetpp.common.ui.GenericTreeUtils;
  import org.omnetpp.common.ui.HoverSupport;
  import org.omnetpp.common.ui.HtmlHoverInfo;
  import org.omnetpp.common.ui.IHoverInfoProvider;
  import org.omnetpp.common.util.ActionExt;
  import org.omnetpp.common.util.StringUtils;
  import org.omnetpp.inifile.editor.model.InifileAnalyzer;
  import org.omnetpp.ned.core.NedResourcesPlugin;
  import org.omnetpp.ned.model.INedElement;
  import org.omnetpp.ned.model.interfaces.INedTypeElement;
  import org.omnetpp.ned.model.interfaces.INedTypeInfo;
  import org.omnetpp.ned.model.interfaces.INedTypeLookupContext;
  import org.omnetpp.ned.model.interfaces.INedTypeResolver;
  import org.omnetpp.ned.model.interfaces.ISubmoduleOrConnection;
  import org.omnetpp.ned.model.pojo.ExtendsElement;
  import org.omnetpp.ned.model.pojo.InterfaceNameElement;
  import org.omnetpp.ned.model.ui.NedModelLabelProvider;
  
  /**
   * Displays the inheritance tree of a NED type.
   *
   * @author Andras
   */
  //XXX currently doesn't show inner types
  //XXX add tooltip (see fixme below)
  public class NedInheritanceView extends AbstractModuleView {
      private TreeViewer treeViewer;
  
      private MenuManager contextMenuManager = new MenuManager("#PopupMenu");
  
      // hashmap to save/restore view's state when switching across editors
      private WeakHashMap<IEditorInput, ISelection> selectedElements = new WeakHashMap<IEditorInput, ISelection>();
  
      public NedInheritanceView() {
      }
  
      @Override
      public Control createViewControl(Composite parent) {
          createTreeViewer(parent);
          createActions();
          return treeViewer.getTree();
      }
  
      private void createTreeViewer(Composite parent) {
          treeViewer = new TreeViewer(parent, SWT.SINGLE);
  
          // set label provider and content provider
          treeViewer.setLabelProvider(new GenericTreeLabelProvider(NedModelLabelProvider.getInstance()));
          treeViewer.setContentProvider(new GenericTreeContentProvider());
  
          treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
              public void selectionChanged(SelectionChangedEvent event) {
                  IEditorPart editor = getAssociatedEditor();
                  if (!event.getSelection().isEmpty() && editor != null) {
                      // remember selection (we'll try to restore it after tree rebuild)
                      selectedElements.put(editor.getEditorInput(), event.getSelection());
                  }
              }
          });
  
          // create context menu
          getViewSite().registerContextMenu(contextMenuManager, treeViewer);
          treeViewer.getTree().setMenu(contextMenuManager.createContextMenu(treeViewer.getTree()));
  
          // add tooltip support to the tree
          new HoverSupport().adapt(treeViewer.getTree(), new IHoverInfoProvider() {
              public HtmlHoverInfo getHoverFor(Control control, int x, int y) {
                  Item item = treeViewer.getTree().getItem(new Point(x,y));
                  Object element = item==null ? null : item.getData();
                  if (element instanceof GenericTreeNode)
                      element = ((GenericTreeNode)element).getPayload();
                  //FIXME produce some text
                  return null;
              }
          });
  
      }
  
      private void createActions() {
          IAction pinAction = getOrCreatePinAction();
  
          class GotoNedFileAction extends ActionExt {
              GotoNedFileAction() {
                  setText("Open NED Declaration");
              }
              @Override
              public void run() {
                  INedElement sel = getNedElementFromSelection();
                  if (sel != null)
                      NedResourcesPlugin.openNedElementInEditor(sel);
              }
              public void selectionChanged(SelectionChangedEvent event) {
                  INedElement sel = getNedElementFromSelection();
                  setEnabled(sel != null);
              }
              private INedElement getNedElementFromSelection() {
                  Object element = ((IStructuredSelection)treeViewer.getSelection()).getFirstElement();
                  if (element instanceof GenericTreeNode)
                      element = ((GenericTreeNode)element).getPayload();
                  return (INedElement)element;
              }
          };
  
          final ActionExt gotoNedAction = new GotoNedFileAction();
          treeViewer.addSelectionChangedListener(gotoNedAction);
  
          // add double-click support to the tree
          treeViewer.addDoubleClickListener(new IDoubleClickListener() {
              public void doubleClick(DoubleClickEvent event) {
                  gotoNedAction.run();
              }
          });
  
          // build menus and toolbar
          contextMenuManager.add(gotoNedAction);
          contextMenuManager.add(pinAction);
  
          IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
          toolBarManager.add(pinAction);
  
          IMenuManager menuManager = getViewSite().getActionBars().getMenuManager();
          menuManager.add(pinAction);
      }
  
      @Override
      protected void showMessage(String text) {
          super.showMessage(text);
          treeViewer.setInput(null);
      }
  
      @Override
      public void setFocus() {
          if (isShowingMessage())
              super.setFocus();
          else
              treeViewer.getTree().setFocus();
      }
  
      public void buildContent(INedElement selectedElement, final InifileAnalyzer analyzer, final String section, String key) {
          INedTypeInfo inputNedType = null;
  
          // find first usable parent
          while (inputNedType == null && selectedElement != null) {
              if (selectedElement instanceof INedTypeElement)
                  inputNedType = ((INedTypeElement)selectedElement).getNedTypeInfo();
              else if (selectedElement instanceof ISubmoduleOrConnection)
                  inputNedType = ((ISubmoduleOrConnection)selectedElement).getNedTypeInfo();
              selectedElement = selectedElement.getParent();
          }
  
          if (inputNedType == null) {
              showMessage("No NED type selected.");
              return;
          }
  
          // build tree
          final GenericTreeNode root = new GenericTreeNode("root");
  
          List<INedTypeInfo> extendsChain = inputNedType.getInheritanceChain();
          INedTypeInfo rootType = extendsChain.get(extendsChain.size()-1);
          buildInheritanceTreeOf(rootType, root, new HashSet<INedTypeInfo>());
  
          // prevent collapsing all treeviewer nodes: only set it on viewer if it's different from old input
          if (!GenericTreeUtils.treeEquals(root, (GenericTreeNode)treeViewer.getInput())) {
              treeViewer.setInput(root);
  
              // open root node (useful in case preserving the selection fails)
              treeViewer.expandToLevel(2);
  
              // try to preserve selection
              ISelection oldSelection = selectedElements.get(getAssociatedEditor().getEditorInput());
              if (oldSelection != null)
                  treeViewer.setSelection(oldSelection, true);
          }
  
          // refresh the viewer anyway, because e.g. parameter value changes are not reflected in the input tree
          treeViewer.refresh();
  
          // update label
          String text = inputNedType.getName() + " - " + StringUtils.capitalize(inputNedType.getNedElement().getReadableTagName());
          if (getPinnedToEditor() != null)
              text += ", in " + getPinnedToEditor().getEditorInput().getName() + " (pinned)";
          setContentDescription(text);
      }
  
      private void buildInheritanceTreeOf(INedTypeInfo typeInfo, GenericTreeNode parentNode, Set<INedTypeInfo> visited) {
          GenericTreeNode node = new GenericTreeNode(typeInfo.getNedElement());
          parentNode.addChild(node);
  
          if (!visited.contains(typeInfo)) {  // cycle detection
              visited.add(typeInfo);
              for (INedTypeInfo type : getSubtypesOf(typeInfo))
                  buildInheritanceTreeOf(type, node, visited);
          }
      }
  
      private List<INedTypeInfo> getSubtypesOf(INedTypeInfo inputType) {
          List<INedTypeInfo> result = new ArrayList<INedTypeInfo>();
          INedTypeResolver res = inputType.getResolver();
  
          // examine all NED types we know of, whether they extend/implement this one (not too efficient)
          for (INedTypeInfo type : res.getToplevelNedTypesFromAllProjects()) {
              INedTypeLookupContext lookupContext = type.getNedElement().getParentLookupContext();
              for (INedElement child : type.getNedElement()) {
                  String superName = null;
                  if (child instanceof ExtendsElement)
                      superName = ((ExtendsElement)child).getName();
                  else if (child instanceof InterfaceNameElement)
                      superName = ((InterfaceNameElement)child).getName();
                  if (superName != null) {
                      INedTypeInfo superType = res.lookupNedType(superName, lookupContext);
                      if (superType == inputType)
                          result.add(type);
                  }
              }
          }
          return result;
      }
  
  }
  