public static JBPopup createPopup(
      final List<? extends GotoRelatedItem> items, final String title) {
    Object[] elements = new Object[items.size()];
    // todo[nik] move presentation logic to GotoRelatedItem class
    final Map<PsiElement, GotoRelatedItem> itemsMap = new HashMap<PsiElement, GotoRelatedItem>();
    for (int i = 0; i < items.size(); i++) {
      GotoRelatedItem item = items.get(i);
      elements[i] = item.getElement() != null ? item.getElement() : item;
      itemsMap.put(item.getElement(), item);
    }

    return getPsiElementPopup(
        elements,
        itemsMap,
        title,
        new Processor<Object>() {
          @Override
          public boolean process(Object element) {
            if (element instanceof PsiElement) {
              //noinspection SuspiciousMethodCalls
              itemsMap.get(element).navigate();
            } else {
              ((GotoRelatedItem) element).navigate();
            }
            return true;
          }
        });
  }
  /**
   * Returns navigation popup that shows list of related items from {@code items} list
   *
   * @param items
   * @param title
   * @param showContainingModules Whether the popup should show additional information that aligned
   *     at the right side of the dialog.<br>
   *     It's usually a module name or library name of corresponding navigation item.<br>
   *     {@code false} by default
   * @return
   */
  @NotNull
  public static JBPopup getRelatedItemsPopup(
      final List<? extends GotoRelatedItem> items, String title, boolean showContainingModules) {
    Object[] elements = new Object[items.size()];
    // todo[nik] move presentation logic to GotoRelatedItem class
    final Map<PsiElement, GotoRelatedItem> itemsMap = new HashMap<PsiElement, GotoRelatedItem>();
    for (int i = 0; i < items.size(); i++) {
      GotoRelatedItem item = items.get(i);
      elements[i] = item.getElement() != null ? item.getElement() : item;
      itemsMap.put(item.getElement(), item);
    }

    return getPsiElementPopup(
        elements,
        itemsMap,
        title,
        showContainingModules,
        element -> {
          if (element instanceof PsiElement) {
            //noinspection SuspiciousMethodCalls
            itemsMap.get(element).navigate();
          } else {
            ((GotoRelatedItem) element).navigate();
          }
          return true;
        });
  }
 @Override
 protected void collectNavigationMarkers(
     @NotNull PsiElement element, Collection<? super RelatedItemLineMarkerInfo> result) {
   final PsiElement pythonStub = getPythonStub(element);
   if (pythonStub != null) {
     final List<GotoRelatedItem> relatedItems =
         GotoRelatedItem.createItems(Collections.singletonList(pythonStub));
     result.add(
         new RelatedItemLineMarkerInfo<PsiElement>(
             element,
             element.getTextRange(),
             ICON,
             Pass.LINE_MARKERS,
             element1 -> "Has stub item in " + pythonStub.getContainingFile().getName(),
             new GutterIconNavigationHandler<PsiElement>() {
               @Override
               public void navigate(MouseEvent e, PsiElement elt) {
                 final PsiElement pythonStub = getPythonStub(elt);
                 if (pythonStub != null) {
                   PsiNavigateUtil.navigate(pythonStub);
                 }
               }
             },
             GutterIconRenderer.Alignment.RIGHT,
             relatedItems));
   }
 }
  @NotNull
  private static Collection<PsiClass> findRelatedActivities(
      @NotNull XmlFile file,
      @NotNull AndroidFacet facet,
      @NotNull DomFileDescription<?> description) {
    if (description instanceof LayoutDomFileDescription) {
      final Computable<List<GotoRelatedItem>> computable =
          AndroidGotoRelatedProvider.getLazyItemsForXmlFile(file, facet);

      if (computable == null) {
        return Collections.emptyList();
      }
      final List<GotoRelatedItem> items = computable.compute();

      if (items.isEmpty()) {
        return Collections.emptyList();
      }
      final PsiClass activityClass = findActivityClass(facet.getModule());

      if (activityClass == null) {
        return Collections.emptyList();
      }
      final List<PsiClass> result = new ArrayList<PsiClass>();

      for (GotoRelatedItem item : items) {
        final PsiElement element = item.getElement();

        if (element instanceof PsiClass) {
          final PsiClass aClass = (PsiClass) element;

          if (aClass.isInheritor(activityClass, true)) {
            result.add(aClass);
          }
        }
      }
      return result;
    } else {
      return findRelatedActivitiesForMenu(file, facet);
    }
  }
