<commit_before>/*
 * Copyright 2000-2017 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.ide.util;

import com.intellij.ide.ui.UISettings;
import com.intellij.navigation.ColoredItemPresentation;
import com.intellij.navigation.ItemPresentation;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.IPopupChooserBuilder;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.FileStatusManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.problems.WolfTheProblemSolver;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.JBColor;
import com.intellij.ui.ListSpeedSearch;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.speedSearch.SpeedSearchUtil;
import com.intellij.util.IconUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.text.Matcher;
import com.intellij.util.text.MatcherHolder;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.swing.*;
import java.awt.*;
import java.util.Comparator;
import java.util.regex.Pattern;

import static com.intellij.openapi.vfs.newvfs.VfsPresentationUtil.getFileBackgroundColor;

public abstract class PsiElementListCellRenderer<T extends PsiElement> extends JPanel implements ListCellRenderer {
  private static final String LEFT = BorderLayout.WEST;
  private static final Pattern CONTAINER_PATTERN = Pattern.compile("(\\(in |\\()?([^)]*)(\\))?");

  private boolean myFocusBorderEnabled = Registry.is("psi.element.list.cell.renderer.focus.border.enabled");
  protected int myRightComponentWidth;

  protected PsiElementListCellRenderer() {
    super(new BorderLayout());
  }

  private class MyAccessibleContext extends JPanel.AccessibleJPanel {
    @Override
    public String getAccessibleName() {
      LayoutManager lm = getLayout();
      assert lm instanceof BorderLayout;
      Component leftCellRendererComp = ((BorderLayout)lm).getLayoutComponent(LEFT);
      return leftCellRendererComp instanceof Accessible ?
             leftCellRendererComp.getAccessibleContext().getAccessibleName() : super.getAccessibleName();
    }
  }

  @Override
  public AccessibleContext getAccessibleContext() {
    if (accessibleContext == null) {
      accessibleContext = new MyAccessibleContext();
    }
    return accessibleContext;
  }

  protected static Color getBackgroundColor(@Nullable Object value) {
    if (value instanceof PsiElement) {
      PsiElement psiElement = (PsiElement)value;
      Project project = psiElement.getProject();

      VirtualFile file = null;
      PsiFile psiFile = psiElement.getContainingFile();

      if (psiFile != null) {
        file = psiFile.getVirtualFile();
      }
      else if (psiElement instanceof PsiDirectory) {
        file = ((PsiDirectory)psiElement).getVirtualFile();
      }
      Color fileBgColor = file != null ? getFileBackgroundColor(project, file) : null;
      if (fileBgColor != null) {
        return fileBgColor;
      }
    }

    return UIUtil.getListBackground();
  }

  public static class ItemMatchers {
    @Nullable public final Matcher nameMatcher;
    @Nullable final Matcher locationMatcher;

    public ItemMatchers(@Nullable Matcher nameMatcher, @Nullable Matcher locationMatcher) {
      this.nameMatcher = nameMatcher;
      this.locationMatcher = locationMatcher;
    }
  }

  private class LeftRenderer extends ColoredListCellRenderer {
    private final String myModuleName;
    private final ItemMatchers myMatchers;

    LeftRenderer(final String moduleName, @NotNull ItemMatchers matchers) {
      myModuleName = moduleName;
      myMatchers = matchers;
    }

    @Override
    protected void customizeCellRenderer(@NotNull JList list, Object value, int index, boolean selected, boolean hasFocus) {
      Color bgColor = UIUtil.getListBackground();
      Color color = list.getForeground();
      setPaintFocusBorder(hasFocus && UIUtil.isToUseDottedCellBorder() && myFocusBorderEnabled);
      if (value instanceof PsiElement) {
        T element = (T)value;
        String name = element.isValid() ? getElementText(element) : "INVALID";
        PsiFile psiFile = element.isValid() ? element.getContainingFile() : null;
        boolean isProblemFile = false;

        if (psiFile != null) {
          VirtualFile vFile = psiFile.getVirtualFile();
          if (vFile != null) {
            Project project = psiFile.getProject();
            if (WolfTheProblemSolver.getInstance(project).isProblemFile(vFile)) {
              isProblemFile = true;
            }
            FileStatus status = FileStatusManager.getInstance(project).getStatus(vFile);
            color = status.getColor();

            Color fileBgColor = getFileBackgroundColor(project, vFile);
            bgColor = fileBgColor == null ? bgColor : fileBgColor;
          }
        }

        TextAttributes attributes = element.isValid() ? getNavigationItemAttributes(value) : null;

        SimpleTextAttributes nameAttributes = attributes != null ? SimpleTextAttributes.fromTextAttributes(attributes) : null;

        if (nameAttributes == null) nameAttributes = new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, color);

        assert name != null : "Null name for PSI element " + element + " (by " + PsiElementListCellRenderer.this + ")";
        SpeedSearchUtil.appendColoredFragmentForMatcher(name, this, nameAttributes, myMatchers.nameMatcher, bgColor, selected);
        if (!element.isValid()) {
          append(" Invalid", SimpleTextAttributes.ERROR_ATTRIBUTES);
          return;
        }
        setIcon(PsiElementListCellRenderer.this.getIcon(element));

        String containerText = getContainerTextForLeftComponent(element, name + (myModuleName != null ? myModuleName + "        " : ""));
        if (containerText != null) {
          appendLocationText(selected, bgColor, isProblemFile, containerText);
        }
      }
      else if (!customizeNonPsiElementLeftRenderer(this, list, value, index, selected, hasFocus)) {
        setIcon(IconUtil.getEmptyIcon(false));
        append(value == null ? "" : value.toString(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, list.getForeground()));
      }
      setBackground(selected ? UIUtil.getListSelectionBackground() : bgColor);
    }

    private void appendLocationText(boolean selected, Color bgColor, boolean isProblemFile, String containerText) {
      SimpleTextAttributes locationAttrs = new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, JBColor.GRAY);
      if (isProblemFile) {
        SimpleTextAttributes wavedAttributes = SimpleTextAttributes.merge(new SimpleTextAttributes(SimpleTextAttributes.STYLE_WAVED, JBColor.GRAY, JBColor.RED), locationAttrs);
        java.util.regex.Matcher matcher = CONTAINER_PATTERN.matcher(containerText);
        if (matcher.matches()) {
          String prefix = matcher.group(1);
          SpeedSearchUtil.appendColoredFragmentForMatcher(" " + ObjectUtils.notNull(prefix, ""), this, locationAttrs, myMatchers.locationMatcher, bgColor, selected);

          String strippedContainerText = matcher.group(2);
          SpeedSearchUtil.appendColoredFragmentForMatcher(ObjectUtils.notNull(strippedContainerText, ""), this, wavedAttributes, myMatchers.locationMatcher, bgColor, selected);<commit_msg>diagnostics & workaround for EA-118809 - IAE: SpeedSearchUtil.$$$reportNull$$$<commit_after>/*
 * Copyright 2000-2017 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.ide.util;

import com.intellij.ide.ui.UISettings;
import com.intellij.navigation.ColoredItemPresentation;
import com.intellij.navigation.ItemPresentation;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.IPopupChooserBuilder;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.FileStatusManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.problems.WolfTheProblemSolver;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.JBColor;
import com.intellij.ui.ListSpeedSearch;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.speedSearch.SpeedSearchUtil;
import com.intellij.util.IconUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.text.Matcher;
import com.intellij.util.text.MatcherHolder;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.swing.*;
import java.awt.*;
import java.util.Comparator;
import java.util.regex.Pattern;

import static com.intellij.openapi.vfs.newvfs.VfsPresentationUtil.getFileBackgroundColor;

public abstract class PsiElementListCellRenderer<T extends PsiElement> extends JPanel implements ListCellRenderer {
  private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.PsiElementListCellRenderer");
  private static final String LEFT = BorderLayout.WEST;
  private static final Pattern CONTAINER_PATTERN = Pattern.compile("(\\(in |\\()?([^)]*)(\\))?");

  private boolean myFocusBorderEnabled = Registry.is("psi.element.list.cell.renderer.focus.border.enabled");
  protected int myRightComponentWidth;

  protected PsiElementListCellRenderer() {
    super(new BorderLayout());
  }

  private class MyAccessibleContext extends JPanel.AccessibleJPanel {
    @Override
    public String getAccessibleName() {
      LayoutManager lm = getLayout();
      assert lm instanceof BorderLayout;
      Component leftCellRendererComp = ((BorderLayout)lm).getLayoutComponent(LEFT);
      return leftCellRendererComp instanceof Accessible ?
             leftCellRendererComp.getAccessibleContext().getAccessibleName() : super.getAccessibleName();
    }
  }

  @Override
  public AccessibleContext getAccessibleContext() {
    if (accessibleContext == null) {
      accessibleContext = new MyAccessibleContext();
    }
    return accessibleContext;
  }

  protected static Color getBackgroundColor(@Nullable Object value) {
    if (value instanceof PsiElement) {
      PsiElement psiElement = (PsiElement)value;
      Project project = psiElement.getProject();

      VirtualFile file = null;
      PsiFile psiFile = psiElement.getContainingFile();

      if (psiFile != null) {
        file = psiFile.getVirtualFile();
      }
      else if (psiElement instanceof PsiDirectory) {
        file = ((PsiDirectory)psiElement).getVirtualFile();
      }
      Color fileBgColor = file != null ? getFileBackgroundColor(project, file) : null;
      if (fileBgColor != null) {
        return fileBgColor;
      }
    }

    return UIUtil.getListBackground();
  }

  public static class ItemMatchers {
    @Nullable public final Matcher nameMatcher;
    @Nullable final Matcher locationMatcher;

    public ItemMatchers(@Nullable Matcher nameMatcher, @Nullable Matcher locationMatcher) {
      this.nameMatcher = nameMatcher;
      this.locationMatcher = locationMatcher;
    }
  }

  private class LeftRenderer extends ColoredListCellRenderer {
    private final String myModuleName;
    private final ItemMatchers myMatchers;

    LeftRenderer(final String moduleName, @NotNull ItemMatchers matchers) {
      myModuleName = moduleName;
      myMatchers = matchers;
    }

    @Override
    protected void customizeCellRenderer(@NotNull JList list, Object value, int index, boolean selected, boolean hasFocus) {
      Color bgColor = UIUtil.getListBackground();
      Color color = list.getForeground();
      setPaintFocusBorder(hasFocus && UIUtil.isToUseDottedCellBorder() && myFocusBorderEnabled);
      if (value instanceof PsiElement) {
        T element = (T)value;
        String name = element.isValid() ? getElementText(element) : "INVALID";
        PsiFile psiFile = element.isValid() ? element.getContainingFile() : null;
        boolean isProblemFile = false;

        if (psiFile != null) {
          VirtualFile vFile = psiFile.getVirtualFile();
          if (vFile != null) {
            Project project = psiFile.getProject();
            if (WolfTheProblemSolver.getInstance(project).isProblemFile(vFile)) {
              isProblemFile = true;
            }
            FileStatus status = FileStatusManager.getInstance(project).getStatus(vFile);
            color = status.getColor();

            Color fileBgColor = getFileBackgroundColor(project, vFile);
            bgColor = fileBgColor == null ? bgColor : fileBgColor;
          }
        }

        TextAttributes attributes = element.isValid() ? getNavigationItemAttributes(value) : null;

        SimpleTextAttributes nameAttributes = attributes != null ? SimpleTextAttributes.fromTextAttributes(attributes) : null;

        if (nameAttributes == null) nameAttributes = new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, color);

        if (name == null) {
          LOG.error("Null name for PSI element " + element + " (by " + PsiElementListCellRenderer.this + ")");
          name = "Unknown";
        }
        SpeedSearchUtil.appendColoredFragmentForMatcher(name, this, nameAttributes, myMatchers.nameMatcher, bgColor, selected);
        if (!element.isValid()) {
          append(" Invalid", SimpleTextAttributes.ERROR_ATTRIBUTES);
          return;
        }
        setIcon(PsiElementListCellRenderer.this.getIcon(element));

        String containerText = getContainerTextForLeftComponent(element, name + (myModuleName != null ? myModuleName + "        " : ""));
        if (containerText != null) {
          appendLocationText(selected, bgColor, isProblemFile, containerText);
        }
      }
      else if (!customizeNonPsiElementLeftRenderer(this, list, value, index, selected, hasFocus)) {
        setIcon(IconUtil.getEmptyIcon(false));
        append(value == null ? "" : value.toString(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, list.getForeground()));
      }
      setBackground(selected ? UIUtil.getListSelectionBackground() : bgColor);
    }

    private void appendLocationText(boolean selected, Color bgColor, boolean isProblemFile, String containerText) {
      SimpleTextAttributes locationAttrs = new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, JBColor.GRAY);
      if (isProblemFile) {
        SimpleTextAttributes wavedAttributes = SimpleTextAttributes.merge(new SimpleTextAttributes(SimpleTextAttributes.STYLE_WAVED, JBColor.GRAY, JBColor.RED), locationAttrs);
        java.util.regex.Matcher matcher = CONTAINER_PATTERN.matcher(containerText);
        if (matcher.matches()) {
          String prefix = matcher.group(1);
          SpeedSearchUtil.appendColoredFragmentForMatcher(" " + ObjectUtils.notNull(prefix, ""), this, locationAttrs, myMatchers.locationMatcher, bgColor, selected);

          String strippedContainerText = matcher.group(2);
          SpeedSearchUtil.appendColoredFragmentForMatcher(ObjectUtils.notNull(strippedContainerText, ""), this, wavedAttributes, myMatchers.locationMatcher, bgColor, selected);
{'fraction_non_alphanumeric': 0.09480369515011547, 'fraction_numerical': 0.0019630484988452657, 'mean_word_length': 4.088425381903643, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6348650', 'n_tokens_mistral': 4845, 'n_tokens_neox': 4714, 'n_words': 1062}