From c159ce5325991abbabb46f81e8756c2feea0eaab Mon Sep 17 00:00:00 2001 From: hengsin Date: Wed, 10 Feb 2021 02:17:15 +0800 Subject: [PATCH] IDEMPIERE-4695 Add document factory base class backed by Map and Lambda functional object (#572) --- ...g.adempiere.base.MappedDocumentFactory.xml | 10 ++ .../src/org/adempiere/base/Core.java | 22 +++++ .../base/DefaultDocumentFactory.java | 38 ------- .../src/org/adempiere/base/IDocFactory.java | 29 +++++- .../base/IMappedDocumentFactory.java | 71 ++++++++++++++ .../adempiere/base/MappedDocumentFactory.java | 98 +++++++++++++++++++ .../test/model/MappedDocumentFactoryTest.java | 70 +++++++++++++ 7 files changed, 299 insertions(+), 39 deletions(-) create mode 100644 org.adempiere.base/OSGI-INF/org.adempiere.base.MappedDocumentFactory.xml create mode 100644 org.adempiere.base/src/org/adempiere/base/IMappedDocumentFactory.java create mode 100644 org.adempiere.base/src/org/adempiere/base/MappedDocumentFactory.java create mode 100644 org.idempiere.test/src/org/idempiere/test/model/MappedDocumentFactoryTest.java diff --git a/org.adempiere.base/OSGI-INF/org.adempiere.base.MappedDocumentFactory.xml b/org.adempiere.base/OSGI-INF/org.adempiere.base.MappedDocumentFactory.xml new file mode 100644 index 0000000000..8d1ace2953 --- /dev/null +++ b/org.adempiere.base/OSGI-INF/org.adempiere.base.MappedDocumentFactory.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base/src/org/adempiere/base/Core.java b/org.adempiere.base/src/org/adempiere/base/Core.java index aae7cf8a49..48bb1afc2d 100644 --- a/org.adempiere.base/src/org/adempiere/base/Core.java +++ b/org.adempiere.base/src/org/adempiere/base/Core.java @@ -1002,4 +1002,26 @@ public class Core { } return factoryService; } + + private static IServiceReferenceHolder s_mappedDocumentFactoryReference = null; + + /** + * + * @return {@link IMappedDocumentFactory} + */ + public static IMappedDocumentFactory getMappedDocumentFactory() { + IMappedDocumentFactory factoryService = null; + if (s_mappedDocumentFactoryReference != null) { + factoryService = s_mappedDocumentFactoryReference.getService(); + if (factoryService != null) + return factoryService; + } + + IServiceReferenceHolder serviceReference = Service.locator().locate(IMappedDocumentFactory.class).getServiceReference(); + if (serviceReference != null) { + factoryService = serviceReference.getService(); + s_mappedDocumentFactoryReference = serviceReference; + } + return factoryService; + } } diff --git a/org.adempiere.base/src/org/adempiere/base/DefaultDocumentFactory.java b/org.adempiere.base/src/org/adempiere/base/DefaultDocumentFactory.java index 10f679ccf5..37e1e83ec1 100644 --- a/org.adempiere.base/src/org/adempiere/base/DefaultDocumentFactory.java +++ b/org.adempiere.base/src/org/adempiere/base/DefaultDocumentFactory.java @@ -14,7 +14,6 @@ package org.adempiere.base; import java.lang.reflect.Constructor; -import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.logging.Level; @@ -23,7 +22,6 @@ import org.compiere.model.MAcctSchema; import org.compiere.model.MTable; import org.compiere.util.AdempiereUserError; import org.compiere.util.CLogger; -import org.compiere.util.DB; import org.compiere.util.Env; /** @@ -35,42 +33,6 @@ public class DefaultDocumentFactory implements IDocFactory { private final static CLogger s_log = CLogger.getCLogger(DefaultDocumentFactory.class); - @Override - public Doc getDocument(MAcctSchema as, int AD_Table_ID, int Record_ID, - String trxName) { - String tableName = MTable.getTableName(Env.getCtx(), AD_Table_ID); - // - Doc doc = null; - StringBuilder sql = new StringBuilder("SELECT * FROM ") - .append(tableName) - .append(" WHERE ").append(tableName).append("_ID=? AND Processed='Y'"); - PreparedStatement pstmt = null; - ResultSet rs = null; - try - { - pstmt = DB.prepareStatement (sql.toString(), trxName); - pstmt.setInt (1, Record_ID); - rs = pstmt.executeQuery (); - if (rs.next ()) - { - doc = getDocument(as, AD_Table_ID, rs, trxName); - } - else - s_log.severe("Not Found: " + tableName + "_ID=" + Record_ID); - } - catch (Exception e) - { - s_log.log (Level.SEVERE, sql.toString(), e); - } - finally - { - DB.close(rs, pstmt); - rs = null; - pstmt = null; - } - return doc; - } - @Override public Doc getDocument(MAcctSchema as, int AD_Table_ID, ResultSet rs, String trxName) { diff --git a/org.adempiere.base/src/org/adempiere/base/IDocFactory.java b/org.adempiere.base/src/org/adempiere/base/IDocFactory.java index ca5a90de9a..37b0569ae3 100644 --- a/org.adempiere.base/src/org/adempiere/base/IDocFactory.java +++ b/org.adempiere.base/src/org/adempiere/base/IDocFactory.java @@ -13,10 +13,16 @@ *****************************************************************************/ package org.adempiere.base; +import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.util.logging.Level; import org.compiere.acct.Doc; import org.compiere.model.MAcctSchema; +import org.compiere.model.MTable; +import org.compiere.util.CLogger; +import org.compiere.util.DB; +import org.compiere.util.Env; /** * @@ -24,6 +30,7 @@ import org.compiere.model.MAcctSchema; * */ public interface IDocFactory { + /** * Create Posting document * @param as accounting schema @@ -32,7 +39,27 @@ public interface IDocFactory { * @param trxName transaction name * @return Document or null */ - public Doc getDocument(MAcctSchema as, int AD_Table_ID, int Record_ID, String trxName); + public default Doc getDocument(MAcctSchema as, int AD_Table_ID, int Record_ID, String trxName) { + String tableName = MTable.getTableName(Env.getCtx(), AD_Table_ID); + // + Doc doc = null; + StringBuilder sql = new StringBuilder("SELECT * FROM ") + .append(tableName) + .append(" WHERE ") + .append(tableName).append("_ID=? AND Processed='Y'"); + try (PreparedStatement pstmt = DB.prepareStatement (sql.toString(), trxName);) { + pstmt.setInt (1, Record_ID); + ResultSet rs = pstmt.executeQuery (); + if (rs.next ()) { + doc = getDocument(as, AD_Table_ID, rs, trxName); + } + else + CLogger.getCLogger(getClass()).severe("Not Found: " + tableName + "_ID=" + Record_ID); + } catch (Exception e) { + CLogger.getCLogger(getClass()).log (Level.SEVERE, sql.toString(), e); + } + return doc; + } /** * Create Posting document diff --git a/org.adempiere.base/src/org/adempiere/base/IMappedDocumentFactory.java b/org.adempiere.base/src/org/adempiere/base/IMappedDocumentFactory.java new file mode 100644 index 0000000000..27cb2cc2c2 --- /dev/null +++ b/org.adempiere.base/src/org/adempiere/base/IMappedDocumentFactory.java @@ -0,0 +1,71 @@ +/*********************************************************************** + * 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.sql.ResultSet; +import java.util.function.Function; + +import org.compiere.acct.Doc; +import org.compiere.model.MAcctSchema; + +/** + * + * @author hengsin + * + */ +public interface IMappedDocumentFactory { + + /** + * add table name + gaap (optional) to Doc mapping + * @param gaap map to c_acctschema.gaap (optional) + * @param tableName + * @param supplier + */ + public void addMapping(String gaap, String tableName, Function supplier); + + /** + * + * @param gaap + * @param tableName + */ + public void removeMapping(String gaap, String tableName); + + /** + * + * parameter class for doc supplier + * + */ + public final static class Parameter { + public MAcctSchema as; + public ResultSet rs; + public String trxName; + + public Parameter(MAcctSchema as, ResultSet rs, String trxName) { + this.as = as; + this.rs = rs; + this.trxName = trxName; + } + } +} \ No newline at end of file diff --git a/org.adempiere.base/src/org/adempiere/base/MappedDocumentFactory.java b/org.adempiere.base/src/org/adempiere/base/MappedDocumentFactory.java new file mode 100644 index 0000000000..0b83da8a53 --- /dev/null +++ b/org.adempiere.base/src/org/adempiere/base/MappedDocumentFactory.java @@ -0,0 +1,98 @@ +/*********************************************************************** + * 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.sql.ResultSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +import org.compiere.acct.Doc; +import org.compiere.model.MAcctSchema; +import org.compiere.model.MTable; +import org.compiere.util.Env; +import org.compiere.util.Util; +import org.osgi.service.component.annotations.Component; + +@Component(name = "org.adempiere.base.MappedDocumentFactory", + service = {IDocFactory.class, IMappedDocumentFactory.class}, + immediate = true, + property = {"service.ranking:Integer=1", "gaap=*"}) +/** + * + * Document factory backed by map between tablename + gaap and lambda function object. + * If you create a subclass of this and register it as osgi service, don't register for the IMappedDocumentFactory interface. + * @author hengsin + * + */ +public class MappedDocumentFactory implements IDocFactory, IMappedDocumentFactory { + + private final ConcurrentHashMap> documentMap = new ConcurrentHashMap<>(); + + /** + * default constructor + */ + public MappedDocumentFactory() { + } + + @Override + public Doc getDocument(MAcctSchema as, int AD_Table_ID, ResultSet rs, String trxName) { + String tableName = MTable.getTableName(Env.getCtx(), AD_Table_ID); + String key = tableName + "|" + as.getGAAP(); + Function function = documentMap.get(key); + if (function != null) { + return function.apply(new Parameter(as, rs, trxName)); + } + + key = tableName + "|*"; + function = documentMap.get(key); + if (function != null) { + return function.apply(new Parameter(as, rs, trxName)); + } + + return null; + } + + @Override + public void addMapping(String gaap, String tableName, Function supplier) { + StringBuilder key = new StringBuilder(); + key.append(tableName).append("|"); + if (Util.isEmpty(gaap, true)) + key.append("*"); + else + key.append(gaap); + documentMap.put(key.toString(), supplier); + } + + @Override + public void removeMapping(String gaap, String tableName) { + StringBuilder key = new StringBuilder(); + key.append(tableName).append("|"); + if (Util.isEmpty(gaap, true)) + key.append("*"); + else + key.append(gaap); + documentMap.remove(key.toString()); + } +} diff --git a/org.idempiere.test/src/org/idempiere/test/model/MappedDocumentFactoryTest.java b/org.idempiere.test/src/org/idempiere/test/model/MappedDocumentFactoryTest.java new file mode 100644 index 0000000000..c50b7355fd --- /dev/null +++ b/org.idempiere.test/src/org/idempiere/test/model/MappedDocumentFactoryTest.java @@ -0,0 +1,70 @@ +/*********************************************************************** + * 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.assertTrue; + +import java.sql.ResultSet; + +import org.adempiere.base.Core; +import org.adempiere.base.IMappedDocumentFactory; +import org.compiere.acct.Doc; +import org.compiere.acct.DocManager; +import org.compiere.acct.Doc_InOut; +import org.compiere.model.MAcctSchema; +import org.compiere.model.MClientInfo; +import org.compiere.model.MInOut; +import org.compiere.util.Env; +import org.idempiere.test.AbstractTestCase; +import org.junit.jupiter.api.Test; + +/** + * + * @author hengsin + * + */ +public class MappedDocumentFactoryTest extends AbstractTestCase { + + public MappedDocumentFactoryTest() { + } + + @Test + public void testDefaultMappedDocumentFactory() { + IMappedDocumentFactory factory = Core.getMappedDocumentFactory(); + factory.addMapping(null, MInOut.Table_Name, (p) -> new MyDocInOut(p.as, p.rs, p.trxName)); + + int C_AcctSchema_ID = MClientInfo.get().getC_AcctSchema1_ID(); + MAcctSchema as = new MAcctSchema(Env.getCtx(), C_AcctSchema_ID, getTrxName()); + Doc doc = DocManager.getDocument(as, MInOut.Table_ID, 100, getTrxName()); + assertTrue(doc != null && doc instanceof MyDocInOut, "Doc not instanceof MyDocInOut. " + (doc == null ? "Doc is Null" : "Doc="+doc.getClass().getName())); + } + + private final static class MyDocInOut extends Doc_InOut { + + public MyDocInOut(MAcctSchema as, ResultSet rs, String trxName) { + super(as, rs, trxName); + } + } +}