Bug 449487: New Extensibility Concept for Scout

https://bugs.eclipse.org/bugs/show_bug.cgi?id=449487

Bugfix for RowData generation in nested TableExtensions (inside IPageWithTableExtension)
diff --git a/org.eclipse.scout.sdk.test/resources/operation/formData/formdata.client/src/formdata/client/ui/desktop/outline/pages/PageWithTableExtension.java b/org.eclipse.scout.sdk.test/resources/operation/formData/formdata.client/src/formdata/client/ui/desktop/outline/pages/PageWithTableExtension.java
new file mode 100644
index 0000000..73453de
--- /dev/null
+++ b/org.eclipse.scout.sdk.test/resources/operation/formData/formdata.client/src/formdata/client/ui/desktop/outline/pages/PageWithTableExtension.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2010 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ ******************************************************************************/
+package formdata.client.ui.desktop.outline.pages;
+
+import org.eclipse.scout.commons.annotations.Data;
+import org.eclipse.scout.commons.annotations.Order;
+import org.eclipse.scout.rt.client.extension.ui.basic.table.AbstractTableExtension;
+import org.eclipse.scout.rt.client.extension.ui.desktop.outline.pages.AbstractPageWithTableExtension;
+import org.eclipse.scout.rt.client.ui.basic.table.columns.AbstractBigDecimalColumn;
+
+import formdata.client.ui.desktop.outline.pages.BaseTablePage.Table;
+import formdata.shared.services.pages.PageWithTableExtensionData;
+
+@Data(PageWithTableExtensionData.class)
+public class PageWithTableExtension extends AbstractPageWithTableExtension<BaseTablePage.Table, BaseTablePage> {
+
+  public PageWithTableExtension(BaseTablePage owner) {
+    super(owner);
+  }
+
+  public class TableExtension extends AbstractTableExtension<BaseTablePage.Table> {
+
+    public TableExtension(Table owner) {
+      super(owner);
+    }
+
+    @Order(1000.0)
+    public class BigDecimalTestColumn extends AbstractBigDecimalColumn {
+    }
+  }
+}
diff --git a/org.eclipse.scout.sdk.test/resources/operation/formData/formdata.shared/src/formdata/shared/services/pages/PageWithTableExtensionData.java b/org.eclipse.scout.sdk.test/resources/operation/formData/formdata.shared/src/formdata/shared/services/pages/PageWithTableExtensionData.java
new file mode 100644
index 0000000..9ba82da
--- /dev/null
+++ b/org.eclipse.scout.sdk.test/resources/operation/formData/formdata.shared/src/formdata/shared/services/pages/PageWithTableExtensionData.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2010 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ ******************************************************************************/
+package formdata.shared.services.pages;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+import javax.annotation.Generated;
+
+import org.eclipse.scout.commons.annotations.Extends;
+
+/**
+ * <b>NOTE:</b><br>
+ * This class is auto generated by the Scout SDK. No manual modifications recommended.
+ * 
+ * @generated
+ */
+@Extends(BaseTablePageData.BaseTableRowData.class)
+@Generated(value = "org.eclipse.scout.sdk.workspace.dto.pagedata.PageDataDtoUpdateOperation", comments = "This class is auto generated by the Scout SDK. No manual modifications recommended.")
+public class PageWithTableExtensionData implements Serializable {
+
+  private static final long serialVersionUID = 1L;
+  public static final String bigDecimalTest = "bigDecimalTest";
+  private BigDecimal m_bigDecimalTest;
+
+  public PageWithTableExtensionData() {
+  }
+
+  public BigDecimal getBigDecimalTest() {
+    return m_bigDecimalTest;
+  }
+
+  public void setBigDecimalTest(BigDecimal bigDecimalTest) {
+    m_bigDecimalTest = bigDecimalTest;
+  }
+}
diff --git a/org.eclipse.scout.sdk.test/src/org/eclipse/scout/sdk/internal/test/operation/pagedata/PageBeanDataTest.java b/org.eclipse.scout.sdk.test/src/org/eclipse/scout/sdk/internal/test/operation/pagedata/PageBeanDataTest.java
index c0c481c..f298145 100644
--- a/org.eclipse.scout.sdk.test/src/org/eclipse/scout/sdk/internal/test/operation/pagedata/PageBeanDataTest.java
+++ b/org.eclipse.scout.sdk.test/src/org/eclipse/scout/sdk/internal/test/operation/pagedata/PageBeanDataTest.java
@@ -14,12 +14,14 @@
 import org.eclipse.jdt.core.IMethod;
 import org.eclipse.jdt.core.IType;
 import org.eclipse.scout.sdk.internal.test.operation.formdata.AbstractSdkTestWithFormDataProject;
