From da62cc13f6d84d16937e73c666f17030bd0f2f83 Mon Sep 17 00:00:00 2001 From: DarunNayeem Date: Sat, 4 Jul 2026 22:30:20 +0600 Subject: [PATCH 1/2] Refractoring code smells Before, the library mixed startup work, indexing, and ordering logic directly inside the static initializer in EmojiManager.java, and EmojiParser.java repeated nearly identical anonymous transformer logic for aliases and HTML encodings. EmojiLoader.java also used manual stream handling and verbose JSON field branching. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After, the bootstrap logic is split into small helpers for indexing and sorting, the parser’s transformation rules are centralized in dedicated methods, and the loader uses simpler field extraction and safer cleanup. I also preserved variation selectors in parser candidates because that was required to keep the round-trip emoji tests passing. This refactor improves readability and maintainability without changing the library’s public API. It addresses three areas: static initialization in the emoji manager, duplicated transformation logic in the parser, and stream/JSON handling in the loader. The parser change also fixes a loss of variation selector data that broke round-trip behavior on some emoji sequences. --- .../java/com/vdurmont/emoji/EmojiLoader.java | 32 ++--- .../java/com/vdurmont/emoji/EmojiManager.java | 61 +++++--- .../java/com/vdurmont/emoji/EmojiParser.java | 132 ++++++++++++------ 3 files changed, 146 insertions(+), 79 deletions(-) diff --git a/src/main/java/com/vdurmont/emoji/EmojiLoader.java b/src/main/java/com/vdurmont/emoji/EmojiLoader.java index 9658e887..09fa50e6 100644 --- a/src/main/java/com/vdurmont/emoji/EmojiLoader.java +++ b/src/main/java/com/vdurmont/emoji/EmojiLoader.java @@ -48,15 +48,21 @@ public static List loadEmojis(InputStream stream) throws IOException { private static String inputStreamToString( InputStream stream ) throws IOException { - StringBuilder sb = new StringBuilder(); - InputStreamReader isr = new InputStreamReader(stream, "UTF-8"); - BufferedReader br = new BufferedReader(isr); - String read; - while((read = br.readLine()) != null) { - sb.append(read); + BufferedReader br = null; + try { + StringBuilder sb = new StringBuilder(); + InputStreamReader isr = new InputStreamReader(stream, "UTF-8"); + br = new BufferedReader(isr); + String read; + while ((read = br.readLine()) != null) { + sb.append(read); + } + return sb.toString(); + } finally { + if (br != null) { + br.close(); + } } - br.close(); - return sb.toString(); } protected static Emoji buildEmojiFromJSON( @@ -67,14 +73,8 @@ protected static Emoji buildEmojiFromJSON( } byte[] bytes = json.getString("emoji").getBytes("UTF-8"); - String description = null; - if (json.has("description")) { - description = json.getString("description"); - } - boolean supportsFitzpatrick = false; - if (json.has("supports_fitzpatrick")) { - supportsFitzpatrick = json.getBoolean("supports_fitzpatrick"); - } + String description = json.optString("description", null); + boolean supportsFitzpatrick = json.optBoolean("supports_fitzpatrick", false); List aliases = jsonArrayToStringList(json.getJSONArray("aliases")); List tags = jsonArrayToStringList(json.getJSONArray("tags")); return new Emoji(description, supportsFitzpatrick, aliases, tags, bytes); diff --git a/src/main/java/com/vdurmont/emoji/EmojiManager.java b/src/main/java/com/vdurmont/emoji/EmojiManager.java index 7c3ef729..b6976719 100644 --- a/src/main/java/com/vdurmont/emoji/EmojiManager.java +++ b/src/main/java/com/vdurmont/emoji/EmojiManager.java @@ -26,31 +26,24 @@ public class EmojiManager { static final EmojiTrie EMOJI_TRIE; static { + InputStream stream = null; try { - InputStream stream = EmojiLoader.class.getResourceAsStream(PATH); + stream = EmojiLoader.class.getResourceAsStream(PATH); List emojis = EmojiLoader.loadEmojis(stream); ALL_EMOJIS = emojis; - for (Emoji emoji : emojis) { - for (String tag : emoji.getTags()) { - if (EMOJIS_BY_TAG.get(tag) == null) { - EMOJIS_BY_TAG.put(tag, new HashSet()); - } - EMOJIS_BY_TAG.get(tag).add(emoji); - } - for (String alias : emoji.getAliases()) { - EMOJIS_BY_ALIAS.put(alias, emoji); - } - } - + indexEmojis(emojis); EMOJI_TRIE = new EmojiTrie(emojis); - Collections.sort(ALL_EMOJIS, new Comparator() { - public int compare(Emoji e1, Emoji e2) { - return e2.getUnicode().length() - e1.getUnicode().length(); - } - }); - stream.close(); + sortEmojisByUnicodeLength(ALL_EMOJIS); } catch (IOException e) { throw new RuntimeException(e); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + // Ignore cleanup failures while loading the emoji database. + } + } } } @@ -59,6 +52,36 @@ public int compare(Emoji e1, Emoji e2) { */ private EmojiManager() {} + private static void indexEmojis(List emojis) { + for (Emoji emoji : emojis) { + addEmojiToTagIndex(emoji); + addEmojiToAliasIndex(emoji); + } + } + + private static void addEmojiToTagIndex(Emoji emoji) { + for (String tag : emoji.getTags()) { + if (EMOJIS_BY_TAG.get(tag) == null) { + EMOJIS_BY_TAG.put(tag, new HashSet()); + } + EMOJIS_BY_TAG.get(tag).add(emoji); + } + } + + private static void addEmojiToAliasIndex(Emoji emoji) { + for (String alias : emoji.getAliases()) { + EMOJIS_BY_ALIAS.put(alias, emoji); + } + } + + private static void sortEmojisByUnicodeLength(List emojis) { + Collections.sort(emojis, new Comparator() { + public int compare(Emoji e1, Emoji e2) { + return e2.getUnicode().length() - e1.getUnicode().length(); + } + }); + } + /** * Returns all the {@link com.vdurmont.emoji.Emoji}s for a given tag. * diff --git a/src/main/java/com/vdurmont/emoji/EmojiParser.java b/src/main/java/com/vdurmont/emoji/EmojiParser.java index 5dd533cd..f9ac3cf2 100644 --- a/src/main/java/com/vdurmont/emoji/EmojiParser.java +++ b/src/main/java/com/vdurmont/emoji/EmojiParser.java @@ -53,26 +53,7 @@ public static String parseToAliases( ) { EmojiTransformer emojiTransformer = new EmojiTransformer() { public String transform(UnicodeCandidate unicodeCandidate) { - switch (fitzpatrickAction) { - default: - case PARSE: - if (unicodeCandidate.hasFitzpatrick()) { - return ":" + - unicodeCandidate.getEmoji().getAliases().get(0) + - "|" + - unicodeCandidate.getFitzpatrickType() + - ":"; - } - case REMOVE: - return ":" + - unicodeCandidate.getEmoji().getAliases().get(0) + - ":"; - case IGNORE: - return ":" + - unicodeCandidate.getEmoji().getAliases().get(0) + - ":" + - unicodeCandidate.getFitzpatrickUnicode(); - } + return transformToAlias(unicodeCandidate, fitzpatrickAction); } }; @@ -231,15 +212,7 @@ public static String parseToHtmlDecimal( ) { EmojiTransformer emojiTransformer = new EmojiTransformer() { public String transform(UnicodeCandidate unicodeCandidate) { - switch (fitzpatrickAction) { - default: - case PARSE: - case REMOVE: - return unicodeCandidate.getEmoji().getHtmlDecimal(); - case IGNORE: - return unicodeCandidate.getEmoji().getHtmlDecimal() + - unicodeCandidate.getFitzpatrickUnicode(); - } + return transformToHtmlDecimal(unicodeCandidate, fitzpatrickAction); } }; @@ -286,15 +259,7 @@ public static String parseToHtmlHexadecimal( ) { EmojiTransformer emojiTransformer = new EmojiTransformer() { public String transform(UnicodeCandidate unicodeCandidate) { - switch (fitzpatrickAction) { - default: - case PARSE: - case REMOVE: - return unicodeCandidate.getEmoji().getHtmlHexadecimal(); - case IGNORE: - return unicodeCandidate.getEmoji().getHtmlHexadecimal() + - unicodeCandidate.getFitzpatrickUnicode(); - } + return transformToHtmlHexadecimal(unicodeCandidate, fitzpatrickAction); } }; @@ -421,9 +386,12 @@ public static List extractEmojis(String input) { List result = new ArrayList(); for (UnicodeCandidate emoji : emojis) { if (emoji.getEmoji().supportsFitzpatrick() && emoji.hasFitzpatrick()) { - result.add(emoji.getEmoji().getUnicode(emoji.getFitzpatrick())); + result.add(emoji.getEmoji().getUnicode() + + emoji.getVariationSelectorUnicode() + + emoji.getFitzpatrickUnicode()); } else { - result.add(emoji.getEmoji().getUnicode()); + result.add(emoji.getEmoji().getUnicode() + + emoji.getVariationSelectorUnicode()); } } return result; @@ -465,11 +433,18 @@ protected static UnicodeCandidate getNextUnicodeCandidate(char[] chars, int star if (emojiEnd != -1) { Emoji emoji = EmojiManager.getByUnicode(new String(chars, i, emojiEnd - i)); - String fitzpatrickString = (emojiEnd + 2 <= chars.length) ? - new String(chars, emojiEnd, 2) : + String variationSelector = null; + int candidateEnd = emojiEnd; + if (candidateEnd < chars.length && isVariationSelector(chars[candidateEnd])) { + variationSelector = new String(chars, candidateEnd, 1); + candidateEnd++; + } + String fitzpatrickString = (candidateEnd + 2 <= chars.length) ? + new String(chars, candidateEnd, 2) : null; return new UnicodeCandidate( emoji, + variationSelector, fitzpatrickString, i ); @@ -512,11 +487,18 @@ protected static int getEmojiEndPos(char[] text, int startPos) { public static class UnicodeCandidate { private final Emoji emoji; + private final String variationSelector; private final Fitzpatrick fitzpatrick; private final int startIndex; - private UnicodeCandidate(Emoji emoji, String fitzpatrick, int startIndex) { + private UnicodeCandidate( + Emoji emoji, + String variationSelector, + String fitzpatrick, + int startIndex + ) { this.emoji = emoji; + this.variationSelector = variationSelector; this.fitzpatrick = Fitzpatrick.fitzpatrickFromUnicode(fitzpatrick); this.startIndex = startIndex; } @@ -529,10 +511,18 @@ public boolean hasFitzpatrick() { return getFitzpatrick() != null; } + public boolean hasVariationSelector() { + return variationSelector != null; + } + public Fitzpatrick getFitzpatrick() { return fitzpatrick; } + public String getVariationSelectorUnicode() { + return hasVariationSelector() ? variationSelector : ""; + } + public String getFitzpatrickType() { return hasFitzpatrick() ? fitzpatrick.name().toLowerCase() : ""; } @@ -550,7 +540,61 @@ public int getEmojiEndIndex() { } public int getFitzpatrickEndIndex() { - return getEmojiEndIndex() + (fitzpatrick != null ? 2 : 0); + return getEmojiEndIndex() + + (variationSelector != null ? variationSelector.length() : 0) + + (fitzpatrick != null ? 2 : 0); + } + } + + private static String transformToAlias( + UnicodeCandidate unicodeCandidate, + FitzpatrickAction fitzpatrickAction + ) { + String alias = unicodeCandidate.getEmoji().getAliases().get(0); + switch (fitzpatrickAction) { + default: + case PARSE: + if (unicodeCandidate.hasFitzpatrick()) { + return ":" + alias + "|" + unicodeCandidate.getFitzpatrickType() + ":"; + } + case REMOVE: + return ":" + alias + ":"; + case IGNORE: + return ":" + alias + ":" + + unicodeCandidate.getVariationSelectorUnicode() + + unicodeCandidate.getFitzpatrickUnicode(); + } + } + + private static String transformToHtmlDecimal( + UnicodeCandidate unicodeCandidate, + FitzpatrickAction fitzpatrickAction + ) { + switch (fitzpatrickAction) { + default: + case PARSE: + case REMOVE: + return unicodeCandidate.getEmoji().getHtmlDecimal(); + case IGNORE: + return unicodeCandidate.getEmoji().getHtmlDecimal() + + unicodeCandidate.getVariationSelectorUnicode() + + unicodeCandidate.getFitzpatrickUnicode(); + } + } + + private static String transformToHtmlHexadecimal( + UnicodeCandidate unicodeCandidate, + FitzpatrickAction fitzpatrickAction + ) { + switch (fitzpatrickAction) { + default: + case PARSE: + case REMOVE: + return unicodeCandidate.getEmoji().getHtmlHexadecimal(); + case IGNORE: + return unicodeCandidate.getEmoji().getHtmlHexadecimal() + + unicodeCandidate.getVariationSelectorUnicode() + + unicodeCandidate.getFitzpatrickUnicode(); } } From ae7bdb478f1c6652b4ccf1da3230aa8191a7aef5 Mon Sep 17 00:00:00 2001 From: Darun Nayeem Date: Sat, 4 Jul 2026 22:39:28 +0600 Subject: [PATCH 2/2] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/main/java/com/vdurmont/emoji/EmojiManager.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/vdurmont/emoji/EmojiManager.java b/src/main/java/com/vdurmont/emoji/EmojiManager.java index b6976719..40258401 100644 --- a/src/main/java/com/vdurmont/emoji/EmojiManager.java +++ b/src/main/java/com/vdurmont/emoji/EmojiManager.java @@ -29,6 +29,9 @@ public class EmojiManager { InputStream stream = null; try { stream = EmojiLoader.class.getResourceAsStream(PATH); + if (stream == null) { + throw new IllegalStateException("Emoji database not found on classpath: " + PATH); + } List emojis = EmojiLoader.loadEmojis(stream); ALL_EMOJIS = emojis; indexEmojis(emojis);