I have tried to find information about this but have come up empty handed:
I gather it is possible to create a class dynamically in Java using reflection or proxies but I can't find out how. I'm implementing a simple database framework where I create the SQL queries using reflection. The method gets the object with the database fields as a parameter and creates the query based on that. But it would be very useful if I could also create the object itself dynamically so I wouldn't have the need to have a simple data wrapper object for each table.
The dynamic classes would only need simple fields (String, Integer, Double), e.g.
public class Data {
public Integer id;
public String name;
}
Is this possible and how would I do this?
EDIT: This is how I would use this:
/** Creates an SQL query for updating a row's values in the database.
*
* #param entity Table name.
* #param toUpdate Fields and values to update. All of the fields will be
* updated, so each field must have a meaningful value!
* #param idFields Fields used to identify the row(s).
* #param ids Id values for id fields. Values must be in the same order as
* the fields.
* #return
*/
#Override
public String updateItem(String entity, Object toUpdate, String[] idFields,
String[] ids) {
StringBuilder sb = new StringBuilder();
sb.append("UPDATE ");
sb.append(entity);
sb.append("SET ");
for (Field f: toUpdate.getClass().getDeclaredFields()) {
String fieldName = f.getName();
String value = new String();
sb.append(fieldName);
sb.append("=");
sb.append(formatValue(f));
sb.append(",");
}
/* Remove last comma */
sb.deleteCharAt(sb.toString().length()-1);
/* Add where clause */
sb.append(createWhereClause(idFields, ids));
return sb.toString();
}
/** Formats a value for an sql query.
*
* This function assumes that the field type is equivalent to the field
* in the database. In practice this means that this field support two
* types of fields: string (varchar) and numeric.
*
* A string type field will be escaped with single parenthesis (') because
* SQL databases expect that. Numbers are returned as-is.
*
* If the field is null, a string containing "NULL" is returned instead.
*
* #param f The field where the value is.
* #return Formatted value.
*/
String formatValue(Field f) {
String retval = null;
String type = f.getClass().getName();
if (type.equals("String")) {
try {
String value = (String)f.get(f);
if (value != null) {
retval = "'" + value + "'";
} else {
retval = "NULL";
}
} catch (Exception e) {
System.err.println("No such field: " + e.getMessage());
}
} else if (type.equals("Integer")) {
try {
Integer value = (Integer)f.get(f);
if (value != null) {
retval = String.valueOf(value);
} else {
retval = "NULL";
}
} catch (Exception e) {
System.err.println("No such field: " + e.getMessage());
}
} else {
try {
String value = (String) f.get(f);
if (value != null) {
retval = value;
} else {
retval = "NULL";
}
} catch (Exception e) {
System.err.println("No such field: " + e.getMessage());
}
}
return retval;
}
There are many different ways to achieve this (e.g proxies, ASM), but the simplest approach, one that you can start with when prototyping is:
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
public class MakeTodayClass {
Date today = new Date();
String todayMillis = Long.toString(today.getTime());
String todayClass = "z_" + todayMillis;
String todaySource = todayClass + ".java";
public static void main (String args[]){
MakeTodayClass mtc = new MakeTodayClass();
mtc.createIt();
if (mtc.compileIt()) {
System.out.println("Running " + mtc.todayClass + ":\n\n");
mtc.runIt();
}
else
System.out.println(mtc.todaySource + " is bad.");
}
public void createIt() {
try {
FileWriter aWriter = new FileWriter(todaySource, true);
aWriter.write("public class "+ todayClass + "{");
aWriter.write(" public void doit() {");
aWriter.write(" System.out.println(\""+todayMillis+"\");");
aWriter.write(" }}\n");
aWriter.flush();
aWriter.close();
}
catch(Exception e){
e.printStackTrace();
}
}
public boolean compileIt() {
String [] source = { new String(todaySource)};
ByteArrayOutputStream baos= new ByteArrayOutputStream();
new sun.tools.javac.Main(baos,source[0]).compile(source);
// if using JDK >= 1.3 then use
// public static int com.sun.tools.javac.Main.compile(source);
return (baos.toString().indexOf("error")==-1);
}
public void runIt() {
try {
Class params[] = {};
Object paramsObj[] = {};
Class thisClass = Class.forName(todayClass);
Object iClass = thisClass.newInstance();
Method thisMethod = thisClass.getDeclaredMethod("doit", params);
thisMethod.invoke(iClass, paramsObj);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
It is possible to generate classes (via cglib, asm, javassist, bcel), but you shouldn't do it that way. Why?
the code that's using the library should expect type Object and get all the fields using reflection - not a good idea
java is statically typed language, and you want to introduce dynamic typing - it's not the place.
If you simply want the data in an undefined format, then you can return it in an array, like Object[], or Map<String, Object> if you want them named, and get it from there - it will save you much trouble with unneeded class generation for the only purpose of containing some data that will be obtained by reflection.
What you can do instead is have predefined classes that will hold the data, and pass them as arguments to querying methods. For example:
public <T> T executeQuery(Class<T> expectedResultClass,
String someArg, Object.. otherArgs) {..}
Thus you can use reflection on the passed expectedResultClass to create a new object of that type and populate it with the result of the query.
That said, I think you could use something existing, like an ORM framework (Hibernate, EclipseLink), spring's JdbcTemplate, etc.
This is possible, but (I believe) you need something like ASM or BCEL.
Alternately, you could use something with more power (like Groovy).
It will take a couple of minutes to create a data model class for each table, which you can easily map to the database with an ORM like Hibernate or by writing your own JDBC DAOs. It is far easier than delving deeply into reflection.
You could create a utility that interrogates the database structure for a table, and creates the data model class and DAO for you. Alternatively you could create the model in Java and create a utility to create the database schema and DAO from that (using reflection and Java 5 Annotations to assist). Don't forget that javaFieldNames are different from database_column_names typically.
Recently I needed to create about 200 simple classes from medatata (objects filled with static data) and I did it through the open source burningwave library, with the following scenario:
The classes needed to have a certain prefix in the name, for example "Registro "*.java;
The classes needed to extend from a superclass Registro.java
The classes needed to contain JPA annotations like #Entity, #Column (in attributes), Lombok annotations and custom annotations.
Here is the link to the repository with the complete project: https://github.com/leandrosoares6/criacao-classes-entidade-efd
Here is the code snippet responsible for creating the classes:
public class RegistrosClassFactory {
private static final String PACOTE = "com.example.demo.model.registros";
private static final String SCHEMA = "MY_SCHEMA";
private static final String PREFIXO = "Registro";
static void criaRegistros() {
List<RegistroTest> registros = RegistroMetadataFactory.criaMetadados();
criaClasses(registros);
}
private static void criaClasses(List<RegistroTest> registros) {
for (RegistroTest registroTest : registros) {
UnitSourceGenerator gerador = UnitSourceGenerator.create(PACOTE);
ClassSourceGenerator registro = ClassSourceGenerator
.create(TypeDeclarationSourceGenerator.create(PREFIXO + registroTest.getNome()))
.addModifier(Modifier.PUBLIC)
.addAnnotation(AnnotationSourceGenerator.create(Getter.class))
.addAnnotation(AnnotationSourceGenerator.create(Setter.class))
.addAnnotation(AnnotationSourceGenerator.create(NoArgsConstructor.class))
.addAnnotation(AnnotationSourceGenerator.create(ToString.class))
.addAnnotation(AnnotationSourceGenerator.create(Entity.class))
.addAnnotation(AnnotationSourceGenerator.create(Table.class)
.addParameter("name",
VariableSourceGenerator.create(String.format("\"%s\"",
registroTest.getNomeTabelaBd())))
.addParameter("schema", VariableSourceGenerator
.create(String.format("\"%s\"", SCHEMA))));
criaColunas(registroTest.getCampos(), registro);
registro.addConstructor(FunctionSourceGenerator.create().addModifier(Modifier.PUBLIC)
.addParameter(VariableSourceGenerator.create(String.class, "linha"))
.addBodyCodeLine("super(linha);")).expands(Registro.class);
gerador.addClass(registro);
// System.out.println("\nRegistro gerado:\n" + gerador.make());
String caminhoPastaRegistros = System.getProperty("user.dir") + "/src/main/java/";
gerador.storeToClassPath(caminhoPastaRegistros);
}
}
private static void criaColunas(List<Campo> campos, ClassSourceGenerator registro) {
for (Campo campo : campos) {
VariableSourceGenerator field = VariableSourceGenerator
.create(TypeDeclarationSourceGenerator.create(String.class),
campo.getNomeAtributo())
.addModifier(Modifier.PRIVATE)
.addAnnotation(AnnotationSourceGenerator.create(Column.class)
.addParameter("name", VariableSourceGenerator
.create(String.format("\"%s\"", campo.getNome())))
)
.addAnnotation(AnnotationSourceGenerator.create(Indice.class).addParameter(
"valor",
VariableSourceGenerator.create(String.valueOf(campo.getSequencial()))));
if (campo.getNome().equals("ID")) {
field.addAnnotation(AnnotationSourceGenerator.create(Id.class));
}
if (campo.getEId()) {
field.addAnnotation(AnnotationSourceGenerator.create(CampoTipoId.class));
}
if (campo.getEData()) {
field.addAnnotation(AnnotationSourceGenerator.create(CampoTipoData.class));
}
if (campo.getEDataPart()) {
field.addAnnotation(AnnotationSourceGenerator.create(CampoTipoDataPart.class));
}
registro.addField(field);
}
}
}
I'm aware of the performance drawback of reflection but for my little project I needed this and I created a project lib which converts JSON to Java and then finally .class in JVM context.
Anyone need such thing can have a look into my open source solution, which requires JDK to compile the code.
https://medium.com/#davutgrbz/the-need-history-c91c9d38ec9?sk=f076487e78a1ff5a66ef8eb1aa88f930
Related
I am working on a Java class that contains a ton of numeric fields. Most of them would begin with something like 'CMTH' or 'FYTD'. Is it possible to initialize all fields of the same type that begin or end with a certain value. For example I have the following fields:
CMthRepCaseACR CMthRepUnitACR CMthRecCaseACR CMthRecUnitACR CMthHecCaseACR CMthHecUnitACR FYTDHecCaseACR FYTDHecUnitACR CMthBBKCaseACR CMthBBKUnitACR CMthPIHCaseACR .
I am trying to figure if it is possible to initialize all fields to zero that end with an 'ACR' or begin with an 'Cmth"
I know I can do something like cmtha = cmthb = cmthc = 0 but I was wondering there was a command where you can some kind of mask to initialize
Thanks
Assuming that you cannot change that said Java class (and e.g. use a collection or map to store the values) your best bet is probably reflection (see also: Trail: The Reflection API). Reflection gives you access to all fields of the class and you can then implement whatever matching you'd like.
Here's a short demo to get you started, minus error handling, sanity checks and adaptions to your actual class:
import java.util.stream.Stream;
public class Demo {
private static class DemoClass {
private int repCaseACR = 1;
private int CMthRepUnit = 2;
private int foo = 3;
private int bar = 4;
#Override
public String toString() {
return "DemoClass [repCaseACR=" + repCaseACR + ", CMthRepUnit=" + CMthRepUnit + ", foo=" + foo + ", bar="
+ bar + "]";
}
}
public static void main(String[] args) {
DemoClass demoClass = new DemoClass();
System.out.println("before: " + demoClass);
resetFields(demoClass, "CMth", null);
System.out.println("after prefix reset: " + demoClass);
resetFields(demoClass, null, "ACR");
System.out.println("after suffix reset: " + demoClass);
}
private static void resetFields(DemoClass instance, String prefix, String suffix) {
Stream.of(instance.getClass().getDeclaredFields())
.filter(field ->
(prefix != null && field.getName().startsWith(prefix))
|| (suffix != null && field.getName().endsWith(suffix)))
.forEach(field -> {
field.setAccessible(true);
try {
field.set(instance, 0);
} catch (IllegalArgumentException | IllegalAccessException e) {
// TODO handle me
}
});
}
}
Output:
before: DemoClass [repCaseACR=1, CMthRepUnit=2, foo=3, bar=4]
after prefix reset: DemoClass [repCaseACR=1, CMthRepUnit=0, foo=3, bar=4]
after suffix reset: DemoClass [repCaseACR=0, CMthRepUnit=0, foo=3, bar=4]
Note: Both links are seriously dated but the core functionality of reflection is still the same.
I am trying to solve this problem for an UDF I am creating for hiveql environment.
public ObjectInspector initialize(ObjectInspector[] arguments)
throws UDFArgumentException {
if (arguments.length != 1) {
throw new UDFArgumentException("Usage : multiple_prop(primitive var) ");
}
// This will be an string
moi = (PrimitiveObjectInspector) arguments[0];
ArrayList structFieldNames = new ArrayList();
ArrayList structFieldObjectInspectors = new ArrayList();
structFieldNames.add("fields name"); <-- Issue is here
How could I do to get the field name in there? It can be easily done for structObjectInspectors, but how do we manage this in PrimitiveObjectInspectors?
Complete code would be this one
public class prop_step2 extends GenericUDF {
private PrimitiveObjectInspector moi;
#Override
public ObjectInspector initialize(ObjectInspector[] arguments)
throws UDFArgumentException {
if (arguments.length != 1) {
throw new UDFArgumentException("Usage : multiple_prop(primitive var) ");
}
// This will be an string
moi = (PrimitiveObjectInspector) arguments[0];
ArrayList structFieldNames = new ArrayList();
ArrayList structFieldObjectInspectors = new ArrayList();
// Change this to get the input variable name, and not the type name
structFieldNames.add(moi.getTypeName());<-- Change this to field name
structFieldObjectInspectors.add( PrimitiveObjectInspectorFactory.writableStringObjectInspector );
return ObjectInspectorFactory.getStandardStructObjectInspector(structFieldNames, structFieldObjectInspectors);
}
#Override
public Object evaluate(DeferredObject[] arguments) throws HiveException {
Object[] result;
result = new Object[1];
Text elem1 = new Text((String) moi.getPrimitiveJavaObject(arguments[0].get()));
result[0]= elem1;
return result;
}
#Override
public String getDisplayString(String[] children) {
return "stop";
}}
When this would be finished, i would like to call this udf from hive:
CREATE TEMPORARY FUNCTION step AS 'UDFpack.prop_step2';
select
step(bit) as sd
from my_table
And i would expect that if in an upper select i did this : sd.bit i would obtain the value of 'bit'.
It's simply not possible. The information passed to the UDF - the ObjectInspectors - do not contain their name. That's why you can see the output column names being changed to _col0, _col1 .. in the intermediary stages of a Hive explain plan. I am also quite annoyed by this and think this is an oversight by Hive.
A workaround would be to put your input into a struct and parse that.
i.e step(named_struct('bit',bit)) and then you can get the field name of the struct in your UDF. But it's not nearly as nice
so as part of some work I've been doing I was given a file with WebServices that are being used in a Swift application. I have zero familiarity with WebServices and only know Java through syntax understanding. I need to call one of these gets with a parameter from the swift application. What I'm trying to figure out first and foremost is how I can call one of these webservices with a parameter from the URL it's associated with. For example down below I want to call the method
http://localhost:9000/ListVehicleByPlateNumber
and I want to specify the parameter through the URL say something like
http://localhost:9000/ListVehicleByPlateNumber?para="123"
But this doesn't assign any value to the parameter and I'm not getting results. If I hardcode so that the string used in the function is = "123" it gives me the results I'm looking for. I just need to know how I can pass this parameter through the url, syntax-wise.
Routes file
GET /ListVehicleByPlateNumber controllers.NewVehicle.listVehicleByPlateNumber(para: String ?="")
Controller
public Result listVehicleByPlateNumber(String para){
NewVehicleModel v = new NewVehicleModel();
List<NewVehicleModel> vehiclesC = v.searchByPlateVehicle(para);
ObjectNode wrapper = Json.newObject();
ObjectNode msg = Json.newObject();
if(vehiclesC != null) {
msg.set("VehicleList", toJson(vehiclesC));
wrapper.set("success", msg);
return ok(wrapper);
}else{
msg.put("error", "There are no vehicles with the plate number");
wrapper.set("error", msg);
return badRequest(wrapper);
}
}
Where it's called
public List<NewVehicleModel> searchByPlateVehicle(String plateNumber){
Transaction t = Ebean.beginTransaction();
List<NewVehicleModel> vehicles = new ArrayList<>();
try {
String sql = "SELECT V.idNewVehicle, V.VehicleType,V.PlateNumber,V.VehicleJurisdiction,V.State,V.Vin,V.Year, " +
"V.Make,V.modelos,V.RegistrationNumber,V.InsuranceCompany,V.PurchaseDate,V.ExpirationDate,V.idPersonaFK " +
"FROM NewVehicle V " +
"WHERE V.PlateNumber = :plateNumber";
RawSql rawSql = RawSqlBuilder.parse(sql)
.columnMapping("V.idNewVehicle", "idNewVehicle")
.columnMapping("V.State", "state")
.columnMapping("V.VehicleType", "vehicleType")
.columnMapping("V.PlateNumber", "plateNumber")
.columnMapping("V.VehicleJurisdiction", "vehicleJurisdiction")
.columnMapping("V.Vin", "vin")
.columnMapping("V.Year", "year")
.columnMapping("V.Make", "make")
.columnMapping("V.modelos", "modelos")
.columnMapping("V.RegistrationNumber", "registrationNumber")
.columnMapping("V.InsuranceCompany", "insuranceCompany")
.columnMapping("V.PurchaseDate", "purchaseDate")
.columnMapping("V.ExpirationDate", "expirationDate")
.columnMapping("V.idPersonaFK", "idPersonaFK")
.create();
Query<NewVehicleModel> query = Ebean.find(NewVehicleModel.class);
query.setRawSql(rawSql)
.setParameter("plateNumber", plateNumber);
vehicles = query.findList();
t.commit();
}
catch (Exception e){
System.out.println(e.getMessage());
}finally {
t.end();
}
return vehicles;
}
Found my own answer. I ended up casting from Integer to String here's how it looks in routes
GET /ListVehicleByPlateNumber/:para controllers.NewVehicle.listVehicleByPlateNumber(para: Integer )
Controller
public Result listVehicleByPlateNumber(int para){
String p = String.valueOf(para);
URI Format for value 123 example.
http://localhost:9000/ListVehicleByPlateNumber/123
So I am working on a solution right now wherein we have 2 requirements:
Format SSN / Telephone Number in Hyphen form which is otherwise
currently being displayed without it.
Format an amount field in the format "$0.00".
Currently we have written a method formatAsHyphen and formatAmount as below:
/**
* This method converts the given string into US format SSN / Telephone number
* #param valueToFormat
* #param fieldToFormat , It should be either 'S' for SSN and 'T' for Mobile Number
* #return
*/
public String formatWithHyphen (String valueToFormat, String fieldToFormat) {
if(valueToFormat != null && valueToFormat.length() > 1) {
StringBuilder formattedValue = new StringBuilder(valueToFormat);
if(fieldToFormat.equalsIgnoreCase("S")) {
//format as SSN
formattedValue = formattedValue.insert(3, '-').insert(6, '-');
} else if(fieldToFormat.equalsIgnoreCase("T")) {
//format as telephone number
formattedValue = formattedValue.insert(3, '-').insert(7, '-');
}
return formattedValue.toString();
}
else {
return null;
}
}
/**
* This method converts a given amount string to a US $ formatted amount.
*
* #param amountToFormat
* #return
*/
public String formatAmount(String amountToFormat) {
try {
if(amountToFormat!=null && amountToFormat.length() > 0) {
Locale locale = new Locale("en", "US");
NumberFormat formatter = NumberFormat.getCurrencyInstance(locale);
return formatter.format(Double.parseDouble(amountToFormat));
}
else {
return null;
}
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
} catch (IllegalArgumentException iae) {
iae.printStackTrace();
}
return null;
}
Now the issue is:
There are multiple pojo classes (TempAssist, SuppNutrition, ChildCare etc) which has the field related to
Amount and SSN / Telephone number
When we get those fields from database, firstly, the unformatted data is
set in the corresponding setters and then in the UI layer, we get
value through getter() and apply the above 2 functions to it and then
finally respond to the client in JSON format.
Its not a clean solution as set happens twice and the code is literally bloated with GET and SET's.
What I am looking for:
An Annotation (for instance, #Format(type="ssn") which I can apply on POJO fields which will ensure that whichever fields are annotated will have SSN updated with hyphen.
This is a web application which does not use Spring framework so any suggestions on Spring cannot be implemented.
Create a class extending JsonSerializer and then on your getter use the #JsonSerialize(using=MySerializer.class) annotation
One of the serializer could be something like:
public class MySerializer extends JsonSerializer<String> {
#Override
public void serialize( String value
, JsonGenerator jgen
, SerializerProvider provider) throws IOException
, JsonProcessingException {
jgen.writeString(MyUtilsClass.formatWithHyphen(value) );
}
}
I'm trying to create a validate java class that receives 4 inputs from an object passed as 1 from the requester. The class needs to convert float inputs to string and evaluate each input to meet a certain format and then throw exceptions complete with error message and code when it fails.
What I have is in two methods and would like to know if there is a better way to combine these two classes into one validate method for the main class to call. I don't seem to be able to get around using the pattern/matcher concept to insure the inputs are formatted correctly. Any help you can give would be very much appreciated.
public class Validator {
private static final String MoneyPattern ="^\\d{1,7}(\\.\\d{1,2})$" ;
private static final String PercentagePattern = "^\\d{1,3}\\.\\d{1,2}$";
private static final String CalendarYearPattern = "^20[1-9][0-9]$";
private int errorcode = 0;
private String errormessage = null;
public Validator(MyInput input){
}
private boolean verifyInput(){
String Percentage = ((Float) input.getPercentage().toString();
String Income = ((Float) input.getIncome().toString();
String PublicPlan = ((Float) input.getPublicPlan().toString();
String Year = ((Float) input.getYear();
try {
if (!doesMatch(Income, MoneyPattern)) {
errormessage = errormessage + "income,";
}
if (!doesMatch(PublicPlan, MoneyPattern)) {
errormessage = errormessage + "insurance plan,";
}
if (!doesMatch(Percentage, PercentagePattern)) {
errormessage = errormessage + "Percentage Plan,";
}
if (!doesMatch(Year, CalendarYearPattern)) {
errormessage = errormessage + "Year,";
}
} catch (Exception e){
errorcode = 111;
errormessage = e.getMessage();
}
}
private boolean doesMatch(String s, String pattern) throws Exception{
try {
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(s);
if (!s.equals("")){
if(m.find()){
return true;
} else {
return false;
}
}else {
return false;
}
} catch (PatternSyntaxException pse){
errorcode = 111;
errormessage = pse.getMessage();
}
}
}
This code is borked from the word "go". You have a constructor into which you pass a MyInput reference, but there's no code in the ctor and no private data member to receive it. It looks like you expect to use input in your doesMatch() method, but it's a NullPointerException waiting to happen.
Your code doesn't follow the Sun Java coding standards; variable names should be lower case.
Why you wouldn't do that input validation in the ctor, when you actually receive the value, is beyond me. Perhaps you really meant to pass it into that verifyInput() method.
I would worry about correctness and readability before concerning myself with efficiency.
I'd have methods like this:
public boolean isValidMoney(String money) { // put the regex here }
public boolean isValidYear(String year) { // the regex here }
I think I'd prefer a real Money class to a String. There's no abstraction whatsoever.
Here's one bit of honesty:
private static final String CalendarYearPattern = "^20[1-9][0-9]$";
I guess you either don't think this code will still be running in the 22nd century or you won't be here to maintain it.
One way of doing this would be with DynamicBeans.
package com.acme.validator;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.beanutils.PropertyUtils;
public class Validator {
//A simple optimisation of the pattern
private static final Pattern MoneyPattern = Pattern.compile("^\\d{1,7}(\\.\\d{1,2})$");
private static final Pattern PercentagePattern = Pattern.compile("^\\d{1,3}\\.\\d{1,2}$");
private static final Pattern CalendarYearPattern = Pattern.compile("^20[1-9][0-9]$");
public String Validator(MyInput input) {
String errormessage = "";
/*
* Setting these up as Maps.
* Ideally this would be a 'simple bean'
* but that goes beyond the scope of the
* original question
*/
Map<String,Pattern> patternMap = new HashMap<String,Pattern>();
patternMap.put("percentage", PercentagePattern);
patternMap.put("publicPlan", MoneyPattern);
patternMap.put("income", MoneyPattern);
patternMap.put("year", CalendarYearPattern);
Map<String,String> errorMap = new HashMap<String,String>();
errorMap.put("percentage", "Percentage Plan,");
errorMap.put("publicPlan", "insurance plan,");
errorMap.put("income", "income,");
errorMap.put("year", "Year,");
for (String key : patternMap.keySet()) {
try {
String match = ((Float) PropertyUtils.getSimpleProperty(input, key)).toString();
Matcher m = patternMap.get(key).matcher(match);
if ("".equals(match) || !m.find()) {
errormessage = errormessage + errorMap.get(key);
}
} catch (Exception e) {
errormessage = e.getMessage(); //since getMessage() could be null, you need to work out some way of handling this in the response
//don't know the point of the error code so remove this altogether
break; //Assume an exception trumps any validation failure
}
}
return errormessage;
}
}
I've made a few assumptions about the validation rules (for simplicity used 2 maps but you could also use a single map and a bean containing both the Pattern and the Message and even the 'error code' if that is important).
The key 'flaw' in your original setup and what would hamper the solution above, is that you are using 'year' as Float in the input bean.
(new Float(2012)).toString()
The above returns "2012.0". This will always fail your pattern. When you start messing about with the different types of objects potentially in the input bean, you may need to consider ensuring they are String at the time of creating the input bean and not, as is the case here, when they are retrieved.
Good Luck with the rest of your Java experience.