DefaultValueExtractor.java
/*
* Copyright 2019-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bremersee.comparator;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Optional;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* The default value extractor supports field names and paths as described in {@link
* org.bremersee.comparator.model.SortOrder}. A field name can be just the field name (for example
* {@code firstName}), a method name, that is not generated by {@link
* ValueExtractor#getPossibleMethodNames(String)} (for example {@code toString}) or a path of field
* names separated by dots (.), for example {@code person.firstName}.
*
* @author Christian Bremer
*/
@ToString
@EqualsAndHashCode
public class DefaultValueExtractor implements ValueExtractor {
private final boolean throwingException;
/**
* Instantiates a new default value extractor that will throw {@link ValueExtractorException}, if
* the given field cannot be found.
*/
public DefaultValueExtractor() {
this(true);
}
/**
* Instantiates a new default value extractor.
*
* @param throwingException if {@code true} and the given field cannot be found, {@link
* ValueExtractorException} will be thrown; otherwise {@code null} will be returned
*/
public DefaultValueExtractor(boolean throwingException) {
this.throwingException = throwingException;
}
@Override
public Object findValue(Object obj, String fieldPath) {
final String fieldIdentifier = trimFieldPath(fieldPath);
if (obj == null || fieldIdentifier == null || fieldIdentifier.isEmpty()) {
return obj;
}
final int index = fieldIdentifier.indexOf('.');
final String fieldName = index < 0
? fieldIdentifier
: fieldIdentifier.substring(0, index).trim();
Object value;
Optional<Field> field = findField(obj.getClass(), fieldName);
if (field.isPresent()) {
value = invoke(field.get(), obj);
} else {
Optional<Method> method = findMethod(obj.getClass(), fieldName);
if (method.isPresent()) {
value = invoke(method.get(), obj);
} else if (throwingException) {
throw new ValueExtractorException(
"Field [" + fieldName + "] was not found on object [" + obj + "].");
} else {
value = null;
}
}
return index < 0 ? value : findValue(value, fieldIdentifier.substring(index + 1));
}
private static String trimFieldPath(String field) {
if (field == null) {
return null;
}
String tmp = field
.replace("..", ".")
.trim();
while (tmp.startsWith(".")) {
tmp = tmp.substring(1).trim();
}
while (tmp.endsWith(".")) {
tmp = tmp.substring(0, tmp.length() - 1).trim();
}
return tmp;
}
}