Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions src/main/java/com/github/tminglei/bind/FrameworkUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public class FrameworkUtils {
public static final Constraint PASS_VALIDATE
= (name, data, messages, options) -> Collections.EMPTY_LIST;


private static final Pattern OBJ_ELEMENT_NAME = Pattern.compile("^(.*)\\.([^\\.]+)$");
private static final Pattern ARR_ELEMENT_NAME = Pattern.compile("^(.*)\\[([\\d]+)\\]$");

public static <T> List<T> unmodifiableList(List<T> list) {
return list == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(list);
}
Expand Down Expand Up @@ -75,8 +79,6 @@ public static boolean isEmptyInput(String name, Map<String, String> data, InputM
}
}

static final Pattern OBJ_ELEMENT_NAME = Pattern.compile("^(.*)\\.([^\\.]+)$");
static final Pattern ARR_ELEMENT_NAME = Pattern.compile("^(.*)\\[([\\d]+)\\]$");
// return (parent, name, isArray:false) or (name, index, isArray:true)
static String[] splitName(String name) {
logger.trace("splitting name for {}", name);
Expand Down Expand Up @@ -267,7 +269,7 @@ public static String getLabel(String fullName, Messages messages, Options option
// make a Constraint which will try to check and collect errors
public static <T> Constraint
checking(Function<String, T> check, String messageOrKey, boolean isKey, String... extraMessageArgs) {
return mkSimpleConstraint(((label, vString, messages) -> {
return mkSimpleConstraint((label, vString, messages) -> {
logger.debug("checking for {}", vString);

if (isEmptyStr(vString)) return null;
Expand All @@ -281,12 +283,12 @@ public static String getLabel(String fullName, Messages messages, Options option
return String.format(msgTemplate, messageArgs.toArray());
}
}
}), null);
}, null);
}

// make a compound Constraint, which checks whether any inputting constraints passed
public static Constraint anyPassed(Constraint... constraints) {
return ((name, data, messages, options) -> {
return (name, data, messages, options) -> {
logger.debug("checking any passed for {}", name);

List<Map.Entry<String, String>> errErrors = new ArrayList<>();
Expand All @@ -303,7 +305,7 @@ public static Constraint anyPassed(Constraint... constraints) {
.collect(Collectors.joining(", ", "[", "]"));
return Arrays.asList(entry(name,
String.format(messages.get("error.anypassed"), label, errStr)));
});
};
}

// Computes the available indexes for the given key in this set of data.
Expand Down
47 changes: 25 additions & 22 deletions src/main/java/com/github/tminglei/bind/Mappings.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
*/
public class Mappings {
private static final Logger logger = LoggerFactory.getLogger(Mappings.class);
public static final String PATTERN = "^[\\d]+$";
public static final String S_NOT_A_DATE_LONG = "'%s' not a date long";
public static final String ERROR_PATTERN = "error.pattern";

/////////////////////////////////// pre-defined field mappings ////////////////////////

Expand Down Expand Up @@ -171,16 +174,16 @@ public static Mapping<LocalDate> date(String pattern, Constraint... constraints)
InputMode.SINGLE,
mkSimpleConverter(s -> {
if (isEmptyStr(s)) return null;
else if (s.matches("^[\\d]+$")) {
else if (s.matches(PATTERN)) {
Instant instant = new Date(Long.parseLong(s)).toInstant();
return LocalDateTime.ofInstant(instant, ZoneId.of("UTC")).toLocalDate();
} else {
return LocalDate.parse(s, formatter);
}
}), new MappingMeta(LocalDate.class)
).constraint(anyPassed(
checking(s -> new Date(Long.parseLong(s)), "'%s' not a date long", false),
checking(formatter::parse, "error.pattern", true, pattern)
checking(s -> new Date(Long.parseLong(s)), S_NOT_A_DATE_LONG, false),
checking(formatter::parse, ERROR_PATTERN, true, pattern)
)).constraint(constraints);
}

Expand All @@ -198,16 +201,16 @@ public static Mapping<LocalDateTime> datetime(String pattern, Constraint... cons
InputMode.SINGLE,
mkSimpleConverter(s -> {
if (isEmptyStr(s)) return null;
else if (s.matches("^[\\d]+$")) {
else if (s.matches(PATTERN)) {
Instant instant = new Date(Long.parseLong(s)).toInstant();
return LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
} else {
return LocalDateTime.parse(s, formatter);
}
}), new MappingMeta(LocalDateTime.class)
).constraint(anyPassed(
checking(s -> new Date(Long.parseLong(s)), "'%s' not a date long", false),
checking(formatter::parse, "error.pattern", true, pattern)
checking(s -> new Date(Long.parseLong(s)), S_NOT_A_DATE_LONG, false),
checking(formatter::parse, ERROR_PATTERN, true, pattern)
)).constraint(constraints);
}

Expand All @@ -225,16 +228,16 @@ public static Mapping<LocalTime> time(String pattern, Constraint... constraints)
InputMode.SINGLE,
mkSimpleConverter(s -> {
if (isEmptyStr(s)) return null;
else if (s.matches("^[\\d]+$")) {
else if (s.matches(PATTERN)) {
Instant instant = new Date(Long.parseLong(s)).toInstant();
return LocalDateTime.ofInstant(instant, ZoneId.of("UTC")).toLocalTime();
} else {
return LocalTime.parse(s, formatter);
}
}), new MappingMeta(LocalTime.class)
).constraint(anyPassed(
checking(s -> new Date(Long.parseLong(s)), "'%s' not a date long", false),
checking(formatter::parse, "error.pattern", true, pattern)
checking(s -> new Date(Long.parseLong(s)), S_NOT_A_DATE_LONG, false),
checking(formatter::parse, ERROR_PATTERN, true, pattern)
)).constraint(constraints);
}

Expand All @@ -250,7 +253,7 @@ else if (s.matches("^[\\d]+$")) {
public static <T> Mapping<T> ignored(T instead) {
return new FieldMapping<T>(
InputMode.POLYMORPHIC,
((name, data) -> instead),
(name, data) -> instead,
new MappingMeta(instead.getClass())
).options(o -> o._ignoreConstraints(true));
}
Expand Down Expand Up @@ -278,14 +281,14 @@ public static <T> Mapping<T> defaultVal(Mapping<T> base, T defaultVal, Constrain
public static <T> Mapping<Optional<T>> optional(Mapping<T> base, Constraint... constraints) {
return new FieldMapping<Optional<T>>(
base.options()._inputMode(),
((name, data) -> {
(name, data) -> {
logger.debug("optional - converting {}", name);

if (isEmptyInput(name, data, base.options()._inputMode())) {
return Optional.empty();
} else return Optional.of(base.convert(name, data));
}),
((name, data, messages, options) -> {
},
(name, data, messages, options) -> {
logger.debug("optional - validating {}", name);

if (isEmptyInput(name, data, base.options()._inputMode())) {
Expand All @@ -295,7 +298,7 @@ public static <T> Mapping<Optional<T>> optional(Mapping<T> base, Constraint... c
.options(o -> o._label(o._label().orElse(options._label().orElse(null))))
.validate(name, data, messages, options);
}
}), new MappingMeta(Optional.class, base)
}, new MappingMeta(Optional.class, base)
).options(o -> o._ignoreConstraints(true))
.constraint(constraints);
}
Expand All @@ -310,20 +313,20 @@ public static <T> Mapping<Optional<T>> optional(Mapping<T> base, Constraint... c
public static <T> Mapping<List<T>> list(Mapping<T> base, Constraint... constraints) {
return new FieldMapping<List<T>>(
InputMode.MULTIPLE,
((name, data) -> {
(name, data) -> {
logger.debug("list - converting {}", name);

return indexes(name, data).stream()
.map(i -> base.convert(name + "[" + i + "]", data))
.collect(Collectors.toList());
}),
((name, data, messages, options) -> {
},
(name, data, messages, options) -> {
logger.debug("list - validating {}", name);

return indexes(name, data).stream()
.flatMap(i -> base.validate(name + "[" + i + "]", data, messages, options).stream())
.collect(Collectors.toList());
}), new MappingMeta(List.class, base)
}, new MappingMeta(List.class, base)
).constraint(constraints);
}

Expand All @@ -340,7 +343,7 @@ public static <V> Mapping<Map<String, V>> map(Mapping<V> vBase, Constraint... co
public static <K, V> Mapping<Map<K, V>> map(Mapping<K> kBase, Mapping<V> vBase, Constraint... constraints) {
return new FieldMapping<Map<K, V>>(
InputMode.MULTIPLE,
((name, data) -> {
(name, data) -> {
logger.debug("map - converting {}", name);

return keys(name, data).stream()
Expand All @@ -356,8 +359,8 @@ public static <K, V> Mapping<Map<K, V>> map(Mapping<K> kBase, Mapping<V> vBase,
Map.Entry::getKey,
Map.Entry::getValue
));
}),
((name, data, messages, options) -> {
},
(name, data, messages, options) -> {
logger.debug("map - validating {}", name);

return keys(name, data).stream()
Expand All @@ -370,7 +373,7 @@ public static <K, V> Mapping<Map<K, V>> map(Mapping<K> kBase, Mapping<V> vBase,
).stream();
})
.collect(Collectors.toList());
}), new MappingMeta(Map.class, kBase, vBase)
}, new MappingMeta(Map.class, kBase, vBase)
).constraint(constraints);
}

Expand Down
18 changes: 9 additions & 9 deletions src/main/java/com/github/tminglei/bind/Processors.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public static PreProcessor expandJson() {
}
public static PreProcessor expandJson(String prefix) {
return mkPreProcessorWithMeta((prefix1, data, options) -> {
logger.debug("expanding json at '{}'", (prefix == null ? prefix1 : prefix));
logger.debug("expanding json at '{}'", prefix == null ? prefix1 : prefix);

String thePrefix = prefix == null ? prefix1 : prefix;
String jsonStr = data.get(thePrefix);
Expand Down Expand Up @@ -132,7 +132,7 @@ public static PreProcessor expandJsonKeys() {
}
public static PreProcessor expandJsonKeys(String prefix) {
return mkPreProcessorWithMeta((prefix1, data, options) -> {
logger.debug("expanding json keys at '{}'", (prefix == null ? prefix1 : prefix));
logger.debug("expanding json keys at '{}'", prefix == null ? prefix1 : prefix);

Map<String, String> data1 = expandJson(prefix).apply(prefix1, data, options);
Map<String, String> data2 = expandListKeys(prefix).apply(prefix1, data1, options);
Expand All @@ -149,7 +149,7 @@ public static PreProcessor expandListKeys() {
}
public static PreProcessor expandListKeys(String prefix) {
return mkPreProcessorWithMeta((prefix1, data, options) -> {
logger.debug("expanding list keys at '{}'", (prefix == null ? prefix1 : prefix));
logger.debug("expanding list keys at '{}'", prefix == null ? prefix1 : prefix);

String thePrefix = prefix == null ? prefix1 : prefix;
Pattern p = Pattern.compile("^" + Pattern.quote(thePrefix) + "\\[[\\d]+\\].*");
Expand Down Expand Up @@ -228,7 +228,7 @@ public static PreProcessor changePrefix(String from, String to) {
*/
public static Function<List<Map.Entry<String, String>>, Map<String, Object>>
errsTree() {
return ((errors) -> {
return (errors) -> {
logger.debug("converting errors list to errors tree");

Map<String, Object> root = new HashMap<>();
Expand All @@ -239,7 +239,7 @@ public static PreProcessor changePrefix(String from, String to) {
workObj.add(error.getValue());
}
return root;
});
};
}

///////////////////////////////// pre-defined touched checkers /////////////////////
Expand All @@ -250,13 +250,13 @@ public static PreProcessor changePrefix(String from, String to) {
* @return new created touched checker
*/
public static TouchedChecker listTouched(List<String> touched) {
return ((prefix, data) -> {
return (prefix, data) -> {
logger.debug("checking touched in list for '{}'", prefix);

return touched.stream()
.filter(key -> key.startsWith(prefix))
.count() > 0;
});
};
}

/**
Expand All @@ -266,14 +266,14 @@ public static TouchedChecker listTouched(List<String> touched) {
* @return new created touched checker
*/
public static TouchedChecker prefixTouched(String dataPrefix, String touchedPrefix) {
return ((prefix, data) -> {
return (prefix, data) -> {
logger.debug("checking touched with data prefix '{}' and touched prefix '{}' for '{}'",
dataPrefix, touchedPrefix, prefix);

String prefixToBeChecked = prefix.replaceAll("^" + Pattern.quote(dataPrefix), touchedPrefix);
return data.keySet().stream()
.filter(key -> key.startsWith(prefixToBeChecked))
.count() > 0;
});
};
}
}
21 changes: 12 additions & 9 deletions src/main/java/com/github/tminglei/bind/PropertyUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@
*/
public class PropertyUtils {

public static final String PROPERTY_NAME_IS_NULL = "Property name is NULL!";
public static final String CANNOT_FIND_PROPERTY_IN_CLASS = "Can't find property '%1$s' in class %2$s";
private static Map<String,Map<String,PropertyDescriptor>> pdCache =
Collections.synchronizedMap( new WeakHashMap<>() );

public static Object readProperty( Object bean, String propName ) {
Objects.requireNonNull(bean, "Bean object is NULL!");
Objects.requireNonNull(propName, "Property name is NULL!");
Objects.requireNonNull(propName, PROPERTY_NAME_IS_NULL);

PropertyDescriptor pd = findPropertyDescriptor( bean.getClass(), propName );

if( pd == null)
throw new IllegalArgumentException( String.format("Can't find property '%1$s' in class %2$s",
throw new IllegalArgumentException( String.format(CANNOT_FIND_PROPERTY_IN_CLASS,
propName, bean.getClass().getName()));

try {
Expand All @@ -29,7 +34,7 @@ public static Object readProperty( Object bean, String propName ) {
if (method == null)
throw new UnsupportedOperationException("Property '" + pd.getName() + "' is not readable");

return (method.invoke(bean, new Object[0]));
return method.invoke(bean, new Object[0]);
}
catch (Exception e) {
throw new RuntimeException( String.format("Exception occurred when reading property '%1$s': %2$s",
Expand All @@ -39,12 +44,12 @@ public static Object readProperty( Object bean, String propName ) {

public static void writeProperty( Object bean, String propName, Object propValue ) {
Objects.requireNonNull(bean, "Bean object is NULL!");
Objects.requireNonNull(propName, "Property name is NULL!");
Objects.requireNonNull(propName, PROPERTY_NAME_IS_NULL);

PropertyDescriptor pd = findPropertyDescriptor( bean.getClass(), propName );

if( pd == null)
throw new IllegalArgumentException( String.format("Can't find property '%1$s' in class %2$s",
throw new IllegalArgumentException( String.format(CANNOT_FIND_PROPERTY_IN_CLASS,
propName, bean.getClass().getName()));

try {
Expand All @@ -63,12 +68,12 @@ public static void writeProperty( Object bean, String propName, Object propValue

public static Class<?> getPropertyType( Class<?> beanclazz, String propName ) {
Objects.requireNonNull(beanclazz, "Bean class is NULL!");
Objects.requireNonNull(propName, "Property name is NULL!");
Objects.requireNonNull(propName, PROPERTY_NAME_IS_NULL);

PropertyDescriptor pd = findPropertyDescriptor( beanclazz, propName );

if( pd == null)
throw new IllegalArgumentException( String.format("Can't find property '%1$s' in class %2$s",
throw new IllegalArgumentException( String.format(CANNOT_FIND_PROPERTY_IN_CLASS,
propName, beanclazz.getName()));

return pd.getPropertyType();
Expand Down Expand Up @@ -136,8 +141,6 @@ public static Class<?> getReturnType( Class<?> declaringClass, String methodName

//---------------------------------------------------- inner support methods ---

static Map<String,Map<String,PropertyDescriptor>> pdCache =
Collections.synchronizedMap( new WeakHashMap<>() );

static Map<String,PropertyDescriptor> introspect( Class<?> beanclazz ) {
Map<String,PropertyDescriptor> pdmap = pdCache.get( beanclazz.getName() );
Expand Down