Skip to content

Commit

Permalink
- Added more overloaded methods with an varargs Emoji parameter.
Browse files Browse the repository at this point in the history
- All operations now also take non "fully-qualified"  emojis into account.
  • Loading branch information
felldo committed Sep 22, 2023
1 parent 376c789 commit cd5584d
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 70 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ org.gradle.caching=true

group=net.fellbaum
description=A Java library for conveniently working with emojis
version=1.2.0
version=1.3.0
130 changes: 85 additions & 45 deletions lib/src/main/java/net/fellbaum/jemoji/EmojiManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ public final class EmojiManager {
final String fileContent = readFileAsString();
try {
final List<Emoji> emojis = new ObjectMapper().readValue(fileContent, new TypeReference<List<Emoji>>() {
}).stream()
.filter(emoji -> emoji.getQualification() == Qualification.FULLY_QUALIFIED || emoji.getQualification() == Qualification.COMPONENT)
.collect(Collectors.toList());
});

EMOJI_UNICODE_TO_EMOJI = Collections.unmodifiableMap(
emojis.stream().collect(Collectors.toMap(Emoji::getEmoji, Function.identity()))
Expand Down Expand Up @@ -326,7 +324,18 @@ public static Set<Emoji> extractEmojis(final String text) {
* @return The text without emojis.
*/
public static String removeAllEmojis(final String text) {
return removeEmojis(text, EMOJIS_LENGTH_DESCENDING);
return removeAllEmojisExcept(text, Collections.emptyList());
}

/**
* Removes the given emojis from the given text.
*
* @param text The text to remove emojis from.
* @param emojisToRemove The emojis to remove.
* @return The text without the given emojis.
*/
public static String removeEmojis(final String text, final Emoji... emojisToRemove) {
return removeEmojis(text, Arrays.asList(emojisToRemove));
}

/**
Expand All @@ -337,8 +346,31 @@ public static String removeAllEmojis(final String text) {
* @return The text without the given emojis.
*/
public static String removeEmojis(final String text, final Collection<Emoji> emojisToRemove) {
final LinkedHashMap<Integer, List<Emoji>> FIRST_CODEPOINT_TO_EMOJIS_ORDER_CODEPOINT_LENGTH_DESCENDING = emojisToRemove.stream().sorted(EMOJI_CODEPOINT_COMPARATOR).collect(getEmojiLinkedHashMapCollector());
final Set<Emoji> emojis = new HashSet<>(EMOJIS_LENGTH_DESCENDING);
emojis.removeAll(emojisToRemove);
return removeAllEmojisExcept(text, emojis);
}

/**
* Removes all emojis except the given emojis from the given text.
*
* @param text The text to remove emojis from.
* @param emojisToKeep The emojis to keep.
* @return The text with only the given emojis.
*/
public static String removeAllEmojisExcept(final String text, final Emoji... emojisToKeep) {
return removeAllEmojisExcept(text, Arrays.asList(emojisToKeep));
}

/**
* Removes all emojis except the given emojis from the given text.
*
* @param text The text to remove emojis from.
* @param emojisToKeep The emojis to keep.
* @return The text with only the given emojis.
*/
public static String removeAllEmojisExcept(final String text, final Collection<Emoji> emojisToKeep) {
if (isStringNullOrEmpty(text)) return "";
final int[] textCodePointsArray = text.codePoints().toArray();
final long textCodePointsLength = textCodePointsArray.length;

Expand All @@ -349,7 +381,7 @@ public static String removeEmojis(final String text, final Collection<Emoji> emo
final int currentCodepoint = textCodePointsArray[textIndex];
sb.appendCodePoint(currentCodepoint);

final List<Emoji> emojisByCodePoint = FIRST_CODEPOINT_TO_EMOJIS_ORDER_CODEPOINT_LENGTH_DESCENDING.get(currentCodepoint);
final List<Emoji> emojisByCodePoint = EMOJI_FIRST_CODEPOINT_TO_EMOJIS_ORDER_CODEPOINT_LENGTH_DESCENDING.get(currentCodepoint);
if (emojisByCodePoint == null) continue;
for (final Emoji emoji : emojisByCodePoint) {
final int[] emojiCodePointsArray = emoji.getEmoji().codePoints().toArray();
Expand All @@ -359,14 +391,20 @@ public static String removeEmojis(final String text, final Collection<Emoji> emo
continue;
}

for (int i = 0; i < emojiCodePointsLength; i++) {
if (textCodePointsArray[textIndex + i] != emojiCodePointsArray[i]) {
for (int emojiCodePointIndex = 0; emojiCodePointIndex < emojiCodePointsLength; emojiCodePointIndex++) {
//break out because the emoji is not the same
if (textCodePointsArray[textIndex + emojiCodePointIndex] != emojiCodePointsArray[emojiCodePointIndex]) {
break;
}
if (i == emojiCodePointsLength - 1) {
sb.delete(sb.length() - Character.charCount(currentCodepoint), sb.length());

if (emojiCodePointIndex == (emojiCodePointsLength - 1)) {
textIndex += emojiCodePointsLength - 1;
sb.delete(sb.length() - Character.charCount(currentCodepoint), sb.length());

if (emojisToKeep.contains(emoji)) {
// if the emoji should be kept, add it again
sb.append(emoji.getEmoji());
}
continue nextTextIteration;
}
}
Expand All @@ -376,31 +414,6 @@ public static String removeEmojis(final String text, final Collection<Emoji> emo
return sb.toString();
}

/**
* Removes all emojis except the given emojis from the given text.
*
* @param text The text to remove emojis from.
* @param emojisToKeep The emojis to keep.
* @return The text with only the given emojis.
*/
public static String removeAllEmojisExcept(final String text, final Collection<Emoji> emojisToKeep) {
final Set<Emoji> emojisToRemove = new HashSet<>(EMOJIS_LENGTH_DESCENDING);
emojisToRemove.removeAll(emojisToKeep);

return removeEmojis(text, emojisToRemove);
}

/**
* Removes all emojis except the given emojis from the given text.
*
* @param text The text to remove emojis from.
* @param emojisToKeep The emojis to keep.
* @return The text with only the given emojis.
*/
public static String removeAllEmojisExcept(final String text, final Emoji... emojisToKeep) {
return removeAllEmojisExcept(text, Arrays.asList(emojisToKeep));
}

/**
* Replaces all emojis in the text with the given replacement string.
*
Expand Down Expand Up @@ -435,6 +448,18 @@ public static String replaceEmojis(final String text, final String replacementSt
return replaceEmojis(text, emoji -> replacementString, emojisToReplace);
}

/**
* Replaces the given emojis with the given replacement string.
*
* @param text The text to replace emojis from.
* @param replacementString The replacement string.
* @param emojisToReplace The emojis to replace.
* @return The text with the given emojis replaced.
*/
public static String replaceEmojis(final String text, final String replacementString, final Emoji... emojisToReplace) {
return replaceEmojis(text, emoji -> replacementString, Arrays.asList(emojisToReplace));
}

/**
* Replaces all emojis in the text with the given replacement function.
*
Expand All @@ -446,8 +471,6 @@ public static String replaceEmojis(final String text, final String replacementSt
public static String replaceEmojis(final String text, Function<Emoji, String> replacementFunction, final Collection<Emoji> emojisToReplace) {
if (isStringNullOrEmpty(text)) return "";

final LinkedHashMap<Integer, List<Emoji>> FIRST_CODEPOINT_TO_EMOJIS_ORDER_CODEPOINT_LENGTH_DESCENDING = emojisToReplace.stream().sorted(EMOJI_CODEPOINT_COMPARATOR).collect(getEmojiLinkedHashMapCollector());

final int[] textCodePointsArray = text.codePoints().toArray();
final long textCodePointsLength = textCodePointsArray.length;

Expand All @@ -458,7 +481,7 @@ public static String replaceEmojis(final String text, Function<Emoji, String> re
final int currentCodepoint = textCodePointsArray[textIndex];
sb.appendCodePoint(currentCodepoint);

final List<Emoji> emojisByCodePoint = FIRST_CODEPOINT_TO_EMOJIS_ORDER_CODEPOINT_LENGTH_DESCENDING.get(currentCodepoint);
final List<Emoji> emojisByCodePoint = EMOJI_FIRST_CODEPOINT_TO_EMOJIS_ORDER_CODEPOINT_LENGTH_DESCENDING.get(currentCodepoint);
if (emojisByCodePoint == null) continue;
for (final Emoji emoji : emojisByCodePoint) {
final int[] emojiCodePointsArray = emoji.getEmoji().codePoints().toArray();
Expand All @@ -468,17 +491,22 @@ public static String replaceEmojis(final String text, Function<Emoji, String> re
continue;
}

for (int i = 0; i < emojiCodePointsLength; i++) {
if (textCodePointsArray[textIndex + i] != emojiCodePointsArray[i]) {
for (int emojiCodePointIndex = 0; emojiCodePointIndex < emojiCodePointsLength; emojiCodePointIndex++) {
//break out because the emoji is not the same
if (textCodePointsArray[textIndex + emojiCodePointIndex] != emojiCodePointsArray[emojiCodePointIndex]) {
break;
}
if (i == emojiCodePointsLength - 1) {
//Does the same but is slower apparently
//sb.replace(sb.length() - Character.charCount(currentCodepoint), sb.length(), replacementString);
sb.delete(sb.length() - Character.charCount(currentCodepoint), sb.length());
sb.append(replacementFunction.apply(emoji));

if (emojiCodePointIndex == (emojiCodePointsLength - 1)) {
textIndex += emojiCodePointsLength - 1;
sb.delete(sb.length() - Character.charCount(currentCodepoint), sb.length());

if (emojisToReplace.contains(emoji)) {
sb.append(replacementFunction.apply(emoji));
} else {
sb.append(emoji.getEmoji());
}

continue nextTextIteration;
}
}
Expand All @@ -488,6 +516,18 @@ public static String replaceEmojis(final String text, Function<Emoji, String> re
return sb.toString();
}

/**
* Replaces all emojis in the text with the given replacement function.
*
* @param text The text to replace emojis from.
* @param replacementFunction The replacement function.
* @param emojisToReplace The emojis to replace.
* @return The text with all emojis replaced.
*/
public static String replaceEmojis(final String text, Function<Emoji, String> replacementFunction, final Emoji... emojisToReplace) {
return replaceEmojis(text, replacementFunction, Arrays.asList(emojisToReplace));
}

private static boolean isStringNullOrEmpty(final String string) {
return null == string || string.isEmpty();
}
Expand Down
51 changes: 27 additions & 24 deletions lib/src/test/java/net/fellbaum/jemoji/EmojiManagerTest.java
Original file line number Diff line number Diff line change
@@ -1,101 +1,104 @@
package net.fellbaum.jemoji;

import org.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;

import static org.junit.Assert.*;

public class EmojiManagerTest {

public static final String ALL_EMOJIS_STRING = EmojiManager.getAllEmojisLengthDescending().stream().map(Emoji::getEmoji).collect(Collectors.joining());
private static final String SIMPLE_EMOJI_STRING = "Hello ❤️ World";
private static final String SIMPLE_EMOJI_STRING = "Hello ❤️ ❤ ❤❤️ World";

@Test
public void extractEmojisInOrder() {
List<Emoji> emojis = EmojiManager.extractEmojisInOrder(ALL_EMOJIS_STRING + ALL_EMOJIS_STRING);

Assert.assertEquals(EmojiManager.getAllEmojisLengthDescending().size() * 2, emojis.size());
assertEquals(EmojiManager.getAllEmojisLengthDescending().size() * 2, emojis.size());

List<Emoji> allEmojis = new ArrayList<>(EmojiManager.getAllEmojisLengthDescending());
allEmojis.addAll(EmojiManager.getAllEmojisLengthDescending());
Assert.assertEquals(allEmojis, emojis);
assertEquals(allEmojis, emojis);
}

@Test
public void extractEmojis() {
Set<Emoji> emojis = EmojiManager.extractEmojis(ALL_EMOJIS_STRING + ALL_EMOJIS_STRING);

Assert.assertEquals(EmojiManager.getAllEmojisLengthDescending().size(), emojis.size());
assertEquals(EmojiManager.getAllEmojisLengthDescending().size(), emojis.size());
Set<Emoji> allEmojis = EmojiManager.getAllEmojis();
Assert.assertEquals(allEmojis, emojis);
assertEquals(allEmojis, emojis);
}

@Test
public void getEmoji() {
String emojiString = "👍";

Optional<Emoji> emoji = EmojiManager.getEmoji(emojiString);
Assert.assertTrue(emoji.isPresent());
Assert.assertEquals(emojiString, emoji.get().getEmoji());
assertTrue(emoji.isPresent());
assertEquals(emojiString, emoji.orElseThrow(RuntimeException::new).getEmoji());
}

@Test
public void isEmoji() {
String emojiString = "\uD83D\uDC4D";

Assert.assertTrue(EmojiManager.isEmoji(emojiString));
assertTrue(EmojiManager.isEmoji(emojiString));
}

@Test
public void getByAlias() {
String alias = "smile";

Optional<Emoji> emoji = EmojiManager.getByAlias(alias);
Assert.assertTrue(emoji.isPresent());
Assert.assertEquals("😄", emoji.get().getEmoji());
assertTrue(emoji.isPresent());
assertEquals("😄", emoji.orElseThrow(RuntimeException::new).getEmoji());
}

@Test
public void getByAliasWithColon() {
String alias = ":smile:";

Optional<Emoji> emoji = EmojiManager.getByAlias(alias);
Assert.assertTrue(emoji.isPresent());
Assert.assertEquals("😄", emoji.get().getEmoji());
assertTrue(emoji.isPresent());
assertEquals("😄", emoji.orElseThrow(RuntimeException::new).getEmoji());
}

@Test
public void containsEmoji() {
Assert.assertTrue(EmojiManager.containsEmoji(SIMPLE_EMOJI_STRING));
assertTrue(EmojiManager.containsEmoji(SIMPLE_EMOJI_STRING));
}

@Test
public void removeEmojis() {
Assert.assertEquals("Hello World", EmojiManager.removeAllEmojis(SIMPLE_EMOJI_STRING));
assertEquals("Hello World", EmojiManager.removeAllEmojis(SIMPLE_EMOJI_STRING));
}

@Test
public void removeAllEmojisExcept() {
Assert.assertEquals("Hello ❤️ World", EmojiManager.removeAllEmojisExcept(SIMPLE_EMOJI_STRING + "👍", Collections.singletonList(EmojiManager.getEmoji("❤️").get())));
assertEquals("Hello ❤️ ❤️ World", EmojiManager.removeAllEmojisExcept(SIMPLE_EMOJI_STRING + "👍", EmojiManager.getEmoji("❤️").orElseThrow(RuntimeException::new)));
}

@Test
public void replaceEmojis() {
Assert.assertEquals("Hello :heart: World", EmojiManager.replaceEmojis(SIMPLE_EMOJI_STRING, ":heart:", Collections.singletonList(EmojiManager.getEmoji("❤️").get())));
assertEquals("Hello :heart: ❤ ❤:heart: World", EmojiManager.replaceEmojis(SIMPLE_EMOJI_STRING, ":heart:", EmojiManager.getEmoji("❤️").orElseThrow(RuntimeException::new)));
}

@Test
public void replaceOnlyUnqualifiedEmoji() {
assertEquals("Hello ❤️ :heart: :heart:❤️ World", EmojiManager.replaceEmojis(SIMPLE_EMOJI_STRING, ":heart:", EmojiManager.getEmoji("❤").orElseThrow(RuntimeException::new)));
}

@Test
public void replaceAllEmojis() {
Assert.assertEquals("Hello something World something something something", EmojiManager.replaceAllEmojis(SIMPLE_EMOJI_STRING + " 👍 👨🏿‍🦱 😊", "something"));
assertEquals("Hello something something somethingsomething World something something something", EmojiManager.replaceAllEmojis(SIMPLE_EMOJI_STRING + " 👍 👨🏿‍🦱 😊", "something"));
}

@Test
public void replaceAllEmojisFunction() {
Assert.assertEquals("Hello SMILEYS_AND_EMOTION World PEOPLE_AND_BODY PEOPLE_AND_BODY SMILEYS_AND_EMOTION", EmojiManager.replaceAllEmojis(SIMPLE_EMOJI_STRING + " 👍 👨🏿‍🦱 😊", emoji -> emoji.getGroup().toString()));
assertEquals("Hello SMILEYS_AND_EMOTION SMILEYS_AND_EMOTION SMILEYS_AND_EMOTIONSMILEYS_AND_EMOTION World PEOPLE_AND_BODY PEOPLE_AND_BODY SMILEYS_AND_EMOTION", EmojiManager.replaceAllEmojis(SIMPLE_EMOJI_STRING + " 👍 👨🏿‍🦱 😊", emoji -> emoji.getGroup().toString()));
}

}

0 comments on commit cd5584d

Please sign in to comment.