+import org.eclipse.scout.sdk.operation.IOperation;
 import org.eclipse.scout.sdk.testing.SdkAssert;
 import org.eclipse.scout.sdk.testing.TestWorkspaceUtility;
 import org.eclipse.scout.sdk.util.type.TypeUtility;
 import org.eclipse.scout.sdk.util.typecache.ITypeHierarchy;
 import org.eclipse.scout.sdk.workspace.dto.pagedata.DataAnnotation;
 import org.eclipse.scout.sdk.workspace.dto.pagedata.PageDataDtoUpdateOperation;
+import org.eclipse.scout.sdk.workspace.dto.pagedata.RowDataDtoUpdateOperation;
 import org.eclipse.scout.sdk.workspace.type.ScoutTypeUtility;
 import org.junit.Test;
 
@@ -36,50 +38,61 @@
   public static final String ExtendedTablePageWithoutExtendedTableFqn = "formdata.client.ui.desktop.outline.pages.ExtendedTablePageWithoutExtendedTable";
   public static final String BaseWithExtendedTableTablePage = "formdata.client.ui.desktop.outline.pages.BaseWithExtendedTableTablePage";
   public static final String ExtendedExtendedTablePageWithExtendedTable = "formdata.client.ui.desktop.outline.pages.ExtendedExtendedTablePageWithExtendedTable";
+  public static final String PageWithTableExtension = "formdata.client.ui.desktop.outline.pages.PageWithTableExtension";
 
-  private IType createPageData(String typeName) throws Exception {
+  private void createPageData(String typeName, boolean rowData) throws Exception {
     IType field = SdkAssert.assertTypeExists(typeName);
 
     ITypeHierarchy superTypeHierarchy = TypeUtility.getSupertypeHierarchy(field);
     DataAnnotation annotation = ScoutTypeUtility.findDataAnnotation(field, superTypeHierarchy);
-    PageDataDtoUpdateOperation op = new PageDataDtoUpdateOperation(field, annotation);
+    IOperation op = null;
+    if (rowData) {
+      op = new RowDataDtoUpdateOperation(field, annotation);
+    }
+    else {
+      op = new PageDataDtoUpdateOperation(field, annotation);
+    }
     TestWorkspaceUtility.executeAndBuildWorkspace(op);
+  }
 
-    IType pageData = op.getDerivedType();
-    return pageData;
+  @Test
+  public void testPageWithTableExtensionData() throws Exception {
+    createPageData(PageWithTableExtension, true);
+    TestWorkspaceUtility.assertNoCompileErrors();
+    testApiOfPageWithTableExtensionData();
   }
 
   @Test
   public void testAbstractTableField() throws Exception {
-    createPageData(BaseTablePage);
+    createPageData(BaseTablePage, false);
     TestWorkspaceUtility.assertNoCompileErrors();
     testApiOfBaseTablePageData();
   }
 
   @Test
   public void testExtendedTablePage() throws Exception {
-    createPageData(ExtendedTablePage);
+    createPageData(ExtendedTablePage, false);
     TestWorkspaceUtility.assertNoCompileErrors();
     testApiOfExtendedTablePageData();
   }
 
   @Test
   public void testExtendedTablePageWithoutExtendedTable() throws Exception {
-    createPageData(ExtendedTablePageWithoutExtendedTableFqn);
+    createPageData(ExtendedTablePageWithoutExtendedTableFqn, false);
     TestWorkspaceUtility.assertNoCompileErrors();
     testApiOfExtendedTablePageWithoutExtendedTableData();
   }
 
   @Test
   public void testBaseWithExtendedTableTablePage() throws Exception {
-    createPageData(BaseWithExtendedTableTablePage);
+    createPageData(BaseWithExtendedTableTablePage, false);
     TestWorkspaceUtility.assertNoCompileErrors();
     testApiOfBaseWithExtendedTableTablePageData();
   }
 
   @Test
   public void testExtendedExtendedTablePageWithExtendedTable() throws Exception {
-    createPageData(ExtendedExtendedTablePageWithExtendedTable);
+    createPageData(ExtendedExtendedTablePageWithExtendedTable, false);
     TestWorkspaceUtility.assertNoCompileErrors();
     testApiOfExtendedExtendedTablePageWithExtendedTableData();
   }
@@ -455,4 +468,39 @@
     SdkAssert.assertEquals("inner types count of 'ExtendedExtendedTablePageWithExtendedTableRowData'", 0, extendedExtendedTablePageWithExtendedTableRowData.getTypes().length);
   }
 
