From 4d93067d1a7c74b19cfd702166542e712b2af2c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Saulo=20Jos=C3=A9=20Gil?= Date: Sat, 6 Nov 2021 03:50:14 -0300 Subject: [PATCH] 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 --- .../adempiere/base/annotation/Parameter.java | 51 ++++++++++ .../src/org/compiere/process/SvrProcess.java | 97 +++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 org.adempiere.base/src/org/adempiere/base/annotation/Parameter.java diff --git a/org.adempiere.base/src/org/adempiere/base/annotation/Parameter.java b/org.adempiere.base/src/org/adempiere/base/annotation/Parameter.java new file mode 100644 index 0000000000..d902560553 --- /dev/null +++ b/org.adempiere.base/src/org/adempiere/base/annotation/Parameter.java @@ -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:
+ * [1] If the parameter annotation has a name, then it must match exactly the process parameter + * metadata definition. For example:
+ * @Parameter(name="C_BPartner_ID") int foo will fill foo with + * the value of the parameter named C_BPartner_ID.
+ * [2] Class fields with the p_ prefix will be matched automatically. Example:
+ * @Parameter Integer p_C_BPartner_ID will match a parameter named C_BPartner_ID. + * [3] Fields with their names matching metadata names after stripping the "_" character. Example: + * @Parameter Integer cBPartnerId will match a parameter named C_BPartner_ID. + * [4] Fields with their names matching exactly their metadata names. Example: + * @Parameter Integer C_BPartner_ID will match a parameter named C_BPartner_ID. + * @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 ""; +} diff --git a/org.adempiere.base/src/org/compiere/process/SvrProcess.java b/org.adempiere.base/src/org/compiere/process/SvrProcess.java index 89ff2ff75b..2e5e4f6075 100644 --- a/org.adempiere.base/src/org/compiere/process/SvrProcess.java +++ b/org.adempiere.base/src/org/compiere/process/SvrProcess.java @@ -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 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 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