diff --git a/src/main/java/ch/njol/skript/effects/EffReplace.java b/src/main/java/ch/njol/skript/effects/EffReplace.java index f6a1fd5bad8..c3458548041 100644 --- a/src/main/java/ch/njol/skript/effects/EffReplace.java +++ b/src/main/java/ch/njol/skript/effects/EffReplace.java @@ -38,57 +38,67 @@ import org.bukkit.inventory.ItemStack; import org.eclipse.jdt.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.regex.Matcher; +import java.util.regex.Pattern; @Name("Replace") -@Description("Replaces all occurrences of a given text with another text. Please note that you can only change variables and a few expressions, e.g. a message or a line of a sign.") -@Examples({"replace \"\" in {textvar} with \"%item%\"", - "replace every \"&\" with \"§\" in line 1", - "# The following acts as a simple chat censor, but it will e.g. censor mass, hassle, assassin, etc. as well:", - "on chat:", - " replace all \"kys\", \"idiot\" and \"noob\" with \"****\" in the message", - " ", - "replace all stone and dirt in player's inventory and player's top inventory with diamond"}) -@Since("2.0, 2.2-dev24 (replace in multiple strings and replace items in inventory), 2.5 (replace first, case sensitivity)") +@Description("Replaces all occurrences of a given text/regex with another text. Please note that you can only change " + + "variables and a few expressions, e.g. a message or a line of a sign.") +@Examples({ + "replace \"\" in {_msg} with \"[%name of player's tool%]\"", + "replace every \"&\" with \"§\" in line 1 of targeted block", + "", + "# Very simple chat censor", + "on chat:", + "\treplace all \"idiot\" and \"noob\" with \"****\" in the message", + "\treplace using regex \"\\b(idiot|noob)\\b\" with \"****\" in the message # Regex version for better results", + "", + "replace all stone and dirt in player's inventory and player's top inventory with diamond" +}) +@Since("2.0, 2.2-dev24 (multiple strings, items in inventory), 2.5 (replace first, case sensitivity), INSERT VERSION (regex)") public class EffReplace extends Effect { static { Skript.registerEffect(EffReplace.class, - "replace (all|every|) %strings% in %strings% with %string% [(1¦with case sensitivity)]", - "replace (all|every|) %strings% with %string% in %strings% [(1¦with case sensitivity)]", - "replace first %strings% in %strings% with %string% [(1¦with case sensitivity)]", - "replace first %strings% with %string% in %string% [(1¦with case sensitivity)]", - "replace (all|every|) %itemtypes% in %inventories% with %itemtype%", - "replace (all|every|) %itemtypes% with %itemtype% in %inventories%"); + "replace [all:(all|every)|first:[the] first] %strings% in %strings% with %string% [case:with case sensitivity]", + "replace [all:(all|every)|first:[the] first] %strings% with %string% in %strings% [case:with case sensitivity]", + "regex:(replace [using] regex|regex replace) %strings% in %strings% with %string%", + "regex:(replace [using] regex|regex replace) %strings% with %string% in %strings%", + "replace [all|every] %itemtypes% in %inventories% with %itemtype%", + "replace [all|every] %itemtypes% with %itemtype% in %inventories%"); } - - @SuppressWarnings("null") + + @SuppressWarnings("NotNullFieldNotInitialized") private Expression haystack, needles, replacement; - private boolean replaceString = true; - private boolean replaceFirst = false; + private boolean replaceString; + private boolean replaceRegex; + private boolean replaceFirst; private boolean caseSensitive = false; - @SuppressWarnings({"null"}) @Override + @SuppressWarnings("null") public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { haystack = exprs[1 + matchedPattern % 2]; replaceString = matchedPattern < 4; - replaceFirst = matchedPattern > 1 && matchedPattern < 4; + replaceFirst = parseResult.hasTag("first"); + replaceRegex = parseResult.hasTag("regex"); if (replaceString && !ChangerUtils.acceptsChange(haystack, ChangeMode.SET, String.class)) { - Skript.error(haystack + " cannot be changed and can thus not have parts replaced."); + Skript.error(haystack + " cannot be changed and can thus not have parts replaced"); return false; } - if (SkriptConfig.caseSensitive.value() || parseResult.mark == 1) { + if (SkriptConfig.caseSensitive.value() || parseResult.hasTag("case")) { caseSensitive = true; } needles = exprs[0]; replacement = exprs[2 - matchedPattern % 2]; return true; } - - @SuppressWarnings("null") + @Override + @SuppressWarnings("null") protected void execute(Event event) { Object[] needles = this.needles.getAll(event); if (haystack instanceof ExpressionList) { @@ -106,43 +116,65 @@ private void replace(Event event, Object[] needles, Expression haystackExpr) if (replacement == null || haystack == null || haystack.length == 0 || needles == null || needles.length == 0) return; if (replaceString) { - if (replaceFirst) { - for (int x = 0; x < haystack.length; x++) - for (Object n : needles) { - assert n != null; - haystack[x] = StringUtils.replaceFirst((String)haystack[x], (String)n, Matcher.quoteReplacement((String)replacement), caseSensitive); + String stringReplacement = (String) replacement; + if (replaceRegex) { // replace all/first - regex + List patterns = new ArrayList<>(needles.length); + for (Object needle : needles) { + assert needle != null; + try { + patterns.add(Pattern.compile((String) needle)); + } catch (Exception ignored) { } + } + for (int i = 0; i < haystack.length; i++) { + for (Pattern pattern : patterns) { + assert pattern != null; + Matcher matcher = pattern.matcher((String) haystack[i]); + if (replaceFirst) // first + haystack[i] = matcher.replaceFirst(stringReplacement); + else // all + haystack[i] = matcher.replaceAll(stringReplacement); + } + } + } else if (replaceFirst) { // replace first - string + for (int i = 0; i < haystack.length; i++) { + for (Object needle : needles) { + assert needle != null; + haystack[i] = StringUtils.replaceFirst((String) haystack[i], (String) needle, Matcher.quoteReplacement(stringReplacement), caseSensitive); } - } else { - for (int x = 0; x < haystack.length; x++) - for (Object n : needles) { - assert n != null; - haystack[x] = StringUtils.replace((String) haystack[x], (String) n, (String) replacement, caseSensitive); + } + } else { // replace all - string + for (int i = 0; i < haystack.length; i++) { + for (Object needle : needles) { + assert needle != null; + haystack[i] = StringUtils.replace((String) haystack[i], (String) needle, stringReplacement, caseSensitive); } + } } haystackExpr.change(event, haystack, ChangeMode.SET); } else { - for (Inventory inv : (Inventory[]) haystack) - for (ItemType needle : (ItemType[]) needles) - for (Map.Entry entry : inv.all(needle.getMaterial()).entrySet()) { + for (Inventory inventory : (Inventory[]) haystack) { + for (ItemType needle : (ItemType[]) needles) { + for (Map.Entry entry : inventory.all(needle.getMaterial()).entrySet()) { int slot = entry.getKey(); ItemStack itemStack = entry.getValue(); if (new ItemType(itemStack).isSimilar(needle)) { ItemStack newItemStack = ((ItemType) replacement).getRandom(); newItemStack.setAmount(itemStack.getAmount()); - - inv.setItem(slot, newItemStack); + inventory.setItem(slot, newItemStack); } } + } + } } } - + @Override public String toString(@Nullable Event event, boolean debug) { if (replaceFirst) - return "replace first " + needles.toString(event, debug) + " in " + haystack.toString(event, debug) + " with " + replacement.toString(event, debug) + return "replace first " + needles.toString(event, debug) + (replaceRegex ? " (regex) " : "") + " in " + haystack.toString(event, debug) + " with " + replacement.toString(event, debug) + "(case sensitive: " + caseSensitive + ")"; - return "replace " + needles.toString(event, debug) + " in " + haystack.toString(event, debug) + " with " + replacement.toString(event, debug) + return "replace " + needles.toString(event, debug) + (replaceRegex ? " (regex) " : "") + " in " + haystack.toString(event, debug) + " with " + replacement.toString(event, debug) + "(case sensitive: " + caseSensitive + ")"; }