+  /**
+   * @Generated with org.eclipse.scout.sdk.testing.codegen.ApiTestGenerator
+   */
+  private void testApiOfPageWithTableExtensionData() throws Exception {
+    // type PageWithTableExtensionData
+    IType pageWithTableExtensionData = SdkAssert.assertTypeExists("formdata.shared.services.pages.PageWithTableExtensionData");
+    SdkAssert.assertHasFlags(pageWithTableExtensionData, 1);
+    SdkAssert.assertHasSuperIntefaceSignatures(pageWithTableExtensionData, new String[]{"QSerializable;"});
+    SdkAssert.assertAnnotation(pageWithTableExtensionData, "org.eclipse.scout.commons.annotations.Extends");
+    SdkAssert.assertAnnotation(pageWithTableExtensionData, "javax.annotation.Generated");
+
+    // fields of PageWithTableExtensionData
+    SdkAssert.assertEquals("field count of 'PageWithTableExtensionData'", 3, pageWithTableExtensionData.getFields().length);
+    IField serialVersionUID = SdkAssert.assertFieldExist(pageWithTableExtensionData, "serialVersionUID");
+    SdkAssert.assertHasFlags(serialVersionUID, 26);
+    SdkAssert.assertFieldSignature(serialVersionUID, "J");
+    IField bigDecimalTest = SdkAssert.assertFieldExist(pageWithTableExtensionData, "bigDecimalTest");
+    SdkAssert.assertHasFlags(bigDecimalTest, 25);
+    SdkAssert.assertFieldSignature(bigDecimalTest, "QString;");
+    IField m_bigDecimalTest = SdkAssert.assertFieldExist(pageWithTableExtensionData, "m_bigDecimalTest");
+    SdkAssert.assertHasFlags(m_bigDecimalTest, 2);
+    SdkAssert.assertFieldSignature(m_bigDecimalTest, "QBigDecimal;");
+
+    SdkAssert.assertEquals("method count of 'PageWithTableExtensionData'", 3, pageWithTableExtensionData.getMethods().length);
+    IMethod pageWithTableExtensionData1 = SdkAssert.assertMethodExist(pageWithTableExtensionData, "PageWithTableExtensionData", new String[]{});
+    SdkAssert.assertTrue(pageWithTableExtensionData1.isConstructor());
+    SdkAssert.assertMethodReturnTypeSignature(pageWithTableExtensionData1, "V");
+    IMethod getBigDecimalTest = SdkAssert.assertMethodExist(pageWithTableExtensionData, "getBigDecimalTest", new String[]{});
+    SdkAssert.assertMethodReturnTypeSignature(getBigDecimalTest, "QBigDecimal;");
+    IMethod setBigDecimalTest = SdkAssert.assertMethodExist(pageWithTableExtensionData, "setBigDecimalTest", new String[]{"QBigDecimal;"});
+    SdkAssert.assertMethodReturnTypeSignature(setBigDecimalTest, "V");
+
+    SdkAssert.assertEquals("inner types count of 'PageWithTableExtensionData'", 0, pageWithTableExtensionData.getTypes().length);
+  }
+
 }
diff --git a/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/extensions/runtime/classes/IRuntimeClasses.java b/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/extensions/runtime/classes/IRuntimeClasses.java
index b6451d7..4df005b 100644
--- a/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/extensions/runtime/classes/IRuntimeClasses.java
+++ b/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/extensions/runtime/classes/IRuntimeClasses.java
@@ -212,6 +212,7 @@
   String IPage = "org.eclipse.scout.rt.client.ui.desktop.outline.pages.IPage"; // NO_UCD
   String IPageWithNodes = "org.eclipse.scout.rt.client.ui.desktop.outline.pages.IPageWithNodes"; // NO_UCD
   String IPageWithNodesExtension = "org.eclipse.scout.rt.client.extension.ui.desktop.outline.pages.IPageWithNodesExtension"; // NO_UCD
