From 054d7f93b7307f7e866759767b269a6b4e14e40e Mon Sep 17 00:00:00 2001 From: David Wlazlo Date: Thu, 5 Sep 2024 17:16:16 +0200 Subject: [PATCH] Form-State-XML: Return success flag for XML-import on forms The import of the state of a form via XML document is lenient and ignores unknown fields or fields with errors. Such errors are only logged but some systems need to know if a form state could be imported successfully or not. Add a boolean result type which indicates whether the XML-import was successful. 373731 --- .../ui/form/StoreAndLoadXml1FormTest.java | 113 ----- .../ui/form/StoreAndLoadXml2FormTest.java | 231 --------- ...java => StoreAndLoadXmlFormFieldTest.java} | 116 ++++- .../StoreAndLoadXmlFormPropertiesTest.java | 253 ++++++++++ .../form/StoreAndLoadXmlFormTablesTest.java | 339 +++++++++++++ ...eAndLoadXmlWithFieldExtensionFormTest.java | 184 +++++++ ...oreAndLoadXmlWithFieldFilterFormTest.java} | 6 +- ...dLoadXmlWithPropertyExtensionFormTest.java | 12 +- .../StoreAndLoadXmlWrappedFormFieldTest.java | 448 +++++++++++++++++- .../scout/rt/client/ui/form/AbstractForm.java | 76 ++- .../scout/rt/client/ui/form/IForm.java | 14 +- .../ui/form/fields/AbstractFormField.java | 9 +- .../ui/form/fields/AbstractValueField.java | 11 +- .../rt/client/ui/form/fields/IFormField.java | 202 ++++---- .../fields/tablefield/AbstractTableField.java | 11 +- .../wrappedform/AbstractWrappedFormField.java | 7 +- .../composer/AbstractComposerField.java | 23 +- .../rt/platform/util/ObjectUtilityTest.java | 59 +++ .../scout/rt/platform/util/BeanUtility.java | 23 +- .../scout/rt/platform/util/ObjectUtility.java | 28 ++ 20 files changed, 1654 insertions(+), 511 deletions(-) delete mode 100644 org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml1FormTest.java delete mode 100644 org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml2FormTest.java rename org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/{StoreAndLoadXml3FormTest.java => StoreAndLoadXmlFormFieldTest.java} (69%) create mode 100644 org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlFormPropertiesTest.java create mode 100644 org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlFormTablesTest.java create mode 100644 org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWithFieldExtensionFormTest.java rename org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/{StoreAndLoadXml4FormTest.java => StoreAndLoadXmlWithFieldFilterFormTest.java} (91%) diff --git a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml1FormTest.java b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml1FormTest.java deleted file mode 100644 index bb0d0315de6..00000000000 --- a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml1FormTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2010, 2023 BSI Business Systems Integration AG - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.scout.rt.client.ui.form; - -import static org.junit.Assert.*; - -import org.eclipse.scout.rt.client.dto.FormData; -import org.eclipse.scout.rt.client.testenvironment.TestEnvironmentClientSession; -import org.eclipse.scout.rt.client.ui.form.fields.groupbox.IGroupBox; -import org.eclipse.scout.rt.testing.client.runner.ClientTestRunner; -import org.eclipse.scout.rt.testing.client.runner.RunWithClientSession; -import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; -import org.eclipse.scout.testing.client.form.DynamicCancelButton; -import org.eclipse.scout.testing.client.form.DynamicForm; -import org.eclipse.scout.testing.client.form.DynamicGroupBox; -import org.eclipse.scout.testing.client.form.FormHandler; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests exporting a from to an XML and vice versa. - */ -@RunWith(ClientTestRunner.class) -@RunWithSubject("default") -@RunWithClientSession(TestEnvironmentClientSession.class) -public class StoreAndLoadXml1FormTest { - - private static final boolean EXPECTED_BOOLEAN = true; - private static final int EXPECTED_INTEGER = 42; - private static final String EXPECTED_TEXT = "a test text"; - - @Test - public void testStoreAndLoadPrimitiveType() { - DynamicGroupBox mainBox = new DynamicGroupBox(new DynamicCancelButton()); - final DynamicFormWithProperties f = new DynamicFormWithProperties("Form1", mainBox); - try { - f.start(new FormHandler()); - - // change values - f.setPrimitiveBoolean(EXPECTED_BOOLEAN); - f.setPrimitiveInteger(EXPECTED_INTEGER); - f.setText(EXPECTED_TEXT); - - // export to xml and check result - String xml = f.storeToXmlString(); - assertNotNull(xml); - assertTrue(xml.contains("primitiveInteger")); - assertTrue(xml.contains("primitiveBoolean")); - assertTrue(xml.contains("text")); - - // reset properties - f.setPrimitiveBoolean(false); - f.setPrimitiveInteger(0); - f.setText(null); - - // import xml and check properties - f.loadFromXmlString(xml); - assertTrue(f.isPrimitiveBoolean()); - assertEquals(42, f.getPrimitiveInteger()); - assertEquals(EXPECTED_TEXT, f.getText()); - } - finally { - f.doClose(); - } - } - - public static final class DynamicFormWithProperties extends DynamicForm { - private boolean m_primitiveBoolean; - private int m_primitiveInteger; - private String m_text; - - private DynamicFormWithProperties(String title, IGroupBox mainBox) { - super(title, mainBox); - } - - @FormData - public boolean isPrimitiveBoolean() { - return m_primitiveBoolean; - } - - @FormData - public void setPrimitiveBoolean(boolean primitiveBoolean) { - m_primitiveBoolean = primitiveBoolean; - } - - @FormData - public int getPrimitiveInteger() { - return m_primitiveInteger; - } - - @FormData - public void setPrimitiveInteger(int primitiveInteger) { - m_primitiveInteger = primitiveInteger; - } - - @FormData - public String getText() { - return m_text; - } - - @FormData - public void setText(String text) { - m_text = text; - } - } -} diff --git a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml2FormTest.java b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml2FormTest.java deleted file mode 100644 index 266faae00ec..00000000000 --- a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml2FormTest.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (c) 2010, 2023 BSI Business Systems Integration AG - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.scout.rt.client.ui.form; - -import static org.junit.Assert.assertArrayEquals; - -import java.io.Serializable; - -import org.eclipse.scout.rt.client.testenvironment.TestEnvironmentClientSession; -import org.eclipse.scout.rt.client.ui.basic.table.AbstractTable; -import org.eclipse.scout.rt.client.ui.basic.table.columns.AbstractLongColumn; -import org.eclipse.scout.rt.client.ui.basic.table.columns.AbstractObjectColumn; -import org.eclipse.scout.rt.client.ui.basic.table.columns.AbstractStringColumn; -import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXml2FormTest.TestForm.MainBox.GroupBox.TableField; -import org.eclipse.scout.rt.client.ui.form.fields.button.AbstractCloseButton; -import org.eclipse.scout.rt.client.ui.form.fields.groupbox.AbstractGroupBox; -import org.eclipse.scout.rt.client.ui.form.fields.tablefield.AbstractTableField; -import org.eclipse.scout.rt.platform.Order; -import org.eclipse.scout.rt.testing.client.runner.ClientTestRunner; -import org.eclipse.scout.rt.testing.client.runner.RunWithClientSession; -import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Load and store of tables containing custom values and classes (classloading issues) - */ -@RunWith(ClientTestRunner.class) -@RunWithSubject("default") -@RunWithClientSession(TestEnvironmentClientSession.class) -public class StoreAndLoadXml2FormTest { - - static final Object[][] TABLE_DATA = new Object[][]{ - new Object[]{1L, "One", new java.util.Date()}, - new Object[]{2L, "Two", new StoreAndLoadXml2FormTest.InnerClass()}, - new Object[]{3L, "Three", new StoreAndLoadXml2FormTest.InnerClass.InnerInnerClass()} - }; - - public static class InnerClass implements Serializable { - private static final long serialVersionUID = 1L; - - private final String m_value = "Inner Level 1"; - - @Override - public int hashCode() { - return m_value.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj == null || obj.getClass() != this.getClass()) { - return false; - } - return ((InnerClass) obj).m_value.equals(this.m_value); - } - - public static class InnerInnerClass implements Serializable { - private static final long serialVersionUID = 2L; - - private final String m_value = "Inner Level 2"; - - @Override - public int hashCode() { - return m_value.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj == null || obj.getClass() != this.getClass()) { - return false; - } - return ((InnerInnerClass) obj).m_value.equals(this.m_value); - } - } - } - - public static class TestForm extends AbstractForm { - - public TestForm() { - super(); - } - - @Override - protected String getConfiguredTitle() { - return "TestForm"; - } - - public void startModify() { - startInternal(new ModifyHandler()); - } - - public TableField getTableField() { - return getFieldByClass(TableField.class); - } - - public MainBox getMainBox() { - return getFieldByClass(MainBox.class); - } - - @Order(10) - public class MainBox extends AbstractGroupBox { - - @Override - protected int getConfiguredGridColumnCount() { - return 2; - } - - @Order(10) - public class GroupBox extends AbstractGroupBox { - - @Override - protected int getConfiguredGridColumnCount() { - return 2; - } - - @Order(10) - public class TableField extends AbstractTableField { - - @Override - protected int getConfiguredGridW() { - return 2; - } - - @Override - protected int getConfiguredGridH() { - return 6; - } - - @Override - protected void execReloadTableData() { - Object[][] data = TABLE_DATA; - getTable().replaceRowsByMatrix(data); - super.execReloadTableData(); - } - - public class Table extends AbstractTable { - - public KeyColumn getKeyColumn() { - return getColumnSet().getColumnByClass(KeyColumn.class); - } - - public StringColumn getStringColumn() { - return getColumnSet().getColumnByClass(StringColumn.class); - } - - public CustomColumn getCustomColumn() { - return getColumnSet().getColumnByClass(CustomColumn.class); - } - - @Order(10) - public class KeyColumn extends AbstractLongColumn { - @Override - protected boolean getConfiguredPrimaryKey() { - return true; - } - - @Override - protected boolean getConfiguredDisplayable() { - return false; - } - } - - @Order(20) - public class StringColumn extends AbstractStringColumn { - @Override - protected String getConfiguredHeaderText() { - return "String"; - } - - @Override - protected int getConfiguredWidth() { - return 100; - } - } - - @Order(30) - public class CustomColumn extends AbstractObjectColumn { - @Override - protected String getConfiguredHeaderText() { - return "Custom"; - } - - @Override - protected int getConfiguredWidth() { - return 100; - } - } - } - } - } - - @Order(20) - public class CloseButton extends AbstractCloseButton { - } - } - - public class ModifyHandler extends AbstractFormHandler { - @Override - protected void execLoad() { - getTableField().reloadTableData(); - } - } - } - - @Test - public void test() { - TestForm f = new TestForm(); - try { - f.startModify(); - assertArrayEquals(TABLE_DATA, f.getTableField().getTable().getTableData()); - //store xml and clear - String xml = f.storeToXmlString(); - f.getTableField().getTable().discardAllRows(); - assertArrayEquals(new Object[0][0], f.getTableField().getTable().getTableData()); - //load xml - f.loadFromXmlString(xml); - assertArrayEquals(TABLE_DATA, f.getTableField().getTable().getTableData()); - } - finally { - f.doClose(); - } - } - -} diff --git a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml3FormTest.java b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlFormFieldTest.java similarity index 69% rename from org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml3FormTest.java rename to org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlFormFieldTest.java index 85849a0faf2..5ab980c598a 100644 --- a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml3FormTest.java +++ b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlFormFieldTest.java @@ -9,16 +9,21 @@ */ package org.eclipse.scout.rt.client.ui.form; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.util.List; import org.eclipse.scout.rt.client.testenvironment.TestEnvironmentClientSession; import org.eclipse.scout.rt.client.ui.form.fields.IFormField; +import org.eclipse.scout.rt.client.ui.form.fields.groupbox.AbstractGroupBox; +import org.eclipse.scout.rt.client.ui.form.fields.listbox.AbstractListBox; +import org.eclipse.scout.rt.client.ui.form.fields.longfield.AbstractLongField; +import org.eclipse.scout.rt.client.ui.form.fields.stringfield.AbstractStringField; import org.eclipse.scout.rt.client.ui.form.fixture.AbstractTestGroupBox.InnerTestGroupBox; import org.eclipse.scout.rt.client.ui.form.fixture.TestForm; import org.eclipse.scout.rt.client.ui.form.fixture.TestForm.MainBox.G1Box; import org.eclipse.scout.rt.client.ui.form.fixture.TestForm.MainBox.G2Box; +import org.eclipse.scout.rt.platform.Order; import org.eclipse.scout.rt.platform.util.CollectionUtility; import org.eclipse.scout.rt.platform.util.StringUtility; import org.eclipse.scout.rt.platform.util.XmlUtility; @@ -33,7 +38,7 @@ @RunWith(ClientTestRunner.class) @RunWithSubject("anna") @RunWithClientSession(TestEnvironmentClientSession.class) -public class StoreAndLoadXml3FormTest { +public class StoreAndLoadXmlFormFieldTest { @Test public void testXmlFieldIds() { @@ -98,7 +103,7 @@ public void testStoreLoad() { String xml = f.storeToXmlString(); f = new TestForm(); - f.loadFromXmlString(xml); + assertTrue(f.loadFromXmlString(xml)); //new form should contain the stored values assertEquals("t1", f.getText1Field().getValue()); @@ -112,6 +117,41 @@ public void testStoreLoad() { assertEquals("g2L", CollectionUtility.firstElement(f.getG2Box().getTestListBox().getValue())); } + @Test + public void testLoadUnknownFieldWithValue() { + MissingFieldSourceTestForm source = new MissingFieldSourceTestForm(); + source.getText1Field().setValue("t1"); + source.getMyListBox().setValue(CollectionUtility.hashSet("test")); + String xml = source.storeToXmlString(); + + MissingFieldTargetTestForm target = new MissingFieldTargetTestForm(); + assertFalse(target.loadFromXmlString(xml)); + assertEquals("t1", target.getText1Field().getValue()); + } + + @Test + public void testLoadUnknownFieldWithoutValue() { + MissingFieldSourceTestForm source = new MissingFieldSourceTestForm(); + source.getText1Field().setValue("t1"); + source.getMyListBox().setValue(CollectionUtility.emptyHashSet()); + String xml = source.storeToXmlString(); + + MissingFieldTargetTestForm target = new MissingFieldTargetTestForm(); + assertTrue(target.loadFromXmlString(xml)); + assertEquals("t1", target.getText1Field().getValue()); + } + + @Test + public void testLoadInvalidField() { + InvalidFieldSourceTestForm source = new InvalidFieldSourceTestForm(); + source.getText1Field().setValue("t1"); + String xml = source.storeToXmlString(); + + InvalidFieldTargetTestForm target = new InvalidFieldTargetTestForm(); + assertFalse(target.loadFromXmlString(xml)); + assertNull(target.getText1Field().getValue()); + } + @Test public void testLegacyLoad() { TestForm f = new TestForm(); @@ -134,7 +174,7 @@ public void testLegacyLoad() { // value should be imported to first field found f = new TestForm(); - f.loadFromXml(xml.getDocumentElement()); + assertTrue(f.loadFromXml(xml.getDocumentElement())); assertEquals("t3", f.getText3Field().getValue()); @@ -183,4 +223,72 @@ public String getXmlFieldQname() { return m_xmlFieldQname; } } + + public static final class MissingFieldSourceTestForm extends AbstractForm { + + public MainBox.MyListBox getMyListBox() { + return getFieldByClass(MainBox.MyListBox.class); + } + + public MainBox.Text1Field getText1Field() { + return getFieldByClass(MainBox.Text1Field.class); + } + + @Order(10) + public class MainBox extends AbstractGroupBox { + + @Order(10) + public class Text1Field extends AbstractStringField { + } + + @Order(20) + public class MyListBox extends AbstractListBox { + } + } + } + + public static final class MissingFieldTargetTestForm extends AbstractForm { + + public MainBox.Text1Field getText1Field() { + return getFieldByClass(MainBox.Text1Field.class); + } + + @Order(10) + public class MainBox extends AbstractGroupBox { + + @Order(10) + public class Text1Field extends AbstractStringField { + } + } + } + + public static final class InvalidFieldSourceTestForm extends AbstractForm { + + public MainBox.Text1Field getText1Field() { + return getFieldByClass(MainBox.Text1Field.class); + } + + @Order(10) + public class MainBox extends AbstractGroupBox { + + @Order(10) + public class Text1Field extends AbstractStringField { + } + } + } + + public static final class InvalidFieldTargetTestForm extends AbstractForm { + + public MainBox.Text1Field getText1Field() { + return getFieldByClass(MainBox.Text1Field.class); + } + + @Order(10) + public class MainBox extends AbstractGroupBox { + + @Order(10) + public class Text1Field extends AbstractLongField { // invalid type on purpose + } + } + } } diff --git a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlFormPropertiesTest.java b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlFormPropertiesTest.java new file mode 100644 index 00000000000..ef1318e7b98 --- /dev/null +++ b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlFormPropertiesTest.java @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2010, 2023 BSI Business Systems Integration AG + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.scout.rt.client.ui.form; + +import static org.junit.Assert.*; + +import org.eclipse.scout.rt.client.dto.FormData; +import org.eclipse.scout.rt.client.testenvironment.TestEnvironmentClientSession; +import org.eclipse.scout.rt.client.ui.form.fields.groupbox.IGroupBox; +import org.eclipse.scout.rt.testing.client.runner.ClientTestRunner; +import org.eclipse.scout.rt.testing.client.runner.RunWithClientSession; +import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; +import org.eclipse.scout.testing.client.form.DynamicCancelButton; +import org.eclipse.scout.testing.client.form.DynamicForm; +import org.eclipse.scout.testing.client.form.DynamicGroupBox; +import org.eclipse.scout.testing.client.form.FormHandler; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests exporting properties from/to an XML and vice versa. + */ +@RunWith(ClientTestRunner.class) +@RunWithSubject("default") +@RunWithClientSession(TestEnvironmentClientSession.class) +public class StoreAndLoadXmlFormPropertiesTest { + + private static final boolean EXPECTED_BOOLEAN = true; + private static final int EXPECTED_INTEGER = 42; + private static final String EXPECTED_TEXT = "a test text"; + + @Test + public void testStoreAndLoadPrimitiveType() { + DynamicGroupBox mainBox = new DynamicGroupBox(new DynamicCancelButton()); + final DynamicFormWithAllProperties f = new DynamicFormWithAllProperties("Form1", mainBox); + try { + initializeFormWithAllProperties(f); + String xml = assertStoredAllPropertiesToXml(f); + + // reset properties + f.setPrimitiveBoolean(false); + f.setPrimitiveInteger(0); + f.setText(null); + + // import xml and check properties + assertTrue(f.loadFromXmlString(xml)); + assertTrue(f.isPrimitiveBoolean()); + assertEquals(42, f.getPrimitiveInteger()); + assertEquals(EXPECTED_TEXT, f.getText()); + } + finally { + f.doClose(); + } + } + + @Test + public void testUnknownPropertyWithValue() { + DynamicGroupBox mainBox = new DynamicGroupBox(new DynamicCancelButton()); + final DynamicFormWithAllProperties source = new DynamicFormWithAllProperties("Form2Source", mainBox); + final DynamicFormWithSomeProperties target = new DynamicFormWithSomeProperties("Form2Target", mainBox); + try { + initializeFormWithAllProperties(source); + String xml = assertStoredAllPropertiesToXml(source); + + // import xml to a form with one property missing + target.start(new FormHandler()); + assertFalse(target.loadFromXmlString(xml)); // import unsuccessful + // assert that valid properties were imported correctly + assertEquals(EXPECTED_INTEGER, target.getPrimitiveInteger()); + assertEquals(EXPECTED_BOOLEAN, target.isPrimitiveBoolean()); + } + finally { + source.doClose(); + target.doClose(); + } + } + @Test + public void testUnknownPropertyWithoutValue() { + DynamicGroupBox mainBox = new DynamicGroupBox(new DynamicCancelButton()); + final DynamicFormWithAllProperties source = new DynamicFormWithAllProperties("Form2Source", mainBox); + final DynamicFormWithSomeProperties target = new DynamicFormWithSomeProperties("Form2Target", mainBox); + try { + initializeFormWithAllProperties(source); + source.setText(null); // this property will be lost, but has no value anyway + String xml = assertStoredAllPropertiesToXml(source); + + // import xml to a form with one property missing + target.start(new FormHandler()); + assertTrue(target.loadFromXmlString(xml)); // import successful because unknown property had no value + // assert that valid properties were imported correctly + assertEquals(EXPECTED_INTEGER, target.getPrimitiveInteger()); + assertEquals(EXPECTED_BOOLEAN, target.isPrimitiveBoolean()); + } + finally { + source.doClose(); + target.doClose(); + } + } + + @Test + public void testInvalidProperty() { + DynamicGroupBox mainBox = new DynamicGroupBox(new DynamicCancelButton()); + final DynamicFormWithAllProperties source = new DynamicFormWithAllProperties("Form3Source", mainBox); + final DynamicFormWithInvalidProperties target = new DynamicFormWithInvalidProperties("Form3Target", mainBox); + try { + initializeFormWithAllProperties(source); + String xml = assertStoredAllPropertiesToXml(source); + + // import xml to a form containing same property name but different type + target.start(new FormHandler()); + assertFalse(target.loadFromXmlString(xml)); + assertEquals(0, target.getText()); + assertEquals(EXPECTED_BOOLEAN, target.isPrimitiveBoolean()); + assertEquals(EXPECTED_INTEGER, target.getPrimitiveInteger()); + } + finally { + source.doClose(); + target.doClose(); + } + } + + private static void initializeFormWithAllProperties(DynamicFormWithAllProperties source) { + source.start(new FormHandler()); + source.setPrimitiveBoolean(EXPECTED_BOOLEAN); + source.setPrimitiveInteger(EXPECTED_INTEGER); + source.setText(EXPECTED_TEXT); + } + + private static String assertStoredAllPropertiesToXml(DynamicFormWithAllProperties source) { + String xml = source.storeToXmlString(); + assertNotNull(xml); + assertTrue(xml.contains("primitiveInteger")); + assertTrue(xml.contains("primitiveBoolean")); + assertTrue(xml.contains("text")); + return xml; + } + + protected static final class DynamicFormWithAllProperties extends DynamicForm { + private boolean m_primitiveBoolean; + private int m_primitiveInteger; + private String m_text; + + private DynamicFormWithAllProperties(String title, IGroupBox mainBox) { + super(title, mainBox); + } + + @FormData + public boolean isPrimitiveBoolean() { + return m_primitiveBoolean; + } + + @FormData + public void setPrimitiveBoolean(boolean primitiveBoolean) { + m_primitiveBoolean = primitiveBoolean; + } + + @FormData + public int getPrimitiveInteger() { + return m_primitiveInteger; + } + + @FormData + public void setPrimitiveInteger(int primitiveInteger) { + m_primitiveInteger = primitiveInteger; + } + + @FormData + public String getText() { + return m_text; + } + + @FormData + public void setText(String text) { + m_text = text; + } + } + + protected static final class DynamicFormWithSomeProperties extends DynamicForm { + private boolean m_primitiveBoolean; + private int m_primitiveInteger; + + private DynamicFormWithSomeProperties(String title, IGroupBox mainBox) { + super(title, mainBox); + } + + @FormData + public boolean isPrimitiveBoolean() { + return m_primitiveBoolean; + } + + @FormData + public void setPrimitiveBoolean(boolean primitiveBoolean) { + m_primitiveBoolean = primitiveBoolean; + } + + @FormData + public int getPrimitiveInteger() { + return m_primitiveInteger; + } + + @FormData + public void setPrimitiveInteger(int primitiveInteger) { + m_primitiveInteger = primitiveInteger; + } + } + + protected static final class DynamicFormWithInvalidProperties extends DynamicForm { + private boolean m_primitiveBoolean; + private int m_primitiveInteger; + private int m_text; // this property has an invalid type - this is expected + + private DynamicFormWithInvalidProperties(String title, IGroupBox mainBox) { + super(title, mainBox); + } + + @FormData + public boolean isPrimitiveBoolean() { + return m_primitiveBoolean; + } + + @FormData + public void setPrimitiveBoolean(boolean primitiveBoolean) { + m_primitiveBoolean = primitiveBoolean; + } + + @FormData + public int getPrimitiveInteger() { + return m_primitiveInteger; + } + + @FormData + public void setPrimitiveInteger(int primitiveInteger) { + m_primitiveInteger = primitiveInteger; + } + + @FormData + public int getText() { + return m_text; + } + + @FormData + public void setText(int text) { + m_text = text; + } + } +} diff --git a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlFormTablesTest.java b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlFormTablesTest.java new file mode 100644 index 00000000000..c051c5ef4ac --- /dev/null +++ b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlFormTablesTest.java @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2010, 2023 BSI Business Systems Integration AG + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.scout.rt.client.ui.form; + +import static org.junit.Assert.*; + +import java.io.Serializable; + +import org.eclipse.scout.rt.client.testenvironment.TestEnvironmentClientSession; +import org.eclipse.scout.rt.client.ui.basic.table.AbstractTable; +import org.eclipse.scout.rt.client.ui.basic.table.columns.AbstractLongColumn; +import org.eclipse.scout.rt.client.ui.basic.table.columns.AbstractObjectColumn; +import org.eclipse.scout.rt.client.ui.basic.table.columns.AbstractStringColumn; +import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXmlFormTablesTest.FullTestForm.MainBox.GroupBox.FullTableField; +import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXmlFormTablesTest.FullTestForm.MainBox.GroupBox.FullTableField.FullTestTable; +import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXmlFormTablesTest.PartialTestForm.MainBox.GroupBox.PartialTableField; +import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXmlFormTablesTest.PartialTestForm.MainBox.GroupBox.PartialTableField.PartialTestTable; +import org.eclipse.scout.rt.client.ui.form.fields.button.AbstractCloseButton; +import org.eclipse.scout.rt.client.ui.form.fields.groupbox.AbstractGroupBox; +import org.eclipse.scout.rt.client.ui.form.fields.tablefield.AbstractTableField; +import org.eclipse.scout.rt.platform.Order; +import org.eclipse.scout.rt.testing.client.runner.ClientTestRunner; +import org.eclipse.scout.rt.testing.client.runner.RunWithClientSession; +import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Load and store of tables containing custom values and classes (classloading issues) + */ +@RunWith(ClientTestRunner.class) +@RunWithSubject("default") +@RunWithClientSession(TestEnvironmentClientSession.class) +public class StoreAndLoadXmlFormTablesTest { + + static final Object[][] TABLE_DATA = new Object[][]{ + new Object[]{1L, "One", new java.util.Date()}, + new Object[]{2L, "Two", new StoreAndLoadXmlFormTablesTest.InnerClass()}, + new Object[]{3L, "Three", new StoreAndLoadXmlFormTablesTest.InnerClass.InnerInnerClass()} + }; + + public static class InnerClass implements Serializable { + private static final long serialVersionUID = 1L; + + private final String m_value = "Inner Level 1"; + + @Override + public int hashCode() { + return m_value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + return ((InnerClass) obj).m_value.equals(this.m_value); + } + + public static class InnerInnerClass implements Serializable { + private static final long serialVersionUID = 2L; + + private final String m_value = "Inner Level 2"; + + @Override + public int hashCode() { + return m_value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + return ((InnerInnerClass) obj).m_value.equals(this.m_value); + } + } + } + + public static class AbstractTestTable extends AbstractTable { + + public AbstractTestTable.KeyColumn getKeyColumn() { + return getColumnSet().getColumnByClass(AbstractTestTable.KeyColumn.class); + } + + public AbstractTestTable.StringColumn getStringColumn() { + return getColumnSet().getColumnByClass(AbstractTestTable.StringColumn.class); + } + + @Order(10) + public class KeyColumn extends AbstractLongColumn { + @Override + protected boolean getConfiguredPrimaryKey() { + return true; + } + + @Override + protected boolean getConfiguredDisplayable() { + return false; + } + } + + @Order(20) + public class StringColumn extends AbstractStringColumn { + @Override + protected String getConfiguredHeaderText() { + return "String"; + } + + @Override + protected int getConfiguredWidth() { + return 100; + } + } + } + + public static class TestFormWithoutTable extends AbstractForm { + + @Order(10) + public class MainBox extends AbstractGroupBox { + } + } + + public static class FullTestForm extends AbstractForm { + + @Override + protected String getConfiguredTitle() { + return "FullTestForm"; + } + + public void startModify() { + startInternal(new ModifyHandler()); + } + + public FullTableField getFullTableField() { + return getFieldByClass(FullTableField.class); + } + + public MainBox getMainBox() { + return getFieldByClass(MainBox.class); + } + + @Order(10) + public class MainBox extends AbstractGroupBox { + + @Override + protected int getConfiguredGridColumnCount() { + return 2; + } + + @Order(10) + public class GroupBox extends AbstractGroupBox { + + @Override + protected int getConfiguredGridColumnCount() { + return 2; + } + + @Order(10) + public class FullTableField extends AbstractTableField { + + @Override + protected int getConfiguredGridW() { + return 2; + } + + @Override + protected int getConfiguredGridH() { + return 6; + } + + @Override + protected void execReloadTableData() { + Object[][] data = TABLE_DATA; + getTable().replaceRowsByMatrix(data); + super.execReloadTableData(); + } + + public class FullTestTable extends AbstractTestTable { + + public FullTestTable.CustomColumn getCustomColumn() { + return getColumnSet().getColumnByClass(FullTestTable.CustomColumn.class); + } + + @Order(30) + public class CustomColumn extends AbstractObjectColumn { + @Override + protected String getConfiguredHeaderText() { + return "Custom"; + } + + @Override + protected int getConfiguredWidth() { + return 100; + } + } + } + } + } + + @Order(20) + public class CloseButton extends AbstractCloseButton { + } + } + + public class ModifyHandler extends AbstractFormHandler { + @Override + protected void execLoad() { + getFullTableField().reloadTableData(); + } + } + } + + public static class PartialTestForm extends AbstractForm { + + @Override + protected String getConfiguredTitle() { + return "PartialTestForm"; + } + + public PartialTableField getPartialTableField() { + return getFieldByClass(PartialTableField.class); + } + + public MainBox getMainBox() { + return getFieldByClass(MainBox.class); + } + + @Order(10) + public class MainBox extends AbstractGroupBox { + + @Override + protected int getConfiguredGridColumnCount() { + return 2; + } + + @Order(10) + public class GroupBox extends AbstractGroupBox { + + @Override + protected int getConfiguredGridColumnCount() { + return 2; + } + + @Order(10) + public class PartialTableField extends AbstractTableField { + + public class PartialTestTable extends AbstractTestTable { + } + } + } + } + } + + @Test + public void testLoadTableData() { + FullTestForm f = new FullTestForm(); + try { + f.startModify(); + assertArrayEquals(TABLE_DATA, f.getFullTableField().getTable().getTableData()); + //store xml and clear + String xml = f.storeToXmlString(); + f.getFullTableField().getTable().discardAllRows(); + assertArrayEquals(new Object[0][0], f.getFullTableField().getTable().getTableData()); + //load xml + assertTrue(f.loadFromXmlString(xml)); + assertArrayEquals(TABLE_DATA, f.getFullTableField().getTable().getTableData()); + } + finally { + f.doClose(); + } + } + + @Test + public void testLoadInvalidTableData() { + FullTestForm sourceForm = new FullTestForm(); + PartialTestForm targetForm = new PartialTestForm(); + try { + sourceForm.startModify(); + assertArrayEquals(TABLE_DATA, sourceForm.getFullTableField().getTable().getTableData()); + String xml = sourceForm.storeToXmlString(); + + //load xml + targetForm.start(); + assertArrayEquals(new Object[0][0], targetForm.getPartialTableField().getTable().getTableData()); + assertFalse(targetForm.loadFromXmlString(xml)); + assertArrayEquals(new Object[0][0], targetForm.getPartialTableField().getTable().getTableData()); + } + finally { + targetForm.doClose(); + sourceForm.doClose(); + } + } + + @Test + public void testLoadUnknownTableWithValue() { + FullTestForm sourceForm = new FullTestForm(); + TestFormWithoutTable targetForm = new TestFormWithoutTable(); + try { + sourceForm.startModify(); + assertArrayEquals(TABLE_DATA, sourceForm.getFullTableField().getTable().getTableData()); + String xml = sourceForm.storeToXmlString(); + + //load xml + targetForm.start(); + assertFalse(targetForm.loadFromXmlString(xml)); + } + finally { + targetForm.doClose(); + sourceForm.doClose(); + } + } + + @Test + public void testLoadUnknownTableWithoutValue() { + FullTestForm sourceForm = new FullTestForm(); + TestFormWithoutTable targetForm = new TestFormWithoutTable(); + try { + sourceForm.start(); + assertArrayEquals(new Object[0][0], sourceForm.getFullTableField().getTable().getTableData()); + String xml = sourceForm.storeToXmlString(); + + //load xml + targetForm.start(); + assertTrue(targetForm.loadFromXmlString(xml)); + } + finally { + targetForm.doClose(); + sourceForm.doClose(); + } + } +} diff --git a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWithFieldExtensionFormTest.java b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWithFieldExtensionFormTest.java new file mode 100644 index 00000000000..6a655761466 --- /dev/null +++ b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWithFieldExtensionFormTest.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2010, 2023 BSI Business Systems Integration AG + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.scout.rt.client.ui.form; + +import static org.junit.Assert.*; + +import org.eclipse.scout.extension.AbstractLocalExtensionTestCase; +import org.eclipse.scout.rt.client.extension.ui.form.AbstractFormExtension; +import org.eclipse.scout.rt.client.extension.ui.form.fields.groupbox.AbstractGroupBoxExtension; +import org.eclipse.scout.rt.client.testenvironment.TestEnvironmentClientSession; +import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXmlWithFieldExtensionFormTest.TestForm.MainBox; +import org.eclipse.scout.rt.client.ui.form.fields.groupbox.AbstractGroupBox; +import org.eclipse.scout.rt.client.ui.form.fields.integerfield.AbstractIntegerField; +import org.eclipse.scout.rt.client.ui.form.fields.stringfield.AbstractStringField; +import org.eclipse.scout.rt.platform.BEANS; +import org.eclipse.scout.rt.platform.Order; +import org.eclipse.scout.rt.shared.extension.IExtensionRegistry; +import org.eclipse.scout.rt.testing.client.runner.ClientTestRunner; +import org.eclipse.scout.rt.testing.client.runner.RunWithClientSession; +import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests loading and storing to xml of forms with extensions containing fields. + */ +@RunWith(ClientTestRunner.class) +@RunWithSubject("default") +@RunWithClientSession(TestEnvironmentClientSession.class) +public class StoreAndLoadXmlWithFieldExtensionFormTest extends AbstractLocalExtensionTestCase { + + @After + public void cleanup() { + BEANS.get(IExtensionRegistry.class).deregister(TestFormExtensionWithOneField.class); + BEANS.get(IExtensionRegistry.class).deregister(TestFormExtensionWithInvalidField.class); + } + + @Test + public void testExtensionFieldOnForm() { + BEANS.get(IExtensionRegistry.class).register(TestFormExtensionWithOneField.class, TestForm.class); + + // setup initial form with extension containing data + TestForm form = new TestForm(); + TestFormExtensionWithOneField formExtension = form.getExtension(TestFormExtensionWithOneField.class); + assertEquals(2, form.getAllExtensions().size()); + assertNotNull(formExtension); + form.getTextField().setValue("textFieldValue"); + formExtension.getExtensionTextField().setValue("extensionTextFieldValue"); + String xml = form.storeToXmlString(); + + TestForm target = new TestForm(); + TestFormExtensionWithOneField targetExtension = target.getExtension(TestFormExtensionWithOneField.class); + assertNotNull(targetExtension); + assertTrue(target.loadFromXmlString(xml)); + assertEquals("textFieldValue", target.getTextField().getValue()); + assertEquals("extensionTextFieldValue", targetExtension.getExtensionTextField().getValue()); + } + + @Test + public void testExtensionFieldMissingOnForm() { + BEANS.get(IExtensionRegistry.class).register(TestFormExtensionWithOneField.class, TestForm.class); + + // setup initial form with extension containing data + TestForm form = new TestForm(); + TestFormExtensionWithOneField formExtension = form.getExtension(TestFormExtensionWithOneField.class); + assertEquals(2, form.getAllExtensions().size()); + assertNotNull(formExtension); + form.getTextField().setValue("textFieldValue"); + formExtension.getExtensionTextField().setValue("extensionTextFieldValue"); + String xml = form.storeToXmlString(); + + // deregister extension and try to load XML + BEANS.get(IExtensionRegistry.class).deregister(TestFormExtensionWithOneField.class); + + TestForm target = new TestForm(); + assertNull(target.getExtension(TestFormExtensionWithOneField.class)); + assertFalse(target.loadFromXmlString(xml)); + assertEquals("textFieldValue", target.getTextField().getValue()); + } + + @Test + public void testExtensionFieldInvalidType() { + BEANS.get(IExtensionRegistry.class).register(TestFormExtensionWithOneField.class, TestForm.class); + + // setup initial form with extension containing data + TestForm form = new TestForm(); + TestFormExtensionWithOneField formExtension = form.getExtension(TestFormExtensionWithOneField.class); + assertEquals(2, form.getAllExtensions().size()); + assertNotNull(formExtension); + form.getTextField().setValue("textFieldValue"); + formExtension.getExtensionTextField().setValue("extensionTextFieldValue"); + String xml = form.storeToXmlString(); + + // switch extension and try to load XML + BEANS.get(IExtensionRegistry.class).deregister(TestFormExtensionWithOneField.class); + BEANS.get(IExtensionRegistry.class).register(TestFormExtensionWithInvalidField.class, TestForm.class); + + TestForm target = new TestForm(); + assertNull(target.getExtension(TestFormExtensionWithOneField.class)); + TestFormExtensionWithInvalidField targetExtension = target.getExtension(TestFormExtensionWithInvalidField.class); + assertNotNull(targetExtension); + assertFalse(target.loadFromXmlString(xml)); + assertEquals("textFieldValue", target.getTextField().getValue()); + } + + public static final class TestForm extends AbstractForm { + + public MainBox.GroupBox.TextField getTextField() { + return getFieldByClass(MainBox.GroupBox.TextField.class); + } + + public MainBox getMainBox() { + return getFieldByClass(MainBox.class); + } + + public MainBox.GroupBox getGroupBox() { + return getFieldByClass(MainBox.GroupBox.class); + } + + @Order(10.0) + public class MainBox extends AbstractGroupBox { + + @Order(10.0) + public class GroupBox extends AbstractGroupBox { + + @Order(10.0) + public class TextField extends AbstractStringField { + } + } + } + } + + public static final class TestFormExtensionWithOneField extends AbstractFormExtension { + + public TestFormExtensionWithOneField(TestForm owner) { + super(owner); + } + + public GroupBoxExtension.ExtensionTextField getExtensionTextField() { + return getOwner().getFieldByClass(GroupBoxExtension.ExtensionTextField.class); + } + + public class GroupBoxExtension extends AbstractGroupBoxExtension { + + public GroupBoxExtension(MainBox.GroupBox owner) { + super(owner); + } + + @Order(10) + public class ExtensionTextField extends AbstractStringField { + } + } + } + + public static final class TestFormExtensionWithInvalidField extends AbstractFormExtension { + + public TestFormExtensionWithInvalidField(TestForm owner) { + super(owner); + } + + public GroupBoxExtension.ExtensionTextField getExtensionTextField() { + return getOwner().getFieldByClass(GroupBoxExtension.ExtensionTextField.class); + } + + public class GroupBoxExtension extends AbstractGroupBoxExtension { + + public GroupBoxExtension(MainBox.GroupBox owner) { + super(owner); + } + + @Order(10) + public class ExtensionTextField extends AbstractIntegerField { // invalid type on purpose + } + } + } +} diff --git a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml4FormTest.java b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWithFieldFilterFormTest.java similarity index 91% rename from org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml4FormTest.java rename to org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWithFieldFilterFormTest.java index 010db6446ed..b12164c6221 100644 --- a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXml4FormTest.java +++ b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWithFieldFilterFormTest.java @@ -12,8 +12,8 @@ import static org.junit.Assert.*; import org.eclipse.scout.rt.client.testenvironment.TestEnvironmentClientSession; -import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXml4FormTest.TestForm.MainBox.GroupBox.ExcludedStringField; -import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXml4FormTest.TestForm.MainBox.GroupBox.IncludedStringField; +import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXmlWithFieldFilterFormTest.TestForm.MainBox.GroupBox.ExcludedStringField; +import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXmlWithFieldFilterFormTest.TestForm.MainBox.GroupBox.IncludedStringField; import org.eclipse.scout.rt.client.ui.form.fields.button.AbstractCloseButton; import org.eclipse.scout.rt.client.ui.form.fields.groupbox.AbstractGroupBox; import org.eclipse.scout.rt.client.ui.form.fields.stringfield.AbstractStringField; @@ -30,7 +30,7 @@ @RunWith(ClientTestRunner.class) @RunWithSubject("default") @RunWithClientSession(TestEnvironmentClientSession.class) -public class StoreAndLoadXml4FormTest { +public class StoreAndLoadXmlWithFieldFilterFormTest { static final String TEST_DATA = "testdata"; diff --git a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWithPropertyExtensionFormTest.java b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWithPropertyExtensionFormTest.java index 0acf3d80a9e..5791196cc81 100644 --- a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWithPropertyExtensionFormTest.java +++ b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWithPropertyExtensionFormTest.java @@ -37,7 +37,7 @@ import org.w3c.dom.Element; /** - * Tests loading and storing to xml of forms with extensions. + * Tests loading and storing to xml of forms with extensions for properties. */ @RunWith(ClientTestRunner.class) @RunWithSubject("default") @@ -73,7 +73,7 @@ public void testOneExtensionPropertyOnForm() { assertFalse(xml.contains("\"textFieldProperty\"")); TestForm loadedForm = new TestForm(); - loadedForm.loadFromXmlString(xml); + assertTrue(loadedForm.loadFromXmlString(xml)); TestFormExtension loadedFormExtension = loadedForm.getExtension(TestFormExtension.class); assertEquals("staticPropertyValue", loadedForm.getProperty()); @@ -114,7 +114,7 @@ public void testNonExistingExtensionPropertyOnForm() { assertFalse(xml.contains("\"textFieldProperty\"")); TestForm loadedForm = new TestForm(); - loadedForm.loadFromXmlString(xml); + assertFalse(loadedForm.loadFromXmlString(xml)); TestFormExtension loadedFormExtension = loadedForm.getExtension(TestFormExtension.class); assertEquals("staticPropertyValue", loadedForm.getProperty()); @@ -157,7 +157,7 @@ public void testTwoExtensionPropertyOnForm() { assertFalse(xml.contains("\"textFieldProperty\"")); // field properties are not stored in XML TestForm loadedForm = new TestForm(); - loadedForm.loadFromXmlString(xml); + assertTrue(loadedForm.loadFromXmlString(xml)); TestFormExtension loadedFormExtension = loadedForm.getExtension(TestFormExtension.class); OtherTestFormExtension loadedOtherFormExtension = loadedForm.getExtension(OtherTestFormExtension.class); @@ -202,7 +202,7 @@ public void testExtensionPropertyOnFormField() { assertFalse(xml.contains("name=\"extensionProperty\"")); // field extension properties are not stored in XML TestForm loadedForm = new TestForm(); - loadedForm.loadFromXmlString(xml); + assertTrue(loadedForm.loadFromXmlString(xml)); TestFormFieldExtension loadedFieldExtension = loadedForm.getTextField().getExtension(TestFormFieldExtension.class); @@ -231,7 +231,7 @@ public void testLoadXmlWithoutPropertiesElement() { } TestForm loadedForm = new TestForm(); - loadedForm.loadFromXmlString(xml); + assertTrue(loadedForm.loadFromXmlString(xml)); assertNull(loadedForm.getProperty()); assertEquals("staticFieldValue", loadedForm.getTextField().getValue()); diff --git a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWrappedFormFieldTest.java b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWrappedFormFieldTest.java index 01aa05ee5c8..b5949d447ba 100644 --- a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWrappedFormFieldTest.java +++ b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/ui/form/StoreAndLoadXmlWrappedFormFieldTest.java @@ -13,14 +13,15 @@ import java.util.List; +import org.eclipse.scout.rt.client.dto.FormData; import org.eclipse.scout.rt.client.testenvironment.TestEnvironmentClientSession; -import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXmlWrappedFormFieldTest.InnerTestForm.MainBox.InnerTextField; import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXmlWrappedFormFieldTest.TestForm.MainBox.GroupBox; import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXmlWrappedFormFieldTest.TestForm.MainBox.GroupBox.TextField; import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXmlWrappedFormFieldTest.TestForm.MainBox.GroupBox.Variant1Field; import org.eclipse.scout.rt.client.ui.form.StoreAndLoadXmlWrappedFormFieldTest.TestForm.MainBox.GroupBox.Variant2Field; import org.eclipse.scout.rt.client.ui.form.fields.IFormField; import org.eclipse.scout.rt.client.ui.form.fields.groupbox.AbstractGroupBox; +import org.eclipse.scout.rt.client.ui.form.fields.integerfield.AbstractIntegerField; import org.eclipse.scout.rt.client.ui.form.fields.stringfield.AbstractStringField; import org.eclipse.scout.rt.client.ui.form.fields.wrappedform.AbstractWrappedFormField; import org.eclipse.scout.rt.platform.Order; @@ -43,18 +44,22 @@ @RunWithClientSession(TestEnvironmentClientSession.class) public class StoreAndLoadXmlWrappedFormFieldTest { - private static final String STRING1 = "sample text"; - private static final String STRING2 = "Variant1.innerTextField"; - private static final String STRING3 = "Variant2.innerTextField"; + private static final String TOP_LEVE_TEXT_FIELD_VALUE = "sample text"; + private static final String INNER_FORM1_TEXT_FIELD_VALUE = "Variant1.innerTextField"; + private static final String INNER_FORM1_TEXT_PROPERTY_VALUE = "Variant1.innerTextProperty"; + private static final String INNER_FORM2_TEXT_FIELD_VALUE = "Variant2.innerTextField"; + private static final String INNER_FORM2_TEXT_PROPERTY_VALUE = "Variant2.innerTextProperty"; @Test public void testStoreAndLoad() { TestForm testForm = new TestForm(); // set values - testForm.getTextField().setValue(STRING1); - testForm.getVariant1Field().getInnerForm().getInnerTextField().setValue(STRING2); - testForm.getVariant2Field().getInnerForm().getInnerTextField().setValue(STRING3); + testForm.getTextField().setValue(TOP_LEVE_TEXT_FIELD_VALUE); + testForm.getVariant1Field().getInnerForm().getInnerTextField().setValue(INNER_FORM1_TEXT_FIELD_VALUE); + testForm.getVariant2Field().getInnerForm().getInnerTextField().setValue(INNER_FORM2_TEXT_FIELD_VALUE); + testForm.getVariant1Field().getInnerForm().setText(INNER_FORM1_TEXT_PROPERTY_VALUE); + testForm.getVariant2Field().getInnerForm().setText(INNER_FORM2_TEXT_PROPERTY_VALUE); assertFormValues(testForm); // get xml of form state @@ -64,17 +69,131 @@ public void testStoreAndLoad() { // clear all values testForm.getTextField().setValue(null); testForm.getVariant1Field().getInnerForm().getInnerTextField().setValue(null); + testForm.getVariant1Field().getInnerForm().setText(null); testForm.getVariant2Field().getInnerForm().getInnerTextField().setValue(null); + testForm.getVariant2Field().getInnerForm().setText(null); // set xml to form again - testForm.loadFromXmlString(xml); + assertTrue(testForm.loadFromXmlString(xml)); assertFormValues(testForm); } + @Test + public void testLoadUnknownInnerFormWithoutValue() { + TestForm testForm = new TestForm(); + String xml = testForm.storeToXmlString(); + + // should not be successful + // only best-effort. we do not check inner forms recursively for values + TestFormWithoutInnerForm target = new TestFormWithoutInnerForm(); + assertFalse(target.loadFromXmlString(xml)); + assertNull(target.getTextField().getValue()); + } + + @Test + public void testLoadUnknownInnerFormWithFieldValue() { + TestForm testForm = new TestForm(); + testForm.getVariant1Field().getInnerForm().getInnerTextField().setValue(INNER_FORM1_TEXT_FIELD_VALUE); + String xml = testForm.storeToXmlString(); + + // should not be successful + TestFormWithoutInnerForm target = new TestFormWithoutInnerForm(); + assertFalse(target.loadFromXmlString(xml)); + assertNull(target.getTextField().getValue()); + } + + @Test + public void testLoadUnknownInnerFormWithPropertyValue() { + TestForm testForm = new TestForm(); + testForm.getVariant1Field().getInnerForm().setText(INNER_FORM1_TEXT_PROPERTY_VALUE); + String xml = testForm.storeToXmlString(); + + // should not be successful + TestFormWithoutInnerForm target = new TestFormWithoutInnerForm(); + assertFalse(target.loadFromXmlString(xml)); + assertNull(target.getTextField().getValue()); + } + + @Test + public void testLoadInnerFormWithUnknownFieldWithValue() { + TestForm testForm = new TestForm(); + testForm.getVariant1Field().getInnerForm().getInnerTextField().setValue(INNER_FORM1_TEXT_FIELD_VALUE); + String xml = testForm.storeToXmlString(); + + // should not be successful + TestFormWithInnerFormWithoutField target = new TestFormWithInnerFormWithoutField(); + assertFalse(target.loadFromXmlString(xml)); + assertNull(target.getTextField().getValue()); + } + + @Test + public void testLoadInnerFormWithUnknownFieldWithoutValue() { + TestForm testForm = new TestForm(); + testForm.getVariant1Field().getInnerForm().getInnerTextField().setValue(null); + String xml = testForm.storeToXmlString(); + + // should be successful because no value is set + TestFormWithInnerFormWithoutField target = new TestFormWithInnerFormWithoutField(); + assertTrue(target.loadFromXmlString(xml)); + assertNull(target.getTextField().getValue()); + } + + @Test + public void testLoadInnerFormWithInvalidField() { + TestForm testForm = new TestForm(); + testForm.getVariant1Field().getInnerForm().getInnerTextField().setValue(INNER_FORM1_TEXT_FIELD_VALUE); + String xml = testForm.storeToXmlString(); + + // should not be successful + TestFormWithInnerFormWithInvalidField target = new TestFormWithInnerFormWithInvalidField(); + assertFalse(target.loadFromXmlString(xml)); + assertNull(target.getVariant1Field().getInnerForm().getInnerTextField().getValue()); + } + + @Test + public void testLoadInnerFormWithUnknownPropertyWithValue() { + TestForm testForm = new TestForm(); + testForm.getVariant1Field().getInnerForm().setText(INNER_FORM1_TEXT_PROPERTY_VALUE); + String xml = testForm.storeToXmlString(); + + // should not be successful + TestFormWithInnerFormWithoutProperty target = new TestFormWithInnerFormWithoutProperty(); + assertFalse(target.loadFromXmlString(xml)); + assertNull(target.getTextField().getValue()); + assertNull(target.getVariant1Field().getInnerForm().getInnerTextField().getValue()); + } + + @Test + public void testLoadInnerFormWithUnknownPropertyWithoutValue() { + TestForm testForm = new TestForm(); + testForm.getVariant1Field().getInnerForm().setText(null); + String xml = testForm.storeToXmlString(); + + // should be successful because no value is set + TestFormWithInnerFormWithoutProperty target = new TestFormWithInnerFormWithoutProperty(); + assertTrue(target.loadFromXmlString(xml)); + assertNull(target.getTextField().getValue()); + assertNull(target.getVariant1Field().getInnerForm().getInnerTextField().getValue()); + } + + @Test + public void testLoadInnerFormWithInvalidProperty() { + TestForm testForm = new TestForm(); + testForm.getVariant1Field().getInnerForm().setText(INNER_FORM1_TEXT_PROPERTY_VALUE); + String xml = testForm.storeToXmlString(); + + // should not be successful + TestFormWithInnerFormWithInvalidProperty target = new TestFormWithInnerFormWithInvalidProperty(); + assertFalse(target.loadFromXmlString(xml)); + assertEquals(0L, target.getVariant1Field().getInnerForm().getText()); + } + private void assertFormValues(TestForm testForm) { - assertEquals(STRING1, testForm.getTextField().getValue()); - assertEquals(STRING2, testForm.getVariant1Field().getInnerForm().getInnerTextField().getValue()); - assertEquals(STRING3, testForm.getVariant2Field().getInnerForm().getInnerTextField().getValue()); + assertEquals(TOP_LEVE_TEXT_FIELD_VALUE, testForm.getTextField().getValue()); + assertEquals(INNER_FORM1_TEXT_FIELD_VALUE, testForm.getVariant1Field().getInnerForm().getInnerTextField().getValue()); + assertEquals(INNER_FORM1_TEXT_PROPERTY_VALUE, testForm.getVariant1Field().getInnerForm().getText()); + assertEquals(INNER_FORM2_TEXT_FIELD_VALUE, testForm.getVariant2Field().getInnerForm().getInnerTextField().getValue()); + assertEquals(INNER_FORM2_TEXT_PROPERTY_VALUE, testForm.getVariant2Field().getInnerForm().getText()); } private void assertXmlDocument(String xml) { @@ -104,7 +223,13 @@ private void assertXmlDocument(String xml) { private void assertWrappedForm(Element element) { assertForm(InnerTestForm.class, element); - assertEmptyProperties(element); + + // properties + Element xProps = XmlUtility.getFirstChildElement(element, "properties"); + assertNotNull(xProps); + List props = XmlUtility.getChildElements(xProps); + assertEquals(1, props.size()); + assertEquals("text", props.get(0).getAttribute("name")); // fields Element xFlds = XmlUtility.getFirstChildElement(element, "fields"); @@ -180,8 +305,20 @@ protected Class getConfiguredInnerForm() { public static final class InnerTestForm extends AbstractForm { - public InnerTextField getInnerTextField() { - return getFieldByClass(InnerTextField.class); + private String m_text; + + public MainBox.InnerTextField getInnerTextField() { + return getFieldByClass(MainBox.InnerTextField.class); + } + + @FormData + public String getText() { + return m_text; + } + + @FormData + public void setText(String text) { + m_text = text; } @Order(10.0) @@ -192,4 +329,287 @@ public class InnerTextField extends AbstractStringField { } } } + + public static final class TestFormWithoutInnerForm extends AbstractForm { + + public MainBox.GroupBox.TextField getTextField() { + return getFieldByClass(MainBox.GroupBox.TextField.class); + } + + @Order(10.0) + public class MainBox extends AbstractGroupBox { + + @Order(50.0) + public class GroupBox extends AbstractGroupBox { + + @Order(10.0) + public class TextField extends AbstractStringField { + } + } + } + } + + public static final class TestFormWithInnerFormWithoutProperty extends AbstractForm { + + public MainBox.GroupBox.TextField getTextField() { + return getFieldByClass(MainBox.GroupBox.TextField.class); + } + + public MainBox.GroupBox.Variant1Field getVariant1Field() { + return getFieldByClass(MainBox.GroupBox.Variant1Field.class); + } + + public MainBox.GroupBox.Variant2Field getVariant2Field() { + return getFieldByClass(MainBox.GroupBox.Variant2Field.class); + } + + @Order(10.0) + public class MainBox extends AbstractGroupBox { + + @Order(50.0) + public class GroupBox extends AbstractGroupBox { + + @Order(10.0) + public class TextField extends AbstractStringField { + } + + @Order(20.0) + public class Variant1Field extends AbstractWrappedFormField { + @Override + protected Class getConfiguredInnerForm() { + return InnerTestFormWithoutProperty.class; + } + } + + @Order(30.0) + public class Variant2Field extends AbstractWrappedFormField { + + @Override + protected Class getConfiguredInnerForm() { + return InnerTestFormWithoutProperty.class; + } + } + } + } + } + + public static final class TestFormWithInnerFormWithoutField extends AbstractForm { + + public MainBox.GroupBox.TextField getTextField() { + return getFieldByClass(MainBox.GroupBox.TextField.class); + } + + public MainBox.GroupBox.Variant1Field getVariant1Field() { + return getFieldByClass(MainBox.GroupBox.Variant1Field.class); + } + + public MainBox.GroupBox.Variant2Field getVariant2Field() { + return getFieldByClass(MainBox.GroupBox.Variant2Field.class); + } + + @Order(10.0) + public class MainBox extends AbstractGroupBox { + + @Order(50.0) + public class GroupBox extends AbstractGroupBox { + + @Order(10.0) + public class TextField extends AbstractStringField { + } + + @Order(20.0) + public class Variant1Field extends AbstractWrappedFormField { + @Override + protected Class getConfiguredInnerForm() { + return InnerTestFormWithoutField.class; + } + } + + @Order(30.0) + public class Variant2Field extends AbstractWrappedFormField { + + @Override + protected Class getConfiguredInnerForm() { + return InnerTestFormWithoutField.class; + } + } + } + } + } + + public static final class InnerTestFormWithoutProperty extends AbstractForm { + + public MainBox.InnerTextField getInnerTextField() { + return getFieldByClass(MainBox.InnerTextField.class); + } + + @Order(10.0) + public class MainBox extends AbstractGroupBox { + + @Order(10.0) + public class InnerTextField extends AbstractStringField { + } + } + } + + public static final class InnerTestFormWithoutField extends AbstractForm { + + private String m_text; + + @FormData + public String getText() { + return m_text; + } + + @FormData + public void setText(String text) { + m_text = text; + } + + @Order(10.0) + public class MainBox extends AbstractGroupBox { + } + } + + public static final class InnerTestFormWithInvalidProperty extends AbstractForm { + + private long m_text; // property has invalid type + + public MainBox.InnerTextField getInnerTextField() { + return getFieldByClass(MainBox.InnerTextField.class); + } + + @FormData + public long getText() { + return m_text; + } + + @FormData + public void setText(long text) { + m_text = text; + } + + @Order(10.0) + public class MainBox extends AbstractGroupBox { + + @Order(10.0) + public class InnerTextField extends AbstractStringField { + } + } + } + + public static final class TestFormWithInnerFormWithInvalidProperty extends AbstractForm { + + public MainBox.GroupBox.TextField getTextField() { + return getFieldByClass(MainBox.GroupBox.TextField.class); + } + + public MainBox.GroupBox.Variant1Field getVariant1Field() { + return getFieldByClass(MainBox.GroupBox.Variant1Field.class); + } + + public MainBox.GroupBox.Variant2Field getVariant2Field() { + return getFieldByClass(MainBox.GroupBox.Variant2Field.class); + } + + @Order(10.0) + public class MainBox extends AbstractGroupBox { + + @Order(50.0) + public class GroupBox extends AbstractGroupBox { + + @Order(10.0) + public class TextField extends AbstractStringField { + } + + @Order(20.0) + public class Variant1Field extends AbstractWrappedFormField { + @Override + protected Class getConfiguredInnerForm() { + return InnerTestFormWithInvalidProperty.class; + } + } + + @Order(30.0) + public class Variant2Field extends AbstractWrappedFormField { + + @Override + protected Class getConfiguredInnerForm() { + return InnerTestFormWithInvalidProperty.class; + } + } + } + } + } + + public static final class TestFormWithInnerFormWithInvalidField extends AbstractForm { + + public MainBox.GroupBox.TextField getTextField() { + return getFieldByClass(MainBox.GroupBox.TextField.class); + } + + public MainBox.GroupBox.Variant1Field getVariant1Field() { + return getFieldByClass(MainBox.GroupBox.Variant1Field.class); + } + + public MainBox.GroupBox.Variant2Field getVariant2Field() { + return getFieldByClass(MainBox.GroupBox.Variant2Field.class); + } + + @Order(10.0) + public class MainBox extends AbstractGroupBox { + + @Order(50.0) + public class GroupBox extends AbstractGroupBox { + + @Order(10.0) + public class TextField extends AbstractStringField { + } + + @Order(20.0) + public class Variant1Field extends AbstractWrappedFormField { + @Override + protected Class getConfiguredInnerForm() { + return InnerTestFormWithInvalidField.class; + } + } + + @Order(30.0) + public class Variant2Field extends AbstractWrappedFormField { + + @Override + protected Class getConfiguredInnerForm() { + return InnerTestFormWithInvalidField.class; + } + } + } + } + } + + public static final class InnerTestFormWithInvalidField extends AbstractForm { + + private String m_text; + + public MainBox.InnerTextField getInnerTextField() { + return getFieldByClass(MainBox.InnerTextField.class); + } + + @FormData + public String getText() { + return m_text; + } + + @FormData + public void setText(String text) { + m_text = text; + } + + @Order(10.0) + public class MainBox extends AbstractGroupBox { + + @Order(10.0) + public class InnerTextField extends AbstractIntegerField { + } + } + } } diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/AbstractForm.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/AbstractForm.java index 5ce4d9433be..35b990aabbc 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/AbstractForm.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/AbstractForm.java @@ -130,6 +130,7 @@ import org.eclipse.scout.rt.platform.util.Assertions; import org.eclipse.scout.rt.platform.util.BeanUtility; import org.eclipse.scout.rt.platform.util.CollectionUtility; +import org.eclipse.scout.rt.platform.util.ObjectUtility; import org.eclipse.scout.rt.platform.util.PreferredValue; import org.eclipse.scout.rt.platform.util.StringUtility; import org.eclipse.scout.rt.platform.util.XmlUtility; @@ -2217,12 +2218,12 @@ protected boolean isBlockingInternal() { } @Override - public void loadFromXmlString(String xml) { + public boolean loadFromXmlString(String xml) { if (xml == null) { - return; + return true; } Document xmlDocument = XmlUtility.getXmlDocument(xml); - loadFromXml(xmlDocument.getDocumentElement()); + return loadFromXml(xmlDocument.getDocumentElement()); } @Override @@ -2297,7 +2298,7 @@ public void storeToXml(Element root) { /** * Adds a <property> element for every given property to the parent element. * - * @see #loadPropertiesFromXml(Element) + * @see #loadPropertiesFromXml(Element, Map) */ protected void storePropertiesToXml(Element parent, Map props) { for (Entry entry : props.entrySet()) { @@ -2314,12 +2315,14 @@ protected void storePropertiesToXml(Element parent, Map props) { } @Override - public void loadFromXml(Element root) { + public boolean loadFromXml(Element root) { + boolean success = true; // load properties Element xProps = XmlUtility.getFirstChildElement(root, "properties"); if (xProps != null) { - Map props = loadPropertiesFromXml(xProps); - BeanUtility.setProperties(this, props, true, null); + Map props = new HashMap<>(); + success &= loadPropertiesFromXml(xProps, props); + success &= BeanUtility.setProperties(this, props, true, null); // load extension properties for (Element xExtension : XmlUtility.getChildElements(xProps, "extension")) { @@ -2327,10 +2330,13 @@ public void loadFromXml(Element root) { String extensionQname = xExtension.getAttribute("extensionQname"); IFormExtension extension = findFormExtensionById(extensionQname, extensionId); if (extension == null) { + LOG.warn("Could not find extension with id={}, qname={}", extensionId, extensionQname); + success = false; // unknown extension continue; } - Map extensionProps = loadPropertiesFromXml(xExtension); - BeanUtility.setProperties(extension, extensionProps, true, null); + Map extensionProps = new HashMap<>(); + success &= loadPropertiesFromXml(xExtension, extensionProps); + success &= BeanUtility.setProperties(extension, extensionProps, true, null); } } @@ -2348,7 +2354,17 @@ public void loadFromXml(Element root) { visit(v, IFormField.class); IFormField f = v.getField(); if (f != null) { - f.loadFromXml(xField); + try { + success &= f.loadFromXml(xField); + } + catch (Exception e) { + LOG.warn("Could not load field {}", String.join(".", xmlFieldIds), e); + success = false; // error while loading value + } + } + else if (isXmlFieldWithStoredValue(xField)) { + LOG.warn("Could not resolve field {}", String.join(".", xmlFieldIds)); + success = false; // unknown field with stored value } } } @@ -2365,6 +2381,31 @@ public void loadFromXml(Element root) { } } }, ITabBox.class); + return success; + } + + protected boolean isXmlFieldWithStoredValue(Element xField) { + // heuristic for inner forms: do not check recursively for field values. simply assume that an inner form has a value + return isXmlWrappedFormField(xField) || + // value-fields: check if a non-empty value is set + isXmlFieldWithStoredValue(xField, "value") || + // tables: check if rows are set or indices are selected + isXmlFieldWithStoredValue(xField, "rows") || + isXmlFieldWithStoredValue(xField, "selectedRowIndices"); + } + + protected boolean isXmlWrappedFormField(Element xField) { + return StringUtility.hasText(xField.getAttribute("formId")); + } + + protected boolean isXmlFieldWithStoredValue(Element xField, String attributeName) { + try { + return ObjectUtility.hasValue(XmlUtility.getObjectAttribute(xField, attributeName)); + } + catch (Exception e) { + // cannot deserialize the value, so we assume that it contains actual data if something is serialized + return StringUtility.hasText(xField.getAttribute(attributeName)); + } } /** @@ -2389,22 +2430,27 @@ else if (candidate == null && extension.getClass().getSimpleName().equals(extens /** * Extracts properties from <property> child elements in the given parent element. * - * @return Map of property name to property value + * @param xProps + * The parent element + * @param properties + * The properties map to which the properties should be added + * @return True if all properties were loaded successfully * @see #storePropertiesToXml(Element, Map) */ - protected Map loadPropertiesFromXml(Element xProps) { - Map props = new HashMap<>(); + protected boolean loadPropertiesFromXml(Element xProps, Map properties) { + boolean success = true; for (Element xProp : XmlUtility.getChildElements(xProps, "property")) { String name = xProp.getAttribute("name"); try { Object o = XmlUtility.getObjectAttribute(xProp, "value"); - props.put(name, o); + properties.put(name, o); } catch (Exception e) { LOG.warn("Could not load XML property {}", name, e); + success = false; } } - return props; + return success; } @Override diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/IForm.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/IForm.java index 53a10d14daa..39e857b3c65 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/IForm.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/IForm.java @@ -595,9 +595,9 @@ public interface IForm extends IWidget, ITypeWithSettableClassId, IStyleable, ID boolean setProperty(String name, Object value); /** - * XML export/import of form state + * See {@link #loadFromXml(Element)} */ - void loadFromXmlString(String xml); + boolean loadFromXmlString(String xml); String storeToXmlString(); @@ -605,7 +605,15 @@ public interface IForm extends IWidget, ITypeWithSettableClassId, IStyleable, ID void storeToXml(Element root); - void loadFromXml(Element root); + /** + * Imports the form state from an XML document. + * + * @param root + * The XML document that contains the form state. + * @return True, if the XML document was loaded successfully. All fields and properties in the XML document which + * contain values were found on the form and loaded successfully. + */ + boolean loadFromXml(Element root); /** * Wait until form is closed
diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/AbstractFormField.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/AbstractFormField.java index 87a29807b6d..536149612a1 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/AbstractFormField.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/AbstractFormField.java @@ -1300,18 +1300,19 @@ protected final void setXmlFormFieldId(Element x, IFormField f) { } @Override - public void loadFromXml(Element x) { + public boolean loadFromXml(Element x) { + return true; } @Override - public final void loadFromXmlString(String xml) { + public final boolean loadFromXmlString(String xml) { if (xml == null) { - return; + return true; } try { Document doc = XmlUtility.getXmlDocument(xml); Element root = doc.getDocumentElement(); - loadFromXml(root); + return loadFromXml(root); } catch (Exception e) { throw new ProcessingException("Error in AbstractFormField.setXML: ", e); diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/AbstractValueField.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/AbstractValueField.java index c8f614608ed..cd4f1a3d4ff 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/AbstractValueField.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/AbstractValueField.java @@ -180,12 +180,13 @@ protected void storeValueToXml(Element x) { } @Override - public void loadFromXml(Element x) { - super.loadFromXml(x); - loadValueFromXml(x); + public boolean loadFromXml(Element x) { + boolean success = super.loadFromXml(x); + success &= loadValueFromXml(x); + return success; } - protected void loadValueFromXml(Element x) { + protected boolean loadValueFromXml(Element x) { try { VALUE value = TypeCastUtility.castValue(XmlUtility.getObjectAttribute(x, "value"), getHolderType()); setValue(value); @@ -193,7 +194,9 @@ protected void loadValueFromXml(Element x) { catch (Exception e) { // be lenient, maybe the field was changed LOG.warn("Could not load form XML [{}]", getClass().getName(), e); + return false; } + return true; } @Override diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/IFormField.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/IFormField.java index 397ec065df6..adf7c650bfd 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/IFormField.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/IFormField.java @@ -245,11 +245,21 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen String storeToXmlString(); - void loadFromXmlString(String xml); + /** + * See {@link #loadFromXml(Element)} + */ + boolean loadFromXmlString(String xml); void storeToXml(Element x); - void loadFromXml(Element x); + /** + * Imports the form field state from an XML element. + * + * @param x + * The XML element that contains the field state + * @return True, if the XML element was loaded successfully + */ + boolean loadFromXml(Element x); /** * add verbose information to the search filter @@ -317,8 +327,8 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen /** * @return Returns the list of fields that are enclosing this field, starting with the furthermost (from outside to - * inside). An enclosing field is part of the enclosing classes path that is abstract or the outermost - * enclosing class. The latter is the primary type. + * inside). An enclosing field is part of the enclosing classes path that is abstract or the outermost + * enclosing class. The latter is the primary type. * @since 3.8.1 */ List getEnclosingFieldList(); @@ -339,14 +349,14 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen /** * @param pos - * one of the LABEL_POSITION_* constants or a custom constant interpreted by the ui + * one of the LABEL_POSITION_* constants or a custom constant interpreted by the ui * @since 19.11.2009 */ void setLabelPosition(byte pos); /** - * Sets whether the form-field label is HTML enabled (true) or plain-text (false). When label position - * is LABEL_POSITION_ON_FIELD this property has no effect, since we can only render plain text. + * Sets whether the form-field label is HTML enabled (true) or plain-text (false). When label position is + * LABEL_POSITION_ON_FIELD this property has no effect, since we can only render plain text. */ void setLabelHtmlEnabled(boolean labelHtmlEnabled); @@ -363,7 +373,7 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen /** * @param w - * the fixed label witdh >0 or LABEL_WIDTH_DEFAULT or LABEL_WIDTH_UI for ui-dependent label width + * the fixed label witdh >0 or LABEL_WIDTH_DEFAULT or LABEL_WIDTH_UI for ui-dependent label width * @since 19.11.2009 */ void setLabelWidthInPixel(int w); @@ -376,22 +386,22 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen /** * @param labelUseUiWidth - * {@code true} if this fields label should be as width as preferred by the ui, {@code false} otherwise + * {@code true} if this fields label should be as width as preferred by the ui, {@code false} otherwise * @since 10.09.2020 */ void setLabelUseUiWidth(boolean labelUseUiWidth); /** * @return negative for left, 0 for center and positive for right, LABEL_HORIZONTAL_ALIGNMENT_DEFAULT for default of - * ui + * ui * @since 19.11.2009 */ byte getLabelHorizontalAlignment(); /** * @param a - * negative for left, 0 for center and positive for right, LABEL_HORIZONTAL_ALIGNMENT_DEFAULT for default of - * ui + * negative for left, 0 for center and positive for right, LABEL_HORIZONTAL_ALIGNMENT_DEFAULT for default of + * ui * @since 19.11.2009 */ void setLabelHorizontalAlignment(byte a); @@ -424,13 +434,13 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen /** * @return null iff no status (e.g. error status) is set, non-null if a status is set, e.g. if the value has semantic - * errors. + * errors. */ IMultiStatus getErrorStatus(); /** * @param status - * Sets the error multi-status. Use {@link #addErrorStatus(IStatus)} to add a single error. + * Sets the error multi-status. Use {@link #addErrorStatus(IStatus)} to add a single error. */ void setErrorStatus(IMultiStatus status); @@ -441,7 +451,7 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen /** * @return true if field content (value on value fields) is valid, no error status is set on field and mandatory - * property is met. + * property is met. */ boolean isContentValid(); @@ -495,7 +505,7 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen /** * @return the grid data hints used by the {@link org.eclipse.scout.rt.client.ui.form.fields.internal.GridDataBuilder - * GridDataBuilder} to create the final grid data which can be accessed using {@link #getGridData()}. + * GridDataBuilder} to create the final grid data which can be accessed using {@link #getGridData()}. */ GridData getGridDataHints(); @@ -503,7 +513,7 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen /** * @return the resulting (validated) grid data which is also used by the ui layout manager to layout this field in a - * logical grid. + * logical grid. */ GridData getGridData(); @@ -572,7 +582,7 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Sets whether or not the status icon is visible. * * @param statusVisible - * {@code true} if status icon should be visible, {@code false} otherwise + * {@code true} if status icon should be visible, {@code false} otherwise */ void setStatusVisible(boolean statusVisible); @@ -583,16 +593,18 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen void setStatusPosition(String statusPosition); /** - * @return The {@link IValidateContentDescriptor} that will be used by {@link IForm#validateForm()} if this field contains invalid content (see {@link #validateContent()}). May be {@code null}. - * The resulting instance may be modified directly or replaced using {@link #setValidateContentDescriptor(IValidateContentDescriptor)}. + * @return The {@link IValidateContentDescriptor} that will be used by {@link IForm#validateForm()} if this field + * contains invalid content (see {@link #validateContent()}). May be {@code null}. The resulting instance may + * be modified directly or replaced using {@link #setValidateContentDescriptor(IValidateContentDescriptor)}. */ IValidateContentDescriptor getValidateContentDescriptor(); /** - * Sets the {@link IValidateContentDescriptor} to be used by {@link IForm#validateForm()} if this field contains invalid content (see {@link #validateContent()}). + * Sets the {@link IValidateContentDescriptor} to be used by {@link IForm#validateForm()} if this field contains + * invalid content (see {@link #validateContent()}). * * @param validateContentDescriptor - * The new {@link IValidateContentDescriptor} or {@code null} if no descriptor is required. + * The new {@link IValidateContentDescriptor} or {@code null} if no descriptor is required. */ void setValidateContentDescriptor(IValidateContentDescriptor validateContentDescriptor); @@ -603,7 +615,7 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen /** * @return If the label of this {@link IFormField} is visible. It is visible if all label-visibility dimensions are - * true. + * true. */ boolean isLabelVisible(); @@ -611,7 +623,7 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Changes the value of the default label-visibility dimension to the given value. * * @param b - * The new value specifying if the label of this {@link IFormField} is visible. + * The new value specifying if the label of this {@link IFormField} is visible. */ void setLabelVisible(boolean b); @@ -619,16 +631,16 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Checks if the given label-visibility dimension is set to true. * * @param dimension - * The dimension to check. Must not be null. + * The dimension to check. Must not be null. * @return true if the given dimension is set to visible. By default all dimensions are visible. * @throws AssertionException - * if the given dimension is null. + * if the given dimension is null. * @throws IllegalStateException - * if too many dimensions are used. This {@link IFormField} supports up to - * {@link NamedBitMaskHelper#NUM_BITS} dimensions for label visibility. One dimension is already used by the - * {@link IFormField} itself. Therefore 7 dimensions may be used by developers.
- * Note: these dimensions are shared amongst all {@link IFormField}s of an application. They are not - * available by instance but by class! + * if too many dimensions are used. This {@link IFormField} supports up to + * {@link NamedBitMaskHelper#NUM_BITS} dimensions for label visibility. One dimension is already used by the + * {@link IFormField} itself. Therefore 7 dimensions may be used by developers.
+ * Note: these dimensions are shared amongst all {@link IFormField}s of an application. They are not + * available by instance but by class! */ boolean isLabelVisible(String dimension); @@ -636,23 +648,23 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Changes the label-visibility value of the given dimension. * * @param visible - * The new value for the given dimension. + * The new value for the given dimension. * @param dimension - * The dimension to change. Must not be null. + * The dimension to change. Must not be null. * @throws AssertionException - * if the given dimension is null. + * if the given dimension is null. * @throws IllegalStateException - * if too many dimensions are used. This {@link IFormField} supports up to - * {@link NamedBitMaskHelper#NUM_BITS} dimensions for label visibility. One dimension is already used by the - * {@link IFormField} itself. Therefore 7 dimensions may be used by developers.
- * Note: these dimensions are shared amongst all {@link IFormField}s of an application. They are not - * available by instance but by class! + * if too many dimensions are used. This {@link IFormField} supports up to + * {@link NamedBitMaskHelper#NUM_BITS} dimensions for label visibility. One dimension is already used by the + * {@link IFormField} itself. Therefore 7 dimensions may be used by developers.
+ * Note: these dimensions are shared amongst all {@link IFormField}s of an application. They are not + * available by instance but by class! */ void setLabelVisible(boolean visible, String dimension); /** * @return true if this {@link IFormField} and all parent {@link IFormField}s are visible (all - * dimensions). + * dimensions). * @see #isVisible() */ boolean isVisibleIncludingParents(); @@ -666,7 +678,7 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Changes the visible property of this {@link IFormField} to the given value. * * @param visible - * The new visible value. + * The new visible value. */ void setVisible(boolean visible); @@ -674,10 +686,10 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Changes the visible property of this {@link IFormField} to the given value. * * @param visible - * The new visible value. + * The new visible value. * @param updateParents - * if true the visible property of all parent {@link IFormField}s are updated to same value as - * well. + * if true the visible property of all parent {@link IFormField}s are updated to same value as + * well. */ void setVisible(boolean visible, boolean updateParents); @@ -685,13 +697,13 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Changes the visible property of this {@link IFormField} to the given value. * * @param visible - * The new visible value. + * The new visible value. * @param updateParents - * if true the visible property of all parent {@link IFormField}s are updated to same value as - * well. + * if true the visible property of all parent {@link IFormField}s are updated to same value as + * well. * @param updateChildren - * if true the visible property of all child {@link IFormField}s (recursive) are updated to same - * value as well. + * if true the visible property of all child {@link IFormField}s (recursive) are updated to same + * value as well. */ void setVisible(boolean visible, boolean updateParents, boolean updateChildren); @@ -699,7 +711,7 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Sets a new visible-permission that is used to calculate the visible-granted property of this {@link IFormField}. * * @param p - * The new {@link Permission} that is used to calculate the visible-granted value. + * The new {@link Permission} that is used to calculate the visible-granted value. * @see ACCESS#check(Permission) * @see #setVisibleGranted(boolean) */ @@ -719,7 +731,7 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Changes the visible-granted property of this {@link IFormField} to the given value. * * @param visibleGranted - * The new visible-granted value. + * The new visible-granted value. */ void setVisibleGranted(boolean visibleGranted); @@ -727,10 +739,10 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Changes the visible-granted property of this {@link IFormField} to the given value. * * @param visible - * The new visible-granted value. + * The new visible-granted value. * @param updateParents - * if true the visible-granted property of all parent {@link IFormField}s are updated to same - * value as well. + * if true the visible-granted property of all parent {@link IFormField}s are updated to same + * value as well. */ void setVisibleGranted(boolean visible, boolean updateParents); @@ -738,13 +750,13 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Changes the visible-granted property of this {@link IFormField} to the given value. * * @param visible - * The new visible-granted value. + * The new visible-granted value. * @param updateParents - * if true the visible-granted property of all parent {@link IFormField}s are updated to same - * value as well. + * if true the visible-granted property of all parent {@link IFormField}s are updated to same + * value as well. * @param updateChildren - * if true the visible-granted property of all child {@link IFormField}s (recursive) are updated - * to same value as well. + * if true the visible-granted property of all child {@link IFormField}s (recursive) are updated + * to same value as well. */ void setVisibleGranted(boolean visible, boolean updateParents, boolean updateChildren); @@ -752,18 +764,18 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Changes the field-visibility value of the given dimension. * * @param visible - * The new visibility value for the given dimension. + * The new visibility value for the given dimension. * @param dimension - * The dimension to change. Must not be null. + * The dimension to change. Must not be null. * @throws AssertionException - * if the given dimension is null. + * if the given dimension is null. * @throws IllegalStateException - * if too many dimensions are used. This {@link IFormField} supports up to - * {@link NamedBitMaskHelper#NUM_BITS} dimensions for visibility. Two dimensions are already used by the - * {@link IFormField} itself ({@link IDimensions#VISIBLE}, {@link IDimensions#VISIBLE_GRANTED}). Therefore 6 - * dimensions may be used by developers.
- * Note: these dimensions are shared amongst all {@link IFormField}s of an application. They are not - * available by instance but by class! + * if too many dimensions are used. This {@link IFormField} supports up to + * {@link NamedBitMaskHelper#NUM_BITS} dimensions for visibility. Two dimensions are already used by the + * {@link IFormField} itself ({@link IDimensions#VISIBLE}, {@link IDimensions#VISIBLE_GRANTED}). Therefore 6 + * dimensions may be used by developers.
+ * Note: these dimensions are shared amongst all {@link IFormField}s of an application. They are not + * available by instance but by class! */ @Override void setVisible(boolean visible, String dimension); @@ -772,20 +784,20 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Changes the field-visibility value of the given dimension. * * @param visible - * The new visibility value for the given dimension. + * The new visibility value for the given dimension. * @param updateParents - * if true all parent {@link IFormField}s are updated to same value as well. + * if true all parent {@link IFormField}s are updated to same value as well. * @param dimension - * The dimension to change. Must not be null. + * The dimension to change. Must not be null. * @throws AssertionException - * if the given dimension is null. + * if the given dimension is null. * @throws IllegalStateException - * if too many dimensions are used. This {@link IFormField} supports up to - * {@link NamedBitMaskHelper#NUM_BITS} dimensions for visibility. Two dimensions are already used by the - * {@link IFormField} itself ({@link IDimensions#VISIBLE}, {@link IDimensions#VISIBLE_GRANTED}). Therefore 6 - * dimensions may be used by developers.
- * Note: these dimensions are shared amongst all {@link IFormField}s of an application. They are not - * available by instance but by class! + * if too many dimensions are used. This {@link IFormField} supports up to + * {@link NamedBitMaskHelper#NUM_BITS} dimensions for visibility. Two dimensions are already used by the + * {@link IFormField} itself ({@link IDimensions#VISIBLE}, {@link IDimensions#VISIBLE_GRANTED}). Therefore 6 + * dimensions may be used by developers.
+ * Note: these dimensions are shared amongst all {@link IFormField}s of an application. They are not + * available by instance but by class! */ void setVisible(boolean visible, boolean updateParents, String dimension); @@ -793,22 +805,22 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Changes the field-visibility value of the given dimension. * * @param visible - * The new visibility value for the given dimension. + * The new visibility value for the given dimension. * @param updateParents - * if true all parent {@link IFormField}s are updated to same value as well. + * if true all parent {@link IFormField}s are updated to same value as well. * @param updateChildren - * if true all child {@link IFormField}s (recursive) are updated to same value as well. + * if true all child {@link IFormField}s (recursive) are updated to same value as well. * @param dimension - * The dimension to change. Must not be null. + * The dimension to change. Must not be null. * @throws AssertionException - * if the given dimension is null. + * if the given dimension is null. * @throws IllegalStateException - * if too many dimensions are used. This {@link IFormField} supports up to - * {@link NamedBitMaskHelper#NUM_BITS} dimensions for visibility. Two dimensions are already used by the - * {@link IFormField} itself ({@link IDimensions#VISIBLE}, {@link IDimensions#VISIBLE_GRANTED}). Therefore 6 - * dimensions may be used by developers.
- * Note: these dimensions are shared amongst all {@link IFormField}s of an application. They are not - * available by instance but by class! + * if too many dimensions are used. This {@link IFormField} supports up to + * {@link NamedBitMaskHelper#NUM_BITS} dimensions for visibility. Two dimensions are already used by the + * {@link IFormField} itself ({@link IDimensions#VISIBLE}, {@link IDimensions#VISIBLE_GRANTED}). Therefore 6 + * dimensions may be used by developers.
+ * Note: these dimensions are shared amongst all {@link IFormField}s of an application. They are not + * available by instance but by class! */ void setVisible(boolean visible, boolean updateParents, boolean updateChildren, String dimension); @@ -817,7 +829,7 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * During the initialization phase the children are not changed. * * @param fieldStyle - * One of {@link #FIELD_STYLE_CLASSIC}, {@link #FIELD_STYLE_ALTERNATIVE}. + * One of {@link #FIELD_STYLE_CLASSIC}, {@link #FIELD_STYLE_ALTERNATIVE}. */ void setFieldStyle(String fieldStyle); @@ -825,9 +837,9 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Sets the field style on this field and on every child field if requested. * * @param fieldStyle - * One of {@link #FIELD_STYLE_CLASSIC}, {@link #FIELD_STYLE_ALTERNATIVE}. + * One of {@link #FIELD_STYLE_CLASSIC}, {@link #FIELD_STYLE_ALTERNATIVE}. * @param recursive - * {@code true} if the field style property of the children should be changed as well. + * {@code true} if the field style property of the children should be changed as well. */ void setFieldStyle(String fieldStyle, boolean recursive); @@ -841,7 +853,7 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * During the initialization phase the children are not changed. * * @param disabledStyle - * One of {@link #DISABLED_STYLE_DEFAULT}, {@link #DISABLED_STYLE_READ_ONLY}. + * One of {@link #DISABLED_STYLE_DEFAULT}, {@link #DISABLED_STYLE_READ_ONLY}. */ void setDisabledStyle(int disabledStyle); @@ -849,9 +861,9 @@ public interface IFormField extends IWidget, IOrdered, IStyleable, IVisibleDimen * Sets the disabled style on this field and on every child field if requested. * * @param disabledStyle - * One of {@link #DISABLED_STYLE_DEFAULT}, {@link #DISABLED_STYLE_READ_ONLY}. + * One of {@link #DISABLED_STYLE_DEFAULT}, {@link #DISABLED_STYLE_READ_ONLY}. * @param recursive - * {@code true} if the disabled style property of the children should be changed as well. + * {@code true} if the disabled style property of the children should be changed as well. */ void setDisabledStyle(int disabledStyle, boolean recursive); diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/tablefield/AbstractTableField.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/tablefield/AbstractTableField.java index b7835687a85..58e891e146d 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/tablefield/AbstractTableField.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/tablefield/AbstractTableField.java @@ -293,22 +293,24 @@ public void importFormFieldData(AbstractFormFieldData source, boolean valueChang } @Override - public void loadFromXml(Element x) { - super.loadFromXml(x); + public boolean loadFromXml(Element x) { + boolean success = super.loadFromXml(x); if (m_table != null) { int[] selectedRowIndices = null; try { selectedRowIndices = (int[]) XmlUtility.getObjectAttribute(x, "selectedRowIndices"); } catch (Exception e) { - LOG.warn("reading attribute 'selectedRowIndices'", e); + LOG.warn("Could not read attribute 'selectedRowIndices'", e); + success = false; } Object[][] dataMatrix = null; try { dataMatrix = (Object[][]) XmlUtility.getObjectAttribute(x, "rows"); } catch (Exception e) { - LOG.warn("reading attribute 'rows'", e); + LOG.warn("Could not read attribute 'rows'", e); + success = false; } m_table.discardAllRows(); if (dataMatrix != null && dataMatrix.length > 0) { @@ -318,6 +320,7 @@ public void loadFromXml(Element x) { m_table.selectRows(m_table.getRows(selectedRowIndices)); } } + return success; } @Override diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/wrappedform/AbstractWrappedFormField.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/wrappedform/AbstractWrappedFormField.java index 246cb6489f6..12bf101bfbb 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/wrappedform/AbstractWrappedFormField.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/wrappedform/AbstractWrappedFormField.java @@ -258,11 +258,12 @@ public List getChildren() { } @Override - public void loadFromXml(Element x) { - super.loadFromXml(x); + public boolean loadFromXml(Element x) { + boolean success = super.loadFromXml(x); if (getInnerForm() != null) { - getInnerForm().loadFromXml(x); + success &= getInnerForm().loadFromXml(x); } + return success; } @Override diff --git a/org.eclipse.scout.rt.datamodel.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/composer/AbstractComposerField.java b/org.eclipse.scout.rt.datamodel.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/composer/AbstractComposerField.java index daedaa0356b..5b170cec7ed 100644 --- a/org.eclipse.scout.rt.datamodel.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/composer/AbstractComposerField.java +++ b/org.eclipse.scout.rt.datamodel.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/composer/AbstractComposerField.java @@ -435,21 +435,22 @@ public List getEntities() { } @Override - public void loadFromXml(Element x) { - super.loadFromXml(x); + public boolean loadFromXml(Element x) { + boolean success = super.loadFromXml(x); ITree tree = getTree(); try { tree.setTreeChanging(true); - // getTree().removeAllChildNodes(getTree().getRootNode()); - loadXMLRec(x, getTree().getRootNode()); + success &= loadXMLRec(x, getTree().getRootNode()); } finally { tree.setTreeChanging(false); } + return success; } - private void loadXMLRec(Element x, ITreeNode parent) { + private boolean loadXMLRec(Element x, ITreeNode parent) { + boolean success = true; // build tree for (Element xmlElem : XmlUtility.getChildElements(x)) { if ("attribute".equals(xmlElem.getTagName())) { @@ -474,6 +475,7 @@ private void loadXMLRec(Element x, ITreeNode parent) { } catch (Exception e) { LOG.warn("read op", e); + success = false; continue; } List valueList = new ArrayList<>(); @@ -487,6 +489,7 @@ private void loadXMLRec(Element x, ITreeNode parent) { } catch (Exception e) { LOG.warn("read value for attribute {}", id, e); + success = false; continue; } List displayValueList = new ArrayList<>(); @@ -505,12 +508,13 @@ private void loadXMLRec(Element x, ITreeNode parent) { IDataModelAttribute foundAtt = (attPath != null ? attPath.getAttribute() : null); if (foundAtt == null) { LOG.warn("cannot find attribute with id={}", id); + success = false; continue; } ITreeNode node = addAttributeNode(parent, foundAtt, aggregationType, op, valueList, displayValueList); if (node != null) { // add children recursive - loadXMLRec(xmlElem, node); + success &= loadXMLRec(xmlElem, node); } } else if ("entity".equals(xmlElem.getTagName())) { @@ -525,12 +529,13 @@ else if ("entity".equals(xmlElem.getTagName())) { IDataModelEntity foundEntity = (entityPath != null ? entityPath.lastElement() : null); if (foundEntity == null) { LOG.warn("cannot find entity with id={}", id); + success = false; continue; } ITreeNode node = addEntityNode(parent, foundEntity, negated, null, text != null ? Collections.singletonList(text) : null); if (node != null) { // add children recursive - loadXMLRec(xmlElem, node); + success &= loadXMLRec(xmlElem, node); } } else if ("or".equals(xmlElem.getTagName())) { @@ -554,11 +559,11 @@ else if ("or".equals(xmlElem.getTagName())) { } if (node != null) { // add children recursive - loadXMLRec(xmlElem, node); + success &= loadXMLRec(xmlElem, node); } } - } + return success; } @Override diff --git a/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/util/ObjectUtilityTest.java b/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/util/ObjectUtilityTest.java index ab12ec8b873..0f90e2a609a 100644 --- a/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/util/ObjectUtilityTest.java +++ b/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/util/ObjectUtilityTest.java @@ -12,8 +12,13 @@ import static org.junit.Assert.*; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.function.BiFunction; import org.junit.BeforeClass; @@ -211,4 +216,58 @@ public String toString() { }; assertEquals("object", ObjectUtility.toString(o)); } + + @Test + public void testHasValue() { + assertFalse(ObjectUtility.hasValue(null)); + assertTrue(ObjectUtility.hasValue(new Object())); + + // common primitives + assertTrue(ObjectUtility.hasValue((byte) 0)); + assertTrue(ObjectUtility.hasValue(' ')); + assertTrue(ObjectUtility.hasValue(0)); + assertTrue(ObjectUtility.hasValue(1)); + assertTrue(ObjectUtility.hasValue(-10)); + assertTrue(ObjectUtility.hasValue(Integer.MAX_VALUE)); + assertTrue(ObjectUtility.hasValue(Integer.MIN_VALUE)); + assertTrue(ObjectUtility.hasValue(Float.MAX_VALUE)); + assertTrue(ObjectUtility.hasValue(Float.MIN_VALUE)); + assertTrue(ObjectUtility.hasValue(0.0)); + assertTrue(ObjectUtility.hasValue(1.0)); + assertTrue(ObjectUtility.hasValue(true)); + assertTrue(ObjectUtility.hasValue(false)); + + // optionals + assertFalse(ObjectUtility.hasValue(Optional.empty())); + assertTrue(ObjectUtility.hasValue(Optional.of(0))); + assertTrue(ObjectUtility.hasValue(Optional.of(true))); + assertTrue(ObjectUtility.hasValue(Optional.of(""))); + + // strings + assertFalse(ObjectUtility.hasValue("")); + assertFalse(ObjectUtility.hasValue(" ")); + assertTrue(ObjectUtility.hasValue("abc")); + + // collections + assertFalse(ObjectUtility.hasValue(List.of())); + assertTrue(ObjectUtility.hasValue(List.of(1))); + ArrayList listWithNullValue = new ArrayList<>(); + listWithNullValue.add(null); + assertTrue(ObjectUtility.hasValue(listWithNullValue)); + assertTrue(ObjectUtility.hasValue(List.of(""))); + + // maps + assertFalse(ObjectUtility.hasValue(Map.of())); + assertTrue(ObjectUtility.hasValue(Map.of(1, 2))); + HashMap mapWithNullValue = new HashMap<>(); + mapWithNullValue.put(null, null); + assertTrue(ObjectUtility.hasValue(mapWithNullValue)); + assertTrue(ObjectUtility.hasValue(Map.of("", ""))); + + // arrays + assertFalse(ObjectUtility.hasValue(new int[]{})); + assertTrue(ObjectUtility.hasValue(new int[]{1, 2})); + assertTrue(ObjectUtility.hasValue(new Object[]{null})); + assertTrue(ObjectUtility.hasValue(new String[]{""})); + } } diff --git a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/util/BeanUtility.java b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/util/BeanUtility.java index 82ec1702940..e5be56755ee 100644 --- a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/util/BeanUtility.java +++ b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/util/BeanUtility.java @@ -85,17 +85,32 @@ public static Map getProperties(Object from, Class stopClazz, } /** + * Sets all given properties to the target object. + * + * @param to + * The target object + * @param propertyMap + * A map of property name to the value that should be set * @param lenient * true just logs warnings on exceptions, false throws exceptions set all properties on to, filtering with * filter + * @param filter + * Filter that should be applied to properties + * @return True if all properties were set successfully */ - public static void setProperties(Object to, Map map, boolean lenient, IPropertyFilter filter) { + public static boolean setProperties(Object to, Map propertyMap, boolean lenient, IPropertyFilter filter) { + boolean success = true; FastBeanInfo toInfo = getFastBeanInfo(to.getClass(), null); - for (Entry entry : map.entrySet()) { + for (Entry entry : propertyMap.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); try { FastPropertyDescriptor desc = toInfo.getPropertyDescriptor(name); + if (desc == null && ObjectUtility.hasValue(value)) { + // property with a value could not be found on target object + // do not log a warning because this is an expected use case + success = false; + } if (desc != null && (filter == null || filter.accept(desc))) { Method writeMethod = desc.getWriteMethod(); if (writeMethod != null) { @@ -105,13 +120,15 @@ public static void setProperties(Object to, Map map, boolean len } catch (Exception e) { if (lenient) { - LOG.warn("Could not set property property '{}' to value '{}'", name, value, e); + LOG.warn("Could not set property '{}' to value '{}'", name, value, e); + success = false; } else { throw new ProcessingException("property " + name + " with value " + value, e); } } } + return success; } /** diff --git a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/util/ObjectUtility.java b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/util/ObjectUtility.java index d53127b6310..698f953cef5 100644 --- a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/util/ObjectUtility.java +++ b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/util/ObjectUtility.java @@ -14,7 +14,9 @@ import java.util.Arrays; import java.util.Collection; import java.util.Date; +import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.Supplier; import org.eclipse.scout.rt.platform.util.date.DateUtility; @@ -205,4 +207,30 @@ public static boolean isOneOf(Object o, Collection elements) { } return false; } + + /** + * @return {@code true} if the given object represents an actual value as opposed to an empty value such as null, + * empty optional, empty collection, empty string etc. + */ + public static boolean hasValue(Object o) { + if (o == null) { + return false; + } + if (o instanceof Optional && ((Optional) o).isEmpty()) { + return false; + } + if (o instanceof String && !StringUtility.hasText((String) o)) { + return false; + } + if (o instanceof Collection && ((Collection) o).isEmpty()) { + return false; + } + if (o instanceof Map && ((Map) o).isEmpty()) { + return false; + } + if (o.getClass().isArray() && Array.getLength(o) == 0) { + return false; + } + return true; + } }