RangeHelper.java
package tools.jackson.datatype.guava.deser.util;
import java.lang.reflect.Field;
import java.util.regex.Pattern;
import com.google.common.collect.BoundType;
import com.google.common.collect.Range;
import tools.jackson.databind.*;
import tools.jackson.databind.cfg.MapperConfig;
import tools.jackson.databind.introspect.AnnotatedField;
import tools.jackson.databind.introspect.TypeResolutionContext;
import tools.jackson.databind.util.ClassUtil;
public class RangeHelper
{
public static class RangeProperties implements java.io.Serializable
{
private static final long serialVersionUID = 2L;
public final String lowerEndpoint, upperEndpoint;
public final String lowerBoundType, upperBoundType;
protected RangeProperties() {
this("lowerEndpoint", "upperEndpoint",
"lowerBoundType", "upperBoundType");
}
public RangeProperties(String lowerEP, String upperEP,
String lowerBT, String upperBT) {
lowerEndpoint = lowerEP;
upperEndpoint = upperEP;
lowerBoundType = lowerBT;
upperBoundType = upperBT;
}
protected Field[] fields() {
return new Field[] {
_field(lowerEndpoint),
_field(upperEndpoint),
_field(lowerBoundType),
_field(upperBoundType)
};
}
private Field _field(String name) {
try {
return getClass().getField(name);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
}
private final static RangeProperties STD_NAMES = new RangeProperties();
private final static Field[] FIELDS = STD_NAMES.fields();
private final static Pattern PATTERN_DOUBLE_DOT = Pattern.compile("\\.\\.");
public static RangeProperties standardNames() {
return STD_NAMES;
}
public static RangeProperties getPropertyNames(MapperConfig<?> config, PropertyNamingStrategy pns) {
if (pns == null) {
return STD_NAMES;
}
final TypeResolutionContext typeCtxt = new TypeResolutionContext.Empty(config.getTypeFactory());
return new RangeProperties(
_find(config, typeCtxt, pns, FIELDS[0]),
_find(config, typeCtxt, pns, FIELDS[1]),
_find(config, typeCtxt, pns, FIELDS[2]),
_find(config, typeCtxt, pns, FIELDS[3])
);
}
private static String _find(MapperConfig<?> config, TypeResolutionContext typeCtxt,
PropertyNamingStrategy pns, Field field) {
AnnotatedField af = new AnnotatedField(typeCtxt, field, null);
return pns.nameForField(config, af, field.getName());
}
public static Range<? extends Comparable> getRangeFromString(String rangeInterval,
DeserializationContext context, KeyDeserializer fromStringDeserializer,
JavaType rangeType, Class<?> targetClass)
{
if (_isValidBracketNotation(rangeInterval)) {
BoundType lowerBoundType = rangeInterval.startsWith("[") ? BoundType.CLOSED : BoundType.OPEN;
BoundType upperBoundType = rangeInterval.endsWith("]") ? BoundType.CLOSED : BoundType.OPEN;
rangeInterval = rangeInterval.substring(1, rangeInterval.length() - 1);
String[] parts = PATTERN_DOUBLE_DOT.split(rangeInterval);
if (parts.length == 2) {
boolean isLowerInfinite = parts[0].equals("-���");
boolean isUpperInfinite = parts[1].equals("+���");
if (isLowerInfinite && isUpperInfinite) {
return RangeFactory.all();
} else if (isLowerInfinite) {
return RangeFactory.upTo(deserializeStringified(context, parts[1], fromStringDeserializer, rangeType), upperBoundType);
} else if (isUpperInfinite) {
return RangeFactory.downTo(deserializeStringified(context, parts[0], fromStringDeserializer, rangeType), lowerBoundType);
} else {
return RangeFactory.range(deserializeStringified(context, parts[0], fromStringDeserializer, rangeType),
lowerBoundType,
deserializeStringified(context, parts[1], fromStringDeserializer, rangeType),
upperBoundType);
}
}
} else {
String msg = "Invalid Range: should start with '[' or '(', end with ')' or ']'";
return (Range<?>) context.handleWeirdStringValue(targetClass, rangeInterval, msg);
}
// Give generic failure if no specific reason can be given.
// Although most likely will happen because `..` is absent, since we are validating brackets above.
return (Range<?>) context.handleWeirdStringValue(targetClass, rangeInterval,
"Invalid bracket-notation representation (possibly missing \"..\" delimiter in your Stringified Range)");
}
private static Comparable<?> deserializeStringified(DeserializationContext context,
String value, KeyDeserializer fromStringDeserializer, JavaType rangeType)
{
Object obj = fromStringDeserializer.deserializeKey(value, context);
if (!(obj instanceof Comparable)) {
// 02-Jan-2024, tatu: Not sure this is possible but let's double-check
context.reportBadDefinition(rangeType,
String.format(
"Stringified endpoint '%s' deserialized to a %s, which does not implement `Comparable`",
value,
ClassUtil.classNameOf(obj)));
}
return (Comparable<?>) obj;
}
private static boolean _isValidBracketNotation(String range) {
if (range.isEmpty()) {
return false;
}
char first = range.charAt(0);
char last = range.charAt(range.length() - 1);
return (first == '[' || first == '(') && (last == ']' || last == ')');
}
}