If I had defined an annotation like this:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Input {
String type() default "text";
String name();
String pattern() default "";
}
and use it for this methods:
#Column(name="nome", unique = true, nullable = false)
#Order(value=1)
#Input
private String nome;
#Column(name="resumo", length=140)
#Order(value=2)
#Input
private String resumo;
Is there any way to assign to attribute name the name of the annotated field (for instance: for the field String nome the value would be nome and for the field String resumo would be resumo)?
You cannot default the annotation variables to field names. But where ever you are processing the annotation, you can code such that it defaults to field's name. Example below
Field field = ... // get fields
Annotation annotation = field.getAnnotation(Input.class);
if(annotation instanceof Input){
Input inputAnnotation = (Input) annotation;
String name = inputAnnotation.name();
if(name == null) { // if the name not defined, default it to field name
name = field.getName();
}
System.out.println("name: " + name); //use the name
}
Related
I have these 4 classes -
Component.java -> which will be annotated using custom annotation,
Properties.java -> the annotation class
Utilities.java -> class where the annotation is used
AnnotationProcessor.java -> where the annotation logic using reflection is built
What I want is, based on the annotation, I will set a property of the Component object.
This is my code -
//class which will be annotated using custom annotation
public class Component {
private String name;
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//annotation class
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Properties {
String compName();
}
//class where the annotation is used
public class Utility {
#Properties(compName = "myName")
Component comp1;
#Properties(compName = "myPassword")
Component comp2;
public void login() throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
comp1 = new Component();
comp2 = new Component();
comp1.setId("1111");
comp2.setId("2222");
AnnotationProcessor annotationProcessor = new AnnotationProcessor();
annotationProcessor.process(this, comp1);
annotationProcessor.process(this, comp2);
}}
//class where the annotation is processed
public class AnnotationProcessor {
public void process (Object callingClass, Component c){
System.out.println("In AnnotationProcessor:process");
Class<? extends Object> aClass = callingClass.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field: declaredFields) {
if(field.isAnnotationPresent(Properties.class)){
if (field.get(callingClass) != null) { //Added newly
Properties annotation = field.getAnnotation(Properties.class);
String val = annotation.compName();
c.setName(val);
field.set(callingClass, c); //Added newly
System.out.println("For field " + field.getName() + ", component id is : " + c.getId() + " and name is : --> " + c.getName());
break; //Added newly
}
}
}
}
I want to achieve to set the name of "comp1" to be "myName" and name of "comp2" to be "myPassword". Basically, the output I expect is this -
For field comp1, component id is : 1111 and name is : --> myName
For field comp2, component id is : 2222 and name is : --> myPassword
But the actual output is this (after //added newly changes)-
In AnnotationProcessor:process
For field comp1, component id is : 1111 and name is : --> myName
In AnnotationProcessor:process
For field comp1, component id is : 2222 and name is : --> myName
My problem is, how to assign the "name" of the component as per the annotation for that component object only and not for all the fields the Utility class has. I feel there is some if condition in the middle that might help achieve this, but not able to find it out.
I have a question about the javax validation api, specifically, is it possible to generate a validation message of the following format.
For example, there are classes with a nested structure: Model, Attribute, and Value:
public class Model {
#NotBlank
private String id;
#NotBlank
private String name;
#Size(min = 1)
private List<Attribute> attributes;
}
public class Attribute {
#NotBlank
private String id;
#NotBlank
private String name;
#Size(min = 1)
private List<Value> values;
}
public class Value {
#NotBlank
private String id;
#NotBlank
private String name;
}
After calling the validation of the model object:
validator.validate(modelObject)
i want to generate validation errors that would contain the object IDs of each of the levels, for example:
M[model_id] A[attribute_id] V [value_id] Value error message
M[model_id] A[attribute_id] Open attribute error message
M[model_id] A[attribute_id] Private attribute error message
M[model_id] A[attribute_id] Attribute error message
M[model_id] Model error message
Is it possible to do something similar?
For business users, this is more readable compared to the default output of the path ' attributes[0].values[0].names'.
I will be grateful for any help!
Note: i found it in the org.hibernate class.validator.internal.engine.validationcontext. Abstract ValidationContext the processedPathsPerBean field that stores the Path and Bean mapping, but this context is not accessible from the outside ((
I found a solution using the "Extensions of the Path"
public String createErrorMessageFrom(ConstraintViolation<E> violation) {
Path path = violation.getPropertyPath();
StringBuilder targetErrorMessage = new StringBuilder();
String fieldWithInvalidValue = null;
if (isRoot(path)) {
fieldWithInvalidValue = path.toString();
} else {
for (Iterator<Path.Node> iterator = violation.getPropertyPath().iterator(); iterator.hasNext(); ) {
Path.Node nextPathNode = iterator.next();
fieldWithInvalidValue = nextPathNode.toString();
if (iterator.hasNext()) {
NamedEntityDto dto = (NamedEntityDto) nextPathNode.as(PropertyNode.class).getValue();
targetErrorMessage
.append(getDtoPrefixForMessage(dto));
}
}
}
targetErrorMessage.append(String.format("The field '%s' %s", fieldWithInvalidValue, violation.getMessage()));
return targetErrorMessage.toString();
}
I have some classes like below:
#Getter
#Setter
class Person{
#JsonProperty("cInfo")
private ContactInformation contactInfo;
private String name;
private String position;
}
#Getter
#Setter
class ContactInformation{
#JsonProperty("pAddress")
private Address address;
}
#Getter
#Setter
class Address{
private String street;
private String district;
}
And what I am going to do is writing an Utils method for the Person object that take one parameter which is the attributeName as String and return the getter value for this attribute.
Ex:
attributeName = name -> return person.getName()
attributeName = position -> return person.getPosition()
attributeName = cInfo.pAddress.street -> return person.getContactInfo().getAddress().getStreet()
attributeName = cInfo.pAddress.district -> return person.getContactInfo().getAddress().getDistrict()
Below is what I've done: I loop through all the fields in the Person object and check if the attributeName equal to either the JsonProperty's Name or the Field's Name then I will return this getter.
Object result;
Field[] fields = Person.class.getDeclaredFields();
for (Field field : fields) {
JsonProperty jsonProperty = field.getDeclaredAnnotation(JsonProperty.class);
if (jsonProperty != null && jsonProperty.value().equals(attributeName)) {
result = Person.class.getMethod("get" + capitalize(field.getName())).invoke(person);
} else {
if (field.getName().equals(attributeName)) {
result = person.class.getMethod("get" + capitalize(field.getName()))
.invoke(person);
}
}
}
This worked but only with the fields that locate direct in the Person class, ex: name, position. With the fields inside of contactInfo or address I am still getting stuck there. Can anyone give me some hint here how can I do it?
Thank you!
Because path like a.b.c related to different objects. So you need to. split by point and for each token call get and use obtained result for next token
UPDATE: something like:
private static Object invkGen(Object passedObj, String attributeName) throws Exception {
final String[] split = attributeName.split("\\.");
Object result = passedObj;
for (String s : split) {
if (result == null) {
break;
}
result = invk(result, s);
}
return result;
}
private static Object invk(Object passedObj, String attributeName) throws Exception {
Object result = null;
final Field[] fields = passedObj.getClass().getDeclaredFields();
for (Field field : fields) {
JsonProperty jsonProperty = field.getDeclaredAnnotation(JsonProperty.class);
if (jsonProperty != null && jsonProperty.value().equals(attributeName)) {
result = Person.class.getMethod("get" + capitalize(field.getName())).invoke(passedObj);
} else {
if (field.getName().equals(attributeName)) {
result = passedObj.getClass().getMethod("get" + capitalize(field.getName()))
.invoke(passedObj);
}
}
}
return result;
}
I have a class named ClBranch.java like below:
#Entity
#Table(name = "PROVINCE")
public class PROVINCE implements Serializable {
#Id
#Column(name="PR_CODE", length = 50)
private String provinceCode
#Column(name="PR_NAME", length = 500)
private String provinceName
......
getter-setter.
}
This is my code:
public static String getClassAnnotationValue(Class classType, Class annotationType, String attributeName) {
String value = null;
Annotation annotation = classType.getAnnotation(annotationType);
if (annotation != null) {
try {
value = (String) annotation.annotationType().getMethod(attributeName).invoke(annotation);
} catch (Exception ex) {
ex.printStackTrace();
}
}
return value;
}
String columnName = getClassAnnotationValue(PROVINCE .class, Column.class, "name");
By this way, I only get ColumnName as PROVINCE. I can not get ColumnName. How can I do it?
The #Column annotation is defined on the fields, not on the class. So you must query annotation values from the private fields:
String columnName = getAnnotationValue(PROVINCE.class.getDeclaredField("provinceCode"), Column.class, "name");
To be able to pass Field objects to your method, change the type of your classType parameter from Class to AnnotatedElement. Then you can pass classes, fields, parameters or methods:
public static String getAnnotationValue(AnnotatedElement element, Class annotationType, String attributeName) {
...
}
I am doing web application in that I want to validate two fields in the JSP form. In registration filed I have so many fields. In that I want to validate password and confirm password fields.
Below is my code:
Action Class:
#Length(min = 6, max = 20)
#Column(name = "PERSON_PASSWORD", nullable = false, length = 20)
public String getPassword() {
return password;
}
#Length(min = 6, max = 20)
#Column(name = "PERSON_CONFORMPASSWORD", nullable = false, length = 20)
public String getConformPassword() {
return conformPassword;
}
Now, how can I validate the two fields contain the same data?
You could use a non-field custom validator to validate any number of fields. For this purpose you should create a custom validator that extend ValidatorSupport and implement validate method which is inherited from Validator interface but doesn't have default implementation. You should write this implementation to do custom validation. For example you want to create a RetypeValidator which validates two fields have the same value. It could look like
public class RetypeValidator extends ValidatorSupport {
private String value = null;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
private String retypeValue = null;
public String getRetypeValue() {
return retypeValue;
}
public void setRetypeValue(String value) {
retypeValue = value;
}
#Override
public void validate(Object object) throws ValidationException {
String value = (String) parse(this.value, String.class);
String retypeValue = (String) parse(this.retypeValue, String.class);
if (value != null && retypeValue != null && !value.equals(retypeValue))
addActionError(getDefaultMessage());
}
}
then you have to add this validator to the configuration in the validators.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator Config 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-config-1.0.dtd">
<validators>
<validator name="retypeValidator" class="org.custom.struts.vaidators.RetypeValidator"/>
</validators>
Now, you have a custom validator name that you could use with #CustomValidator annotation.
#Validations(
customValidators = #CustomValidator(type="retypeValidator", message="the value and retype value should be the same",
parameters = { #ValidationParameter( name = "value", value = "${password}" ), #ValidationParameter( name = "retypeValue", value = "${conformPassword}" )})
)
Note, password and conformPassword are OGNL expressions used to parse when being validated.
You can use if statement to compare
if(password == conformPassword)
{
//TO-DO
}
else
{
//TO-DO
}
if(getPassword().equals(getConformPassword()){
{
//code
}