
dev.harrel.jsonschema.providers.SnakeYamlNode Maven / Gradle / Ivy
package dev.harrel.jsonschema.providers;
import dev.harrel.jsonschema.JsonNode;
import dev.harrel.jsonschema.JsonNodeFactory;
import dev.harrel.jsonschema.SimpleType;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.nodes.*;
import java.io.StringReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
public final class SnakeYamlNode extends AbstractJsonNode {
private static final SafeConstructor CONSTR = new SafeConstructor(new LoaderOptions());
private static final SafeConstructor.ConstructYamlBool BOOLEAN_CREATOR = CONSTR.new ConstructYamlBool();
private static final SafeConstructor.ConstructYamlInt INT_CREATOR = CONSTR.new ConstructYamlInt();
private static final SafeConstructor.ConstructYamlFloat NUMBER_CREATOR = CONSTR.new ConstructYamlFloat();
private BigDecimal asNumber;
private SnakeYamlNode(Node node, String jsonPointer) {
super(Objects.requireNonNull(node), jsonPointer);
}
private SnakeYamlNode(Node node) {
this(node, "");
}
@Override
public boolean asBoolean() {
return (Boolean) BOOLEAN_CREATOR.construct(node);
}
@Override
public String asString() {
return ((ScalarNode) node).getValue();
}
@Override
public BigInteger asInteger() {
return asNumber.toBigInteger();
}
@Override
public BigDecimal asNumber() {
return asNumber;
}
@Override
List createArray() {
List arrayNode = ((SequenceNode) node).getValue();
List elements = new ArrayList<>(arrayNode.size());
for (int i = 0; i < arrayNode.size(); i++) {
elements.add(new SnakeYamlNode(arrayNode.get(i), jsonPointer + "/" + i));
}
return elements;
}
@Override
Map createObject() {
List objectNode = ((MappingNode) node).getValue();
Map map = MapUtil.newHashMap(objectNode.size());
for (NodeTuple entry : objectNode) {
String key = ((ScalarNode) entry.getKeyNode()).getValue();
map.put(key, new SnakeYamlNode(entry.getValueNode(), jsonPointer + "/" + JsonNode.encodeJsonPointer(key)));
}
return map;
}
@Override
SimpleType computeNodeType(Node node) {
if (node instanceof SequenceNode) {
return SimpleType.ARRAY;
} else if (node instanceof MappingNode) {
return SimpleType.OBJECT;
}
if (node.getTag() == Tag.NULL) {
return SimpleType.NULL;
} else if (node.getTag() == Tag.BOOL) {
return SimpleType.BOOLEAN;
} else if (node.getTag() == Tag.INT) {
asNumber = intToBigDecimal(node);
return SimpleType.INTEGER;
} else if (node.getTag() == Tag.FLOAT) {
String asString = ((ScalarNode) node).getValue().toLowerCase();
if (asString.contains(".inf") || asString.contains(".nan")) {
return SimpleType.STRING;
}
asNumber = floatToBigDecimal(node);
if (canConvertToInteger(asNumber)) {
return SimpleType.INTEGER;
} else {
return SimpleType.NUMBER;
}
} else {
return SimpleType.STRING;
}
}
private static BigDecimal intToBigDecimal(Node node) {
Object intObject = INT_CREATOR.construct(node);
if (intObject instanceof Integer || intObject instanceof Long) {
return BigDecimal.valueOf(((Number) intObject).longValue());
} else {
return new BigDecimal((BigInteger) intObject);
}
}
private static BigDecimal floatToBigDecimal(Node node) {
String asString = ((ScalarNode) node).getValue();
if (asString.contains(":")) {
return BigDecimal.valueOf(((Double) NUMBER_CREATOR.construct(node)));
} else {
return new BigDecimal(asString);
}
}
public static final class Factory implements JsonNodeFactory {
private final Yaml yaml;
public Factory() {
this(new Yaml());
}
public Factory(Yaml yaml) {
this.yaml = yaml;
}
@Override
public JsonNode wrap(Object node) {
if (node instanceof SnakeYamlNode) {
return new SnakeYamlNode(((SnakeYamlNode) node).node);
} else if (node instanceof Node) {
Node providerNode = (Node) node;
assertKeyUniqueness(providerNode);
return new SnakeYamlNode(providerNode);
} else {
throw new IllegalArgumentException("Cannot wrap object which is not an instance of org.yaml.snakeyaml.nodes.Node");
}
}
@Override
public JsonNode create(String rawJson) {
Node node = yaml.compose(new StringReader(rawJson));
assertKeyUniqueness(node);
return new SnakeYamlNode(node);
}
private static void assertKeyUniqueness(Node node) {
assertKeyUniqueness(node, Collections.newSetFromMap(new IdentityHashMap<>()));
}
private static void assertKeyUniqueness(Node node, Set visited) {
if (!(node instanceof CollectionNode>) || !visited.add(node)) {
return;
} else if (node instanceof SequenceNode) {
for (Node element : ((SequenceNode) node).getValue()) {
assertKeyUniqueness(element, visited);
}
} else if (node instanceof MappingNode) {
List tuples = ((MappingNode) node).getValue();
Set keys = new HashSet<>();
for (NodeTuple tuple : tuples) {
ScalarNode keyNode = (ScalarNode) tuple.getKeyNode();
if (!keys.add(keyNode.getValue())) {
throw new IllegalArgumentException("Mapping key '" + keyNode.getValue() + "' is duplicated" + keyNode.getStartMark());
}
assertKeyUniqueness(tuple.getValueNode(), visited);
}
}
visited.remove(node);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy