IDEMPIERE-5015 add org.adempiere.base.annotation.Callout annotation (#948)

This commit is contained in:
hengsin 2021-10-27 18:15:58 +08:00 committed by GitHub
parent c43cefd0c9
commit 22c09368e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1053 additions and 257 deletions

View File

@ -13,3 +13,4 @@ Export-Package: org.compiere.model
Bundle-ClassPath: .
Automatic-Module-Name: org.adempiere.base.callout
Bundle-Vendor: iDempiere Community
Import-Package: org.osgi.service.component.annotations;version="1.3.0"

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.adempiere.base.callout.CostAdjustmentCalloutFactory">
<implementation class="org.adempiere.base.callout.CostAdjustmentCalloutFactory"/>
<service>
<provide interface="org.adempiere.base.IColumnCalloutFactory"/>
</service>
</scr:component>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.adempiere.base.callout.factory.AnnotationBasedColumnCalloutFactoryImpl">
<service>
<provide interface="org.adempiere.base.IColumnCalloutFactory"/>
</service>
<implementation class="org.adempiere.base.callout.factory.AnnotationBasedColumnCalloutFactoryImpl"/>
</scr:component>

View File

@ -1,5 +1,4 @@
bin.includes = META-INF/,\
OSGI-INF/costadjustmentcalloutfactory.xml,\
OSGI-INF/,\
.
output.. = target/classes/

View File

@ -1,30 +0,0 @@
/******************************************************************************
* Copyright (C) 2013 Diego Ruiz *
* Copyright (C) 2013 Trek Global *
* 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.callout;
import org.adempiere.base.IColumnCallout;
import org.adempiere.base.IColumnCalloutFactory;
import org.adempiere.model.CalloutBPartnerQuickEntry;
import org.compiere.model.MBPartner;
public class BPartnerCalloutFactory implements IColumnCalloutFactory {
@Override
public IColumnCallout[] getColumnCallouts(String tableName, String columnName) {
if (tableName.equalsIgnoreCase(MBPartner.Table_Name)) {
return new IColumnCallout[]{new CalloutBPartnerQuickEntry()};
}
return null;
}
}

View File

@ -1,186 +0,0 @@
/******************************************************************************
* Copyright (C) 2013 Heng Sin Low *
* Copyright (C) 2013 Trek Global *
* 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.callout;
import java.math.BigDecimal;
import java.util.Properties;
import org.adempiere.base.IColumnCallout;
import org.adempiere.base.IColumnCalloutFactory;
import org.compiere.model.GridField;
import org.compiere.model.GridTab;
import org.compiere.model.GridTable;
import org.compiere.model.I_M_InventoryLine;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MClient;
import org.compiere.model.MCost;
import org.compiere.model.MCostElement;
import org.compiere.model.MDocType;
import org.compiere.model.MInventory;
import org.compiere.model.MProduct;
import org.compiere.util.Env;
import org.compiere.util.Msg;
/**
* @author hengsin
*
*/
public class CostAdjustmentCalloutFactory implements IColumnCalloutFactory {
/**
*
*/
public CostAdjustmentCalloutFactory() {
}
/* (non-Javadoc)
* @see org.adempiere.base.IColumnCalloutFactory#getColumnCallouts(java.lang.String, java.lang.String)
*/
@Override
public IColumnCallout[] getColumnCallouts(String tableName,
String columnName) {
if (tableName.equalsIgnoreCase(I_M_InventoryLine.Table_Name)) {
if (columnName.equalsIgnoreCase(I_M_InventoryLine.COLUMNNAME_M_Product_ID))
return new IColumnCallout[]{new CostAdjustmentLineProduct()};
else if (columnName.equalsIgnoreCase(I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID))
return new IColumnCallout[]{new CostAdjustmentLineASI()};
}
return null;
}
/**
* callout for m_product_id
*/
private static class CostAdjustmentLineProduct implements IColumnCallout {
@Override
public String start(Properties ctx, int WindowNo, GridTab mTab,
GridField mField, Object value, Object oldValue) {
String trxName = null;
if ( mTab != null
&& mTab.getTableModel() != null) {
GridTable gt = mTab.getTableModel();
if (gt.isImporting()) {
trxName = gt.get_TrxName();
}
}
MInventory inventory = new MInventory(ctx, (Integer) mTab.getValue("M_Inventory_ID"), trxName);
if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(inventory.getC_DocType().getDocSubTypeInv())) {
String costingMethod = inventory.getCostingMethod();
if (value == null) {
mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, BigDecimal.ZERO);
mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, BigDecimal.ZERO);
} else {
MProduct product = MProduct.get(ctx, (Integer) value);
MClient client = MClient.get(ctx);
MAcctSchema as = client.getAcctSchema();
String costingLevel = product.getCostingLevel(as);
if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(costingLevel)) {
mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, BigDecimal.ZERO);
mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, BigDecimal.ZERO);
}else {
Object asiValue = mTab.getValue(I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID);
int M_ASI_ID = asiValue != null ? (Integer)asiValue : 0;
int AD_Org_ID = inventory.getAD_Org_ID();
int C_Currency_ID = inventory.getC_Currency_ID();
if (as.getC_Currency_ID() != C_Currency_ID)
{
MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(ctx, client.get_ID());
for (int i = 0; i < ass.length ; i ++)
{
MAcctSchema a = ass[i];
if (a.getC_Currency_ID() == C_Currency_ID)
as = a ;
}
}
MCost cost = product.getCostingRecord(as, AD_Org_ID, M_ASI_ID, costingMethod);
if (cost == null) {
if (!MCostElement.COSTINGMETHOD_StandardCosting.equals(costingMethod)) {
mTab.setValue(mField, null);
return Msg.getMsg(Env.getCtx(), "NoCostingRecord");
}
}
mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, cost.getCurrentCostPrice());
mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, cost.getCurrentCostPrice());
}
}
}
return null;
}
}
/**
* callout for m_attributesetinstance_id
*/
private class CostAdjustmentLineASI implements IColumnCallout {
@Override
public String start(Properties ctx, int WindowNo, GridTab mTab,
GridField mField, Object value, Object oldValue) {
String trxName = null;
if ( mTab != null
&& mTab.getTableModel() != null) {
GridTable gt = mTab.getTableModel();
if (gt.isImporting()) {
trxName = gt.get_TrxName();
}
}
MInventory inventory = new MInventory(ctx, (Integer) mTab.getValue("M_Inventory_ID"), trxName);
if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(inventory.getC_DocType().getDocSubTypeInv())) {
String costingMethod = inventory.getCostingMethod();
Object productValue = mTab.getValue(I_M_InventoryLine.COLUMNNAME_M_Product_ID);
if (productValue == null || ((Integer)productValue).intValue() == 0) return null;
MProduct product = MProduct.get(ctx, (Integer)productValue);
int M_ASI_ID = value != null ? (Integer)value : 0;
int AD_Org_ID = inventory.getAD_Org_ID();
int C_Currency_ID = inventory.getC_Currency_ID();
MClient client = MClient.get(ctx);
MAcctSchema as = client.getAcctSchema();
if (as.getC_Currency_ID() != C_Currency_ID)
{
MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(ctx, client.get_ID());
for (int i = 0; i < ass.length ; i ++)
{
MAcctSchema a = ass[i];
if (a.getC_Currency_ID() == C_Currency_ID)
as = a ;
}
}
MCost cost = product.getCostingRecord(as, AD_Org_ID, M_ASI_ID, costingMethod);
if (cost == null) {
if (!MCostElement.COSTINGMETHOD_StandardCosting.equals(costingMethod)) {
mTab.setValue(mField, null);
return Msg.getMsg(Env.getCtx(), "NoCostingRecord");
}
}
if (cost != null) {
BigDecimal currentCost = (BigDecimal) mTab.getValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice);
if (currentCost == null || currentCost.compareTo(cost.getCurrentCostPrice())==0) return null;
mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, cost.getCurrentCostPrice());
mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, cost.getCurrentCostPrice());
}
}
return null;
}
}
}

View File

@ -0,0 +1,108 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* 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., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.base.callout;
import java.math.BigDecimal;
import java.util.Properties;
import org.adempiere.base.IColumnCallout;
import org.adempiere.base.annotation.Callout;
import org.compiere.model.GridField;
import org.compiere.model.GridTab;
import org.compiere.model.GridTable;
import org.compiere.model.I_M_InventoryLine;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MClient;
import org.compiere.model.MCost;
import org.compiere.model.MCostElement;
import org.compiere.model.MDocType;
import org.compiere.model.MInventory;
import org.compiere.model.MProduct;
import org.compiere.util.Env;
import org.compiere.util.Msg;
/**
*
* @author hengsin
*
*/
@Callout(tableName = I_M_InventoryLine.Table_Name, columnName = I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID)
public class CostAdjustmentLineASI implements IColumnCallout {
@Override
public String start(Properties ctx, int WindowNo, GridTab mTab,
GridField mField, Object value, Object oldValue) {
String trxName = null;
if ( mTab != null
&& mTab.getTableModel() != null) {
GridTable gt = mTab.getTableModel();
if (gt.isImporting()) {
trxName = gt.get_TrxName();
}
}
MInventory inventory = new MInventory(ctx, (Integer) mTab.getValue("M_Inventory_ID"), trxName);
if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(inventory.getC_DocType().getDocSubTypeInv())) {
String costingMethod = inventory.getCostingMethod();
Object productValue = mTab.getValue(I_M_InventoryLine.COLUMNNAME_M_Product_ID);
if (productValue == null || ((Integer)productValue).intValue() == 0) return null;
MProduct product = MProduct.get(ctx, (Integer)productValue);
int M_ASI_ID = value != null ? (Integer)value : 0;
int AD_Org_ID = inventory.getAD_Org_ID();
int C_Currency_ID = inventory.getC_Currency_ID();
MClient client = MClient.get(ctx);
MAcctSchema as = client.getAcctSchema();
if (as.getC_Currency_ID() != C_Currency_ID)
{
MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(ctx, client.get_ID());
for (int i = 0; i < ass.length ; i ++)
{
MAcctSchema a = ass[i];
if (a.getC_Currency_ID() == C_Currency_ID)
as = a ;
}
}
MCost cost = product.getCostingRecord(as, AD_Org_ID, M_ASI_ID, costingMethod);
if (cost == null) {
if (!MCostElement.COSTINGMETHOD_StandardCosting.equals(costingMethod)) {
mTab.setValue(mField, null);
return Msg.getMsg(Env.getCtx(), "NoCostingRecord");
}
}
if (cost != null) {
BigDecimal currentCost = (BigDecimal) mTab.getValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice);
if (currentCost == null || currentCost.compareTo(cost.getCurrentCostPrice())==0) return null;
mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, cost.getCurrentCostPrice());
mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, cost.getCurrentCostPrice());
}
}
return null;
}
}

View File

@ -0,0 +1,110 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* 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., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.base.callout;
import java.math.BigDecimal;
import java.util.Properties;
import org.adempiere.base.IColumnCallout;
import org.adempiere.base.annotation.Callout;
import org.compiere.model.GridField;
import org.compiere.model.GridTab;
import org.compiere.model.GridTable;
import org.compiere.model.I_M_InventoryLine;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MClient;
import org.compiere.model.MCost;
import org.compiere.model.MCostElement;
import org.compiere.model.MDocType;
import org.compiere.model.MInventory;
import org.compiere.model.MProduct;
import org.compiere.util.Env;
import org.compiere.util.Msg;
/**
*
* @author hengsin
*
*/
@Callout(tableName = I_M_InventoryLine.Table_Name, columnName = I_M_InventoryLine.COLUMNNAME_M_Product_ID)
public class CostAdjustmentLineProduct implements IColumnCallout {
@Override
public String start(Properties ctx, int WindowNo, GridTab mTab,
GridField mField, Object value, Object oldValue) {
String trxName = null;
if ( mTab != null
&& mTab.getTableModel() != null) {
GridTable gt = mTab.getTableModel();
if (gt.isImporting()) {
trxName = gt.get_TrxName();
}
}
MInventory inventory = new MInventory(ctx, (Integer) mTab.getValue("M_Inventory_ID"), trxName);
if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(inventory.getC_DocType().getDocSubTypeInv())) {
String costingMethod = inventory.getCostingMethod();
if (value == null) {
mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, BigDecimal.ZERO);
mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, BigDecimal.ZERO);
} else {
MProduct product = MProduct.get(ctx, (Integer) value);
MClient client = MClient.get(ctx);
MAcctSchema as = client.getAcctSchema();
String costingLevel = product.getCostingLevel(as);
if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(costingLevel)) {
mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, BigDecimal.ZERO);
mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, BigDecimal.ZERO);
}else {
Object asiValue = mTab.getValue(I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID);
int M_ASI_ID = asiValue != null ? (Integer)asiValue : 0;
int AD_Org_ID = inventory.getAD_Org_ID();
int C_Currency_ID = inventory.getC_Currency_ID();
if (as.getC_Currency_ID() != C_Currency_ID)
{
MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(ctx, client.get_ID());
for (int i = 0; i < ass.length ; i ++)
{
MAcctSchema a = ass[i];
if (a.getC_Currency_ID() == C_Currency_ID)
as = a ;
}
}
MCost cost = product.getCostingRecord(as, AD_Org_ID, M_ASI_ID, costingMethod);
if (cost == null) {
if (!MCostElement.COSTINGMETHOD_StandardCosting.equals(costingMethod)) {
mTab.setValue(mField, null);
return Msg.getMsg(Env.getCtx(), "NoCostingRecord");
}
}
mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, cost.getCurrentCostPrice());
mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, cost.getCurrentCostPrice());
}
}
}
return null;
}
}

View File

@ -0,0 +1,47 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* 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., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.base.callout.factory;
import org.adempiere.base.AnnotationBasedColumnCalloutFactory;
import org.adempiere.base.IColumnCalloutFactory;
import org.osgi.service.component.annotations.Component;
/**
*
* @author hengsin
*
*/
@Component(immediate = true, service = IColumnCalloutFactory.class)
public class AnnotationBasedColumnCalloutFactoryImpl extends AnnotationBasedColumnCalloutFactory {
public AnnotationBasedColumnCalloutFactoryImpl() {
}
@Override
protected String[] getPackages() {
return new String[] {"org.adempiere.base.callout","org.adempiere.model"};
}
}

View File

@ -16,12 +16,14 @@ package org.adempiere.model;
import java.util.Properties;
import org.adempiere.base.IColumnCallout;
import org.adempiere.base.annotation.Callout;
import org.compiere.model.GridField;
import org.compiere.model.GridTab;
import org.compiere.model.MBPartner;
import org.compiere.util.Env;
import org.compiere.util.Util;
@Callout(tableName = MBPartner.Table_Name, columnName = "*")
public class CalloutBPartnerQuickEntry implements IColumnCallout {
@Override

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.adempiere.base.callout.BPartnerCalloutFactory">
<implementation class="org.adempiere.base.callout.BPartnerCalloutFactory"/>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.adempiere.base.DefaultAnnotationBasedColumnCalloutFactory">
<service>
<provide interface="org.adempiere.base.IColumnCalloutFactory"/>
</service>
<implementation class="org.adempiere.base.DefaultAnnotationBasedColumnCalloutFactory"/>
</scr:component>

View File

@ -64,36 +64,6 @@
</run>
</application>
</extension>
<extension
id="org.adempiere.model.CalloutInfoWindow.element"
point="org.adempiere.base.IColumnCallout">
<callout
class="org.adempiere.model.CalloutInfoWindow"
columnName="AD_Element_ID"
priority="0"
tableName="AD_InfoColumn">
</callout>
</extension>
<extension
id="org.adempiere.model.CalloutInfoWindow.table"
point="org.adempiere.base.IColumnCallout">
<callout
class="org.adempiere.model.CalloutInfoWindow"
columnName="AD_Table_ID"
priority="0"
tableName="AD_InfoWindow">
</callout>
</extension>
<extension
id="org.adempiere.model.CalloutInfoWindow.reference"
point="org.adempiere.base.IColumnCallout">
<callout
class="org.adempiere.model.CalloutInfoWindow"
columnName="AD_Reference_ID"
priority="0"
tableName="AD_InfoColumn">
</callout>
</extension>
<extension
id="org.compiere.model.StandardTaxProvider"
name="Standard Tax Provider"

View File

@ -0,0 +1,251 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* 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., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.base;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import org.adempiere.base.annotation.Callout;
import org.adempiere.base.annotation.Callouts;
import org.compiere.util.CLogger;
import org.osgi.framework.BundleContext;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import io.github.classgraph.AnnotationInfo;
import io.github.classgraph.AnnotationInfoList;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfo;
import io.github.classgraph.ScanResult;
/**
*
* @author hengsin
*
*/
public abstract class AnnotationBasedColumnCalloutFactory implements IColumnCalloutFactory {
private final static CLogger s_log = CLogger.getCLogger(AnnotationBasedColumnCalloutFactory.class);
private BundleContext bundleContext;
private final Map<String, Map<String, List<String>>> tableNameMap = new HashMap<>();
private final Map<String, Constructor<?>[]> constructorCache = new ConcurrentHashMap<>();
public AnnotationBasedColumnCalloutFactory() {
}
@Override
public IColumnCallout[] getColumnCallouts(String tableName, String columnName) {
List<IColumnCallout> callouts = new ArrayList<IColumnCallout>();
ClassLoader classLoader = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader();
Map<String, List<String>> columnNameMap = tableNameMap.get(tableName);
if (columnNameMap != null) {
List<String> calloutClassNames = columnNameMap.get(columnName);
if (calloutClassNames != null) {
newCalloutInstance(callouts, classLoader, calloutClassNames);
}
calloutClassNames = columnNameMap.get("*");
if (calloutClassNames != null) {
newCalloutInstance(callouts, classLoader, calloutClassNames);
}
}
columnNameMap = tableNameMap.get("*");
if (columnNameMap != null) {
List<String> calloutClassNames = columnNameMap.get(columnName);
if (calloutClassNames != null) {
newCalloutInstance(callouts, classLoader, calloutClassNames);
}
calloutClassNames = columnNameMap.get("*");
if (calloutClassNames != null) {
newCalloutInstance(callouts, classLoader, calloutClassNames);
}
}
return callouts.toArray(new IColumnCallout[0]);
}
private void newCalloutInstance(List<IColumnCallout> callouts, ClassLoader classLoader,
List<String> calloutClassNames) {
for(String calloutClass : calloutClassNames) {
Constructor<?>[] constructors = constructorCache.get(calloutClass);
if (constructors == null) {
try {
Class<?> clazz = classLoader.loadClass(calloutClass);
Constructor<?> constructor = clazz.getDeclaredConstructor();
IColumnCallout columnCallout = (IColumnCallout) constructor.newInstance();
callouts.add(columnCallout);
constructors = new Constructor<?>[] {constructor};
constructorCache.put(calloutClass, constructors);
} catch (Exception e) {
s_log.log(Level.WARNING, e.getMessage(), e);
constructors = new Constructor<?>[0];
constructorCache.put(calloutClass, constructors);
}
} else if (constructors.length == 1){
try {
IColumnCallout columnCallout = (IColumnCallout) constructors[0].newInstance();
callouts.add(columnCallout);
} catch (Exception e) {
s_log.log(Level.WARNING, e.getMessage(), e);
constructors = new Constructor<?>[0];
constructorCache.put(calloutClass, constructors);
}
}
}
}
/**
* Subclasses must override this method in order to provide packages to
* scan, discover and register {@link IColumnCallout} classes
* @return array of packages to be accepted during class scanning
* @see ClassGraph#acceptPackagesNonRecursive(String...)
*/
protected abstract String[] getPackages();
@Activate
public void activate(ComponentContext context) throws ClassNotFoundException {
long start = System.currentTimeMillis();
bundleContext = context.getBundleContext();
ClassLoader classLoader = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader();
ClassGraph graph = new ClassGraph()
.enableAnnotationInfo()
.overrideClassLoaders(classLoader)
.disableNestedJarScanning()
.disableModuleScanning()
.acceptPackagesNonRecursive(getPackages());
try (ScanResult scanResult = graph.scan()) {
List<String> processed = new ArrayList<String>();
for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(Callouts.class)) {
if (classInfo.isAbstract())
continue;
String className = classInfo.getName();
AnnotationInfoList annotationInfos = classInfo.getAnnotationInfoRepeatable(Callout.class);
for(AnnotationInfo annotationInfo : annotationInfos) {
processAnnotation(className, annotationInfo);
}
processed.add(className);
}
for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(Callout.class)) {
if (classInfo.isAbstract())
continue;
String className = classInfo.getName();
if (processed.contains(className))
continue;
AnnotationInfo annotationInfo = classInfo.getAnnotationInfo(Callout.class);
processAnnotation(className, annotationInfo);
}
}
long end = System.currentTimeMillis();
if (s_log.isLoggable(Level.INFO))
s_log.info(this.getClass().getSimpleName() + " loaded "+tableNameMap.size() +" classes in "
+((end-start)/1000f) + "s");
}
private void processAnnotation(String className, AnnotationInfo annotationInfo) {
//not sure why but sometime ClassGraph return Object[] instead of the expected String[]
Object[] tableNames = (Object[]) annotationInfo.getParameterValues().getValue("tableName");
Object[] columnNames = (Object[]) annotationInfo.getParameterValues().getValue("columnName");
boolean matchAllTables = false;
for(Object tableName : tableNames) {
if ("*".equals(tableName) ) {
matchAllTables = true;
break;
}
}
boolean matchAllColumns = false;
for(Object columnName : columnNames) {
if ("*".equals(columnName)) {
matchAllColumns = true;
break;
}
}
//not allow to match everything
if (matchAllColumns && matchAllTables)
return;
Map<String, List<String>> columnNameMap = null;
if (matchAllTables) {
columnNameMap = tableNameMap.get("*");
if (columnNameMap == null) {
columnNameMap = new HashMap<String, List<String>>();
tableNameMap.put("*", columnNameMap);
}
if (matchAllColumns) {
addCallout(className, columnNameMap);
} else {
addCallout(className, columnNames, columnNameMap);
}
} else {
for(Object tableName : tableNames) {
columnNameMap = tableNameMap.get(tableName);
if (columnNameMap == null) {
columnNameMap = new HashMap<String, List<String>>();
tableNameMap.put((String)tableName, columnNameMap);
}
if (matchAllColumns) {
addCallout(className, columnNameMap);
} else {
addCallout(className, columnNames, columnNameMap);
}
}
}
}
private void addCallout(String className, Object[] columnNames, Map<String, List<String>> columnNameMap) {
for (Object columnName : columnNames) {
List<String> callouts = columnNameMap.get(columnName);
if (callouts == null ) {
callouts = new ArrayList<String>();
columnNameMap.put((String)columnName, callouts);
}
callouts.add(className);
}
}
private void addCallout(String className, Map<String, List<String>> columnNameMap) {
List<String> callouts = columnNameMap.get("*");
if (callouts == null ) {
callouts = new ArrayList<String>();
columnNameMap.put("*", callouts);
}
callouts.add(className);
}
}