public class ExtensionPointDeclarationRelatedItemLineMarkerProvider
    extends DevkitRelatedLineMarkerProviderBase {

  private static final NotNullFunction<ExtensionPointCandidate, Collection<? extends PsiElement>>
      CONVERTER = candidate -> Collections.singleton(candidate.pointer.getElement());

  private static final NotNullFunction<
          ExtensionPointCandidate, Collection<? extends GotoRelatedItem>>
      RELATED_ITEM_PROVIDER =
          candidate ->
              GotoRelatedItem.createItems(
                  Collections.singleton(candidate.pointer.getElement()), "DevKit");

  @Override
  protected void collectNavigationMarkers(
      @NotNull PsiElement element, Collection<? super RelatedItemLineMarkerInfo> result) {
    if (element instanceof PsiField) {
      process((PsiField) element, result);
    }
  }

  private static void process(
      PsiField psiField, Collection<? super RelatedItemLineMarkerInfo> result) {
    if (!isExtensionPointNameDeclarationField(psiField)) return;

    final PsiClass epClass = resolveExtensionPointClass(psiField);
    if (epClass == null) return;

    final String epName = resolveEpName(psiField);
    if (epName == null) return;

    ExtensionPointLocator locator = new ExtensionPointLocator(epClass);
    List<ExtensionPointCandidate> targets =
        ContainerUtil.filter(
            locator.findDirectCandidates(),
            new Condition<ExtensionPointCandidate>() {
              @Override
              public boolean value(ExtensionPointCandidate candidate) {
                return epName.equals(candidate.epName);
              }
            });

    final RelatedItemLineMarkerInfo<PsiElement> info =
        NavigationGutterIconBuilder.create(AllIcons.Nodes.Plugin, CONVERTER, RELATED_ITEM_PROVIDER)
            .setTargets(targets)
            .setPopupTitle("Choose Extension Point")
            .setTooltipText("Extension Point Declaration")
            .setAlignment(GutterIconRenderer.Alignment.RIGHT)
            .createLineMarkerInfo(psiField.getNameIdentifier());
    result.add(info);
  }

  @Nullable
  private static PsiClass resolveExtensionPointClass(PsiField psiField) {
    final PsiType typeParameter =
        PsiUtil.substituteTypeParameter(
            psiField.getType(), ExtensionPointName.class.getName(), 0, false);
    return PsiUtil.resolveClassInClassTypeOnly(typeParameter);
  }

  private static String resolveEpName(PsiField psiField) {
    final PsiExpression initializer = psiField.getInitializer();

    PsiExpressionList expressionList = null;
    if (initializer instanceof PsiMethodCallExpression) {
      expressionList = ((PsiMethodCallExpression) initializer).getArgumentList();
    } else if (initializer instanceof PsiNewExpression) {
      expressionList = ((PsiNewExpression) initializer).getArgumentList();
    }
    if (expressionList == null) return null;

    final PsiExpression[] expressions = expressionList.getExpressions();
    if (expressions.length != 1) return null;

    final PsiExpression epNameExpression = expressions[0];
    final PsiConstantEvaluationHelper helper =
        JavaPsiFacade.getInstance(psiField.getProject()).getConstantEvaluationHelper();
    final Object o = helper.computeConstantExpression(epNameExpression);
    return o instanceof String ? (String) o : null;
  }

  private static boolean isExtensionPointNameDeclarationField(PsiField psiField) {
    // *do* allow non-public
    if (!psiField.hasModifierProperty(PsiModifier.FINAL)
        || !psiField.hasModifierProperty(PsiModifier.STATIC)
        || psiField.hasModifierProperty(PsiModifier.ABSTRACT)) {
      return false;
    }

    if (!psiField.hasInitializer()) {
      return false;
    }

    final PsiExpression initializer = psiField.getInitializer();
    if (!(initializer instanceof PsiMethodCallExpression)
        && !(initializer instanceof PsiNewExpression)) {
      return false;
    }

    final PsiClass fieldClass = PsiTypesUtil.getPsiClass(psiField.getType());
    if (fieldClass == null) {
      return false;
    }

    return ExtensionPointName.class.getName().equals(fieldClass.getQualifiedName());
  }
}