Package org.eclim.plugin.cdt.command.hierarchy

Source Code of org.eclim.plugin.cdt.command.hierarchy.CallHierarchyCommand$Call

/**
* Copyright (C) 2005 - 2014  Eric Van Dewoestine
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.eclim.plugin.cdt.command.hierarchy;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import java.text.Collator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import org.eclim.annotation.Command;

import org.eclim.command.CommandLine;
import org.eclim.command.Options;

import org.eclim.plugin.cdt.command.search.SearchCommand;

import org.eclim.plugin.cdt.util.CUtils;

import org.eclim.util.StringUtils;

import org.eclim.util.file.Position;

import org.eclipse.cdt.core.CCorePlugin;

import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IBinding;

import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;

import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.index.IIndexName;

import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IFunction;
import org.eclipse.cdt.core.model.IFunctionDeclaration;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.model.IWorkingCopy;

import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;

import org.eclipse.cdt.internal.core.model.ASTCache;

import org.eclipse.cdt.internal.ui.callhierarchy.CallHierarchyUI;

import org.eclipse.cdt.internal.ui.editor.ASTProvider;
import org.eclipse.cdt.internal.ui.editor.WorkingCopyManager;

import org.eclipse.cdt.internal.ui.viewsupport.IndexUI;

import org.eclipse.cdt.ui.CUIPlugin;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;

import org.eclipse.core.runtime.NullProgressMonitor;

import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextSelection;

import org.eclipse.ui.IEditorInput;

import org.eclipse.ui.part.FileEditorInput;

/**
* Command to generate a call hierarchy for a method or function.
*
* @author Eric Van Dewoestine
*/
@Command(
  name = "c_callhierarchy",
  options =
    "REQUIRED p project ARG," +
    "REQUIRED f file ARG," +
    "REQUIRED o offset ARG," +
    "REQUIRED l length ARG," +
    "REQUIRED e encoding ARG," +
    "OPTIONAL c callees NOARG"
)
public class CallHierarchyCommand
  extends SearchCommand
{
  private static final String CALLEES_OPTION = "c";

  @Override
  public Object execute(CommandLine commandLine)
    throws Exception
  {
    String projectName = commandLine.getValue(Options.PROJECT_OPTION);
    String file = commandLine.getValue(Options.FILE_OPTION);
    boolean callees = commandLine.hasOption(CALLEES_OPTION);
    int offset = getOffset(commandLine);
    int length = commandLine.getIntValue(Options.LENGTH_OPTION);

    ICProject cproject = CUtils.getCProject(projectName);

    CUIPlugin cuiPlugin = CUIPlugin.getDefault();
    ITranslationUnit src = CUtils.getTranslationUnit(cproject, file);
    CCorePlugin.getIndexManager().update(
        new ICElement[]{src}, IIndexManager.UPDATE_ALL);
    CCorePlugin.getIndexManager().joinIndexer(3000, new NullProgressMonitor());
    src = src.getWorkingCopy();

    IEditorInput input = new FileEditorInput((IFile)src.getResource());

    // hack... there has to be a better way
    WorkingCopyManager manager = (WorkingCopyManager)
      cuiPlugin.getWorkingCopyManager();
    manager.connect(input);
    manager.setWorkingCopy(input, (IWorkingCopy)src);

    HashMap<String,Object> result = new HashMap<String,Object>();
    try{
      // more hacks to got get around gui dependency
      ASTProvider provider = ASTProvider.getASTProvider();
      Field astCache = ASTProvider.class.getDeclaredField("fCache");
      astCache.setAccessible(true);
      ((ASTCache)astCache.get(provider)).setActiveElement(src);

      TextSelection selection = new TextSelection(offset, length);
      Method findDefinitions = CallHierarchyUI.class.getDeclaredMethod(
          "findDefinitions",
          ICProject.class, IEditorInput.class, ITextSelection.class);
      findDefinitions.setAccessible(true);
      ICElement[] elements = (ICElement[])findDefinitions.invoke(
          null, cproject, input, selection);

      if (elements != null && elements.length > 0) {
        ICElement element = elements[0];
        Set<ICElement> seen = new HashSet<ICElement>();
        ICProject project = element.getCProject();
        ICProject[] scope = getScope(SCOPE_PROJECT, project);
        IIndex index = CCorePlugin.getIndexManager().getIndex(
            scope, IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_DEPENDENT);
        index.acquireReadLock();
        try{
          IIndexName name = IndexUI.elementToName(index, element);
          result = formatElement(index, new Call(name, element), seen, callees);
        }finally{
          index.releaseReadLock();
        }
      }
    }finally{
      manager.removeWorkingCopy(input);
      manager.disconnect(input);
    }

    return result;
  }

  private ArrayList<HashMap<String,Object>> findCallers(
      IIndex index, ICElement element, Set<ICElement> seen)
    throws Exception
  {
    ArrayList<HashMap<String,Object>> results =
      new ArrayList<HashMap<String,Object>>();
    ICProject project = element.getCProject();
    IIndexBinding calleeBinding = IndexUI.elementToBinding(index, element);
    if (calleeBinding != null) {
      results.addAll(findCallers(index, calleeBinding, true, project, seen));
      if (calleeBinding instanceof ICPPMethod) {
        // cdt 8.1.1 requires a second arg (point: IASTNode), but cdt 8.1.1
        // hasn't been released independent of eclipse 4.2.1, so distros are
        // less likely to have it. So rather than attempt to force an
        // eclipse/cdt version, we'll resort to reflection for now this time.
        /*IBinding[] overriddenBindings =
          ClassTypeHelper.findOverridden((ICPPMethod)calleeBinding, null);*/
        IBinding[] overriddenBindings = null;
        try{
          Method findOverridden = ClassTypeHelper.class
            .getMethod("findOverridden", ICPPMethod.class, IASTNode.class);
          overriddenBindings = (IBinding[])
            findOverridden.invoke(null, (ICPPMethod)calleeBinding, null);
        }catch(NoSuchMethodException nsme){
          Method findOverridden = ClassTypeHelper.class
            .getMethod("findOverridden", ICPPMethod.class);
          overriddenBindings = (IBinding[])
            findOverridden.invoke(null, (ICPPMethod)calleeBinding);
        }

        for (IBinding overriddenBinding : overriddenBindings) {
          results.addAll(findCallers(
              index, overriddenBinding, false, project, seen));
        }
      }
    }
    return results;
  }

  private ArrayList<HashMap<String,Object>> findCallers(
      IIndex index,
      IBinding binding,
      boolean includeOrdinaryCalls,
      ICProject project,
      Set<ICElement> seen)
    throws Exception
  {
    IIndexName[] names = index.findNames(
        binding, IIndex.FIND_REFERENCES | IIndex.SEARCH_ACROSS_LANGUAGE_BOUNDARIES);

    ArrayList<Call> calls = new ArrayList<Call>(names.length);
    for (IIndexName name : names) {
      if (includeOrdinaryCalls || name.couldBePolymorphicMethodCall()) {
        IIndexName caller = name.getEnclosingDefinition();
        if (caller == null) {
          continue;
        }

        ICElement element = IndexUI.getCElementForName(project, index, caller);
        if (element == null) {
          continue;
        }
        calls.add(new Call(name, element));
      }
    }

    Collections.sort(calls);

    ArrayList<HashMap<String,Object>> results =
      new ArrayList<HashMap<String,Object>>();
    for (Call call : calls) {
      results.add(formatElement(index, call, seen, false));
    }
    return results;
  }

  private ArrayList<HashMap<String,Object>> findCallees(
      IIndex index, ICElement element, Set<ICElement> seen)
      throws Exception
  {
    ICProject project = element.getCProject();
    IIndexName name = IndexUI.elementToName(index, element);
    IIndexName[] enclosedNames = name.getEnclosedNames();

    ArrayList<Call> calls = new ArrayList<Call>(enclosedNames.length);
    for (IIndexName enclosedName : enclosedNames) {
      IIndexBinding binding = index.findBinding(enclosedName);
      IIndexName[] enclosedDefinitionNames = index.findDefinitions(binding);
      if (enclosedDefinitionNames == null || enclosedDefinitionNames.length == 0){
        continue;
      }

      IIndexName enclosedDefinitionName = enclosedDefinitionNames[0];
      ICElement enclosedElement =
        IndexUI.getCElementForName(project, index, enclosedDefinitionName);
      if (enclosedElement == null) {
        continue;
      }

      if (enclosedElement instanceof IFunction) {
        calls.add(new Call(
              enclosedName, enclosedElement, element.getResource()));
      }
    }

    Collections.sort(calls);

    ArrayList<HashMap<String,Object>> results =
      new ArrayList<HashMap<String,Object>>();
    for (Call call : calls) {
      results.add(formatElement(index, call, seen, true));
    }
    return results;
  }

  private HashMap<String,Object> formatElement(
      IIndex index,
      Call call,
      Set<ICElement> seen,
      boolean callees)
    throws Exception
  {
    HashMap<String,Object> result = new HashMap<String,Object>();

    String[] types = null;
    IIndexName name = call.name;
    ICElement element = call.element;

    if (element instanceof IFunction){
      types = ((IFunction)element).getParameterTypes();
    }else if (element instanceof IFunctionDeclaration){
      types = ((IFunctionDeclaration)element).getParameterTypes();
    }
    String message = element.getElementName() +
      '(' + StringUtils.join(types, ", ") + ')';
    result.put("name", message);

    if (name != null){
      IResource resource = call.resource;
      if (resource != null){
        String file = resource.getLocation().toOSString().replace('\\', '/');
        result.put("position",
            Position.fromOffset(file, null, name.getNodeOffset(), 0));
      }
    }

    if (!seen.contains(element)){
      seen.add(element);
      if (callees) {
        result.put("callees", findCallees(index, element, seen));
      } else {
        result.put("callers", findCallers(index, element, seen));
      }
    }

    return result;
  }

  private static class Call
    implements Comparable<Call>
  {
    private static final Collator COLLATOR = Collator.getInstance();

    public IIndexName name;
    public ICElement element;
    public IResource resource;

    public Call(IIndexName name, ICElement element)
    {
      this.name = name;
      this.element = element;
      this.resource = element.getResource();
    }

    public Call(IIndexName name, ICElement element, IResource resource)
    {
      this(name, element);
      this.resource = resource;
    }

    @Override
    public int compareTo(Call o)
    {
      int result = 0;
      if (resource != null && o.resource != null){
        String location1 =
          resource.getLocation().toOSString() +
          element.getElementName();
        String location2 =
          resource.getLocation().toOSString() +
          element.getElementName();
        result = COLLATOR.compare(location1, location2);
      }else if (resource != null && o.resource == null){
        result = -1;
      }else if (resource == null && o.resource != null){
        result = 1;
      }
      if (result == 0){
        return name.getNodeOffset() - o.name.getNodeOffset();
      }
      return result;
    }
  }
}
TOP

Related Classes of org.eclim.plugin.cdt.command.hierarchy.CallHierarchyCommand$Call

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.