IDEMPIERE-5025 Easier usage of process parameters (#963)
* IDEMPIERE-5025 Easier usage of process parameters Created @Parameter annotation in order to tag process class attributes as parameters and have them to have their values assigned automatically without extra code. * Minor changes
This commit is contained in:
parent
96f34baad4
commit
4d93067d1a
|
@ -0,0 +1,51 @@
|
|||
/******************************************************************************
|
||||
* Product: iDempiere ERP & CRM Smart Business Solution *
|
||||
* This program is free software; you can redistribute it and/or modify it *
|
||||
* under the terms version 2 of the GNU General Public License as published *
|
||||
* by the Free Software Foundation. This program is distributed in the hope *
|
||||
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
|
||||
* See the GNU General Public License for more details. *
|
||||
* You should have received a copy of the GNU General Public License along *
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., *
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
|
||||
*****************************************************************************/
|
||||
package org.adempiere.base.annotation;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.compiere.model.X_AD_Process_Para;
|
||||
import org.compiere.process.SvrProcess;
|
||||
|
||||
/**
|
||||
* Tags a process class field as a process parameter in order to have its value set automatically.
|
||||
* Class fields are matched against process parameters using the following heuristics: <br>
|
||||
* [1] If the parameter annotation has a name, then it must match exactly the process parameter
|
||||
* metadata definition. For example: <br>
|
||||
* <code>@Parameter(name="C_BPartner_ID") int foo</code> will fill <code>foo</code> with
|
||||
* the value of the parameter named <code>C_BPartner_ID</code>. <br>
|
||||
* [2] Class fields with the <code>p_</code> prefix will be matched automatically. Example: <br>
|
||||
* <code>@Parameter Integer p_C_BPartner_ID</code> will match a parameter named <code>C_BPartner_ID</code>.
|
||||
* [3] Fields with their names matching metadata names after stripping the "_" character. Example:
|
||||
* <code>@Parameter Integer cBPartnerId</code> will match a parameter named <code>C_BPartner_ID</code>.
|
||||
* [4] Fields with their names matching exactly their metadata names. Example:
|
||||
* <code>@Parameter Integer C_BPartner_ID</code> will match a parameter named <code>C_BPartner_ID</code>.
|
||||
* @see SvrProcess
|
||||
* @author Saulo Gil
|
||||
*/
|
||||
@Target(FIELD)
|
||||
@Retention(RUNTIME)
|
||||
public @interface Parameter {
|
||||
|
||||
/**
|
||||
* Optional parameter name matching its metadata definition.
|
||||
* @see X_AD_Process_Para#getColumnName()
|
||||
* @return
|
||||
*/
|
||||
String name() default "";
|
||||
}
|
|
@ -16,17 +16,25 @@
|
|||
*****************************************************************************/
|
||||
package org.compiere.process;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.adempiere.base.annotation.Parameter;
|
||||
import org.adempiere.base.event.EventManager;
|
||||
import org.adempiere.base.event.EventProperty;
|
||||
import org.adempiere.base.event.IEventManager;
|
||||
|
@ -234,6 +242,7 @@ public abstract class SvrProcess implements ProcessCall
|
|||
boolean success = true;
|
||||
try
|
||||
{
|
||||
autoFillParameters();
|
||||
prepare();
|
||||
|
||||
// event before process
|
||||
|
@ -719,4 +728,92 @@ public abstract class SvrProcess implements ProcessCall
|
|||
processUI.statusUpdate(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to initialize class fields having the {@link Parameter} annotation
|
||||
* with the values received by this process instance.
|
||||
*/
|
||||
private void autoFillParameters(){
|
||||
Map<String,Field> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
// detects annotated fields in this class and its super classes
|
||||
Class<?> target = getClass();
|
||||
while(target != null && !target.equals(SvrProcess.class)) {
|
||||
for (Field field: getFieldsWithParameters(target)) {
|
||||
field.setAccessible(true);
|
||||
Parameter pa = field.getAnnotation(Parameter.class);
|
||||
if(map.containsValue(field))
|
||||
continue;
|
||||
String name = pa.name().isEmpty() ? field.getName() : pa.name();
|
||||
map.put(name.toLowerCase(), field);
|
||||
}
|
||||
target = target.getSuperclass();
|
||||
}
|
||||
|
||||
if(map.size()==0)
|
||||
return;
|
||||
|
||||
for(ProcessInfoParameter parameter : getParameter()){
|
||||
String name = parameter.getParameterName().trim().toLowerCase();
|
||||
Field field = map.get(name);
|
||||
Field toField = map.containsKey(name + "_to") ? map.get(name + "_to") : null;
|
||||
|
||||
// try to match fields using the "p_" prefix convention
|
||||
if(field==null) {
|
||||
String candidate = "p_" + name;
|
||||
field = map.get(candidate);
|
||||
toField = map.containsKey(candidate + "_to") ? map.get(candidate + "_to") : null;
|
||||
}
|
||||
|
||||
// try to match fields with same name as metadata declaration after stripping "_"
|
||||
if(field==null) {
|
||||
String candidate = name.replace("_", "");
|
||||
field = map.get(candidate);
|
||||
toField = map.containsKey(candidate + "to") ? map.get(candidate + "to") : null;
|
||||
}
|
||||
|
||||
if(field==null)
|
||||
continue;
|
||||
|
||||
Type type = field.getType();
|
||||
try{
|
||||
if (type.equals(Integer.TYPE) || type.equals(Integer.class)) {
|
||||
field.set(this, parameter.getParameterAsInt());
|
||||
if(parameter.getParameter_To()!=null && toField != null)
|
||||
toField.set(this, parameter.getParameter_ToAsInt());
|
||||
} else if (type.equals(String.class)) {
|
||||
field.set(this, (String) parameter.getParameter());
|
||||
} else if (type.equals(java.sql.Timestamp.class)) {
|
||||
field.set(this, (Timestamp) parameter.getParameter());
|
||||
if(parameter.getParameter_To()!=null && toField != null)
|
||||
toField.set(this, (Timestamp) parameter.getParameter_To());
|
||||
} else if (type.equals(BigDecimal.class)) {
|
||||
field.set(this, (BigDecimal) parameter.getParameter());
|
||||
} else if (type.equals(boolean.class) || type.equals(Boolean.class)) {
|
||||
Object tmp = parameter.getParameter();
|
||||
if(tmp instanceof String && tmp != null)
|
||||
field.set(this, "Y".equals(tmp));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}catch(Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find all class attributes having the {@link Parameter} annotation.
|
||||
* @param clazz
|
||||
* @return a list of annotated fields
|
||||
*/
|
||||
private List<Field> getFieldsWithParameters(Class<?> clazz) {
|
||||
if (clazz != null)
|
||||
return Arrays.stream(clazz.getDeclaredFields())
|
||||
.filter(f -> f.getAnnotation(Parameter.class) != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
} // SvrProcess
|
||||
|
|
Loading…
Reference in New Issue