Skip to content

Commit

Permalink
Cleanup conditionals further & add version checks
Browse files Browse the repository at this point in the history
Signed-off-by: Pablo Herrera <[email protected]>
  • Loading branch information
Pablete1234 committed Aug 10, 2024
1 parent 0143fc2 commit b55573e
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 34 deletions.
82 changes: 48 additions & 34 deletions core/src/main/java/tc/oc/pgm/map/ConditionalChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.Set;
import java.util.stream.Collectors;
import org.jdom2.Element;
import tc.oc.pgm.util.platform.Platform;
import tc.oc.pgm.util.xml.InvalidXMLException;
import tc.oc.pgm.util.xml.Node;
import tc.oc.pgm.util.xml.XMLUtils;
Expand All @@ -15,7 +16,9 @@ class ConditionalChecker {
private static final List<AttributeCheck> ATTRIBUTES = List.of(
new AttributeCheck("variant", ConditionalChecker::variant),
new AttributeCheck("has-variant", ConditionalChecker::hasVariant),
new AttributeCheck("constant", ConditionalChecker::constant));
new AttributeCheck("constant", ConditionalChecker::constant),
new AttributeCheck("min-server-version", ConditionalChecker::minServerVersion),
new AttributeCheck("max-server-version", ConditionalChecker::maxServerVersion));
private static final String ALL_ATTRS =
ATTRIBUTES.stream().map(AttributeCheck::key).collect(Collectors.joining("', '", "'", "'"));

Expand All @@ -24,7 +27,7 @@ class ConditionalChecker {
*
* @param ctx the map's context
* @param el The conditional element
* @return true if the conditional
* @return if the conditional passes
* @throws InvalidXMLException if the element is invalid in any way
*/
static boolean test(MapFilePreprocessor ctx, Element el) throws InvalidXMLException {
Expand All @@ -34,69 +37,80 @@ static boolean test(MapFilePreprocessor ctx, Element el) throws InvalidXMLExcept
if (attRes != null) result = result == null ? attRes : result && attRes;
}

if (result == null)
throw new InvalidXMLException("Expected at least one of " + ALL_ATTRS + " attributes", el);

return result;
if (result != null) return result;
throw new InvalidXMLException("Expected at least one of " + ALL_ATTRS + " attributes", el);
}

private static String[] split(String val) {
return val.split("[\\s,]+");
}

