here is the source codes
#Retention(RetentionPolicy.RUNTIME)
public #interface PredicateMeta {
String name();
int data();
String operator();
}
public class AnnotationTest {
public static void main(String[] args) {
Predicate p = getPred();
// how to get annotation values of data, name and operator??
}
public static Predicate getPred() {
#PredicateMeta(data = 0, name = "name", operator = "+")
Predicate p = (o) -> true;
return p;
}
}
How to get the values of annotation?
Also, will it be slow at runtime using annotations than using values from encapsulated fields?
You cant do this with lambdas.
If you try to get p.getClass().getAnnotatedInterfaces(), you will see that there is no annotations.
This is the only way to make this work:
first of all you must give your annotation #Target(ElementType.TYPE_USE)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE_USE)
#interface PredicateMeta {
String name();
int data();
String operator();
}
then use anonymous class:
public static Predicate getPred() {
return new #PredicateMeta(data = 0, name = "name", operator = "+")Predicate() {
#Override
public boolean test(Object o) {
return true;
}
};
}
so when you call this, you can get your annotation and its parameters:
p.getClass().getAnnotatedInterfaces()[0].getAnnotation(PredicateMeta.class)
Related
I am trying to map JsonNullable<List<ChildRequestTO> to Nullable<List<ChildRequestDO>> (see full code below) with mapstruct 1.4.2.Final and I am facing the following error: error: Nullable<List<ChildRequestDO>> does not have an accessible constructor. If I add a constructor for Nullable like
public Nullable(T value) {
this.value = value;
this.isPresent = true;
}
then I get the following error error: Unmapped target property: "value". Mapping from property "JsonNullable<List<ChildRequestTO>> products" to "Nullable<List<ChildRequestDO>> products".
How do I map complex wrapped types in a generic way?
The following mapping code (part of ChildRequestMapper class and applied in ObjectRequestMapper) solves the problem but I want to solve it in a more generic way:
#Named("mappingHelper")
default Nullable<List<ChildRequestDO>> customMapToDOs(JsonNullable<List<ChildRequestTO>> input) {
if (JsonNullable.undefined().equals(input)) {
return Nullable.undefined();
}
if (input.get() == null) {
return Nullable.of(null);
}
var output= input.get()
.stream()
.map(this::mapToDO)
.collect(Collectors.toList());
return Nullable.of(output);
}
Changing the NullableMapper to the code below does not work/compile because I do not know how to tell mapstruct to look for the appropriate mapper to map from T to X.
public static <T, X> Nullable<X> jsonNullableToNullable(JsonNullable<T> jsonNullable) {
if (jsonNullable.isPresent()) {
return Nullable.of(jsonNullable.get());
}
return Nullable.undefined();
}
Full code:
#Mapper(
unmappedTargetPolicy = ReportingPolicy.ERROR,
uses = {ChildRequestMapper.class, NullableMapper.class}
)
public interface ObjectRequestMapper {
#Mapping(target = "slots", source = "slots", qualifiedByName = "mapToSlotDOs")
ModifyObjectRequestDO mapToDO(ModifyObjectRequestTO input);
}
#Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface ChildRequestMapper {
ChildRequestDO mapToDO(ChildRequestTO input);
}
public class NullableMapper {
public static <T> Nullable<T> jsonNullableToNullable(JsonNullable<T> jsonNullable) {
if (jsonNullable.isPresent()) {
return Nullable.of(jsonNullable.get());
}
return Nullable.undefined();
}
}
public class ModifyObjectRequestTO {
private JsonNullable<String> name = JsonNullable.undefined();
private JsonNullable<List<ChildRequestTO>> children = JsonNullable.undefined();
}
public class ModifyObjectRequestDO {
private Nullable<String> name = Nullable.undefined();
private Nullable<List<ChildRequestDO>> children = Nullable.undefined();
}
public class Nullable<T> {
private static final Nullable<?> UNDEFINED = new Nullable<>(null, false);
private final T value;
private final boolean isPresent;
private Nullable(T value, boolean isPresent) {
this.value = value;
this.isPresent = isPresent;
}
public static <T> Nullable<T> undefined() {
#SuppressWarnings("unchecked")
Nullable<T> t = (Nullable<T>) UNDEFINED;
return t;
}
public static <T> Nullable<T> of(T value) {
return new Nullable<T>(value, true);
}
public T get() {
if (!isPresent) {
throw new NoSuchElementException("Value is undefined");
}
return value;
}
public boolean isPresent() {
return isPresent;
}
}
I am using a custom annotation to get the value of an argument based on its name.
The implementation is based on past examples here but I am getting following error.
Could I get some advice on what I am doing wrong please? Thanks.
The error is being throw when the class ExpressionEvaluator calls the method condition().
Error as follows:
EL1027E: Indexing into type 'ExpressionRootObject' is not supported
org.springframework.expression.spel.SpelEvaluationException: EL1027E:
Indexing into type 'ExpressionRootObject' is not supported
Custom Annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface CustomAnnot {
String name();
String countryCode() default "example";
String[] requestKeys();
}
Root Object
public class ExpressionRootObject {
private final Object object;
private final Object[] args;
public ExpressionRootObject(Object object, Object[] args) {
this.object = object;
this.args = args;
}
public Object getObject() {
return object;
}
public Object[] getArgs() {
return args;
}
}
Expression Evaluator
public class ExpressionEvaluator<T> extends CachedExpressionEvaluator {
// shared param discoverer since it caches data internally
private final ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer();
private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(64);
private final Map<AnnotatedElementKey, Method> targetMethodCache = new ConcurrentHashMap<>(64);
/**
* Create the suitable {#link EvaluationContext} for the specified event handling
* on the specified method.
*/
public EvaluationContext createEvaluationContext(Object object, Class<?> targetClass, Method method, Object[] args) {
Method targetMethod = getTargetMethod(targetClass, method);
ExpressionRootObject root = new ExpressionRootObject(object, args);
return new MethodBasedEvaluationContext(root, targetMethod, args, this.paramNameDiscoverer);
}
/**
* Specify if the condition defined by the specified expression matches.
*/
public T condition(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext, Class<T> clazz) {
// IT FAILS AT THIS CALL.
return getExpression(this.conditionCache, elementKey, conditionExpression).getValue(evalContext, clazz);
}
private Method getTargetMethod(Class<?> targetClass, Method method) {
AnnotatedElementKey methodKey = new AnnotatedElementKey(method, targetClass);
Method targetMethod = this.targetMethodCache.get(methodKey);
if (targetMethod == null) {
targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
if (targetMethod == null) {
targetMethod = method;
}
this.targetMethodCache.put(methodKey, targetMethod);
}
return targetMethod;
}
}
Aspect that tries to evaluate
#Aspect
#Component
public class AspectMocking {
#Around("#annotation(customAnnot)")
public Object getMockedData(ProceedingJoinPoint pjp, CustomAnnot customAnnot) throws Throwable {
try{
//This is the call which eventually fails at the condition() method below.
Long l = getValue(pjp, customAnnot.requestKeys()[0]);
} catch(Exception e){
//
}
return null;
}
private Long getValue(ProceedingJoinPoint joinPoint, String condition) {
return getValue(joinPoint.getTarget(), joinPoint.getArgs(),
joinPoint.getTarget().getClass(),
((MethodSignature) joinPoint.getSignature()).getMethod(), condition);
}
private Long getValue(Object object, Object[] args, Class clazz, Method method, String condition) {
if (args == null) {
return null;
}
EvaluationContext evaluationContext = evaluator.createEvaluationContext(object, clazz, method, args);
AnnotatedElementKey methodKey = new AnnotatedElementKey(method, clazz);
return evaluator.condition(condition, methodKey, evaluationContext, Long.class);
}
}
I am annotating the following method with the custom annotation:
#CustomAnnot(name = "APPLE", requestKeys = {"id", "label"})
public Object get(long id, long label) {
// some code
}
I am trying to map a non-iterable value i.e. String to a list of string using mapstruct.
So I am using
#Mapping(target = "abc", expression = "java(java.util.Arrays.asList(x.getY().getXyz()))")
Here abc is List<String>
xyz is a String
But for this i need to check for null explicitly.
Is there any better way of maaping a non-iterable to iterable by converting non iterable to iterable.
Here is an example for non iterable-to-iterable:
public class Source {
private String myString;
public String getMyString() {
return myString;
}
public void setMyString(String myString) {
this.myString = myString;
}
}
public class Target {
private List<String> myStrings;
public List<String> getMyStrings() {
return myStrings;
}
public void setMyStrings(List<String> myStrings) {
this.myStrings = myStrings;
}
}
#Qualifier
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.SOURCE)
public #interface FirstElement {
}
public class NonIterableToIterableUtils {
#FirstElement
public List<String> first(String in ) {
if (StringUtils.isNotEmpty(in)) {
return Arrays.asList(in);
} else {
return null;
}
}
}
#Mapper( uses = NonIterableToIterableUtils.class )
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper( SourceTargetMapper.class );
#Mappings( {
#Mapping( source = "myString", target = "myStrings", qualifiedBy = FirstElement.class )
} )
Target toTarget( Source s );
}
public class Main {
public static void main(String[] args) {
Source s = new Source();
s.setMyString("Item");
Target t = SourceTargetMapper.MAPPER.toTarget( s );
System.out.println( t.getMyStrings().get(0));
}
}
There is a iterable-to-non-iterable example in the MapStruct examples repository. Addtionally there is a pending pull request for non-iterable-to-iterable.
In a nutshell you can use a custom method that would do the mapping. You can also use #Qualifier to have more granural control
Add an empty default method in the mapper, e.g. AnimalMapper.toLions(), and overwrite the default method in a mapper decorator, e.g. AnimalMapperDecorator. It works in my test.
#Repository
#Mapper(componentModel = "spring")
#DecoratedWith(AnimalMapperDecorator.class)
public interface AnimalMapper {
default List<Lion> toLions(Jungle jungle) {
return null;
}
}
public abstract class AnimalMapperDecorator implements AnimalMapper {
#Autowired
#Qualifier("delegate")
private AnimalMapper delegate;
#Override
public List<Lion> toLions(Jungle jungle) {
List<Lion> lions = new ArrayList<>();
Lion king = getKing(jungle);
Lion queen = getQueen(jungle);
Lion leo = getLeo(jungle);
lions.add(king); lions.add(queen); lions.add(leo);
return lions;
}
}
class Test {
#Autowired
private AnimalMapper animalMapper;
public void test() {
Jungle jungle = generateJungle();
List<Lion> lions = animalMapper.toLions(jungle);
// Assert lions
}
}
I have to do Empty String check for 6 attributes of my class which has total 7 attributes, Is there any better way to do rather than checking each attribute separately ?
You can make one common method in class like this
class MyClass {
String attr1, attr2, attr3;
public boolean isValid() {
return !attr1.isEmpty() && !attr2.isEmpty() ;
}
}
One option is to use Hibernate Validator api, you can set #NotNull attributes on 6 of the params.
A note : Frameworks should not only be used if this is required at many places or is already being used as it comes with an overhead.
public class Abc{
#NotNull
private String var1;
#NotNull
#Size(min = 1, max = 4)
private String var2;
#Min(5)
private int var3;
// ...
}
If you are using java 8, you can do like this:
create a generic class Validator :
public class Validator<T> {
private T t;
private List<Throwable> exceptions = new ArrayList<>();
private Validator(T t) {
this.t = t;
}
public static <T> Validator<T> of(final T t) {
return new Validator<T>(t);
}
public <U> Validator<T> validate(final Function<T, U> projection, final Predicate<U> filter, final String message) {
if (!filter.test(projection.apply(t))) {
this.exceptions.add(new IllegalStateException(message));
}
return this;
}
public List<Throwable> get() {
return exceptions;
}
}
in your validator class :
YourObject yourObject = new yourObject("attr1", "attr2", "attr3");
List<Throwable> result = Validator.of(yourObject)
.validate(YourObject::getAttr1, a -> !a.isEmpty(), "attr1 should not be empty")
.validate(YourObject::getAttr2, a -> !a.isEmpty(), "attr2 should not be empty")
.validate(YourObject::getAttr3, a -> !a.isEmpty(), "attr3 should not be empty").get();
check if result is not empty.
If you are using java 7, you can adapt previous code like this :
public class StringValidator {
private List<Throwable> exceptions = new ArrayList<>();
private Validator() {
}
public static StringValidator instance() {
return new Validator();
}
public StringValidator validate(final String value, final String message) {
if (value.isEmpty()) {
this.exceptions.add(new IllegalStateException(message));
}
return this;
}
public List<Throwable> get() {
return exceptions;
}
}
in your validator class :
YourObject yourObject = new yourObject("attr1", "attr2", "attr3");
List<Throwable> result = Validator.instance()
.validate(yourObject.getAttr1(), "attr1 should not be empty")
.validate(yourObject.getAttr2(), "attr2 should not be empty")
.validate(yourObject.getAttr3(), "attr3 should not be empty").get();
When you run a JUnit 4 ParameterizedTest with the Eclipse TestRunner, the graphical representation is rather dumb: for each test you have a node called [0], [1], etc.
Is it possible give the tests [0], [1], etc. explicit names? Implementing a toString method for the tests does not seem to help.
(This is a follow-up question to JUnit test with dynamic number of tests.)
I think there's nothing built in in jUnit 4 to do this.
I've implemented a solution. I've built my own Parameterized class based on the existing one:
public class MyParameterized extends TestClassRunner {
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public static #interface Parameters {
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public static #interface Name {
}
public static Collection<Object[]> eachOne(Object... params) {
List<Object[]> results = new ArrayList<Object[]>();
for (Object param : params)
results.add(new Object[] { param });
return results;
}
// TODO: single-class this extension
private static class TestClassRunnerForParameters extends TestClassMethodsRunner {
private final Object[] fParameters;
private final Class<?> fTestClass;
private Object instance;
private final int fParameterSetNumber;
private final Constructor<?> fConstructor;
private TestClassRunnerForParameters(Class<?> klass, Object[] parameters, int i) throws Exception {
super(klass);
fTestClass = klass;
fParameters = parameters;
fParameterSetNumber = i;
fConstructor = getOnlyConstructor();
instance = fConstructor.newInstance(fParameters);
}
#Override
protected Object createTest() throws Exception {
return instance;
}
#Override
protected String getName() {
String name = null;
try {
Method m = getNameMethod();
if (m != null)
name = (String) m.invoke(instance);
} catch (Exception e) {
}
return String.format("[%s]", (name == null ? fParameterSetNumber : name));
}
#Override
protected String testName(final Method method) {
String name = null;
try {
Method m = getNameMethod();
if (m != null)
name = (String) m.invoke(instance);
} catch (Exception e) {
}
return String.format("%s[%s]", method.getName(), (name == null ? fParameterSetNumber : name));
}
private Constructor<?> getOnlyConstructor() {
Constructor<?>[] constructors = getTestClass().getConstructors();
assertEquals(1, constructors.length);
return constructors[0];
}
private Method getNameMethod() throws Exception {
for (Method each : fTestClass.getMethods()) {
if (Modifier.isPublic((each.getModifiers()))) {
Annotation[] annotations = each.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation.annotationType() == Name.class) {
if (each.getReturnType().equals(String.class))
return each;
else
throw new Exception("Name annotated method doesn't return an object of type String.");
}
}
}
}
return null;
}
}
// TODO: I think this now eagerly reads parameters, which was never the
// point.
public static class RunAllParameterMethods extends CompositeRunner {
private final Class<?> fKlass;
public RunAllParameterMethods(Class<?> klass) throws Exception {
super(klass.getName());
fKlass = klass;
int i = 0;
for (final Object each : getParametersList()) {
if (each instanceof Object[])
super.add(new TestClassRunnerForParameters(klass, (Object[]) each, i++));
else
throw new Exception(String.format("%s.%s() must return a Collection of arrays.", fKlass.getName(), getParametersMethod().getName()));
}
}
private Collection<?> getParametersList() throws IllegalAccessException, InvocationTargetException, Exception {
return (Collection<?>) getParametersMethod().invoke(null);
}
private Method getParametersMethod() throws Exception {
for (Method each : fKlass.getMethods()) {
if (Modifier.isStatic(each.getModifiers())) {
Annotation[] annotations = each.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation.annotationType() == Parameters.class)
return each;
}
}
}
throw new Exception("No public static parameters method on class " + getName());
}
}
public MyParameterized(final Class<?> klass) throws Exception {
super(klass, new RunAllParameterMethods(klass));
}
#Override
protected void validate(MethodValidator methodValidator) {
methodValidator.validateStaticMethods();
methodValidator.validateInstanceMethods();
}
}
To be used like:
#RunWith(MyParameterized.class)
public class ParameterizedTest {
private File file;
public ParameterizedTest(File file) {
this.file = file;
}
#Test
public void test1() throws Exception {}
#Test
public void test2() throws Exception {}
#Name
public String getName() {
return "coolFile:" + file.getName();
}
#Parameters
public static Collection<Object[]> data() {
// load the files as you want
Object[] fileArg1 = new Object[] { new File("path1") };
Object[] fileArg2 = new Object[] { new File("path2") };
Collection<Object[]> data = new ArrayList<Object[]>();
data.add(fileArg1);
data.add(fileArg2);
return data;
}
}
This implies that I instantiate the test class earlier. I hope this won't cause any errors ... I guess I should test the tests :)
JUnit4 now allows specifying a name attribute to the Parameterized annotation, such that you can specify a naming pattern from the index and toString methods of the arguments. E.g.:
#Parameters(name = "{index}: fib({0})={1}")
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
{ 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
}
A code-less though not that comfortable solution is to pass enough context information to identify the test in assert messages. You will still see just testXY[0] failed but the detailed message tells you which one was that.
assertEquals("Not the expected decision for the senator " + this.currentSenatorName + " and the law " + this.votedLaw,
expectedVote, actualVote);
If you use JUnitParams library (as I have described here), the parameterized tests will have their stringified parameters as their own default test names.
Moreover, you can see in their samples, that JUnitParams also allows you to have a custom test name by using #TestCaseName:
#Test
#Parameters({ "1,1", "2,2", "3,6" })
#TestCaseName("factorial({0}) = {1}")
public void custom_names_for_test_case(int argument, int result) { }
#Test
#Parameters({ "value1, value2", "value3, value4" })
#TestCaseName("[{index}] {method}: {params}")
public void predefined_macro_for_test_case_name(String param1, String param2) { }
There's no hint that this feature is or will be implemented. I would request this feature because it's nice to have.