+  String IPageWithTableExtension = "org.eclipse.scout.rt.client.extension.ui.desktop.outline.pages.IPageWithTableExtension"; // NO_UCD
   String IPageWithTable = "org.eclipse.scout.rt.client.ui.desktop.outline.pages.IPageWithTable"; // NO_UCD
   String IPlannerField = "org.eclipse.scout.rt.client.ui.form.fields.plannerfield.IPlannerField"; // NO_UCD
   String IPropertyObserver = "org.eclipse.scout.commons.beans.IPropertyObserver"; // NO_UCD
diff --git a/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/internal/workspace/dto/pagedata/RowDataAutoUpdateHandler.java b/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/internal/workspace/dto/pagedata/RowDataAutoUpdateHandler.java
index e885e14..a7a6806 100644
--- a/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/internal/workspace/dto/pagedata/RowDataAutoUpdateHandler.java
+++ b/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/internal/workspace/dto/pagedata/RowDataAutoUpdateHandler.java
@@ -10,7 +10,11 @@
  ******************************************************************************/
 package org.eclipse.scout.sdk.internal.workspace.dto.pagedata;
 
+import java.util.Set;
+
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.scout.commons.CollectionUtility;
 import org.eclipse.scout.sdk.extensions.runtime.classes.IRuntimeClasses;
 import org.eclipse.scout.sdk.internal.workspace.dto.AbstractDtoUpdateHandler;
 import org.eclipse.scout.sdk.util.type.TypeUtility;
@@ -30,10 +34,22 @@
 
   private boolean checkType(DtoUpdateProperties properties) throws CoreException {
     ITypeHierarchy superTypeHierarchy = ensurePropertySuperTypeHierarchy(properties);
+
+    // direct column or table extension
     if (superTypeHierarchy.contains(TypeUtility.getType(IRuntimeClasses.IColumn)) || superTypeHierarchy.contains(TypeUtility.getType(IRuntimeClasses.ITableExtension))) {
       DataAnnotation dataAnnotation = ensurePropertyDataAnnotation(properties);
       return dataAnnotation != null;
     }
+
+    // check for table extension in IPageWithTableExtension
+    if (superTypeHierarchy.contains(TypeUtility.getType(IRuntimeClasses.IPageWithTableExtension))) {
+      Set<IType> innerTableExtensions = TypeUtility.getInnerTypesOrdered(properties.getType(), TypeUtility.getType(IRuntimeClasses.ITableExtension), null);
+      IType tableExtension = CollectionUtility.firstElement(innerTableExtensions);
+      if (TypeUtility.exists(tableExtension)) {
+        DataAnnotation dataAnnotation = ensurePropertyDataAnnotation(properties);
+        return dataAnnotation != null;
+      }
+    }
     return false;
   }
 
diff --git a/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/internal/workspace/dto/pagedata/TableRowDataTypeSourceBuilder.java b/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/internal/workspace/dto/pagedata/TableRowDataTypeSourceBuilder.java
index a1a2ad2..01e5b89 100644
--- a/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/internal/workspace/dto/pagedata/TableRowDataTypeSourceBuilder.java
+++ b/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/internal/workspace/dto/pagedata/TableRowDataTypeSourceBuilder.java
@@ -112,7 +112,7 @@
     }
 
     // get all columns
-    Set<IType> columns = getColumns(getColumnContainer(), rowDataSuperClassType, monitor);
+    Set<IType> columns = getColumns(getColumnContainer(), rowDataSuperClassType, getModelLocalHierarchy(), monitor);
     if (monitor.isCanceled()) {
       return;
     }
@@ -163,7 +163,7 @@
     return NamingUtility.ensureStartWithLowerCase(ScoutUtility.removeFieldSuffix(column.getElementName()));
   }
 
