public DeclarationLocation getLocation() {
      PythonTree node = element.getNode();
      int lineOffset = node != null ? node.getCharStartIndex() : -1;
      DeclarationLocation loc =
          new DeclarationLocation(element.getFileObject(), lineOffset, element);

      return loc;
    }
  public DeclarationLocation getSuperImplementations(PythonParserResult info, int lexOffset) {
    // Figure out if we're on a method, and if so, locate the nearest
    // method it is overriding.
    // Otherwise, if we're on a class (anywhere, not just definition),
    // go to the super class.
    PythonTree root = PythonAstUtils.getRoot(info);
    if (root != null) {
      // Determine function or call under caret
      int astOffset = PythonAstUtils.getAstOffset(info, lexOffset);
      if (astOffset != -1) {
        AstPath path = AstPath.get(root, astOffset);
        PythonTree leaf = path.leaf();
        String name = null;
        boolean findClass = false; // false=function, true=class
        if (leaf instanceof FunctionDef) {
          name = ((FunctionDef) leaf).getInternalName();
        } else if (leaf instanceof Name) {
          name = ((Name) leaf).getInternalId();
          if (path.leafParent() instanceof ClassDef) {
            findClass = true;
          }
        } else if (leaf instanceof ClassDef) {
          name = ((ClassDef) leaf).getInternalName();
          findClass = true;
        }

        Set<IndexedElement> elements = null;
        PythonIndex index = PythonIndex.get(info.getSnapshot().getSource().getFileObject());
        if (findClass) {
          elements = index.getSuperClasses(name);
        } else {
          ClassDef clz = PythonAstUtils.getClassDef(path);
          if (clz != null) {
            elements = index.getOverridingMethods(clz.getInternalName(), name);
          }
        }

        if (elements != null && elements.size() > 0) {
          // Pick the closest element as the default candidate
          IndexedElement candidate = null;
          int depth = Integer.MAX_VALUE;
          for (IndexedElement element : elements) {
            if (element.getOrder() < depth) {
              candidate = element;
              depth = element.getOrder();
            }
          }

          return getDeclarationLocation(info, candidate, elements);
        }
      }
    }

    return DeclarationLocation.NONE;
  }
    public int compareTo(AlternativeLocation alternative) {
      PythonAltLocation alt = (PythonAltLocation) alternative;

      // The preferred item should be chosen
      if (isPreferred) {
        return -1;
      } else if (alt.isPreferred) {
        return 1;
      } // Can't both be so no else == check

      if (order != alt.order) {
        return order - alt.order;
      }

      // Nodoced items last
      if (element.isNoDoc() != alt.element.isNoDoc()) {
        return element.isNoDoc() ? 1 : -1;
      }

      // Documented items on top
      if (element.isDocumented() != alt.element.isDocumented()) {
        return element.isDocumented() ? -1 : 1;
      }

      // TODO: Sort by classes?
      String thisClz = element.getClz() != null ? element.getClz() : "";
      String thatClz = alt.element.getClz() != null ? alt.element.getClz() : "";
      int cmp = thisClz.compareTo(thatClz);
      if (cmp != 0) {
        return cmp;
      }

      // TODO: Sort by gem?

      // Sort by containing clz - just do fqn here?
      String thisIn = element.getIn() != null ? element.getIn() : "";
      String thatIn = alt.element.getIn() != null ? alt.element.getIn() : "";
      cmp = thisIn.compareTo(thatIn);
      if (cmp != 0) {
        return cmp;
      }

      // Sort by file
      String thisFile = element.getFileObject() != null ? element.getFileObject().getNameExt() : "";
      String thatFile =
          alt.element.getFileObject() != null ? alt.element.getFileObject().getNameExt() : "";
      cmp = thisFile.compareTo(thatFile);

      return cmp;
    }
  private DeclarationLocation getDeclarationLocation(
      PythonParserResult info, IndexedElement candidate, Set<? extends IndexedElement> methods) {
    BaseDocument doc = (BaseDocument) info.getSnapshot().getSource().getDocument(false);
    if (doc == null) {
      return DeclarationLocation.NONE;
    }

    if (candidate != null) {
      FileObject fileObject = candidate.getFileObject();
      if (fileObject == null) {
        return DeclarationLocation.NONE;
      }

      PythonTree node = candidate.getNode();
      int nodeOffset = 0;
      if (node != null) {
        nodeOffset = PythonAstUtils.getNameRange(info, node).getStart();
      }

      DeclarationLocation loc = new DeclarationLocation(fileObject, nodeOffset, candidate);

      if (PythonUtils.isRstFile(fileObject)) {
        loc.setInvalidMessage(
            NbBundle.getMessage(
                PythonDeclarationFinder.class, "BuiltinPython", candidate.getName()));
        return loc;
      }

      if (methods.size() > 1) {
        // Could the :nodoc: alternatives: if there is only one nodoc'ed alternative
        // don't ask user!
        int not_nodoced = 0;
        for (final IndexedElement mtd : methods) {
          if (!mtd.isNoDoc()) {
            not_nodoced++;
          }
        }
        if (not_nodoced >= 2) {
          for (final IndexedElement mtd : methods) {
            loc.addAlternative(new PythonAltLocation(mtd, mtd == candidate));
          }
        }
      }

      return loc;
    }

    return DeclarationLocation.NONE;
  }
  /**
   * Compute the declaration location for a test string (such as MosModule::TestBaz/test_qux).
   *
   * @param fileInProject a file in the project where to perform the search
   * @param testString a string represening a test class and method, such as TestFoo/test_bar
   * @param classLocation if true, returns the location of the class rather then the method.
   */
  public static DeclarationLocation getTestDeclaration(
      FileObject fileInProject, String testString, boolean classLocation) {
    int methodIndex = testString.indexOf('/'); // NOI18N
    if (methodIndex == -1) {
      return DeclarationLocation.NONE;
    }

    String className = testString.substring(0, methodIndex);
    String methodName = testString.substring(methodIndex + 1);

    PythonIndex index = PythonIndex.get(fileInProject);
    Set<IndexedElement> elements =
        index.getAllMembers(methodName, QuerySupport.Kind.EXACT, null, true);
    // Look for one that matches our class name
    if (elements.size() > 0) {
      IndexedElement candidate = null;
      for (IndexedElement element : elements) {
        if (element instanceof IndexedMethod) {
          IndexedMethod method = (IndexedMethod) element;
          if (className.startsWith(method.getModule() + ".")) {
            // Close!
            candidate = method;
            if (className.equals(method.getModule() + "." + method.getClz())) {
              break;
            }
          }
        }
      }

      if (candidate != null) {
        int offset = 0;
        PythonTree node = candidate.getNode();
        if (node != null) {
          offset = PythonAstUtils.getRange(node).getStart();
        }
        return new DeclarationLocation(candidate.getFileObject(), offset);
      }
    }

    return DeclarationLocation.NONE;
  }
  private IndexedElement findBestMatch(
      PythonParserResult info,
      String name,
      Set<? extends IndexedElement> methodSet,
      BaseDocument doc,
      int astOffset,
      int lexOffset,
      AstPath path,
      PythonTree call,
      PythonIndex index) {
    // Make sure that the best fit method actually has a corresponding valid source location
    // and parse tree

    Set<IndexedElement> methods = new HashSet<IndexedElement>(methodSet);

    while (!methods.isEmpty()) {
      IndexedElement method =
          findBestMatchHelper(info, name, methods, doc, astOffset, lexOffset, path, call, index);
      PythonTree node = method.getNode();

      if (node != null) {
        return method;
      }

      if (!methods.contains(method)) {
        // Avoid infinite loop when we somehow don't find the node for
        // the best method and we keep trying it
        methods.remove(methods.iterator().next());
      } else {
        methods.remove(method);
      }
    }

    // Dynamic methods that don't have source (such as the TableDefinition methods "binary",
    // "boolean", etc.
    if (methodSet.size() > 0) {
      return methodSet.iterator().next();
    }

    return null;
  }
    public String getDisplayHtml(HtmlFormatter formatter) {
      formatter.setMaxLength(120);
      if (cachedDisplayItem == null) {
        formatter.reset();

        boolean nodoc = element.isNoDoc();
        boolean documented = element.isDocumented();
        if (isPreferred) {
          formatter.emphasis(true);
        } else if (nodoc) {
          formatter.deprecated(true);
        }

        if (element instanceof IndexedMethod) {
          //                    if (element.getFqn() != null) {
          //                        formatter.appendText(element.getFqn());
          //                        formatter.appendText(".");
          //                    }
          formatter.appendText(element.getName());
          IndexedMethod method = (IndexedMethod) element;
          String[] parameters = method.getParams();

          if ((parameters != null) && (parameters.length > 0)) {
            formatter.appendText("("); // NOI18N

            boolean first = true;
            for (String parameter : parameters) {
              if (first) {
                first = false;
              } else {
                formatter.appendText(", "); // NOI18N
              }
              formatter.parameters(true);
              formatter.appendText(parameter);
              formatter.parameters(false);
            }

            formatter.appendText(")"); // NOI18N
          }
        } else {
          // formatter.appendText(element.getFqn());
          formatter.appendText(element.getName());
        }

        if (element.getClz() != null) {
          formatter.appendText(" ");
          formatter.appendText(NbBundle.getMessage(PythonDeclarationFinder.class, "In"));
          formatter.appendText(" ");
          formatter.appendText(element.getClz());
          formatter.appendHtml(" "); // NOI18N
        }

        String filename = null;
        String url = element.getFilenameUrl();
        if (url == null) {
          // Deleted file?
          // Just leave out the file name
        } else if (url.indexOf("pythonstubs") != -1) { // NOI18N
          filename = NbBundle.getMessage(PythonDeclarationFinder.class, "PythonLib");
          //
          //                    if (url.indexOf("/stub_") == -1) {
          //                        // Not a stub file, such as ftools.py
          //                        // TODO - don't hardcode for version
          //                        String stub = "pythonstubs/2.5/";
          //                        int stubStart = url.indexOf(stub);
          //                        if (stubStart != -1) {
          //                            filename = filename+": " + url.substring(stubStart);
          //                        }
          //                    }
        } else {
          FileObject fo = element.getFileObject();
          if (fo != null) {
            filename = fo.getNameExt();
          } else {
            // Perhaps a file that isn't present here, such as something in site_ruby
            int lastIndex = url.lastIndexOf('/');
            if (lastIndex != -1) {
              String s = url.substring(0, lastIndex);
              int almostLastIndex = s.lastIndexOf('/');
              if (almostLastIndex != -1 && ((url.length() - almostLastIndex) < 40)) {
                filename = url.substring(almostLastIndex + 1);
                if (filename.indexOf(':') != -1) {
                  // Don't include prefix like cluster:, file:, etc.
                  filename = url.substring(lastIndex + 1);
                }
              } else {
                filename = url.substring(lastIndex + 1);
              }
            }
          }

          //                    // TODO - make this work with 1.9 etc.
          //                    //final String GEM_LOC = "lib/ruby/gems/1.8/gems/";
          //                    Pattern p = Pattern.compile("lib/ruby/gems/\\d+\\.\\d+/gems/");
          //                    Matcher m = p.matcher(url);
          //                    //int gemIndex = url.indexOf(GEM_LOC);
          //                    //if (gemIndex != -1) {
          //                    if (m.find()) {
          //                        //int gemIndex = m.start();
          //                        //gemIndex += GEM_LOC.length();
          //                        int gemIndex = m.end();
          //                        int gemEnd = url.indexOf('/', gemIndex);
          //                        if (gemEnd != -1) {
          //                            //int libIndex = url.indexOf("lib/", gemEnd);
          //                            //if (libIndex != -1) {
          //                            //    filename = url.substring(libIndex+4);
          //                            //}
          //                            filename = url.substring(gemIndex, gemEnd) + ": " +
          // filename;
          //                        }
          //                    }
        }

        if (filename != null) {
          formatter.appendText(" ");
          formatter.appendText(NbBundle.getMessage(PythonDeclarationFinder.class, "In"));
          formatter.appendText(" ");
          formatter.appendText(filename);
        }

        if (documented) {
          formatter.appendText(" ");
          formatter.appendText(NbBundle.getMessage(PythonDeclarationFinder.class, "Documented"));
        } else if (nodoc) {
          formatter.appendText(" ");
          formatter.appendText(NbBundle.getMessage(PythonDeclarationFinder.class, "NoDoced"));
        }

        if (isPreferred) {
          formatter.emphasis(false);
        } else if (nodoc) {
          formatter.deprecated(false);
        }

        cachedDisplayItem = formatter.getText();
      }

      return cachedDisplayItem;
    }
 PythonAltLocation(IndexedElement element, boolean isPreferred) {
   this.element = element;
   this.isPreferred = isPreferred;
   order = element.getOrder();
 }
  private IndexedElement findBestMatchHelper(
      PythonParserResult info,
      String name,
      Set<IndexedElement> elements,
      BaseDocument doc,
      int astOffset,
      int lexOffset,
      AstPath path,
      PythonTree callNode,
      PythonIndex index) {

    Set<IndexedElement> candidates = new HashSet<IndexedElement>();

    if (elements.size() == 0) {
      return null;
    } else if (elements.size() == 1) {
      return elements.iterator().next();
    }

    // 1. Prefer matches in the current file
    String searchUrl = info.getSnapshot().getSource().getFileObject().toURL().toExternalForm();
    candidates = new HashSet<IndexedElement>();

    for (IndexedElement element : elements) {
      String url = element.getFilenameUrl();

      if (url.equals(searchUrl)) {
        candidates.add(element);
      }
    }

    if (candidates.size() == 1) {
      return candidates.iterator().next();
    } else if (!candidates.isEmpty()) {
      elements = candidates;
    }

    // 2. See which of the class references are defined in files directly
    //   included by this file.
    Set<String> included = new HashSet<String>();
    candidates = new HashSet<IndexedElement>();

    SymbolTable table = PythonAstUtils.getParseResult(info).getSymbolTable();
    List<Import> imports = table.getImports();
    for (Import imp : imports) {
      List<alias> names = imp.getInternalNames();
      if (names != null) {
        for (alias at : names) {
          included.add(at.getInternalName());
        }
      }
    }
    List<ImportFrom> importsFrom = table.getImportsFrom();
    for (ImportFrom imp : importsFrom) {
      included.add(imp.getInternalModule());
    }

    if (included.size() > 0) {
      for (IndexedElement element : elements) {
        String mod = element.getModule();

        if (included.contains(mod)) {
          candidates.add(element);
        }
      }

      if (candidates.size() == 1) {
        return candidates.iterator().next();
      } else if (!candidates.isEmpty()) {
        elements = candidates;
      }
    }

    // 4. Prefer builtins
    candidates = new HashSet<IndexedElement>();

    for (IndexedElement element : elements) {
      String url = element.getFilenameUrl();

      if (url != null && url.indexOf("pythonstubs") != -1) { // NOI18N
        candidates.add(element);
      }
    }

    if (candidates.size() == 1) {
      return candidates.iterator().next();
    } else if (!candidates.isEmpty()) {
      elements = candidates;
    }

    // 5. Prefer documented classes
    candidates = new HashSet<IndexedElement>();
    for (IndexedElement element : elements) {
      if (element.isDocumented()) {
        candidates.add(element);
      }
    }

    if (candidates.size() == 1) {
      return candidates.iterator().next();
    } else if (!candidates.isEmpty()) {
      elements = candidates;
    }

    // TODO - use some heuristics here!
    return elements.iterator().next();
  }
  private DeclarationLocation findImport(
      PythonParserResult info, String moduleName, String symbol) {
    PythonIndex index = PythonIndex.get(info.getSnapshot().getSource().getFileObject());

    Set<IndexedElement> elements = null;

    if (moduleName != null && symbol != null) {
      elements =
          index.getImportedElements(
              symbol, QuerySupport.Kind.EXACT, Collections.<String>singleton(moduleName), null);
    }

    if (symbol != null && (elements == null || elements.size() == 0)) {
      elements = index.getInheritedElements(null, symbol, QuerySupport.Kind.EXACT);
    }

    if (elements == null || elements.size() == 0) {
      elements = index.getModules(moduleName, QuerySupport.Kind.EXACT);
    }

    if (elements != null && elements.size() > 0) {
      AstPath path = null;
      PythonTree node = null;
      int astOffset = -1;
      int lexOffset = -1;
      return getDeclaration(info, null /*name*/, elements, path, node, index, astOffset, lexOffset);
    }

    // This never gets executed, right? Delete after EA
    for (IndexedElement candidate : elements) {
      // TODO - pick the best of the alternatives here?
      FileObject fileObject = candidate.getFileObject();
      if (fileObject == null) {
        return DeclarationLocation.NONE;
      }

      PythonTree node = candidate.getNode();
      int nodeOffset = node != null ? node.getCharStartIndex() : 0;

      DeclarationLocation loc = new DeclarationLocation(fileObject, nodeOffset, candidate);

      if (elements.size() > 1) {
        // Could the :nodoc: alternatives: if there is only one nodoc'ed alternative
        // don't ask user!
        int not_nodoced = 0;
        for (final IndexedElement mtd : elements) {
          if (!mtd.isNoDoc()) {
            not_nodoced++;
          }
        }
        if (not_nodoced >= 2) {
          for (final IndexedElement mtd : elements) {
            loc.addAlternative(new PythonAltLocation(mtd, mtd == candidate));
          }
        }
      }

      return loc;
    }

    return DeclarationLocation.NONE;
  }