View File

@ -0,0 +1,45 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* 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., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.base;
import org.osgi.service.component.annotations.Component;
/**
*
* @author hengsin
*
*/
@Component(immediate = true, service = IColumnCalloutFactory.class)
public class DefaultAnnotationBasedColumnCalloutFactory extends AnnotationBasedColumnCalloutFactory {
public DefaultAnnotationBasedColumnCalloutFactory() {
}
@Override
protected String[] getPackages() {
return new String[] {"org.adempiere.model"};
}
}

View File

@ -26,6 +26,8 @@ package org.adempiere.base;
import java.util.function.Supplier;
import org.osgi.framework.BundleContext;
/**
*
* @author hengsin
@ -49,4 +51,10 @@ public interface IMappedColumnCalloutFactory {
*/
public void removeMapping(String tableName, String columnName, Supplier<IColumnCallout> supplier);
/**
* scan, discover and register classes with Callout annotation
* @param context
* @param packages
*/
public void scan(BundleContext context, String... packages);
}

View File

@ -24,13 +24,26 @@
**********************************************************************/
package org.adempiere.base;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.function.Supplier;
import java.util.logging.Level;
import org.adempiere.base.annotation.Callout;
import org.adempiere.base.annotation.Callouts;
import org.compiere.util.CLogger;
import org.osgi.framework.BundleContext;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.component.annotations.Component;
import io.github.classgraph.AnnotationInfo;
import io.github.classgraph.AnnotationInfoList;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfo;
import io.github.classgraph.ScanResult;
/**
*
* @author hengsin
@ -42,6 +55,8 @@ import org.osgi.service.component.annotations.Component;
property = {"service.ranking:Integer=1"})
public class MappedColumnCalloutFactory implements IColumnCalloutFactory, IMappedColumnCalloutFactory {
private final static CLogger s_log = CLogger.getCLogger(MappedColumnCalloutFactory.class);
private final HashMap<String, List<Supplier<IColumnCallout>>> calloutMap = new HashMap<>();
/**
@ -101,4 +116,114 @@ public class MappedColumnCalloutFactory implements IColumnCalloutFactory, IMappe
}
}
}
@Override
public void scan(BundleContext context, String... packages) {
ClassLoader classLoader = context.getBundle().adapt(BundleWiring.class).getClassLoader();
ClassGraph graph = new ClassGraph()
.enableAnnotationInfo()
.overrideClassLoaders(classLoader)
.disableNestedJarScanning()
.disableModuleScanning()
.acceptPackagesNonRecursive(packages);
try (ScanResult scanResult = graph.scan()) {
List<String> processed = new ArrayList<String>();
for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(Callouts.class)) {
if (classInfo.isAbstract())
continue;
String className = classInfo.getName();
AnnotationInfoList annotationInfos = classInfo.getAnnotationInfoRepeatable(Callout.class);
for(AnnotationInfo annotationInfo : annotationInfos) {
processAnnotation(className, annotationInfo, classLoader);
}
processed.add(className);
}
for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(Callout.class)) {
if (classInfo.isAbstract())
continue;
String className = classInfo.getName();
if (processed.contains(className))
continue;
AnnotationInfo annotationInfo = classInfo.getAnnotationInfo(Callout.class);
processAnnotation(className, annotationInfo, classLoader);
}
}
}
private void processAnnotation(String className, AnnotationInfo annotationInfo, ClassLoader classLoader) {
Object[] tableNames = (Object[]) annotationInfo.getParameterValues().getValue("tableName");
Object[] columnNames = (Object[]) annotationInfo.getParameterValues().getValue("columnName");
boolean matchAllTables = false;
for(Object tableName : tableNames) {
if ("*".equals(tableName) ) {
matchAllTables = true;
break;
}
}
boolean matchAllColumns = false;
for(Object columnName : columnNames) {
if ("*".equals(columnName)) {
matchAllColumns = true;
break;
}
}
//not allow to match everything
if (matchAllColumns && matchAllTables)
return;
try {
Class<?> clazz = classLoader.loadClass(className);
Constructor<?> constructor = clazz.getDeclaredConstructor();
CalloutSupplier supplier = new CalloutSupplier(constructor);
if (matchAllTables) {
for(Object columnName : columnNames) {
addMapping("*", (String)columnName, supplier);
}
} else {
for(Object tableName : tableNames) {
if (matchAllColumns) {
addMapping((String)tableName, "*", supplier);
} else {
for(Object columnName : columnNames) {
addMapping((String)tableName, (String)columnName, supplier);
}
}
}
}
} catch (Exception e) {
if (s_log.isLoggable(Level.INFO))
s_log.log(Level.INFO, e.getMessage(), e);
}
}
private static class CalloutSupplier implements Supplier<IColumnCallout> {
private Constructor<?> constructor;
private CalloutSupplier(Constructor<?> constructor) {
this.constructor = constructor;
}
@Override
public IColumnCallout get() {
IColumnCallout callout = null;
if (constructor != null) {
try {
callout = (IColumnCallout) constructor.newInstance();
} catch (Exception e) {
constructor = null;
s_log.log(Level.WARNING, e.getMessage(), e);
}
}
return callout;
}
}
}

View File

@ -0,0 +1,56 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* 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., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.base.annotation;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Annotation for Column Callout. This should only be used with class that implements the IColumnCallout interface.
* You can repeat the annotation multiple time for different table and column name combination
* Note that you can't use * for both tableName and columnName attribute.
* @author hengsin
*
*/
@Retention(RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(Callouts.class)
public @interface Callout {
/**
*
* @return table names or * to match all tables
*/
String[] tableName();
/**
*
* @return column names or * to match all columns
*/
String[] columnName();
}

View File

@ -0,0 +1,42 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* 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., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.base.annotation;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
*
* @author hengsin
*
*/
@Retention(RUNTIME)
@Target(ElementType.TYPE)
public @interface Callouts {
Callout[] value() default {};
}

View File

@ -18,6 +18,7 @@ import java.util.Map;
import java.util.Properties;
import org.adempiere.base.IColumnCallout;
import org.adempiere.base.annotation.Callout;
import org.compiere.model.AccessSqlParser;
import org.compiere.model.AccessSqlParser.TableInfo;
import org.compiere.model.GridField;
@ -35,6 +36,8 @@ import org.compiere.util.Env;
* @author hengsin
*
*/
@Callout(tableName = "AD_InfoWindow", columnName = "AD_Table_ID")
@Callout(tableName = "AD_InfoColumn", columnName = {"AD_Element_ID","AD_Reference_ID"})
public class CalloutInfoWindow implements IColumnCallout {
/**

View File

@ -0,0 +1,188 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* 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., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.idempiere.test.model;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.List;
import java.util.StringTokenizer;
import org.adempiere.base.ColumnCalloutManager;
import org.adempiere.base.Core;
import org.adempiere.base.IColumnCallout;
import org.adempiere.model.CalloutInfoWindow;
import org.compiere.Adempiere;
import org.compiere.model.Callout;
import org.compiere.model.I_M_InventoryLine;
import org.compiere.model.MBPartner;
import org.compiere.model.MColumn;
import org.compiere.model.MRule;
import org.compiere.model.Query;
import org.compiere.util.Env;
import org.idempiere.test.AbstractTestCase;
import org.junit.jupiter.api.Test;
/**
*
* @author hengsin
*
*/
public class CalloutTest extends AbstractTestCase {
public CalloutTest() {
}
@Test
public void testAnnotatedCallout() {
List<IColumnCallout> callouts = ColumnCalloutManager.findCallout("AD_InfoWindow", "AD_Table_ID");
assertNotNull(callouts, "Null column callouts for AD_InfoWindow.AD_Table_ID");
assertTrue(callouts.size() > 0, "Empty column callouts for AD_InfoWindow.AD_Table_ID");
int found = 0;
for(IColumnCallout callout : callouts) {
if (callout instanceof CalloutInfoWindow) {
found++;
}
}
assertTrue(found==1, CalloutInfoWindow.class.getName() + " not found for AD_InfoWindow.AD_Table_ID");
callouts = ColumnCalloutManager.findCallout("AD_InfoColumn", "AD_Element_ID");
assertNotNull(callouts, "Null column callouts for AD_InfoColumn.AD_Element_ID");
assertTrue(callouts.size() > 0, "Empty column callouts for AD_InfoColumn.AD_Element_ID");
found = 0;
for(IColumnCallout callout : callouts) {
if (callout instanceof CalloutInfoWindow) {
found++;
}
}
assertTrue(found==1, CalloutInfoWindow.class.getName() + " not found for AD_InfoColumn.AD_Element_ID");
callouts = ColumnCalloutManager.findCallout("AD_InfoColumn", "AD_Reference_ID");
assertNotNull(callouts, "Null column callouts for AD_InfoColumn.AD_Reference_ID");
assertTrue(callouts.size() > 0, "Empty column callouts for AD_InfoColumn.AD_Reference_ID");
found = 0;
for(IColumnCallout callout : callouts) {
if (callout instanceof CalloutInfoWindow) {
found++;
}
}
assertTrue(found==1, CalloutInfoWindow.class.getName() + " not found for AD_InfoColumn.AD_Reference_ID");
String calloutClass = "org.adempiere.base.callout.CostAdjustmentLineASI";
callouts = ColumnCalloutManager.findCallout(I_M_InventoryLine.Table_Name, I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID);
assertNotNull(callouts, "Null column callouts for "+I_M_InventoryLine.Table_Name+"."+I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID);
assertTrue(callouts.size() > 0, "Empty column callouts for "+I_M_InventoryLine.Table_Name+"."+I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID);
found = 0;
for(IColumnCallout callout : callouts) {
if (callout.getClass().getName().equals(calloutClass)) {
found++;
}
}
assertTrue(found==1, calloutClass+" not found for "+I_M_InventoryLine.Table_Name+"."
+I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID);
calloutClass = "org.adempiere.base.callout.CostAdjustmentLineProduct";
callouts = ColumnCalloutManager.findCallout(I_M_InventoryLine.Table_Name, I_M_InventoryLine.COLUMNNAME_M_Product_ID);
assertNotNull(callouts, "Null column callouts for "+I_M_InventoryLine.Table_Name+"."+I_M_InventoryLine.COLUMNNAME_M_Product_ID);
assertTrue(callouts.size() > 0, "Empty column callouts for "+I_M_InventoryLine.Table_Name+"."+I_M_InventoryLine.COLUMNNAME_M_Product_ID);
found = 0;
for(IColumnCallout callout : callouts) {
if (callout.getClass().getName().equals(calloutClass)) {
found++;
}
}
assertTrue(found==1, calloutClass+" not found for "+I_M_InventoryLine.Table_Name+"."
+I_M_InventoryLine.COLUMNNAME_M_Product_ID);
calloutClass = "org.adempiere.base.callout.CostAdjustmentLineProduct";
callouts = ColumnCalloutManager.findCallout(I_M_InventoryLine.Table_Name, I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID);
assertNotNull(callouts, "Null column callouts for "+I_M_InventoryLine.Table_Name+"."+I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID);
assertTrue(callouts.size() > 0, "Empty column callouts for "+I_M_InventoryLine.Table_Name+"."+I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID);
found = 0;
for(IColumnCallout callout : callouts) {
if (callout.getClass().getName().equals(calloutClass)) {
found++;
}
}
assertTrue(found==0, calloutClass+" found for "+I_M_InventoryLine.Table_Name+"."
+I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID);
calloutClass = "org.adempiere.model.CalloutBPartnerQuickEntry";
callouts = ColumnCalloutManager.findCallout(MBPartner.Table_Name, MBPartner.COLUMNNAME_Name);
assertNotNull(callouts, "Null column callouts for "+MBPartner.Table_Name+"."+MBPartner.COLUMNNAME_Name);
assertTrue(callouts.size() > 0, "Empty column callouts for "+MBPartner.Table_Name+"."+MBPartner.COLUMNNAME_Name);
found = 0;
for(IColumnCallout callout : callouts) {
if (callout.getClass().getName().equals(calloutClass)) {
found++;
}
}
assertTrue(found==1, calloutClass+" not found for "+MBPartner.Table_Name+"."
+MBPartner.COLUMNNAME_Name);
}
@Test
public void testCoreCalloutMapping() {
Query query = new Query(Env.getCtx(), MColumn.Table_Name, "AD_Column.IsActive='Y' AND AD_Column.AD_Column_ID < 1000000 "
+ "AND AD_Table.IsActive='Y' AND AD_Table.AD_Table_ID < 1000000 "
+ "AND AD_Column.Callout IS NOT NULL ", getTrxName());
query.addJoinClause("JOIN AD_Table ON (AD_Column.AD_Table_ID=AD_Table.AD_Table_ID)");
query.addJoinClause("JOIN AD_Tab ON (AD_Table.AD_Table_ID=AD_Tab.AD_Table_ID AND AD_Tab.IsActive='Y' AND AD_Tab.AD_Tab_ID<1000000)");
query.addJoinClause("JOIN AD_Window ON (AD_Tab.AD_Window_ID=AD_Window.AD_Window_ID AND AD_Window.IsActive='Y' AND AD_Window.AD_Window_ID<1000000)");
query.addJoinClause("JOIN AD_Menu ON (AD_Window.AD_Window_ID=AD_Menu.AD_Window_ID AND AD_Menu.IsActive='Y' AND AD_Menu.AD_Menu_ID<1000000)");
List<MColumn> columns = query.list();
ClassLoader baseClassLoader = Adempiere.class.getClassLoader();
int calloutCount = 0;
for(MColumn column : columns) {
StringTokenizer st = new StringTokenizer(column.getCallout(), ";,", false);
while (st.hasMoreTokens()) {
String cmd = st.nextToken().trim();
if (cmd.toLowerCase().startsWith(MRule.SCRIPT_PREFIX))
continue;
int methodStart = cmd.lastIndexOf('.');
try {
if (methodStart != -1) // no class
{
String className = cmd.substring(0,methodStart);
String method = cmd.substring(methodStart+1);
Callout callout = Core.getCallout(className, method);
if (callout == null) {
//no match from factory, check java classpath
Class<?> cClass = baseClassLoader.loadClass(className);
callout = (Callout)cClass.getDeclaredConstructor().newInstance();
}
assertNotNull(callout, "Can't get callout "+cmd+" for "+column.getAD_Table()+" "+column);
calloutCount++;
}
} catch (Exception e) {
fail("Can't get callout "+cmd+" for "+column.getAD_Table()+" "+column, e);
}
}
}
assertTrue(calloutCount > 0, "Zero callout loaded");
}
}

View File

@ -39,6 +39,7 @@ import org.compiere.model.GridTab;
import org.compiere.model.MTest;
import org.idempiere.test.AbstractTestCase;
import org.idempiere.test.TestActivator;
import org.idempiere.test.model.annotated.MyAnnotatedTestQtyCallout;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
@ -67,6 +68,11 @@ public class MappedColumnCalloutFactoryTest extends AbstractTestCase {
var list = Core.findCallout(MTest.Table_Name, MTest.COLUMNNAME_T_Amount);
var optional = list.stream().filter(e -> e instanceof MyTestAmountCallout).findFirst();
assertTrue(optional.isPresent(), "Can't find MyTestAmountCallout column callout for " + MTest.Table_Name + "." + MTest.COLUMNNAME_T_Amount);
factory.scan(TestActivator.context, "org.idempiere.test.model.annotated");
list = Core.findCallout(MTest.Table_Name, MTest.COLUMNNAME_T_Qty);
optional = list.stream().filter(e -> e instanceof MyAnnotatedTestQtyCallout).findFirst();
assertTrue(optional.isPresent(), "Can't find MyAnnotatedTestQtyCallout column callout for " + MTest.Table_Name + "." + MTest.COLUMNNAME_T_Qty);
}
@Test

View File

@ -0,0 +1,51 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* 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., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.idempiere.test.model.annotated;
import java.util.Properties;
import org.adempiere.base.IColumnCallout;
import org.adempiere.base.annotation.Callout;
import org.compiere.model.GridField;
import org.compiere.model.GridTab;
import org.compiere.model.MTest;
/**
*
* @author hengsin
*
*/
@Callout(tableName = MTest.Table_Name, columnName = MTest.COLUMNNAME_T_Qty)
public class MyAnnotatedTestQtyCallout implements IColumnCallout {
public MyAnnotatedTestQtyCallout() {
}
@Override
public String start(Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value, Object oldValue) {
return null;
}
}