-  protected static Set<IType> getColumns(IType declaringType, IType rowDataSuperType, IProgressMonitor monitor) throws JavaModelException {
+  protected static Set<IType> getColumns(IType declaringType, IType rowDataSuperType, ITypeHierarchy modelLocalHierarchy, IProgressMonitor monitor) throws JavaModelException {
 
     final ITypeHierarchy fieldHierarchy = TypeUtility.getSupertypeHierarchy(declaringType);
 
@@ -172,6 +172,15 @@
       return CollectionUtility.hashSet(declaringType);
     }
 
+    // the declaring type is a IPageWithTableExtension -> search the inner table extension
+    if (fieldHierarchy.isSubtype(TypeUtility.getType(IRuntimeClasses.IPageWithTableExtension), declaringType)) {
+      Set<IType> innerTableExtensions = TypeUtility.getInnerTypesOrdered(declaringType, TypeUtility.getType(IRuntimeClasses.ITableExtension), ScoutTypeComparators.getSourceRangeComparator(), modelLocalHierarchy);
+      IType tableExtension = CollectionUtility.firstElement(innerTableExtensions);
+      if (TypeUtility.exists(tableExtension)) {
+        declaringType = tableExtension; // switch to the table as column holder
+      }
+    }
+
     // the declaring type holds columns
     TreeSet<IType> allColumnsUpTheHierarchy = new TreeSet<IType>(ScoutTypeComparators.getOrderAnnotationComparator());
     // do not re-use the fieldHierarchy for the subtype filter!
diff --git a/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/operation/form/field/table/TableColumnNewOperation.java b/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/operation/form/field/table/TableColumnNewOperation.java
index 8b4f28f..e26b3de 100644
--- a/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/operation/form/field/table/TableColumnNewOperation.java
+++ b/org.eclipse.scout.sdk/src/org/eclipse/scout/sdk/operation/form/field/table/TableColumnNewOperation.java
@@ -98,22 +98,24 @@
     columnOp.validate();
     columnOp.run(monitor, workingCopyManager);
     m_createdColumn = columnOp.getCreatedType();
-    // getter on declaring table
 
-    InnerTypeGetterCreateOperation getterOp = new InnerTypeGetterCreateOperation(getCreatedColumn(), getDeclaringType(), true);
-    getterOp.setMethodBodySourceBuilder(new IMethodBodySourceBuilder() {
+    if (!TypeUtility.getSupertypeHierarchy(getDeclaringType()).contains(TypeUtility.getType(IRuntimeClasses.IExtension))) { // no getters for extensions
+      // getter on declaring table
+      InnerTypeGetterCreateOperation getterOp = new InnerTypeGetterCreateOperation(getCreatedColumn(), getDeclaringType(), true);
+      getterOp.setMethodBodySourceBuilder(new IMethodBodySourceBuilder() {
 
-      @Override
-      public void createSource(IMethodSourceBuilder methodBuilder, StringBuilder source, String lineDelimiter, IJavaProject ownerProject, IImportValidator validator) throws CoreException {
-        source.append("return getColumnSet().getColumnByClass(");
-        source.append(SignatureUtility.getTypeReference(SignatureCache.createTypeSignature(getCreatedColumn().getFullyQualifiedName()), validator) + ".class");
-        source.append(");");
-      }
-    });
-    IStructuredType structuredType = ScoutTypeUtility.createStructuredTable(getDeclaringType());
-    getterOp.setSibling(structuredType.getSiblingMethodFieldGetter(getterOp.getElementName()));
-    getterOp.validate();
-    getterOp.run(monitor, workingCopyManager);
+        @Override
+        public void createSource(IMethodSourceBuilder methodBuilder, StringBuilder source, String lineDelimiter, IJavaProject ownerProject, IImportValidator validator) throws CoreException {
+          source.append("return getColumnSet().getColumnByClass(");
+          source.append(SignatureUtility.getTypeReference(SignatureCache.createTypeSignature(getCreatedColumn().getFullyQualifiedName()), validator) + ".class");
+          source.append(");");
+        }
+      });
+      IStructuredType structuredType = ScoutTypeUtility.createStructuredTable(getDeclaringType());
+      getterOp.setSibling(structuredType.getSiblingMethodFieldGetter(getterOp.getElementName()));
+      getterOp.validate();
+      getterOp.run(monitor, workingCopyManager);
+    }
   }
 
   protected void appendToColumnBuilder(ITypeSourceBuilder columnBuilder, IProgressMonitor monitor, IWorkingCopyManager workingCopyManager) throws CoreException {