private static boolean variant(MapFilePreprocessor ctx, Element el, String value) {
private static boolean variant(MapFilePreprocessor ctx, Element el, Node node) {
String value = node.getValue();
if (value.indexOf(',') == -1) return value.equals(ctx.getVariant());
return Set.of(split(value)).contains(ctx.getVariant());
}

private static boolean hasVariant(MapFilePreprocessor ctx, Element el, String value) {
private static boolean hasVariant(MapFilePreprocessor ctx, Element el, Node node) {
String value = node.getValue();
if (value.indexOf(',') == -1) return ctx.getVariantIds().contains(value);
return Arrays.stream(split(value)).anyMatch(ctx.getVariantIds()::contains);
}

private static boolean constant(MapFilePreprocessor ctx, Element el, String id)
private static boolean minServerVersion(MapFilePreprocessor ctx, Element el, Node node)
throws InvalidXMLException {
return Platform.MINECRAFT_VERSION.isNoOlderThan(XMLUtils.parseSemanticVersion(node));
}

private static boolean maxServerVersion(MapFilePreprocessor ctx, Element el, Node node)
throws InvalidXMLException {
return Platform.MINECRAFT_VERSION.isNoNewerThan(XMLUtils.parseSemanticVersion(node));
}

private static boolean constant(MapFilePreprocessor ctx, Element el, Node node)
throws InvalidXMLException {
var id = node.getValue();
var value = Node.fromAttr(el, "constant-value");
var cmp = XMLUtils.parseEnum(
Node.fromAttr(el, "constant-comparison"),
Cmp.class,
value == null ? Cmp.DEFINED : Cmp.EQUALS);

var constants = ctx.getConstants();
var isDefined = constants.containsKey(id);
var constant = isDefined ? constants.get(id) : null;

if (!cmp.requireValue) {
if (value != null)
throw new InvalidXMLException("Comparison type " + cmp + " should not have a value", value);
if (!cmp.requireValue && value != null)
throw new InvalidXMLException("Comparison type " + cmp + " should not have a value", value);

if (!constants.containsKey(id)) return cmp == Cmp.UNDEFINED;
if (cmp.requireValue) {
if (value == null)
throw new InvalidXMLException("Required attribute 'constant-value' not set", el);

if (!isDefined)
throw new InvalidXMLException(
"Unknown constant '" + id + "'. Only constants before the conditional may be used.",
el);
if (constant == null) return false;
}

// The only reason these are split is for the IDE to infer nullability
if (!cmp.requireValue) {
return switch (cmp) {
case DEFINED -> true;
case DEFINED_DELETE -> constants.get(id) == null;
case DEFINED_VALUE -> constants.get(id) != null;
// Should never happen
default -> throw new IllegalStateException(cmp + " not supported");
case UNDEFINED -> !isDefined;
case DEFINED -> isDefined;
case DEFINED_DELETE -> isDefined && constant == null;
case DEFINED_VALUE -> isDefined && constant != null;
default -> throw new IllegalStateException("Unexpected value: " + cmp);
};
} else {
String constant = constants.get(id);
if (constant == null) {
if (!constants.containsKey(id))
throw new InvalidXMLException(
"Unknown constant '" + id + "'. Only constants before the conditional may be used.",
el);
return false;
}
if (value == null)
throw new InvalidXMLException("Required attribute 'constant-value' not set", el);

return switch (cmp) {
case EQUALS -> Objects.equals(value.getValue(), constant);
case CONTAINS -> Set.of(split(value.getValue())).contains(constant);
case REGEX -> constant.matches(value.getValue());
case RANGE -> XMLUtils.parseNumericRange(value, Double.class)
.contains(XMLUtils.parseNumber(new Node(el), constant, Double.class, true));
// Should never happen
default -> throw new IllegalStateException(cmp + " not supported");
default -> throw new IllegalStateException("Unexpected value: " + cmp);
};
}
}
Expand All @@ -118,15 +132,15 @@ enum Cmp {
}

interface ElementPredicate {
boolean test(MapFilePreprocessor ctx, Element el, String value) throws InvalidXMLException;
boolean test(MapFilePreprocessor ctx, Element el, Node value) throws InvalidXMLException;
}

record AttributeCheck(String key, ElementPredicate pred) {
Boolean apply(MapFilePreprocessor ctx, Element el) throws InvalidXMLException {
var attr = el.getAttribute(key);
var attr = Node.fromNullable(el.getAttribute(key));
if (attr == null) return null;

return pred.test(ctx, el, attr.getValue());
return pred.test(ctx, el, attr);
}
}
}
20 changes: 20 additions & 0 deletions util/src/main/java/tc/oc/pgm/util/Version.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ public boolean isNoOlderThan(Version other) {
return compareTo(other) >= 0;
}

/**
* Gets whether this version is less than or equal to another version.
*
* @param other Another version.
* @return If this version <= other version.
*/
public boolean isNoNewerThan(Version other) {
return compareTo(other) <= 0;
}

/**
* Gets whether this version is less than another version.
*
Expand All @@ -52,6 +62,16 @@ public boolean isOlderThan(Version other) {
return compareTo(other) < 0;
}

/**
* Gets whether this version is greater than another version.
*
* @param other Another version.
* @return If this version > other version.
*/
public boolean isNewerThan(Version other) {
return compareTo(other) > 0;
}

@Override
public int compareTo(Version other) {
int diff = major - other.major;
Expand Down

0 comments on commit b55573e

Please sign in to comment.