@NotNull
 public static FontInfo getFontAbleToDisplay(
     int codePoint, @JdkConstants.FontStyle int style, @NotNull FontPreferences preferences) {
   boolean tryDefaultFont = true;
   List<String> fontFamilies = preferences.getEffectiveFontFamilies();
   FontInfo result;
   //noinspection ForLoopReplaceableByForEach
   for (int i = 0, len = fontFamilies.size();
       i < len;
       ++i) { // avoid foreach, it instantiates ArrayList$Itr, this traversal happens very often
     final String fontFamily = fontFamilies.get(i);
     result =
         doGetFontAbleToDisplay(codePoint, preferences.getSize(fontFamily), style, fontFamily);
     if (result != null) {
       return result;
     }
     tryDefaultFont &= !FontPreferences.DEFAULT_FONT_NAME.equals(fontFamily);
   }
   int size = FontPreferences.DEFAULT_FONT_SIZE;
   if (!fontFamilies.isEmpty()) {
     size = preferences.getSize(fontFamilies.get(0));
   }
   if (tryDefaultFont) {
     result = doGetFontAbleToDisplay(codePoint, size, style, FontPreferences.DEFAULT_FONT_NAME);
     if (result != null) {
       return result;
     }
   }
   result = doGetFontAbleToDisplay(codePoint, size, style);
   if (LOG.isDebugEnabled()) {
     LOG.debug("Fallback font: " + result.getFont().getFontName());
   }
   return result;
 }
  @Nullable
  private static FontInfo doGetFontAbleToDisplay(
      int codePoint,
      int size,
      @JdkConstants.FontStyle int originalStyle,
      @NotNull String defaultFontFamily) {
    synchronized (lock) {
      @JdkConstants.FontStyle int style = originalStyle;
      if (Patches.JDK_MAC_FONT_STYLE_DETECTION_WORKAROUND && style > 0 && style < 4) {
        Pair<String, Integer>[] replacement = ourStyledFontMap.get(defaultFontFamily);
        if (replacement != null) {
          defaultFontFamily = replacement[style].first;
          style = replacement[style].second;
        }
      }
      if (ourSharedKeyInstance.mySize == size
          && ourSharedKeyInstance.myStyle == style
          && ourSharedKeyInstance.myFamilyName != null
          && ourSharedKeyInstance.myFamilyName.equals(defaultFontFamily)
          && ourSharedDefaultFont != null
          && (codePoint < 128 || ourSharedDefaultFont.canDisplay(codePoint))) {
        return ourSharedDefaultFont;
      }

      ourSharedKeyInstance.myFamilyName = defaultFontFamily;
      ourSharedKeyInstance.mySize = size;
      ourSharedKeyInstance.myStyle = style;

      FontInfo defaultFont = ourUsedFonts.get(ourSharedKeyInstance);
      if (defaultFont == null) {
        defaultFont = new FontInfo(defaultFontFamily, size, style, originalStyle);
        ourUsedFonts.put(ourSharedKeyInstance, defaultFont);
        ourSharedKeyInstance = new FontKey("", 0, 0);
      }

      ourSharedDefaultFont = defaultFont;
      if (codePoint < 128 || defaultFont.canDisplay(codePoint)) {
        return defaultFont;
      } else {
        return null;
      }
    }
  }
  @NotNull
  private static FontInfo doGetFontAbleToDisplay(
      int codePoint, int size, @JdkConstants.FontStyle int style) {
    synchronized (lock) {
      if (ourUndisplayableChars.contains(codePoint)) return ourSharedDefaultFont;

      final Collection<FontInfo> descriptors = ourUsedFonts.values();
      for (FontInfo font : descriptors) {
        if (font.getSize() == size && font.getStyle() == style && font.canDisplay(codePoint)) {
          return font;
        }
      }

      for (int i = 0; i < ourFontNames.size(); i++) {
        String name = ourFontNames.get(i);
        FontInfo font = new FontInfo(name, size, style);
        if (font.canDisplay(codePoint)) {
          ourUsedFonts.put(new FontKey(name, size, style), font);
          ourFontNames.remove(i);
          return font;
        }
      }

      ourUndisplayableChars.add(codePoint);

      return ourSharedDefaultFont;
    }
  }
 @Override
 public void drawChars(
     @NotNull Graphics g,
     @NotNull char[] data,
     int start,
     int end,
     int x,
     int y,
     Color color,
     FontInfo fontInfo) {
   g.setFont(fontInfo.getFont());
   g.setColor(color);
   g.drawChars(data, start, end - start, x, y);
 }