Skip to content

Commit

Permalink
Simple support for maps in StringArgs, some other fixes/changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Patbox committed Apr 10, 2024
1 parent 602ab35 commit 0d98ba7
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 38 deletions.
86 changes: 74 additions & 12 deletions src/main/java/eu/pb4/placeholders/api/arguments/StringArgs.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package eu.pb4.placeholders.api.arguments;

import net.minecraft.util.function.CharPredicate;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

public final class StringArgs {
private static final StringArgs EMPTY = new StringArgs("");
private final List<String> ordered = new ArrayList<>();
private final Map<String, String> keyed = new HashMap<>();
private Map<String, StringArgs> keyedMaps = new HashMap<>();
private final String input;
private int currentOrdered = 0;

Expand All @@ -23,47 +26,68 @@ public static StringArgs ordered(String input, char separator) {
}

public static StringArgs keyed(String input, char separator, char map) {
return keyed(input, separator, map, SimpleArguments::isWrapCharacter);
return keyed(input, separator, map, true, SimpleArguments::isWrapCharacter);
}
public static StringArgs keyed(String input, char separator, char map, CharPredicate wrapCharacters) {
public static StringArgs keyed(String input, char separator, char map, boolean hasMaps, CharPredicate wrapCharacters) {
var args = new StringArgs(input);
keyDecomposition(input, separator, map, wrapCharacters, (key, value) -> {
keyDecomposition(input, 0, separator, map, wrapCharacters, hasMaps, (char) 0, (key, value) -> {
if (key != null) {
args.keyed.put(key, value != null ? SimpleArguments.unwrap(value, wrapCharacters) : "");
}
});
}, args.keyedMaps::put);

return args;
}

public static StringArgs full(String input, char separator, char map) {
return full(input, separator, map, SimpleArguments::isWrapCharacter);
return full(input, separator, map, true, SimpleArguments::isWrapCharacter);
}
public static StringArgs full(String input, char separator, char map, CharPredicate wrapCharacters) {
public static StringArgs full(String input, char separator, char map, boolean hasMaps, CharPredicate wrapCharacters) {
var args = new StringArgs(input);
keyDecomposition(input, separator, map, wrapCharacters, (key, value) -> {
keyDecomposition(input, 0, separator, map, wrapCharacters, hasMaps, (char) 0, (key, value) -> {
if (key != null) {
args.keyed.put(key, value != null ? SimpleArguments.unwrap(value, wrapCharacters) : "");

if (value == null) {
args.ordered.add(SimpleArguments.unwrap(key, wrapCharacters));
}
}
});
}, args.keyedMaps::put);

return args;
}

private static void keyDecomposition(String input, char separator, char map, CharPredicate isWrap, BiConsumer<@Nullable String, @Nullable String> consumer) {
private static int keyDecomposition(String input, int offset, char separator, char map, CharPredicate isWrap, boolean hasMaps, char stopAt, BiConsumer<@Nullable String, @Nullable String> consumer, BiConsumer<String, StringArgs> mapConsumer) {
String key = null;
String value = null;
var b = new StringBuilder();
char wrap = 0;
for (int i = 0; i < input.length(); i++) {
int i = offset;
for (; i < input.length(); i++) {
var chr = input.charAt(i);
var chrN = i != input.length() - 1 ? input.charAt(i + 1) : 0;

if (chr == map && key == null) {
if (chr == stopAt && wrap == 0) {
break;
} else if (key != null && b.isEmpty() && hasMaps && (chr == '{' || chr == '[')) {
var arg = new StringArgs("");
var ti = keyDecomposition(input, i + 1, separator, map, isWrap, true,
chr == '{' ? '}' : ']', (keyx, valuex) -> {
if (keyx != null) {
arg.keyed.put(keyx, valuex != null ? SimpleArguments.unwrap(valuex, isWrap) : "");

if (valuex == null) {
arg.ordered.add(SimpleArguments.unwrap(keyx, isWrap));
}
}
}, arg.keyedMaps::put);
if (ti == input.length()) {
b.append(chr);
} else {
mapConsumer.accept(key, arg);
key = null;
i = ti;
}
} else if (chr == map && key == null) {
key = b.toString();
b = new StringBuilder();
} else if ((chr == '\\' && chrN != 0) || (chrN != 0 && chr == chrN && isWrap.test(chr))) {
Expand Down Expand Up @@ -97,12 +121,18 @@ private static void keyDecomposition(String input, char separator, char map, Cha
} else if (!b.isEmpty()) {
consumer.accept(b.toString(), null);
}

return i;
}

public static StringArgs empty() {
return EMPTY;
}

public static StringArgs emptyNew() {
return new StringArgs("");
}

public String input() {
return input;
}
Expand Down Expand Up @@ -150,6 +180,13 @@ public String getNext(String name, String defaultValue) {
return x != null ? x : defaultValue;
}

public void ifPresent(String key, Consumer<String> valueConsumer) {
var val = get(key);
if (val != null) {
valueConsumer.accept(val);
}
}

public boolean contains(String key) {
return this.keyed.containsKey(key);
}
Expand All @@ -165,4 +202,29 @@ public List<String> ordered() {
public int size() {
return Math.max(this.keyed.size(), this.ordered.size());
}

@ApiStatus.Internal
public List<String> unsafeOrdered() {
return this.ordered;
}

@ApiStatus.Internal
public Map<String, String> unsafeKeyed() {
return this.keyed;
}


@ApiStatus.Internal
public Map<String, StringArgs> unsafeKeyedMap() {
return this.keyedMaps;
}

@Override
public String toString() {
return "StringArgs{" +
"ordered=" + ordered +
", keyed=" + keyed +
", keyedMaps=" + keyedMaps +
'}';
}
}
30 changes: 30 additions & 0 deletions src/main/java/eu/pb4/placeholders/api/parsers/tag/SimpleTags.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package eu.pb4.placeholders.api.parsers.tag;

import eu.pb4.placeholders.api.node.parent.ColorNode;
import eu.pb4.placeholders.api.node.parent.FormattingNode;
import net.minecraft.text.TextColor;
import net.minecraft.util.Formatting;

import java.util.Collection;

public final class SimpleTags {
public static TextTag color(String name, Collection<String> aliases, Formatting formatting) {
return TextTag.enclosing(
name,
aliases,
"color",
true,
(nodes, arg, parser) -> new FormattingNode(nodes, formatting)
);
}

public static TextTag color(String name, Collection<String> aliases, int rgb) {
return TextTag.enclosing(
name,
aliases,
"color",
true,
(nodes, arg, parser) -> new ColorNode(nodes, TextColor.fromRgb(rgb))
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import java.util.List;

public interface TagRegistry {
TagRegistry DEFAULT = new SimpleTagRegistry(true);
TagRegistry SAFE = new SimpleTagRegistry(true);
TagRegistry DEFAULT = SimpleTagRegistry.DEFAULT;
TagRegistry SAFE = SimpleTagRegistry.SAFE;

static TagRegistry create() {
return new SimpleTagRegistry(false);
Expand Down Expand Up @@ -51,10 +51,10 @@ static Builder builderWithSafe() {
}

static void registerDefault(TextTag tag) {
DEFAULT.register(tag);
SimpleTagRegistry.DEFAULT.register(tag);

if (tag.userSafe()) {
SAFE.register(tag);
SimpleTagRegistry.SAFE.register(tag);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import eu.pb4.placeholders.api.arguments.StringArgs;
import eu.pb4.placeholders.api.node.TextNode;

import java.util.Collection;
import java.util.List;
import java.util.function.Function;

Expand All @@ -16,7 +17,7 @@ public static TextTag self(String name, String type, boolean userSafe, Function<
return self(name, List.of(), type, userSafe, creator);
}

public static TextTag self(String name, List<String> aliases, String type, boolean userSafe, Function<StringArgs, TextNode> creator) {
public static TextTag self(String name, Collection<String> aliases, String type, boolean userSafe, Function<StringArgs, TextNode> creator) {
return new TextTag(name, aliases.toArray(new String[0]), type, userSafe, true, NodeCreator.self(creator));
}

Expand All @@ -28,7 +29,7 @@ public static TextTag self(String name, String type, boolean userSafe, NodeCreat
return self(name, List.of(), type, userSafe, creator);
}

public static TextTag self(String name, List<String> aliases, String type, boolean userSafe, NodeCreator creator) {
public static TextTag self(String name, Collection<String> aliases, String type, boolean userSafe, NodeCreator creator) {
return new TextTag(name, aliases.toArray(new String[0]), type, userSafe, true, creator);
}

Expand All @@ -40,7 +41,7 @@ public static TextTag enclosing(String name, String type, boolean userSafe, Node
return enclosing(name, List.of(), type, userSafe, creator);
}

public static TextTag enclosing(String name, List<String> aliases, String type, boolean userSafe, NodeCreator creator) {
public static TextTag enclosing(String name, Collection<String> aliases, String type, boolean userSafe, NodeCreator creator) {
return new TextTag(name, aliases.toArray(new String[0]), type, userSafe, false, creator);
}
}
138 changes: 138 additions & 0 deletions src/main/java/eu/pb4/placeholders/impl/StringArgOps.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package eu.pb4.placeholders.impl;

import com.google.gson.JsonElement;
import com.mojang.datafixers.util.Either;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.DynamicOps;
import com.mojang.serialization.JsonOps;
import eu.pb4.placeholders.api.arguments.StringArgs;

import java.util.stream.Stream;

public class StringArgOps implements DynamicOps<Either<String, StringArgs>> {
public static final StringArgOps INSTANCE = new StringArgOps();
@Override
public Either<String, StringArgs> empty() {
return Either.right(StringArgs.emptyNew());
}

@Override
public <U> U convertTo(DynamicOps<U> outOps, Either<String, StringArgs> input) {
return outOps.empty();
}

@Override
public DataResult<Number> getNumberValue(Either<String, StringArgs> input) {
try {
if (input.left().isPresent()) {
return DataResult.success(Double.valueOf(input.orThrow()));
}
} catch (Throwable ignored) {
return DataResult.success(Boolean.parseBoolean(input.orThrow()) ? 1 : 0);
}

return DataResult.error(() -> input + " is not a number!");
}

@Override
public Either<String, StringArgs> createNumeric(Number i) {
return Either.left(i.toString());
}

@Override
public DataResult<String> getStringValue(Either<String, StringArgs> input) {
return input.left().isPresent() ? DataResult.success(input.left().get()) : DataResult.error(() -> input + " is not a number!");
}

@Override
public Either<String, StringArgs> createString(String value) {
return Either.left(value);
}

@Override
public DataResult<Either<String, StringArgs>> mergeToList(Either<String, StringArgs> list, Either<String, StringArgs> value) {
try {
if (value.left().isPresent()) {
list.right().get().unsafeOrdered().add(value.left().orElseThrow());
} else {
list.right().get().unsafeKeyedMap().put("" + list.right().get().unsafeKeyedMap().size(), value.right().orElseThrow());
}

return DataResult.success(list);
} catch (Throwable e) {
return DataResult.error(() -> list + " is not a list!");
}
}

@Override
public DataResult<Either<String, StringArgs>> mergeToMap(Either<String, StringArgs> map, Either<String, StringArgs> key, Either<String, StringArgs> value) {
try {
if (value.left().isPresent()) {
map.right().get().unsafeKeyed().put(key.left().orElseThrow(), value.left().orElseThrow());
} else {
map.right().get().unsafeKeyedMap().put(key.left().orElseThrow(), value.right().orElseThrow());
}

return DataResult.success(map);
} catch (Throwable e) {
return DataResult.error(() -> key + " is not a correct key!");
}
}

@Override
public DataResult<Stream<Pair<Either<String, StringArgs>, Either<String, StringArgs>>>> getMapValues(Either<String, StringArgs> input) {
try {
return DataResult.success(
Stream.concat(
input.right().get().unsafeKeyed().entrySet().stream()
.map((e) -> new Pair<>(Either.left(e.getKey()), Either.left(e.getValue()))),
input.right().get().unsafeKeyedMap().entrySet().stream()
.map((e) -> new Pair<>(Either.left(e.getKey()), Either.right(e.getValue())))
)
);
} catch (Throwable e) {
return DataResult.error(() -> input + " is not a map!");
}
}

@Override
public Either<String, StringArgs> createMap(Stream<Pair<Either<String, StringArgs>, Either<String, StringArgs>>> map) {
var arg = StringArgs.emptyNew();
map.forEach(x -> x.getSecond()
.ifLeft(y -> arg.unsafeKeyed().put(x.getFirst().left().orElse(""), y))
.ifRight(y -> arg.unsafeKeyedMap().put(x.getFirst().left().orElse(""), y)));
return Either.right(arg);
}

@Override
public DataResult<Stream<Either<String, StringArgs>>> getStream(Either<String, StringArgs> input) {
return DataResult.success(input.left().isPresent() ? Stream.of(input) : Stream.concat(Stream.concat(
input.right().get().unsafeKeyed().values().stream()
.map(Either::left),
input.right().get().unsafeOrdered().stream()
.map(Either::left)),
input.right().get().unsafeKeyedMap().values().stream()
.map(Either::right)
));
}

@Override
public Either<String, StringArgs> createList(Stream<Either<String, StringArgs>> input) {
var arg = StringArgs.emptyNew();
input.forEach(x -> x.ifLeft(arg.unsafeOrdered()::add)
.ifRight(y -> arg.unsafeKeyedMap().put("" + arg.unsafeKeyedMap().size(), y))
);
return Either.right(arg);
}

@Override
public Either<String, StringArgs> remove(Either<String, StringArgs> input, String key) {
input.ifRight(x -> {
x.unsafeKeyed().remove(key);
x.unsafeKeyedMap().remove(key);
});

return input;
}
}
Loading

0 comments on commit 0d98ba7

Please sign in to comment.