diff --git a/org.adempiere.base/META-INF/MANIFEST.MF b/org.adempiere.base/META-INF/MANIFEST.MF
index ae26774fd0..e4491dee0b 100644
--- a/org.adempiere.base/META-INF/MANIFEST.MF
+++ b/org.adempiere.base/META-INF/MANIFEST.MF
@@ -19,6 +19,7 @@ Export-Package: bsh,
bsh.servlet,
bsh.util,
com.akunagroup.uk.postcode,
+ io.github.classgraph,
it.sauronsoftware.cron4j,
org.adempiere.apps.graph,
org.adempiere.base,
diff --git a/org.adempiere.ui.zk/META-INF/MANIFEST.MF b/org.adempiere.ui.zk/META-INF/MANIFEST.MF
index 0af84aea1b..fc7eda5454 100644
--- a/org.adempiere.ui.zk/META-INF/MANIFEST.MF
+++ b/org.adempiere.ui.zk/META-INF/MANIFEST.MF
@@ -65,6 +65,7 @@ Import-Package: com.google.common.annotations;version="30.1.1",
org.jfree.util,
org.osgi.framework;version="1.7.0",
org.osgi.framework.wiring;version="1.2.0",
+ org.osgi.service.component;version="1.4.0",
org.osgi.service.component.annotations;version="1.3.0",
org.osgi.service.event;version="1.3.0",
org.osgi.util.tracker;version="1.5.0",
diff --git a/org.adempiere.ui.zk/OSGI-INF/defaultformfactory.xml b/org.adempiere.ui.zk/OSGI-INF/defaultformfactory.xml
index 115b169bd0..c1d6d91485 100644
--- a/org.adempiere.ui.zk/OSGI-INF/defaultformfactory.xml
+++ b/org.adempiere.ui.zk/OSGI-INF/defaultformfactory.xml
@@ -5,4 +5,5 @@
+
diff --git a/org.adempiere.ui.zk/OSGI-INF/org.adempiere.webui.factory.DefaultScanBasedFormFactory.xml b/org.adempiere.ui.zk/OSGI-INF/org.adempiere.webui.factory.DefaultScanBasedFormFactory.xml
new file mode 100644
index 0000000000..7d16c590d6
--- /dev/null
+++ b/org.adempiere.ui.zk/OSGI-INF/org.adempiere.webui.factory.DefaultScanBasedFormFactory.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/AddAuthorizationForm.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/AddAuthorizationForm.java
index 14597c5819..1bcf956de5 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/AddAuthorizationForm.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/AddAuthorizationForm.java
@@ -53,6 +53,7 @@ import org.zkoss.zul.Timer;
* IDEMPIERE-3101
* @author Carlos Ruiz - globalqss
*/
+@org.idempiere.ui.zk.annotation.Form
public class AddAuthorizationForm extends ADForm {
/**
*
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/CompareCtxHelpSuggestion.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/CompareCtxHelpSuggestion.java
index dbc49418ab..89724751e3 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/CompareCtxHelpSuggestion.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/CompareCtxHelpSuggestion.java
@@ -40,6 +40,7 @@ import org.zkoss.zul.Vlayout;
* @author hengsin
*
*/
+@org.idempiere.ui.zk.annotation.Form
public class CompareCtxHelpSuggestion extends ADForm {
private static final String NEW_VALUE = "newValue";
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/CompareFieldSuggestion.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/CompareFieldSuggestion.java
index 062a876a36..8dcd7d4b9e 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/CompareFieldSuggestion.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/CompareFieldSuggestion.java
@@ -36,6 +36,7 @@ import org.zkoss.zul.Vlayout;
* @author hengsin
*
*/
+@org.idempiere.ui.zk.annotation.Form
public class CompareFieldSuggestion extends ADForm {
private static final String NEW_VALUE = "newValue";
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/MFARegisterForm.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/MFARegisterForm.java
index b6997c3aa1..289c970f89 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/MFARegisterForm.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/MFARegisterForm.java
@@ -57,6 +57,7 @@ import org.zkoss.zul.Space;
* IDEMPIERE-4782
* @author Carlos Ruiz - globalqss - BX Service
*/
+@org.idempiere.ui.zk.annotation.Form
public class MFARegisterForm extends ADForm {
/**
*
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WAllocation.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WAllocation.java
index 5180776b4e..3600b53487 100755
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WAllocation.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WAllocation.java
@@ -85,6 +85,7 @@ import static org.adempiere.webui.ClientInfo.*;
*
* Contributor : Fabian Aguilar - OFBConsulting - Multiallocation
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VAllocation")
public class WAllocation extends Allocation
implements IFormController, EventListener, WTableModelListener, ValueChangeListener
{
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WArchiveViewer.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WArchiveViewer.java
index ac975ee1fe..4ee0b1cdc6 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WArchiveViewer.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WArchiveViewer.java
@@ -99,7 +99,7 @@ import org.zkoss.zul.impl.XulElement;
* @author Niraj Sohun
* @date September 28, 2007
*/
-
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.ArchiveViewer")
public class WArchiveViewer extends Archive implements IFormController, EventListener
{
private static final String ONCLOSE_TIMESTAMP_ATTR = "onclose.timestamp";
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WAttributeGrid.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WAttributeGrid.java
index 0a38be2da2..6501848e7f 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WAttributeGrid.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WAttributeGrid.java
@@ -65,6 +65,7 @@ import org.zkoss.zul.Vbox;
* @author Jorg Janke
* @version $Id: VAttributeGrid.java,v 1.2 2006/07/30 00:51:28 jjanke Exp $
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VAttributeGrid")
public class WAttributeGrid extends ADForm implements EventListener
{
/**
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WBOMDrop.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WBOMDrop.java
index da0334479a..3725043d58 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WBOMDrop.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WBOMDrop.java
@@ -85,6 +85,7 @@ import org.zkoss.zul.Vlayout;
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VBOMDrop")
public class WBOMDrop extends ADForm implements EventListener, ValueChangeListener
{
/**
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WCharge.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WCharge.java
index 66037c5398..7ef43117f9 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WCharge.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WCharge.java
@@ -67,6 +67,7 @@ import org.zkoss.zul.Separator;
* @author Andrew Kimball
*
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VCharge")
public class WCharge extends Charge implements IFormController, EventListener
{
/**
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WCreateFromForm.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WCreateFromForm.java
index 95d4603f9f..d0612839a5 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WCreateFromForm.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WCreateFromForm.java
@@ -49,6 +49,7 @@ import org.zkoss.zul.South;
* @author Elaine
*
*/
+@org.idempiere.ui.zk.annotation.Form
public class WCreateFromForm extends ADForm implements EventListener, WTableModelListener, DialogEvents
{
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WFactReconcile.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WFactReconcile.java
index 15b674c408..89c5717c08 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WFactReconcile.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WFactReconcile.java
@@ -59,6 +59,7 @@ import org.zkoss.zul.Center;
import org.zkoss.zul.North;
import org.zkoss.zul.South;
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VFactReconcile")
public class WFactReconcile extends FactReconcile
implements IFormController, EventListener, WTableModelListener, ValueChangeListener{
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WFileImport.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WFileImport.java
index f5775f64a5..14d589218f 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WFileImport.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WFileImport.java
@@ -75,6 +75,7 @@ import org.zkoss.zul.Vbox;
*
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VFileImport")
public class WFileImport extends ADForm implements EventListener
{
/**
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WGenForm.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WGenForm.java
index fe4c132df8..ff68cea6db 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WGenForm.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WGenForm.java
@@ -80,6 +80,7 @@ import org.zkoss.zul.South;
* Generate custom form window
*
*/
+@org.idempiere.ui.zk.annotation.Form
public class WGenForm extends ADForm implements EventListener, WTableModelListener
{
/**
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WInOutGen.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WInOutGen.java
index 7d75a27bf1..51e2903bcc 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WInOutGen.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WInOutGen.java
@@ -52,6 +52,7 @@ import org.zkoss.zul.North;
* Generate Shipment (manual) view class
*
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VInOutGen")
public class WInOutGen extends InOutGen implements IFormController, EventListener, ValueChangeListener
{
private WGenForm form;
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WInvoiceGen.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WInvoiceGen.java
index f4149b5deb..fc58b9411c 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WInvoiceGen.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WInvoiceGen.java
@@ -52,6 +52,7 @@ import org.zkoss.zul.North;
* Generate Invoice (manual) view class
*
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VInvoiceGen")
public class WInvoiceGen extends InvoiceGen implements IFormController, EventListener, ValueChangeListener
{
private WGenForm form;
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WMatch.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WMatch.java
index 219f413fce..6abbd042b1 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WMatch.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WMatch.java
@@ -78,6 +78,7 @@ import org.zkoss.zul.Vlayout;
* @author Jorg Janke
* @version $Id: VMatch.java,v 1.2 2006/07/30 00:51:28 jjanke Exp $
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VMatch")
public class WMatch extends Match
implements IFormController, EventListener, WTableModelListener
{
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WMerge.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WMerge.java
index 31c2e02ed9..efb8f94425 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WMerge.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WMerge.java
@@ -55,6 +55,7 @@ import org.zkoss.zul.South;
* @author Jorg Janke
* @version $Id: VMerge.java,v 1.2 2006/07/30 00:51:28 jjanke Exp $
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VMerge")
public class WMerge extends Merge implements IFormController, EventListener
{
/**
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WPayPrint.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WPayPrint.java
index af89036da2..99dfcfd13f 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WPayPrint.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WPayPrint.java
@@ -86,6 +86,7 @@ import com.lowagie.text.pdf.PdfReader;
* Carlos Ruiz - GlobalQSS - FR 3132033 - Make payment export class configurable per bank
* Markus Bozem: IDEMPIERE-1546 / IDEMPIERE-3286
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VPayPrint")
public class WPayPrint extends PayPrint implements IFormController, EventListener, ValueChangeListener
{
private CustomForm form = new CustomForm();
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WPaySelect.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WPaySelect.java
index 5d637567fa..0d1fba4617 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WPaySelect.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WPaySelect.java
@@ -90,6 +90,7 @@ import org.zkoss.zul.Space;
* @author Jorg Janke
* @version $Id: VPaySelect.java,v 1.3 2006/07/30 00:51:28 jjanke Exp $
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VPaySelect")
public class WPaySelect extends PaySelect
implements IFormController, EventListener, WTableModelListener, IProcessUI, ValueChangeListener
{
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WPluginManager.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WPluginManager.java
index 070f6621ef..358a19c479 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WPluginManager.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WPluginManager.java
@@ -64,6 +64,7 @@ import org.zkoss.zul.Vbox;
* @author Carlos Ruiz - globalqss - bxservice
*
*/
+@org.idempiere.ui.zk.annotation.Form
public class WPluginManager extends ADForm implements EventListener {
/**
*
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WProcessParameter.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WProcessParameter.java
index f7370feb50..744f27675d 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WProcessParameter.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WProcessParameter.java
@@ -30,6 +30,7 @@ import org.compiere.util.Util;
* @author hengsin
*
*/
+@org.idempiere.ui.zk.annotation.Form
public class WProcessParameter implements IFormController {
private WProcessParameterForm parameterForm = null;
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WReportCustomization.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WReportCustomization.java
index 70994cec63..a7a33c9577 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WReportCustomization.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WReportCustomization.java
@@ -74,6 +74,7 @@ import org.zkoss.zul.Separator;
import org.zkoss.zul.Vbox;
+@org.idempiere.ui.zk.annotation.Form
public class WReportCustomization implements IFormController,EventListener {
private CustomForm form = new CustomForm();
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WResetPassword.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WResetPassword.java
index 24113e8c9f..233749fc3a 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WResetPassword.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WResetPassword.java
@@ -61,6 +61,7 @@ import org.zkoss.zul.South;
* @author Elaine
* @date September 18, 2012
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VResetPassword")
public class WResetPassword implements IFormController, EventListener, ValueChangeListener {
private static final CLogger log = CLogger.getCLogger(WResetPassword.class);
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WSQLProcess.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WSQLProcess.java
index 0e0380dac1..1cc574325f 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WSQLProcess.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WSQLProcess.java
@@ -55,6 +55,7 @@ import org.zkoss.zul.Center;
* @author Andrew Kimball
*
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VSQLProcess")
public class WSQLProcess extends ADForm implements EventListener
{
/**
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WSetupWizard.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WSetupWizard.java
index 8cd81d8076..27db801f43 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WSetupWizard.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WSetupWizard.java
@@ -76,6 +76,7 @@ import org.zkoss.zul.Vbox;
* @author Carlos Ruiz
*
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VSetupWizard")
public class WSetupWizard extends SetupWizard implements IFormController, EventListener
{
private CustomForm form = null;
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WStatementCreateFromBatch.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WStatementCreateFromBatch.java
index 80e46cd682..21b128c333 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WStatementCreateFromBatch.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WStatementCreateFromBatch.java
@@ -62,6 +62,7 @@ import org.zkoss.zul.Hbox;
* @author Elaine
*
*/
+@org.idempiere.ui.zk.annotation.Form
public class WStatementCreateFromBatch extends StatementCreateFromBatch implements IFormController, EventListener
{
private WCreateFromForm form;
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTabEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTabEditor.java
index 041dd95c33..737e9fd875 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTabEditor.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTabEditor.java
@@ -90,6 +90,7 @@ import org.zkoss.zul.West;
* @author Carlos Ruiz
*
*/
+@org.idempiere.ui.zk.annotation.Form
public class WTabEditor extends TabEditor implements IFormController, EventListener, ValueChangeListener
{
// TODO: create messages Property, VisibleFields, NonVisibleField
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTreeBOM.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTreeBOM.java
index 5193fcaee9..ec80a36f13 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTreeBOM.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTreeBOM.java
@@ -70,6 +70,7 @@ import org.zkoss.zul.Treecols;
import org.zkoss.zul.Treeitem;
import org.zkoss.zul.West;
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VTreeBOM")
public class WTreeBOM extends TreeBOM implements IFormController, EventListener {
private int m_WindowNo = 0;
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTreeMaintenance.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTreeMaintenance.java
index 0b067506ce..07955dd2a3 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTreeMaintenance.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTreeMaintenance.java
@@ -68,6 +68,7 @@ import org.zkoss.zul.Treeitem;
* @author Jorg Janke (modify: Sergio Oropeza sergioropeza@gmail.com, soropeza@dcsla.com 06/03/2014)
* @version $Id: VTreeMaintenance.java,v 1.3 2006/07/30 00:51:28 jjanke Exp $
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VTreeMaintenance")
public class WTreeMaintenance extends TreeMaintenance implements IFormController, EventListener
{
private CustomForm form = new CustomForm();
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTrxMaterial.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTrxMaterial.java
index cd89b6ded0..5fa7a07a12 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTrxMaterial.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/form/WTrxMaterial.java
@@ -63,6 +63,7 @@ import org.zkoss.zul.South;
* @author Jorg Janke
* @version $Id: VTrxMaterial.java,v 1.3 2006/07/30 00:51:28 jjanke Exp $
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.form.VTrxMaterial")
public class WTrxMaterial extends TrxMaterial
implements IFormController, EventListener, ValueChangeListener
{
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/graph/WViewPI.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/graph/WViewPI.java
index 465ba35395..e9c2592e96 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/graph/WViewPI.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/graph/WViewPI.java
@@ -3,6 +3,7 @@ package org.adempiere.webui.apps.graph;
import org.adempiere.webui.panel.ADForm;
import org.compiere.model.MGoal;
+@org.idempiere.ui.zk.annotation.Form(name = "org.adempiere.apps.graph.ViewPI")
public class WViewPI extends ADForm {
/**
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/wf/WFEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/wf/WFEditor.java
index ec52412bb6..aefe0bc74d 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/wf/WFEditor.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/wf/WFEditor.java
@@ -68,6 +68,7 @@ import org.zkoss.zul.Vbox;
* @author Low Heng Sin
*
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.wf.WFPanel")
public class WFEditor extends ADForm {
/**
*
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/wf/WWFActivity.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/wf/WWFActivity.java
index ac13b286ea..f63090c565 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/wf/WWFActivity.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/wf/WWFActivity.java
@@ -76,6 +76,7 @@ import org.zkoss.zul.Html;
* @author hengsin
*
*/
+@org.idempiere.ui.zk.annotation.Form(name = "org.compiere.apps.wf.WFActivity")
public class WWFActivity extends ADForm implements EventListener
{
/**
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/DefaultScanBasedFormFactory.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/DefaultScanBasedFormFactory.java
new file mode 100644
index 0000000000..0edbe99e98
--- /dev/null
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/DefaultScanBasedFormFactory.java
@@ -0,0 +1,46 @@
+/***********************************************************************
+ * 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.webui.factory;
+
+import org.osgi.service.component.annotations.Component;
+
+/**
+ *
+ * @author hengsin
+ *
+ */
+@Component(immediate = true, service = IFormFactory.class, property = {"service.ranking:Integer=0"})
+public final class DefaultScanBasedFormFactory extends ScanBasedFormFactory {
+
+ public DefaultScanBasedFormFactory() {
+ }
+
+ @Override
+ protected String[] getPackages() {
+ return new String[] {"org.adempiere.webui.apps.form", "org.adempiere.webui.apps.graph", "org.adempiere.webui.apps.wf",
+ "org.adempiere.webui.panel"};
+ }
+
+}
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/IMappedFormFactory.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/IMappedFormFactory.java
index b7a4f508f8..e4ef40f1d6 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/IMappedFormFactory.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/IMappedFormFactory.java
@@ -26,6 +26,8 @@ package org.adempiere.webui.factory;
import org.adempiere.base.IMappedByNameFactory;
import org.adempiere.webui.panel.ADForm;
+import org.idempiere.ui.zk.annotation.Form;
+import org.osgi.framework.BundleContext;
/**
*
@@ -33,4 +35,10 @@ import org.adempiere.webui.panel.ADForm;
*
*/
public interface IMappedFormFactory extends IMappedByNameFactory {
+ /**
+ * Scan packages for class with {@link Form} annotation and add mapping for it
+ * @param context
+ * @param packages
+ */
+ public void scan(BundleContext context, String... packages);
}
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/MappedFormFactory.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/MappedFormFactory.java
index 141be68b11..c79a9020bd 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/MappedFormFactory.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/MappedFormFactory.java
@@ -25,6 +25,11 @@
package org.adempiere.webui.factory;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.function.Supplier;
+import java.util.logging.Level;
+
/**
*
* @author matheus.marcelino
@@ -32,14 +37,26 @@ package org.adempiere.webui.factory;
*/
import org.adempiere.base.MappedByNameFactory;
import org.adempiere.webui.panel.ADForm;
+import org.adempiere.webui.panel.IFormController;
+import org.compiere.util.CLogger;
+import org.idempiere.ui.zk.annotation.Form;
+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.ClassGraph;
+import io.github.classgraph.ClassInfo;
+import io.github.classgraph.ScanResult;
+
@Component(name = "org.adempiere.webui.factory.MappedFormFactory",
immediate = true,
service = {IFormFactory.class, IMappedFormFactory.class},
property = {"service.ranking:Integer=1"})
public class MappedFormFactory extends MappedByNameFactory implements IFormFactory, IMappedFormFactory {
+ private final static CLogger s_log = CLogger.getCLogger(MappedFormFactory.class);
+
public MappedFormFactory() {
}
@@ -48,4 +65,69 @@ public class MappedFormFactory extends MappedByNameFactory implements IF
return newInstance(formName);
}
+ @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()) {
+ for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(Form.class)) {
+ if (classInfo.isAbstract())
+ continue;
+ String className = classInfo.getName();
+ try {
+ Class> clazz = (Class>) classInfo.loadClass();
+ Constructor> constructor = clazz.getConstructor();
+ FormSupplier supplier = new FormSupplier(constructor);
+ AnnotationInfo annotationInfo = classInfo.getAnnotationInfo(Form.class);
+ String alternateName = null;
+ if (annotationInfo != null)
+ alternateName = (String) annotationInfo.getParameterValues().getValue("name");
+
+ addMapping(className, supplier);
+ if (alternateName != null)
+ addMapping(alternateName, supplier);
+ } catch (Exception e) {
+ if (s_log.isLoggable(Level.INFO))
+ s_log.log(Level.INFO, e.getMessage(), e);
+ }
+ }
+ }
+
+ }
+
+ private static final class FormSupplier implements Supplier {
+
+ private Constructor> constructor;
+
+ private FormSupplier(Constructor> constructor) {
+ this.constructor = constructor;
+ }
+
+ @Override
+ public ADForm get() {
+ ADForm form = null;
+ try {
+ Object formObject = constructor.newInstance();
+ if (formObject != null) {
+ if (formObject instanceof ADForm) {
+ form = (ADForm)formObject;
+ } else if (formObject instanceof IFormController) {
+ IFormController controller = (IFormController) formObject;
+ form = controller.getForm();
+ form.setICustomForm(controller);
+ }
+ }
+ } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
+ | InvocationTargetException e) {
+ s_log.log(Level.WARNING, e.getMessage(), e);
+ }
+ return form;
+ }
+ }
}
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/ScanBasedFormFactory.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/ScanBasedFormFactory.java
new file mode 100644
index 0000000000..3d360b57cc
--- /dev/null
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/ScanBasedFormFactory.java
@@ -0,0 +1,162 @@
+/***********************************************************************
+ * 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.webui.factory;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+
+import org.adempiere.webui.panel.ADForm;
+import org.adempiere.webui.panel.IFormController;
+import org.compiere.util.CLogger;
+import org.compiere.util.Util;
+import org.idempiere.ui.zk.annotation.Form;
+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.ClassGraph;
+import io.github.classgraph.ClassInfo;
+import io.github.classgraph.ScanResult;
+
+/**
+ * Scan, discover and regiser classes with {@link Form} annotation.
+ * @author hengsin
+ *
+ */
+public abstract class ScanBasedFormFactory implements IFormFactory {
+
+ private final Map classCache = new HashMap<>();
+
+ private final Map[]> constructorCache = new ConcurrentHashMap<>();
+
+ private BundleContext bundleContext = null;
+
+ private static final CLogger s_log = CLogger.getCLogger(ScanBasedFormFactory.class);
+
+ public ScanBasedFormFactory() {
+ }
+
+ @Override
+ public ADForm newFormInstance(String formName) {
+ ADForm form = null;
+ String realClassName = classCache.get(formName);
+ if (realClassName != null) {
+ Constructor> constructor = getConstructor(formName);
+ try {
+ Object formObject = constructor.newInstance();
+ if (formObject != null) {
+ if (formObject instanceof ADForm) {
+ form = (ADForm)formObject;
+ } else if (formObject instanceof IFormController) {
+ IFormController controller = (IFormController) formObject;
+ form = controller.getForm();
+ form.setICustomForm(controller);
+ }
+ }
+ } catch (Exception e) {
+ s_log.log(Level.WARNING, e.getMessage(), e);
+ }
+ if (form == null)
+ constructorCache.put(realClassName, new Constructor>[0]);
+ }
+ return form;
+ }
+
+ /**
+ *
+ * @param formName
+ * @return class default constructor
+ */
+ public Constructor> getConstructor(String formName) {
+ String className = classCache.get(formName);
+ Constructor>[] constructors = null;
+ if (className != null) {
+ constructors = constructorCache.get(className);
+ if (constructors == null) {
+ ClassLoader classLoader = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader();
+ try {
+ Class> clazz = classLoader.loadClass(className);
+ Constructor> constructor = clazz.getDeclaredConstructor();
+ if (constructor != null) {
+ constructors = new Constructor>[] {constructor};
+ } else {
+ constructors = new Constructor>[0];
+ }
+ constructorCache.put(className, constructors);
+ } catch (Exception e) {
+ s_log.log(Level.WARNING, e.getMessage(), e);
+ constructors = new Constructor>[0];
+ constructorCache.put(className, constructors);
+ }
+ }
+ }
+ if (constructors != null && constructors.length == 1) {
+ return constructors[0];
+ } else {
+ return null;
+ }
+ }
+
+ 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())
+ {
+
+ for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(Form.class)) {
+ if (classInfo.isAbstract())
+ continue;
+ String className = classInfo.getName();
+ AnnotationInfo annotationInfo = classInfo.getAnnotationInfo(Form.class);
+ String alternateName = (String) annotationInfo.getParameterValues().getValue("name");
+
+ classCache.put(className, className);
+ if (!Util.isEmpty(alternateName, true))
+ classCache.put(alternateName, className);
+ }
+ }
+ long end = System.currentTimeMillis();
+ if (s_log.isLoggable(Level.INFO))
+ s_log.info(this.getClass().getSimpleName() + " loaded "+classCache.size() +" classes in "
+ +((end-start)/1000f) + "s");
+ }
+}
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/CustomForm.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/CustomForm.java
index adaf628f10..c4bbbfab28 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/CustomForm.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/CustomForm.java
@@ -1,5 +1,6 @@
package org.adempiere.webui.panel;
+@org.idempiere.ui.zk.annotation.Form
public class CustomForm extends ADForm
{
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/WProcessParameterForm.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/WProcessParameterForm.java
index 8cea3ca4e8..e02314b13e 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/WProcessParameterForm.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/WProcessParameterForm.java
@@ -42,6 +42,7 @@ import org.zkoss.zul.Hbox;
import org.zkoss.zul.Html;
import org.zkoss.zul.Vlayout;
+@org.idempiere.ui.zk.annotation.Form
public class WProcessParameterForm extends ADForm
{
/**
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/WTabEditorForm.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/WTabEditorForm.java
index 0c48b67585..f608433eef 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/WTabEditorForm.java
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/WTabEditorForm.java
@@ -16,6 +16,7 @@ package org.adempiere.webui.panel;
import org.adempiere.webui.apps.form.WTabEditor;
+@org.idempiere.ui.zk.annotation.Form
public class WTabEditorForm extends ADForm
{
/**
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/annotation/Form.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/annotation/Form.java
new file mode 100644
index 0000000000..7e3a4080b0
--- /dev/null
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/annotation/Form.java
@@ -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.idempiere.ui.zk.annotation;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target(ElementType.TYPE)
+/**
+ * Annotation for web ui form
+ * @author hengsin
+ *
+ */
+public @interface Form {
+
+ /**
+ * Optional alternate name for form (in addition to class name)
+ * @return alternate name
+ */
+ String name() default "String";
+}
diff --git a/org.idempiere.test/src/org/idempiere/test/model/FormTest.java b/org.idempiere.test/src/org/idempiere/test/model/FormTest.java
new file mode 100644
index 0000000000..eb6f21964c
--- /dev/null
+++ b/org.idempiere.test/src/org/idempiere/test/model/FormTest.java
@@ -0,0 +1,76 @@
+/***********************************************************************
+ * 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 java.lang.reflect.Constructor;
+import java.util.List;
+
+import org.adempiere.base.IServiceReferenceHolder;
+import org.adempiere.base.Service;
+import org.adempiere.webui.factory.DefaultScanBasedFormFactory;
+import org.adempiere.webui.factory.IFormFactory;
+import org.compiere.model.MForm;
+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 FormTest extends AbstractTestCase {
+
+ public FormTest() {
+ }
+
+ @Test
+ public void testCoreFormMapping() {
+ DefaultScanBasedFormFactory formFactory = null;
+ List> factories = Service.locator().list(IFormFactory.class).getServiceReferences();
+ if (factories != null) {
+ for(IServiceReferenceHolder factory : factories) {
+ IFormFactory service = factory.getService();
+ if (service != null && service instanceof DefaultScanBasedFormFactory) {
+ formFactory = (DefaultScanBasedFormFactory) service;
+ break;
+ }
+ }
+ }
+
+ assertNotNull(formFactory, "Failed to locate DefaultScanBasedFormFactory");
+
+ Query query = new Query(Env.getCtx(), MForm.Table_Name, "AD_Form_ID < 1000000 AND ClassName IS NOT NULL "
+ + " AND EXISTS (select 1 from ad_menu where isactive='Y' and ad_form_id=ad_form.ad_form_id)", getTrxName());
+ List forms = query.setOnlyActiveRecords(true).list();
+ for (MForm form : forms) {
+ Constructor> constructor = formFactory.getConstructor(form.getClassname());
+ assertNotNull(constructor, "Failed to find class for " + form.toString() + ", " + form.getClassname());
+ }
+ }
+}