You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by ah...@apache.org on 2024/06/19 17:39:50 UTC
(causeway) 01/01: CAUSEWAY-3786: Add switch to activate 2.0.0 data table implementation (experimental)
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch 3786-switch.v2.0.tables
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 3c9fab28098d77ce096c8366b3f77321f8c7b222
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Jun 19 19:39:44 2024 +0200
CAUSEWAY-3786: Add switch to activate 2.0.0 data table implementation
(experimental)
---
.../core/metamodel/object/MmSortUtils.java | 13 +-
.../causeway/core/metamodel/tabular/DataRow.java | 6 +
.../metamodel/tabular/DataTableInteractive.java | 74 +++-
.../tabular/internal/DataTableInternal.java | 5 -
.../metamodel/tabular/optimistic/DataColumnO.java | 49 +++
.../metamodel/tabular/optimistic/DataRowO.java | 115 ++++++
.../metamodel/tabular/optimistic/DataTableO.java | 408 +++++++++++++++++++++
.../metamodel/tabular/optimistic/_ToggleDebug.java | 44 +++
.../models/EntityCollectionModelStandalone.java | 6 +-
.../interaction/act/ActionInteractionWkt.java | 20 +-
.../model/models/interaction/coll/DataRowWkt.java | 54 ++-
.../coll/{DataRowWkt.java => DataRowWktD.java} | 13 +-
.../coll/{DataRowWkt.java => DataRowWktO.java} | 40 +-
.../models/interaction/coll/DataTableModelWkt.java | 91 ++---
...aTableModelWkt.java => DataTableModelWktD.java} | 15 +-
...aTableModelWkt.java => DataTableModelWktO.java} | 77 +++-
.../actionresponse/ActionResultResponseType.java | 46 ++-
.../ajaxtable/CausewayAjaxDataTable.java | 117 +++---
.../ajaxtable/CausewayAjaxHeadersToolbar.java | 35 +-
.../CollectionContentsSortableDataProvider.java | 59 ++-
.../ajaxtable/columns/ToggleboxColumn.java | 17 +-
.../wicket/ui/panels/FormExecutorDefault.java | 2 +-
22 files changed, 1054 insertions(+), 252 deletions(-)
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmSortUtils.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmSortUtils.java
index 347c827cf0..42bbd3e411 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmSortUtils.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/object/MmSortUtils.java
@@ -38,7 +38,18 @@ public class MmSortUtils {
DESCENDING;
}
- public Comparator<ManagedObject> orderingBy(final ObjectAssociation sortProperty, final SortDirection sortDirection) {
+ @Deprecated // use the enum variant instead
+ public Comparator<ManagedObject> orderingBy(
+ final ObjectAssociation sortProperty,
+ final boolean isAscending) {
+ return orderingBy(sortProperty, isAscending
+ ? SortDirection.ASCENDING
+ : SortDirection.DESCENDING);
+ }
+
+ public Comparator<ManagedObject> orderingBy(
+ final ObjectAssociation sortProperty,
+ final SortDirection sortDirection) {
final Comparator<ManagedObject> comparator = (sortDirection != SortDirection.DESCENDING)
? NATURAL_NULL_FIRST
: NATURAL_NULL_FIRST.reversed();
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/DataRow.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/DataRow.java
index 6829d6344e..0e14fef46c 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/DataRow.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/DataRow.java
@@ -19,10 +19,12 @@
package org.apache.causeway.core.metamodel.tabular;
import java.util.Optional;
+import java.util.UUID;
import org.apache.causeway.applib.services.filter.CollectionFilterService.Tokens;
import org.apache.causeway.commons.binding.Bindable;
import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
import org.apache.causeway.core.metamodel.object.ManagedObject;
import lombok.NonNull;
@@ -65,5 +67,9 @@ public interface DataRow {
Can<ManagedObject> getCellElementsForColumn(@NonNull String columnId);
Optional<Tokens> getFilterTokens();
+ @Deprecated // used by OPTIMISTIC data table
+ default UUID getUuid() {
+ throw _Exceptions.unsupportedOperation();
+ }
}
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/DataTableInteractive.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/DataTableInteractive.java
index 9c364a9257..b04b1d7b50 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/DataTableInteractive.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/DataTableInteractive.java
@@ -22,43 +22,98 @@ import java.io.Serializable;
import java.util.Comparator;
import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import org.apache.causeway.applib.annotation.TableDecorator;
import org.apache.causeway.applib.annotation.Where;
import org.apache.causeway.commons.binding.Bindable;
import org.apache.causeway.commons.binding.Observable;
import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
import org.apache.causeway.core.metamodel.interactions.managed.ActionInteraction;
import org.apache.causeway.core.metamodel.interactions.managed.ManagedAction;
+import org.apache.causeway.core.metamodel.interactions.managed.ManagedAction.MementoForArgs;
import org.apache.causeway.core.metamodel.interactions.managed.ManagedCollection;
import org.apache.causeway.core.metamodel.interactions.managed.ManagedMember;
import org.apache.causeway.core.metamodel.interactions.managed.MultiselectChoices;
import org.apache.causeway.core.metamodel.object.ManagedObject;
import org.apache.causeway.core.metamodel.object.MmSortUtils;
+import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
import org.apache.causeway.core.metamodel.spec.feature.ObjectMember;
import org.apache.causeway.core.metamodel.tabular.internal.DataTableInternal;
+import org.apache.causeway.core.metamodel.tabular.optimistic.DataTableO;
import org.apache.causeway.core.metamodel.tabular.simple.DataTable;
import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.val;
public interface DataTableInteractive extends MultiselectChoices {
+ public enum TableImplementation {
+ DEFAULT,
+ OPTIMISTIC;
+
+ public boolean isOptimistic() { return this == OPTIMISTIC;}
+
+ private final static String PROPERTY_NAME_MODEL_SELECT = "causeway.metamodel.dataTableModelSelect";
+ private static boolean isOptimisticSelected() {
+ return "OPTIMISTIC".equalsIgnoreCase(System.getenv(PROPERTY_NAME_MODEL_SELECT))
+ || "OPTIMISTIC".equalsIgnoreCase(System.getProperty(PROPERTY_NAME_MODEL_SELECT));
+ }
+
+ @Getter(lazy=true)
+ private final static TableImplementation selected = isOptimisticSelected() ? OPTIMISTIC : DEFAULT;
+ }
+
// -- FACTORIES
public static DataTableInteractive empty(final ManagedMember managedMember, final Where where) {
- return DataTableInternal.empty(managedMember, where);
+ switch (TableImplementation.getSelected()) {
+ case OPTIMISTIC:
+ return DataTableO.empty(managedMember, where);
+ case DEFAULT:
+ default:
+ return DataTableInternal.empty(managedMember, where);
+ }
}
public static DataTableInteractive forCollection(final ManagedCollection managedCollection) {
- return DataTableInternal.forCollection(managedCollection);
+ switch (TableImplementation.getSelected()) {
+ case OPTIMISTIC:
+ return DataTableO.forCollection(managedCollection);
+ case DEFAULT:
+ default:
+ return DataTableInternal.forCollection(managedCollection);
+ }
+ }
+
+ public static DataTableInteractive forAction(
+ final ManagedAction managedAction,
+ final ManagedObject actionResult) {
+ switch (TableImplementation.getSelected()) {
+ case OPTIMISTIC:
+ throw _Exceptions.unexpectedCodeReach();
+ case DEFAULT:
+ default:
+ return DataTableInternal.forAction(managedAction, actionResult);
+ }
}
+ @Deprecated // args only required for TableImplementation.OPTIMISTIC
public static DataTableInteractive forAction(
final ManagedAction managedAction,
+ final @NonNull Can<ManagedObject> args,
final ManagedObject actionResult) {
- return DataTableInternal.forAction(managedAction, actionResult);
+ switch (TableImplementation.getSelected()) {
+ case OPTIMISTIC:
+ return DataTableO.forAction(managedAction, args, actionResult);
+ case DEFAULT:
+ default:
+ return DataTableInternal.forAction(managedAction, actionResult);
+ }
}
// --
@@ -91,6 +146,9 @@ public interface DataTableInteractive extends MultiselectChoices {
ObjectMember getMetaModel();
Optional<TableDecorator> getTableDecoratorIfAny();
+ default ObjectSpecification getElementType() {
+ return getMetaModel().getElementType();
+ }
// -- ASSOCIATED ACTION
@@ -110,6 +168,11 @@ public interface DataTableInteractive extends MultiselectChoices {
*/
Optional<DataRow> lookupDataRow(int rowIndex);
+ @Deprecated // used by OPTIMISTIC data table
+ default Optional<? extends DataRow> lookupDataRow(final @NonNull UUID uuid) {
+ throw _Exceptions.unsupportedOperation();
+ }
+
// -- PAGING
int getPageSize(int pageSizeDefault);
@@ -133,6 +196,11 @@ public interface DataTableInteractive extends MultiselectChoices {
DataTableMemento createMemento();
+ @Deprecated // used by OPTIMISTIC data table
+ default DataTableMemento createMemento(final MementoForArgs mementoForArgs) {
+ throw _Exceptions.unsupportedOperation();
+ }
+
// -- FILTER SUPPORT
Bindable<String> getSearchArgument();
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/internal/DataTableInternal.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/internal/DataTableInternal.java
index f696e4a88a..43fba90271 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/internal/DataTableInternal.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/internal/DataTableInternal.java
@@ -47,7 +47,6 @@ import org.apache.causeway.core.metamodel.interactions.managed.ManagedMember;
import org.apache.causeway.core.metamodel.object.ManagedObject;
import org.apache.causeway.core.metamodel.object.ManagedObjects;
import org.apache.causeway.core.metamodel.object.PackedManagedObject;
-import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
import org.apache.causeway.core.metamodel.spec.feature.ObjectMember;
import org.apache.causeway.core.metamodel.tabular.DataColumn;
import org.apache.causeway.core.metamodel.tabular.DataRow;
@@ -228,10 +227,6 @@ implements DataTableInteractive {
return managedMember.getMetaModel();
}
- public ObjectSpecification getElementType() {
- return getMetaModel().getElementType();
- }
-
@Override
public Optional<DataRow> lookupDataRow(final int rowIndex) {
return getDataRows().getValue().get(rowIndex)
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/optimistic/DataColumnO.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/optimistic/DataColumnO.java
new file mode 100644
index 0000000000..e6c5a3c447
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/optimistic/DataColumnO.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.causeway.core.metamodel.tabular.optimistic;
+
+import java.util.Optional;
+
+import org.apache.causeway.commons.internal.binding._Observables;
+import org.apache.causeway.commons.internal.binding._Observables.LazyObservable;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.causeway.core.metamodel.tabular.DataColumn;
+
+import lombok.Getter;
+import lombok.NonNull;
+
+public class DataColumnO implements DataColumn {
+
+ @Getter private final @NonNull String columnId;
+ @Getter private final @NonNull ObjectAssociation associationMetaModel;
+ @Getter private final @NonNull LazyObservable<String> columnFriendlyName;
+ @Getter private final @NonNull LazyObservable<Optional<String>> columnDescription;
+
+ public DataColumnO(final DataTableO parentTable, final ObjectAssociation associationMetaModel) {
+ this.associationMetaModel = associationMetaModel;
+ this.columnId = associationMetaModel.getId();
+
+ columnFriendlyName = _Observables.lazy(()->
+ associationMetaModel.getCanonicalFriendlyName());
+
+ columnDescription = _Observables.lazy(()->
+ associationMetaModel.getCanonicalDescription());
+ }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/optimistic/DataRowO.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/optimistic/DataRowO.java
new file mode 100644
index 0000000000..185d2d652c
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/optimistic/DataRowO.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.causeway.core.metamodel.tabular.optimistic;
+
+import java.util.Optional;
+import java.util.UUID;
+
+import org.apache.causeway.applib.services.filter.CollectionFilterService.Tokens;
+import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.internal.binding._Bindables;
+import org.apache.causeway.commons.internal.binding._Bindables.BooleanBindable;
+import org.apache.causeway.core.metamodel.object.ManagedObject;
+import org.apache.causeway.core.metamodel.object.ManagedObjects;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.causeway.core.metamodel.tabular.DataColumn;
+import org.apache.causeway.core.metamodel.tabular.DataRow;
+
+import lombok.Getter;
+import lombok.NonNull;
+
+public class DataRowO implements DataRow {
+
+ @Getter private final UUID uuid = UUID.randomUUID(); // in support of client side sorting
+ private final ManagedObject rowElement;
+ @Getter private final BooleanBindable selectToggle;
+ @Getter private final DataTableO parentTable;
+
+ public DataRowO(
+ final @NonNull DataTableO parentTable,
+ final @NonNull ManagedObject rowElement) {
+ this.parentTable = parentTable;
+ this.rowElement = rowElement;
+
+ selectToggle = _Bindables.forBoolean(false);
+ selectToggle.addListener((e,o,n)->{
+
+ //_ToggleDebug.onSelectRowToggle(rowElement, o, n, parentTable.isToggleAllEvent.get());
+
+ if(parentTable.isToggleAllEvent.get()) {
+ return;
+ }
+ parentTable.getDataRowsSelected().invalidate();
+ // in any case, if we have a toggle state change, clear the toggle all bindable
+ parentTable.clearToggleAll();
+ });
+
+ }
+
+ @Override
+ public ManagedObject getRowElement() {
+ return rowElement;
+ }
+
+ @Override
+ public Optional<DataColumn> lookupColumnById(final @NonNull String columnId) {
+ return parentTable.getDataColumns().getValue().stream()
+ .filter(dataColumn->dataColumn.getColumnId().equals(columnId))
+ .findFirst();
+ }
+
+ /**
+ * Can be none, one or many per table cell.
+ */
+ public Can<ManagedObject> getCellElementsForColumn(final @NonNull DataColumnO column) {
+ final ObjectAssociation assoc = column.getAssociationMetaModel();
+ return assoc.getSpecialization().fold(
+ property->Can.of(property.get(getRowElement())),
+ collection->ManagedObjects.unpack(collection.get(getRowElement())));
+ }
+
+ /**
+ * Can be none, one or many per table cell. (returns empty Can if column not found)
+ */
+ @Override
+ public Can<ManagedObject> getCellElementsForColumn(final @NonNull String columnId) {
+ return lookupColumnById(columnId)
+ .map(this::getCellElementsForColumn)
+ .orElseGet(Can::empty);
+ }
+
+ @Override
+ public int getRowIndex() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public Can<ManagedObject> getCellElementsForColumn(@NonNull final DataColumn column) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Optional<Tokens> getFilterTokens() {
+ // TODO Auto-generated method stub
+ return Optional.empty();
+ }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/optimistic/DataTableO.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/optimistic/DataTableO.java
new file mode 100644
index 0000000000..cff89f3b59
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/optimistic/DataTableO.java
@@ -0,0 +1,408 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.causeway.core.metamodel.tabular.optimistic;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
+
+import org.springframework.lang.Nullable;
+
+import org.apache.causeway.applib.Identifier;
+import org.apache.causeway.applib.annotation.TableDecorator;
+import org.apache.causeway.applib.annotation.Where;
+import org.apache.causeway.commons.binding.Bindable;
+import org.apache.causeway.commons.binding.Observable;
+import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.internal.binding._BindableAbstract;
+import org.apache.causeway.commons.internal.binding._Bindables;
+import org.apache.causeway.commons.internal.binding._Observables;
+import org.apache.causeway.commons.internal.binding._Observables.LazyObservable;
+import org.apache.causeway.commons.internal.collections._Maps;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
+import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
+import org.apache.causeway.core.metamodel.consent.InteractionResult;
+import org.apache.causeway.core.metamodel.context.MetaModelContext;
+import org.apache.causeway.core.metamodel.interactions.InteractionHead;
+import org.apache.causeway.core.metamodel.interactions.InteractionUtils;
+import org.apache.causeway.core.metamodel.interactions.ObjectVisibilityContext;
+import org.apache.causeway.core.metamodel.interactions.VisibilityContext;
+import org.apache.causeway.core.metamodel.interactions.managed.ActionInteraction;
+import org.apache.causeway.core.metamodel.interactions.managed.CollectionInteraction;
+import org.apache.causeway.core.metamodel.interactions.managed.ManagedAction;
+import org.apache.causeway.core.metamodel.interactions.managed.ManagedAction.MementoForArgs;
+import org.apache.causeway.core.metamodel.interactions.managed.ManagedCollection;
+import org.apache.causeway.core.metamodel.interactions.managed.ManagedMember;
+import org.apache.causeway.core.metamodel.object.ManagedObject;
+import org.apache.causeway.core.metamodel.object.PackedManagedObject;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectMember;
+import org.apache.causeway.core.metamodel.tabular.DataColumn;
+import org.apache.causeway.core.metamodel.tabular.DataRow;
+import org.apache.causeway.core.metamodel.tabular.DataTableInteractive;
+import org.apache.causeway.core.metamodel.tabular.DataTableMemento;
+import org.apache.causeway.core.metamodel.tabular.simple.DataTable;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+
+public class DataTableO
+implements DataTableInteractive {
+
+ // -- FACTORIES
+
+ public static DataTableO empty(final ManagedMember managedMember, final Where where) {
+ return new DataTableO(managedMember, where, Can::empty);
+ }
+
+ public static DataTableO forCollection(
+ final ManagedCollection managedCollection) {
+ return new DataTableO(managedCollection, managedCollection.getWhere(), ()->
+ managedCollection
+ .streamElements()
+ .collect(Can.toCan()));
+ }
+
+ public static DataTableO forAction(
+ final ManagedAction managedAction,
+ final Can<ManagedObject> args,
+ final ManagedObject actionResult) {
+
+ if(actionResult==null) {
+ new DataTableO(managedAction, managedAction.getWhere(), Can::empty);
+ }
+ if(!(actionResult instanceof PackedManagedObject)) {
+ throw _Exceptions.unexpectedCodeReach();
+ }
+
+ val elements = ((PackedManagedObject)actionResult).unpack();
+ elements.forEach(ManagedObject::getBookmark);
+
+ return new DataTableO(managedAction, managedAction.getWhere(),
+ ()->elements);
+ }
+
+ // -- CONSTRUCTION
+
+ // as this is a layer of abstraction, don't expose via getter
+ final @NonNull ManagedMember managedMember;
+ final @NonNull Where where;
+
+ @Getter private final @NonNull LazyObservable<Can<ManagedObject>> dataElements;
+ @Getter private final @NonNull _BindableAbstract<String> searchArgument; // filter the data rows
+ @Getter private final @NonNull LazyObservable<Can<DataRow>> dataRowsFiltered;
+ @Getter private final @NonNull LazyObservable<Can<DataRow>> dataRowsSelected;
+ @Getter private final _BindableAbstract<Boolean> selectAllToggle;
+
+ @Getter private final @NonNull LazyObservable<Can<DataColumn>> dataColumns;
+ @Getter private final @NonNull LazyObservable<String> title;
+
+ private DataTableO(
+ // we need access to the owner in support of imperative title and referenced column detection
+ final ManagedMember managedMember,
+ final Where where,
+ final Supplier<Can<ManagedObject>> elementSupplier) {
+
+ this.managedMember = managedMember;
+ this.where = where;
+
+ //dataElements = _Observables.lazy(elementSupplier);
+ dataElements = _Observables.lazy(()->elementSupplier.get().map(
+ MetaModelContext.instanceElseFail()::injectServicesInto));
+
+ searchArgument = _Bindables.forValue(null);
+
+ dataRowsFiltered = _Observables.lazy(()->
+ dataElements.getValue().stream()
+ //XXX future extension: filter by searchArgument
+ .filter(this::ignoreHidden)
+ .sorted(managedMember.getMetaModel().getElementComparator()
+ .orElseGet(()->(a, b)->0)) // else don't sort (no-op comparator for streams)
+ .map(domainObject->new DataRowO(this, domainObject))
+ .collect(Can.toCan()));
+
+ dataRowsSelected = _Observables.lazy(()->
+ dataRowsFiltered.getValue().stream()
+ .filter(dataRow->dataRow.getSelectToggle().getValue().booleanValue())
+ .collect(Can.toCan()));
+
+ selectAllToggle = _Bindables.forValue(Boolean.FALSE);
+ selectAllToggle.addListener((e,o,isAllOn)->{
+ //_Debug.onClearToggleAll(o, isAllOn, isClearToggleAllEvent.get());
+ if(isClearToggleAllEvent.get()) {
+ return;
+ }
+ dataRowsSelected.invalidate();
+ try {
+ isToggleAllEvent.set(true);
+ dataRowsFiltered.getValue().forEach(dataRow->dataRow.getSelectToggle().setValue(isAllOn));
+ } finally {
+ isToggleAllEvent.set(false);
+ }
+ });
+
+ searchArgument.addListener((e,o,n)->{
+ dataRowsFiltered.invalidate();
+ dataRowsSelected.invalidate();
+ });
+
+ dataColumns = _Observables.lazy(()->
+ managedMember.getElementType()
+ .streamAssociationsForColumnRendering(managedMember.getIdentifier(), managedMember.getOwner())
+ .map(assoc->new DataColumnO(this, assoc))
+ .collect(Can.toCan()));
+
+ //XXX future extension: the title could dynamically reflect the number of elements selected
+ //eg... 5 Orders selected
+ title = _Observables.lazy(()->
+ managedMember
+ .getFriendlyName());
+ }
+
+ @Override
+ public int getPageSize(final int pageSizeDefault) {
+ return getMetaModel().getPageSize().orElse(pageSizeDefault);
+ }
+
+ @Override
+ public Optional<TableDecorator> getTableDecoratorIfAny() {
+ return getMetaModel().getTableDecorator();
+ }
+
+ /**
+ * Count filtered data rows.
+ */
+ public int getElementCount() {
+ return dataRowsFiltered.getValue().size();
+ }
+
+ @Override
+ public ObjectMember getMetaModel() {
+ return managedMember.getMetaModel();
+ }
+
+ private final Map<UUID, Optional<DataRowO>> dataRowByUuidLookupCache = _Maps.newConcurrentHashMap();
+ @Override
+ public Optional<DataRowO> lookupDataRow(final @NonNull UUID uuid) {
+ // lookup can be safely cached
+ return dataRowByUuidLookupCache.computeIfAbsent(uuid, __->getDataRowsFiltered().getValue().stream()
+ .map(DataRowO.class::cast)
+ .filter(dr->dr.getUuid().equals(uuid))
+ .findFirst());
+ }
+
+ // -- TOGGLE ALL
+
+ final AtomicBoolean isToggleAllEvent = new AtomicBoolean();
+ private final AtomicBoolean isClearToggleAllEvent = new AtomicBoolean();
+ public void clearToggleAll() {
+ try {
+ isClearToggleAllEvent.set(true);
+ selectAllToggle.setValue(Boolean.FALSE);
+ } finally {
+ isClearToggleAllEvent.set(false);
+ }
+ }
+
+ // -- DATA ROW VISIBILITY
+
+ private boolean ignoreHidden(final ManagedObject adapter) {
+ final InteractionResult visibleResult =
+ InteractionUtils.isVisibleResult(
+ adapter.getSpecification(),
+ createVisibleInteractionContext(adapter));
+ return visibleResult.isNotVetoing();
+ }
+
+ private VisibilityContext createVisibleInteractionContext(final ManagedObject objectAdapter) {
+ return new ObjectVisibilityContext(
+ InteractionHead.regular(objectAdapter),
+ objectAdapter.getSpecification().getFeatureIdentifier(),
+ InteractionInitiatedBy.USER,
+ Where.ALL_TABLES,
+ InteractionUtils.renderPolicy(objectAdapter));
+ }
+
+ // -- ASSOCIATED ACTION WITH MULTI SELECT
+
+ @Override
+ public Can<ManagedObject> getSelected() {
+ return getDataRowsSelected()
+ .getValue()
+ .map(DataRow::getRowElement);
+ }
+
+ @Override
+ public ActionInteraction startAssociatedActionInteraction(final String actionId, final Where where) {
+ val featureId = managedMember.getIdentifier();
+ if(!featureId.getType().isPropertyOrCollection()) {
+ return ActionInteraction.empty(String.format("[no such collection %s; instead got %s;"
+ + "(while searching for an associated action %s)]",
+ featureId,
+ featureId.getType(),
+ actionId));
+ }
+ return ActionInteraction.startWithMultiselect(managedMember.getOwner(), actionId, where, this);
+ }
+
+ // -- EXPORT
+
+ @Override
+ public DataTable export() {
+ return new DataTable(getElementType(),
+ getTitle().getValue(),
+ getDataColumns().getValue()
+ .map(DataColumn::getAssociationMetaModel),
+ getDataRowsFiltered().getValue()
+ .stream()
+ .map(dr->dr.getRowElement())
+ .collect(Can.toCan()));
+ }
+
+ // -- MEMENTO
+
+ public Memento getMemento(final @Nullable ManagedAction.MementoForArgs argsMemento) {
+ return Memento.create(this, argsMemento);
+ }
+
+ /**
+ * Recreation from given 'bookmarkable' {@link ManagedObject} (owner),
+ * without triggering domain events.
+ * Either originates from a <i>Collection</i> or an <i>Action</i>'s
+ * non-scalar result.
+ * <p>
+ * In the <i>Action</i> case, requires the <i>Action</i>'s arguments
+ * for reconstruction.
+ * <p>
+ * Responsibility for recreation of the owner is with the caller
+ * to allow for simpler object graph reconstruction (shared owner).
+ * <p>
+ * However, we keep track of the argument list here.
+ */
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ public static class Memento implements DataTableMemento {
+ private static final long serialVersionUID = 1L;
+
+ static Memento create(
+ final @Nullable DataTableO table,
+ final @Nullable MementoForArgs argsMemento) {
+ val managedMember = table.managedMember;
+
+ return new Memento(
+ managedMember.getIdentifier(),
+ table.where,
+ argsMemento);
+ }
+
+ private final Identifier featureId;
+ private final Where where;
+ private final MementoForArgs argsMemento;
+
+ @Override
+ public DataTableO getDataTableModel(final ManagedObject owner) {
+
+ if(owner.getPojo()==null) {
+ // owner (if entity) might have been deleted
+ throw _Exceptions.illegalArgument("cannot recreate from memento for deleted object");
+ }
+
+ val memberId = featureId.getMemberLogicalName();
+
+ if(featureId.getType().isPropertyOrCollection()) {
+ // bypass domain events
+ val collInteraction = CollectionInteraction.start(owner, memberId, where);
+ val managedColl = collInteraction.getManagedCollection().orElseThrow();
+ // invocation bypassing domain events (pass-through)
+ return new DataTableO(managedColl, where, ()->
+ managedColl.streamElements(InteractionInitiatedBy.PASS_THROUGH).collect(Can.toCan()));
+ }
+ val actionInteraction = ActionInteraction.start(owner, memberId, where);
+ val managedAction = actionInteraction.getManagedActionElseFail();
+ val args = argsMemento.getArgumentList(managedAction.getMetaModel());
+ // invocation bypassing domain events (pass-through)
+ val actionResult = managedAction.invoke(args, InteractionInitiatedBy.PASS_THROUGH)
+ .getSuccessElseFail();
+ return forAction(managedAction, args, actionResult);
+ }
+
+ @Override
+ public void setupBindings(final DataTableInteractive tableInteractive) {
+ // TODO Auto-generated method stub
+
+ }
+ }
+
+ @Override
+ public Observable<Can<DataRow>> getDataRowsFilteredAndSorted() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int getFilteredElementCount() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public Optional<DataRow> lookupDataRow(final int rowIndex) {
+ // TODO Auto-generated method stub
+ return Optional.empty();
+ }
+
+ @Override
+ public Bindable<ColumnSort> getColumnSort() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void doProgrammaticToggle(final Runnable runnable) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Set<Integer> getSelectedRowIndexes() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public DataTableMemento createMemento() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isSearchSupported() {
+ return false;
+ }
+
+ @Override
+ public String getSearchPromptPlaceholderText() {
+ return "";
+ }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/optimistic/_ToggleDebug.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/optimistic/_ToggleDebug.java
new file mode 100644
index 0000000000..8b44d806c0
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/tabular/optimistic/_ToggleDebug.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.causeway.core.metamodel.tabular.optimistic;
+
+import org.apache.causeway.core.metamodel.object.ManagedObject;
+
+class _ToggleDebug {
+
+ public static void onSelectRowToggle(
+ final ManagedObject rowElement,
+ final Boolean old,
+ final Boolean _new,
+ final boolean isToggleAllEvent) {
+
+ System.err.printf("onSelectRowToggle: %s %b->%b (toggle-all: %b)%n",
+ ""+rowElement.getPojo(), old, _new, isToggleAllEvent);
+ }
+
+ public static void onClearToggleAll(
+ final Boolean old,
+ final Boolean isAllOn,
+ final boolean isClearToggleAllEvent) {
+
+ System.err.printf("clear-all: %b->%b (clear-all: %b)%n",
+ old, isAllOn, isClearToggleAllEvent);
+ }
+
+}
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/EntityCollectionModelStandalone.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/EntityCollectionModelStandalone.java
index ad8693e087..461a3813a0 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/EntityCollectionModelStandalone.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/EntityCollectionModelStandalone.java
@@ -18,6 +18,8 @@
*/
package org.apache.causeway.viewer.wicket.model.models;
+import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.core.metamodel.object.ManagedObject;
import org.apache.causeway.core.metamodel.object.PackedManagedObject;
import org.apache.causeway.viewer.wicket.model.models.interaction.BookmarkedObjectWkt;
import org.apache.causeway.viewer.wicket.model.models.interaction.coll.DataTableModelWkt;
@@ -34,7 +36,8 @@ extends EntityCollectionModelAbstract {
public static EntityCollectionModelStandalone forActionModel(
final @NonNull PackedManagedObject collectionAsAdapter,
- final @NonNull ActionModel actionModel) {
+ final @NonNull ActionModel actionModel,
+ final @NonNull Can<ManagedObject> args) {
val action = actionModel.getAction();
@@ -43,6 +46,7 @@ extends EntityCollectionModelAbstract {
BookmarkedObjectWkt
.ofAdapter(actionModel.getParentObject()),
action,
+ args,
collectionAsAdapter));
}
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
index 380fbeefb0..dc5c29409b 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
@@ -39,6 +39,7 @@ import org.apache.causeway.core.metamodel.interactions.managed.PendingParamsSnap
import org.apache.causeway.core.metamodel.object.ManagedObjects;
import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
import org.apache.causeway.core.metamodel.spec.feature.memento.ActionMemento;
+import org.apache.causeway.core.metamodel.tabular.DataTableInteractive.TableImplementation;
import org.apache.causeway.viewer.wicket.model.models.EntityCollectionModel;
import org.apache.causeway.viewer.wicket.model.models.InlinePromptContext;
import org.apache.causeway.viewer.wicket.model.models.ScalarParameterModel;
@@ -256,21 +257,30 @@ extends HasBookmarkedOwnerAbstract<ActionInteraction> {
*/
private transient ParameterNegotiationModel parameterNegotiationModel;
private PendingParamsSnapshot pendingParamsSnapshot;
+
/**
* Start and transiently memoize a new {@link ParameterNegotiationModel}.
*/
private ParameterNegotiationModel startParameterNegotiationModel() {
- var tableModel = actionInteraction().getManagedAction().map(ManagedAction::getMultiselectChoices);
- if(tableModel!=null) {
- // possibly uses outdated tableModel, if any select toggle, filtering or sorting has happened
- // hence detach(), so we invalidate the current actionInteraction() and force recreation
- detach();
+ switch (TableImplementation.getSelected()) {
+ case DEFAULT: {
+ var tableModel = actionInteraction().getManagedAction().map(ManagedAction::getMultiselectChoices);
+ if(tableModel!=null) {
+ // possibly uses outdated tableModel, if any select toggle, filtering or sorting has happened
+ // hence detach(), so we invalidate the current actionInteraction() and force recreation
+ detach();
+ }
+ break;
+ }
+ default:
+ break;
}
this.parameterNegotiationModel = actionInteraction().startParameterNegotiation()
.orElseThrow(()->_Exceptions.noSuchElement(memberId));
this.pendingParamsSnapshot = parameterNegotiationModel.createSnapshotModel();
return parameterNegotiationModel;
}
+
/**
* [CAUSEWAY-3649] safe guard against access to the model while it is not attached
*/
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWkt.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWkt.java
index 3c7c7c053c..738190ec00 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWkt.java
@@ -19,52 +19,46 @@
package org.apache.causeway.viewer.wicket.model.models.interaction.coll;
import java.util.Optional;
+import java.util.UUID;
-import org.apache.wicket.model.ChainingModel;
import org.apache.wicket.model.IModel;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
import org.apache.causeway.core.metamodel.tabular.DataRow;
import org.apache.causeway.core.metamodel.tabular.DataTableInteractive;
+import org.apache.causeway.core.metamodel.tabular.DataTableInteractive.TableImplementation;
-import lombok.Getter;
+public interface DataRowWkt extends IModel<DataRow>{
-public class DataRowWkt
-extends ChainingModel<DataRow> {
-
- private static final long serialVersionUID = 1L;
-
- public static DataRowWkt chain(
+ static DataRowWkt chain(
final IModel<DataTableInteractive> dataTableModelHolder,
final DataRow dataRow) {
- return new DataRowWkt(dataTableModelHolder, dataRow);
- }
- @Getter private final int rowIndex;
-
- private DataRowWkt(
- final IModel<DataTableInteractive> dataTableModelHolder,
- final DataRow dataRow) {
- super(dataTableModelHolder);
- this.rowIndex = dataRow.getRowIndex();
+ switch (TableImplementation.getSelected()) {
+ case OPTIMISTIC:
+ return DataRowWktO.chain(dataTableModelHolder, dataRow);
+ case DEFAULT:
+ default:
+ return DataRowWktD.chain(dataTableModelHolder, dataRow);
+ }
}
- @Override
- public final DataRow getObject() {
- return dataRow().orElse(null);
- }
+ int getRowIndex();
- public Optional<DataRow> dataRow() {
- return dataTableModel().lookupDataRow(rowIndex);
- }
+ @Override
+ DataRow getObject();
- public boolean hasMemoizedDataRow() {
- return ((DataTableModelWkt) super.getTarget()).isAttached();
- }
+ Optional<DataRow> dataRow();
- // -- HELPER
+ boolean hasMemoizedDataRow();
- private DataTableInteractive dataTableModel() {
- return ((DataTableModelWkt) super.getTarget()).getObject();
+ @Deprecated // used by OPTIMISTIC data table
+ default UUID getUuid() {
+ throw _Exceptions.unsupportedOperation();
+ }
+ @Deprecated // used by OPTIMISTIC data table
+ default DataRowToggleWkt getDataRowToggle() {
+ throw _Exceptions.unsupportedOperation();
}
}
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWkt.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWktD.java
similarity index 89%
copy from viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWkt.java
copy to viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWktD.java
index 3c7c7c053c..4ebd31cd41 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWktD.java
@@ -28,20 +28,21 @@ import org.apache.causeway.core.metamodel.tabular.DataTableInteractive;
import lombok.Getter;
-public class DataRowWkt
-extends ChainingModel<DataRow> {
+public class DataRowWktD
+extends ChainingModel<DataRow>
+implements DataRowWkt {
private static final long serialVersionUID = 1L;
- public static DataRowWkt chain(
+ public static DataRowWktD chain(
final IModel<DataTableInteractive> dataTableModelHolder,
final DataRow dataRow) {
- return new DataRowWkt(dataTableModelHolder, dataRow);
+ return new DataRowWktD(dataTableModelHolder, dataRow);
}
@Getter private final int rowIndex;
- private DataRowWkt(
+ private DataRowWktD(
final IModel<DataTableInteractive> dataTableModelHolder,
final DataRow dataRow) {
super(dataTableModelHolder);
@@ -53,10 +54,12 @@ extends ChainingModel<DataRow> {
return dataRow().orElse(null);
}
+ @Override
public Optional<DataRow> dataRow() {
return dataTableModel().lookupDataRow(rowIndex);
}
+ @Override
public boolean hasMemoizedDataRow() {
return ((DataTableModelWkt) super.getTarget()).isAttached();
}
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWkt.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWktO.java
similarity index 60%
copy from viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWkt.java
copy to viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWktO.java
index 3c7c7c053c..35497b6376 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataRowWktO.java
@@ -19,51 +19,73 @@
package org.apache.causeway.viewer.wicket.model.models.interaction.coll;
import java.util.Optional;
+import java.util.UUID;
import org.apache.wicket.model.ChainingModel;
import org.apache.wicket.model.IModel;
import org.apache.causeway.core.metamodel.tabular.DataRow;
import org.apache.causeway.core.metamodel.tabular.DataTableInteractive;
+import org.apache.causeway.viewer.wicket.model.util.PageUtils;
import lombok.Getter;
+import lombok.NonNull;
-public class DataRowWkt
-extends ChainingModel<DataRow> {
+public class DataRowWktO
+extends ChainingModel<DataRow>
+implements DataRowWkt {
private static final long serialVersionUID = 1L;
- public static DataRowWkt chain(
+ public static DataRowWktO chain(
final IModel<DataTableInteractive> dataTableModelHolder,
final DataRow dataRow) {
- return new DataRowWkt(dataTableModelHolder, dataRow);
+ return new DataRowWktO(dataTableModelHolder, dataRow);
}
@Getter private final int rowIndex;
+ @Getter private final @NonNull UUID uuid; // in support of table sorting
+ @Getter private final @NonNull DataRowToggleWkt dataRowToggle;
- private DataRowWkt(
+ private transient DataRow dataRow;
+
+ private DataRowWktO(
final IModel<DataTableInteractive> dataTableModelHolder,
final DataRow dataRow) {
super(dataTableModelHolder);
+ this.dataRow = dataRow;
this.rowIndex = dataRow.getRowIndex();
+ this.uuid = dataRow.getUuid();
+ this.dataRowToggle = new DataRowToggleWkt(this);
}
@Override
public final DataRow getObject() {
- return dataRow().orElse(null);
+ if(dataRow==null) {
+ dataRow = getDataTableModel().lookupDataRow(uuid)
+ .orElse(null);
+ if(dataRow==null) {
+ // [CAUSEWAY-3005] UI out of sync with model: reload page
+ PageUtils.pageReload();
+ }
+ }
+ return dataRow;
}
+ @Override
public Optional<DataRow> dataRow() {
- return dataTableModel().lookupDataRow(rowIndex);
+ return Optional.ofNullable(getObject());
}
+ @Override
public boolean hasMemoizedDataRow() {
- return ((DataTableModelWkt) super.getTarget()).isAttached();
+ return dataRow!=null
+ || getDataTableModel().lookupDataRow(uuid).isPresent();
}
// -- HELPER
- private DataTableInteractive dataTableModel() {
+ private DataTableInteractive getDataTableModel() {
return ((DataTableModelWkt) super.getTarget()).getObject();
}
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java
index 55bf6c9cb9..83360e113a 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java
@@ -18,23 +18,22 @@
*/
package org.apache.causeway.viewer.wicket.model.models.interaction.coll;
+import org.apache.wicket.model.IModel;
+
import org.apache.causeway.applib.Identifier;
-import org.apache.causeway.applib.annotation.Where;
-import org.apache.causeway.core.metamodel.interactions.managed.ManagedAction;
-import org.apache.causeway.core.metamodel.interactions.managed.ManagedCollection;
+import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
import org.apache.causeway.core.metamodel.object.ManagedObject;
import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
import org.apache.causeway.core.metamodel.spec.feature.OneToManyAssociation;
import org.apache.causeway.core.metamodel.tabular.DataTableInteractive;
-import org.apache.causeway.core.metamodel.tabular.DataTableMemento;
+import org.apache.causeway.core.metamodel.tabular.DataTableInteractive.TableImplementation;
import org.apache.causeway.viewer.commons.model.object.HasUiParentObject;
import org.apache.causeway.viewer.commons.model.object.UiObject;
import org.apache.causeway.viewer.wicket.model.models.interaction.BookmarkedObjectWkt;
import org.apache.causeway.viewer.wicket.model.models.interaction.HasBookmarkedOwnerAbstract;
-import lombok.Getter;
import lombok.NonNull;
-import lombok.val;
/**
* Bound to a BookmarkedObjectWkt, with the {@link DataTableInteractive}
@@ -46,9 +45,9 @@ import lombok.val;
*
* @see HasBookmarkedOwnerAbstract
*/
-public class DataTableModelWkt
-extends HasBookmarkedOwnerAbstract<DataTableInteractive>
-implements
+public interface DataTableModelWkt
+extends
+ IModel<DataTableInteractive>,
HasUiParentObject<UiObject> {
// -- FACTORIES
@@ -57,57 +56,43 @@ implements
final @NonNull BookmarkedObjectWkt bookmarkedObjectModel,
final @NonNull ObjectAction actMetaModel,
final @NonNull ManagedObject actionResult) {
+ switch (TableImplementation.getSelected()) {
+ case OPTIMISTIC:
+ throw _Exceptions.unexpectedCodeReach();
+ case DEFAULT:
+ default:
+ return DataTableModelWktD.forActionModel(bookmarkedObjectModel, actMetaModel, actionResult);
+ }
+ }
- val managedAction = ManagedAction
- .of(bookmarkedObjectModel.getObject(), actMetaModel, Where.NOT_SPECIFIED);
- val tableInteractive = DataTableInteractive.forAction(
- managedAction,
- actionResult);
- return new DataTableModelWkt(
- bookmarkedObjectModel, actMetaModel.getFeatureIdentifier(), tableInteractive);
+ public static DataTableModelWkt forActionModel(
+ final @NonNull BookmarkedObjectWkt bookmarkedObjectModel,
+ final @NonNull ObjectAction actMetaModel,
+ final @NonNull Can<ManagedObject> args,
+ final @NonNull ManagedObject actionResult) {
+ switch (TableImplementation.getSelected()) {
+ case OPTIMISTIC:
+ return DataTableModelWktO.forActionModel(bookmarkedObjectModel, actMetaModel, args, actionResult);
+ case DEFAULT:
+ default:
+ return DataTableModelWktD.forActionModel(bookmarkedObjectModel, actMetaModel, actionResult);
+ }
}
public static @NonNull DataTableModelWkt forCollection(
final @NonNull BookmarkedObjectWkt bookmarkedObjectModel,
final @NonNull OneToManyAssociation collMetaModel) {
-
- val tableInteractive = DataTableInteractive.forCollection(
- ManagedCollection
- .of(bookmarkedObjectModel.getObject(), collMetaModel, Where.NOT_SPECIFIED));
- return new DataTableModelWkt(
- bookmarkedObjectModel, collMetaModel.getFeatureIdentifier(), tableInteractive);
- }
-
- // -- CONSTRUCTION
-
- private static final long serialVersionUID = 1L;
-
- @Getter private final Identifier featureIdentifier;
- private final DataTableMemento tableMemento;
-
- private DataTableModelWkt(
- final BookmarkedObjectWkt bookmarkedObject,
- final Identifier featureIdentifier,
- final DataTableInteractive tableInteractive) {
- super(bookmarkedObject);
- this.featureIdentifier = featureIdentifier;
- this.tableMemento = tableInteractive.createMemento();
- setObject(tableInteractive); // memoize
- tableMemento.setupBindings(tableInteractive);
+ switch (TableImplementation.getSelected()) {
+ case OPTIMISTIC:
+ return DataTableModelWktO.forCollection(bookmarkedObjectModel, collMetaModel);
+ case DEFAULT:
+ default:
+ return DataTableModelWktD.forCollection(bookmarkedObjectModel, collMetaModel);
+ }
}
- // --
-
- @Override
- public UiObject getParentUiModel() {
- return ()->super.getBookmarkedOwner();
- }
-
- @Override
- protected DataTableInteractive load() {
- val tableInteractive = tableMemento.getDataTableModel(getBookmarkedOwner());
- tableMemento.setupBindings(tableInteractive);
- return tableInteractive;
- }
+ Identifier getFeatureIdentifier();
+ boolean isAttached();
+ ManagedObject getBookmarkedOwner();
}
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWktD.java
similarity index 92%
copy from viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java
copy to viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWktD.java
index 55bf6c9cb9..82b0a8a9f9 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWktD.java
@@ -27,7 +27,6 @@ import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
import org.apache.causeway.core.metamodel.spec.feature.OneToManyAssociation;
import org.apache.causeway.core.metamodel.tabular.DataTableInteractive;
import org.apache.causeway.core.metamodel.tabular.DataTableMemento;
-import org.apache.causeway.viewer.commons.model.object.HasUiParentObject;
import org.apache.causeway.viewer.commons.model.object.UiObject;
import org.apache.causeway.viewer.wicket.model.models.interaction.BookmarkedObjectWkt;
import org.apache.causeway.viewer.wicket.model.models.interaction.HasBookmarkedOwnerAbstract;
@@ -46,14 +45,14 @@ import lombok.val;
*
* @see HasBookmarkedOwnerAbstract
*/
-public class DataTableModelWkt
+class DataTableModelWktD
extends HasBookmarkedOwnerAbstract<DataTableInteractive>
implements
- HasUiParentObject<UiObject> {
+ DataTableModelWkt {
// -- FACTORIES
- public static DataTableModelWkt forActionModel(
+ public static DataTableModelWktD forActionModel(
final @NonNull BookmarkedObjectWkt bookmarkedObjectModel,
final @NonNull ObjectAction actMetaModel,
final @NonNull ManagedObject actionResult) {
@@ -63,18 +62,18 @@ implements
val tableInteractive = DataTableInteractive.forAction(
managedAction,
actionResult);
- return new DataTableModelWkt(
+ return new DataTableModelWktD(
bookmarkedObjectModel, actMetaModel.getFeatureIdentifier(), tableInteractive);
}
- public static @NonNull DataTableModelWkt forCollection(
+ public static @NonNull DataTableModelWktD forCollection(
final @NonNull BookmarkedObjectWkt bookmarkedObjectModel,
final @NonNull OneToManyAssociation collMetaModel) {
val tableInteractive = DataTableInteractive.forCollection(
ManagedCollection
.of(bookmarkedObjectModel.getObject(), collMetaModel, Where.NOT_SPECIFIED));
- return new DataTableModelWkt(
+ return new DataTableModelWktD(
bookmarkedObjectModel, collMetaModel.getFeatureIdentifier(), tableInteractive);
}
@@ -85,7 +84,7 @@ implements
@Getter private final Identifier featureIdentifier;
private final DataTableMemento tableMemento;
- private DataTableModelWkt(
+ private DataTableModelWktD(
final BookmarkedObjectWkt bookmarkedObject,
final Identifier featureIdentifier,
final DataTableInteractive tableInteractive) {
diff --git a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWktO.java
similarity index 64%
copy from viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java
copy to viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWktO.java
index 55bf6c9cb9..89d808a469 100644
--- a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/interaction/coll/DataTableModelWktO.java
@@ -20,6 +20,7 @@ package org.apache.causeway.viewer.wicket.model.models.interaction.coll;
import org.apache.causeway.applib.Identifier;
import org.apache.causeway.applib.annotation.Where;
+import org.apache.causeway.commons.collections.Can;
import org.apache.causeway.core.metamodel.interactions.managed.ManagedAction;
import org.apache.causeway.core.metamodel.interactions.managed.ManagedCollection;
import org.apache.causeway.core.metamodel.object.ManagedObject;
@@ -27,7 +28,6 @@ import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
import org.apache.causeway.core.metamodel.spec.feature.OneToManyAssociation;
import org.apache.causeway.core.metamodel.tabular.DataTableInteractive;
import org.apache.causeway.core.metamodel.tabular.DataTableMemento;
-import org.apache.causeway.viewer.commons.model.object.HasUiParentObject;
import org.apache.causeway.viewer.commons.model.object.UiObject;
import org.apache.causeway.viewer.wicket.model.models.interaction.BookmarkedObjectWkt;
import org.apache.causeway.viewer.wicket.model.models.interaction.HasBookmarkedOwnerAbstract;
@@ -46,36 +46,53 @@ import lombok.val;
*
* @see HasBookmarkedOwnerAbstract
*/
-public class DataTableModelWkt
+class DataTableModelWktO
extends HasBookmarkedOwnerAbstract<DataTableInteractive>
implements
- HasUiParentObject<UiObject> {
+ DataTableModelWkt {
// -- FACTORIES
- public static DataTableModelWkt forActionModel(
+ public static DataTableModelWktO forActionModel(
final @NonNull BookmarkedObjectWkt bookmarkedObjectModel,
final @NonNull ObjectAction actMetaModel,
+ final @NonNull Can<ManagedObject> args,
final @NonNull ManagedObject actionResult) {
val managedAction = ManagedAction
.of(bookmarkedObjectModel.getObject(), actMetaModel, Where.NOT_SPECIFIED);
- val tableInteractive = DataTableInteractive.forAction(
+
+ val table = DataTableInteractive.forAction(
managedAction,
+ args,
actionResult);
- return new DataTableModelWkt(
- bookmarkedObjectModel, actMetaModel.getFeatureIdentifier(), tableInteractive);
+
+ val tableMemento = table.createMemento(managedAction.getMementoForArgs(args));
+
+ val model = new DataTableModelWktO(
+ bookmarkedObjectModel, actMetaModel.getFeatureIdentifier(), tableMemento);
+
+ model.setObject(table); // memoize
+
+ return model;
}
- public static @NonNull DataTableModelWkt forCollection(
+ public static @NonNull DataTableModelWktO forCollection(
final @NonNull BookmarkedObjectWkt bookmarkedObjectModel,
final @NonNull OneToManyAssociation collMetaModel) {
- val tableInteractive = DataTableInteractive.forCollection(
+ val table = DataTableInteractive.forCollection(
ManagedCollection
.of(bookmarkedObjectModel.getObject(), collMetaModel, Where.NOT_SPECIFIED));
- return new DataTableModelWkt(
- bookmarkedObjectModel, collMetaModel.getFeatureIdentifier(), tableInteractive);
+
+ val tableMemento = table.createMemento(null);
+
+ val model = new DataTableModelWktO(
+ bookmarkedObjectModel, collMetaModel.getFeatureIdentifier(), tableMemento);
+
+ model.setObject(table); // memoize
+
+ return model;
}
// -- CONSTRUCTION
@@ -85,15 +102,13 @@ implements
@Getter private final Identifier featureIdentifier;
private final DataTableMemento tableMemento;
- private DataTableModelWkt(
+ private DataTableModelWktO(
final BookmarkedObjectWkt bookmarkedObject,
final Identifier featureIdentifier,
- final DataTableInteractive tableInteractive) {
+ final DataTableMemento tableMemento) {
super(bookmarkedObject);
this.featureIdentifier = featureIdentifier;
- this.tableMemento = tableInteractive.createMemento();
- setObject(tableInteractive); // memoize
- tableMemento.setupBindings(tableInteractive);
+ this.tableMemento = tableMemento;
}
// --
@@ -105,9 +120,33 @@ implements
@Override
protected DataTableInteractive load() {
- val tableInteractive = tableMemento.getDataTableModel(getBookmarkedOwner());
- tableMemento.setupBindings(tableInteractive);
- return tableInteractive;
+ val dataTableModel = tableMemento.getDataTableModel(getBookmarkedOwner());
+ return dataTableModel;
+ }
+
+ @Override
+ public final void detach() {
+ if(isDataTableModelDetachable()) {
+ // at time of writing breaks object deletion on JDO, see [CAUSEWAY-3530]
+ super.detach();
+ }
+ //FIXME[CAUSEWAY-3522]
+ // perhaps instead call bookmarkedObjectModel().detach();
+ // or add custom HOLLOW flag and mark the bookmarkedObjectModel() hollow
}
+ // -- HELPER
+
+ private final static String PROPERTY_NAME_MODEL_REUSE = "causeway.viewer.wicket.dataTableModelReuse";
+ /**
+ * when set to false, forces detach
+ * @deprecated remove this switch once we have a fix
+ */
+ @Deprecated
+ private static boolean isDataTableModelDetachable() {
+ return "false".equalsIgnoreCase(System.getenv(PROPERTY_NAME_MODEL_REUSE))
+ || "false".equalsIgnoreCase(System.getProperty(PROPERTY_NAME_MODEL_REUSE));
+ }
+
+
}
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ActionResultResponseType.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
index 0ea92ed682..6153f8f7f2 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ActionResultResponseType.java
@@ -31,6 +31,7 @@ import org.apache.causeway.applib.value.Blob;
import org.apache.causeway.applib.value.Clob;
import org.apache.causeway.applib.value.LocalResourcePath;
import org.apache.causeway.applib.value.OpenUrlStrategy;
+import org.apache.causeway.commons.collections.Can;
import org.apache.causeway.commons.internal.assertions._Assert;
import org.apache.causeway.commons.internal.base._Casts;
import org.apache.causeway.commons.internal.base._NullSafe;
@@ -62,7 +63,8 @@ public enum ActionResultResponseType {
public ActionResultResponse interpretResult(
final ActionModel actionModel,
final AjaxRequestTarget target,
- final @NonNull ManagedObject resultAdapter) {
+ final @NonNull ManagedObject resultAdapter,
+ final Can<ManagedObject> args) {
determineScalarAdapter(actionModel.getMetaModelContext(), resultAdapter); // intercepts collections
return toEntityPage(resultAdapter);
}
@@ -79,11 +81,12 @@ public enum ActionResultResponseType {
public ActionResultResponse interpretResult(
final ActionModel actionModel,
final AjaxRequestTarget target,
- final @NonNull ManagedObject resultAdapter) {
+ final @NonNull ManagedObject resultAdapter,
+ final Can<ManagedObject> args) {
_Assert.assertTrue(resultAdapter instanceof PackedManagedObject);
final var collectionModel = EntityCollectionModelStandalone
- .forActionModel((PackedManagedObject)resultAdapter, actionModel);
+ .forActionModel((PackedManagedObject)resultAdapter, actionModel, args);
return ActionResultResponse.toPage(
StandaloneCollectionPage.class, new StandaloneCollectionPage(collectionModel));
}
@@ -96,7 +99,8 @@ public enum ActionResultResponseType {
public ActionResultResponse interpretResult(
final ActionModel actionModel,
final AjaxRequestTarget target,
- final @NonNull ManagedObject resultAdapter) {
+ final @NonNull ManagedObject resultAdapter,
+ final Can<ManagedObject> args) {
final var valueModel = ValueModel.of(actionModel.getAction(), resultAdapter);
valueModel.setActionHint(actionModel);
final var valuePage = new ValuePage(valueModel);
@@ -108,7 +112,8 @@ public enum ActionResultResponseType {
public ActionResultResponse interpretResult(
final ActionModel actionModel,
final AjaxRequestTarget target,
- final @NonNull ManagedObject resultAdapter) {
+ final @NonNull ManagedObject resultAdapter,
+ final Can<ManagedObject> args) {
final Object value = resultAdapter.getPojo();
IRequestHandler handler =
_DownloadHandler.downloadHandler(actionModel.getAction(), value);
@@ -120,7 +125,8 @@ public enum ActionResultResponseType {
public ActionResultResponse interpretResult(
final ActionModel actionModel,
final AjaxRequestTarget target,
- final @NonNull ManagedObject resultAdapter) {
+ final @NonNull ManagedObject resultAdapter,
+ final Can<ManagedObject> args) {
final Object value = resultAdapter.getPojo();
IRequestHandler handler =
_DownloadHandler.downloadHandler(actionModel.getAction(), value);
@@ -132,7 +138,8 @@ public enum ActionResultResponseType {
public ActionResultResponse interpretResult(
final ActionModel actionModel,
final AjaxRequestTarget target,
- final @NonNull ManagedObject resultAdapter) {
+ final @NonNull ManagedObject resultAdapter,
+ final Can<ManagedObject> args) {
final LocalResourcePath localResPath = (LocalResourcePath)resultAdapter.getPojo();
final var webAppContextPath = actionModel.getMetaModelContext().getWebAppContextPath();
return ActionResultResponse
@@ -144,7 +151,8 @@ public enum ActionResultResponseType {
public ActionResultResponse interpretResult(
final ActionModel actionModel,
final AjaxRequestTarget target,
- final @NonNull ManagedObject resultAdapter) {
+ final @NonNull ManagedObject resultAdapter,
+ final Can<ManagedObject> args) {
// open URL server-side redirect
final LocalResourcePath localResPath = (LocalResourcePath)resultAdapter.getPojo();
final var webAppContextPath = actionModel.getMetaModelContext().getWebAppContextPath();
@@ -157,7 +165,8 @@ public enum ActionResultResponseType {
public ActionResultResponse interpretResult(
final ActionModel actionModel,
final AjaxRequestTarget target,
- final @NonNull ManagedObject resultAdapter) {
+ final @NonNull ManagedObject resultAdapter,
+ final Can<ManagedObject> args) {
final URL url = (URL)resultAdapter.getPojo();
return ActionResultResponse
.openUrlInBrowser(target, url.toString(), OpenUrlStrategy.NEW_WINDOW); // default behavior
@@ -168,7 +177,8 @@ public enum ActionResultResponseType {
public ActionResultResponse interpretResult(
final ActionModel actionModel,
final AjaxRequestTarget target,
- final @NonNull ManagedObject resultAdapter) {
+ final @NonNull ManagedObject resultAdapter,
+ final Can<ManagedObject> args) {
// open URL server-side redirect
final Object value = resultAdapter.getPojo();
final var webAppContextPath = actionModel.getMetaModelContext().getWebAppContextPath();
@@ -182,7 +192,8 @@ public enum ActionResultResponseType {
public ActionResultResponse interpretResult(
final ActionModel actionModel,
final AjaxRequestTarget target,
- final @Nullable ManagedObject resultAdapter) {
+ final @Nullable ManagedObject resultAdapter, // arg is not used
+ final Can<ManagedObject> args) {
final VoidModel voidModel = new VoidModel();
voidModel.setActionHint(actionModel);
return ActionResultResponse.toPage(VoidReturnPage.class, new VoidReturnPage(voidModel));
@@ -196,7 +207,8 @@ public enum ActionResultResponseType {
public ActionResultResponse interpretResult(
final ActionModel actionModel,
final AjaxRequestTarget target,
- final @Nullable ManagedObject resultAdapter) {
+ final @Nullable ManagedObject resultAdapter, // arg is not used
+ final Can<ManagedObject> args) {
val currentPage = PageRequestHandlerTracker.getLastHandler(RequestCycle.get()).getPage();
val pageClass = currentPage.getClass();
return ActionResultResponse.toPage(PageRedirectRequest.forPage(pageClass, _Casts.uncheckedCast(currentPage)));
@@ -207,7 +219,8 @@ public enum ActionResultResponseType {
public ActionResultResponse interpretResult(
final ActionModel actionModel,
final AjaxRequestTarget target,
- final @Nullable ManagedObject resultAdapter) {
+ final @Nullable ManagedObject resultAdapter, // arg is not used
+ final Can<ManagedObject> args) {
val signInPage = actionModel.getMetaModelContext()
.lookupServiceElseFail(PageClassRegistry.class)
.getPageClass(PageType.SIGN_IN);
@@ -217,7 +230,7 @@ public enum ActionResultResponseType {
};
public abstract ActionResultResponse interpretResult(
- ActionModel model, AjaxRequestTarget target, ManagedObject resultAdapter);
+ ActionModel model, AjaxRequestTarget target, ManagedObject resultAdapter, Can<ManagedObject> args);
/**
* Only overridden for {@link ActionResultResponseType#OBJECT object}
@@ -231,7 +244,8 @@ public enum ActionResultResponseType {
public static ActionResultResponse determineAndInterpretResult(
final ActionModel model,
final AjaxRequestTarget targetIfAny,
- final @Nullable ManagedObject resultAdapter) {
+ final @Nullable ManagedObject resultAdapter,
+ final Can<ManagedObject> args) {
/*
* XXX won't implement CAUSEWAY-3372 (reload on void action result)
@@ -247,7 +261,7 @@ public enum ActionResultResponseType {
val typeAndAdapter = determineFor(resultAdapter, mapAbsentResultTo, targetIfAny);
return typeAndAdapter.type // mapped to 'mapAbsentResultTo' if adapter is unspecified or null
- .interpretResult(model, targetIfAny, typeAndAdapter.resultAdapter);
+ .interpretResult(model, targetIfAny, typeAndAdapter.resultAdapter, args);
}
public static ActionResultResponse toEntityPage(final @NonNull ManagedObject entityOrViewmodel) {
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/CausewayAjaxDataTable.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/CausewayAjaxDataTable.java
index 240a458b40..d3ccbc5099 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/CausewayAjaxDataTable.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/CausewayAjaxDataTable.java
@@ -18,21 +18,30 @@
*/
package org.apache.causeway.viewer.wicket.ui.components.collectioncontents.ajaxtable;
+import java.io.Serializable;
+import java.util.Iterator;
import java.util.List;
+import java.util.UUID;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.NoRecordsToolbar;
+import org.apache.wicket.markup.repeater.IItemFactory;
+import org.apache.wicket.markup.repeater.IItemReuseStrategy;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.IDataProvider;
import org.apache.wicket.model.IModel;
+import org.apache.causeway.commons.internal.base._Casts;
+import org.apache.causeway.commons.internal.collections._Maps;
import org.apache.causeway.core.metamodel.object.ManagedObjects;
import org.apache.causeway.core.metamodel.tabular.DataRow;
+import org.apache.causeway.core.metamodel.tabular.DataTableInteractive;
import org.apache.causeway.viewer.wicket.model.hints.UiHintContainer;
import org.apache.causeway.viewer.wicket.model.models.UiObjectWkt;
+import org.apache.causeway.viewer.wicket.model.models.interaction.coll.DataRowWkt;
import org.apache.causeway.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.ToggleboxColumn;
import org.apache.causeway.viewer.wicket.ui.util.Wkt;
@@ -62,8 +71,9 @@ public class CausewayAjaxDataTable extends DataTable<DataRow, String> {
this.toggleboxColumn = toggleboxColumn;
setOutputMarkupId(true);
setVersioned(false);
- //[CAUSEWAY-3772] optimization reinstate? though I have no clue what that is doing
- //setItemReuseStrategy((IItemReuseStrategy & Serializable) CausewayAjaxDataTable::itemReuseStrategyWithCast);
+ if(DataTableInteractive.TableImplementation.getSelected().isOptimistic()) {
+ setItemReuseStrategy((IItemReuseStrategy & Serializable) CausewayAjaxDataTable::itemReuseStrategyWithCast);
+ }
}
public void setPageNumberHintAndBroadcast(final AjaxRequestTarget target) {
@@ -137,58 +147,57 @@ public class CausewayAjaxDataTable extends DataTable<DataRow, String> {
return rowElement.getSpecification().getCssClass(rowElement);
}
-// private static Iterator<Item<DataRow>> itemReuseStrategy(
-// final IItemFactory<DataRow> factory,
-// final Iterator<IModel<DataRow>> newModels,
-// final Iterator<Item<DataRow>> existingItems) {
-//
-// val itemByRowIndex = _Maps.<Integer, Item<DataRow>>newHashMap();
-// existingItems.forEachRemaining(item->{
-// val model = item.getModel();
-// if(model instanceof DataRowWkt) {
-// val dataRowWkt = (DataRowWkt)item.getModel();
-// itemByRowIndex.put(dataRowWkt.getRowIndex(), item);
-// }
-// });
-//
-// return new Iterator<Item<DataRow>>() {
-// private int index = 0;
-//
-// @Override
-// public boolean hasNext() {
-// return newModels.hasNext();
-// }
-//
-// @Override
-// public Item<DataRow> next() {
-// final DataRowWkt newModel = (DataRowWkt)newModels.next();
-// final Item<DataRow> oldItem = itemByRowIndex.get(newModel.getRowIndex());
-//
-// final IModel<DataRow> model2 = oldItem != null
-// ? oldItem.getModel()
-// : newModel;
-// return factory.newItem(index++, model2);
-// }
-//
-// @Override
-// public void remove() {
-// throw new UnsupportedOperationException();
-// }
-//
-// };
-//
-// }
-//
-// @SuppressWarnings("unused")
-// private static <T> Iterator<Item<T>> itemReuseStrategyWithCast(
-// final IItemFactory<T> factory,
-// final Iterator<IModel<T>> newModels,
-// final Iterator<Item<T>> existingItems) {
-// return _Casts.uncheckedCast(itemReuseStrategy(
-// _Casts.uncheckedCast(factory),
-// _Casts.uncheckedCast(newModels),
-// _Casts.uncheckedCast(existingItems)));
-// }
+ private static Iterator<Item<DataRow>> itemReuseStrategy(
+ final IItemFactory<DataRow> factory,
+ final Iterator<IModel<DataRow>> newModels,
+ final Iterator<Item<DataRow>> existingItems) {
+
+ val itemByUuid = _Maps.<UUID, Item<DataRow>>newHashMap();
+ existingItems.forEachRemaining(item->{
+ val model = item.getModel();
+ if(model instanceof DataRowWkt) {
+ val dataRowWkt = (DataRowWkt)item.getModel();
+ itemByUuid.put(dataRowWkt.getUuid(), item);
+ }
+ });
+
+ return new Iterator<Item<DataRow>>() {
+ private int index = 0;
+
+ @Override
+ public boolean hasNext() {
+ return newModels.hasNext();
+ }
+
+ @Override
+ public Item<DataRow> next() {
+ final DataRowWkt newModel = (DataRowWkt)newModels.next();
+ final Item<DataRow> oldItem = itemByUuid.get(newModel.getUuid());
+
+ final IModel<DataRow> model2 = oldItem != null
+ ? oldItem.getModel()
+ : newModel;
+ return factory.newItem(index++, model2);
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ };
+
+ }
+
+ private static <T> Iterator<Item<T>> itemReuseStrategyWithCast(
+ final IItemFactory<T> factory,
+ final Iterator<IModel<T>> newModels,
+ final Iterator<Item<T>> existingItems) {
+ return _Casts.uncheckedCast(itemReuseStrategy(
+ _Casts.uncheckedCast(factory),
+ _Casts.uncheckedCast(newModels),
+ _Casts.uncheckedCast(existingItems)));
+ }
private void honorHints() {
headersToolbar.honourSortOrderHints();
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/CausewayAjaxHeadersToolbar.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/CausewayAjaxHeadersToolbar.java
index 3bc744e8a9..6388d6ddc1 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/CausewayAjaxHeadersToolbar.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/CausewayAjaxHeadersToolbar.java
@@ -26,7 +26,6 @@ import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.causeway.commons.internal.base._Casts;
import org.apache.causeway.viewer.wicket.model.hints.UiHintContainer;
import org.apache.causeway.viewer.wicket.model.models.UiObjectWkt;
-import org.apache.causeway.viewer.wicket.ui.util.Wkt;
/**
* Adapted from Wicket's own {@link AjaxFallbackHeadersToolbar}.
@@ -35,16 +34,16 @@ public class CausewayAjaxHeadersToolbar
extends CausewayAjaxHeadersToolbarAbstract<String> {
private static final long serialVersionUID = 1L;
- private final CollectionContentsSortableDataProvider singleSortStateLocator;
+ private final CollectionContentsSortableDataProvider stateLocator;
private CausewayAjaxDataTable table;
public CausewayAjaxHeadersToolbar(
final CausewayAjaxDataTable table,
- final CollectionContentsSortableDataProvider singleSortStateLocator) {
- super(table, _Casts.uncheckedCast(singleSortStateLocator));
+ final CollectionContentsSortableDataProvider stateLocator) {
+ super(table, _Casts.uncheckedCast(stateLocator));
this.table = table;
- this.singleSortStateLocator = singleSortStateLocator;
- Wkt.ajaxEnable(table);
+ table.setOutputMarkupId(true);
+ this.stateLocator = stateLocator;
}
@Override
@@ -52,20 +51,36 @@ extends CausewayAjaxHeadersToolbarAbstract<String> {
super.onInitialize();
}
+ // //////////////////////////////////////
+
@Override
protected WebMarkupContainer newSortableHeader(final String borderId, final String property,
final ISortStateLocator<String> locator) {
- return new CausewayAjaxFallbackOrderByBorder<String>(borderId, table, property, locator);
+ return new CausewayAjaxFallbackOrderByBorder<String>(borderId, table, property, locator/*, getAjaxCallListener()*/);
}
+ // /**
+ // * Returns a decorator that will be used to decorate ajax links used in sortable headers
+ // *
+ // * @return decorator or null for none
+ // */
+ // protected IAjaxCallListener getAjaxCallListener()
+ // {
+ // return null;
+ // }
+
+ // //////////////////////////////////////
+
void honourSortOrderHints() {
- var uiHintContainer = getUiHintContainer();
- if(uiHintContainer == null) return;
+ UiHintContainer uiHintContainer = getUiHintContainer();
+ if(uiHintContainer == null) {
+ return;
+ }
for (SortOrder sortOrder : SortOrder.values()) {
String property = uiHintContainer.getHint(table, sortOrder.name());
if(property != null) {
- singleSortStateLocator.getSortState().setPropertySortOrder(property, sortOrder);
+ stateLocator.getSortState().setPropertySortOrder(property, sortOrder);
}
}
}
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsSortableDataProvider.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsSortableDataProvider.java
index 3cb32edc62..e69cca6341 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsSortableDataProvider.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsSortableDataProvider.java
@@ -20,24 +20,24 @@ package org.apache.causeway.viewer.wicket.ui.components.collectioncontents.ajaxt
import java.util.Iterator;
import java.util.Optional;
-import java.util.OptionalInt;
import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.model.IModel;
-import org.springframework.lang.Nullable;
-
import org.apache.causeway.applib.annotation.TableDecorator;
+import org.apache.causeway.commons.collections.Can;
import org.apache.causeway.core.metamodel.facets.object.tabledec.TableDecoratorFacet;
+import org.apache.causeway.core.metamodel.object.ManagedObject;
+import org.apache.causeway.core.metamodel.object.ManagedObjects;
import org.apache.causeway.core.metamodel.object.MmSortUtils;
+import org.apache.causeway.core.metamodel.spec.feature.OneToOneAssociation;
import org.apache.causeway.core.metamodel.tabular.DataRow;
import org.apache.causeway.core.metamodel.tabular.DataTableInteractive;
import org.apache.causeway.viewer.wicket.model.models.EntityCollectionModelAbstract;
import org.apache.causeway.viewer.wicket.model.models.interaction.coll.DataRowWkt;
-import lombok.NonNull;
import lombok.val;
/**
@@ -79,39 +79,38 @@ extends SortableDataProvider<DataRow, String> {
@Override
public Iterator<DataRow> iterator(final long skip, final long limit) {
- var dataTable = getDataTableModel();
- // honor (single) column sort (if any)
- dataTable.getColumnSort().setValue(columnSort().orElse(null));
- return dataTable.getDataRowsFilteredAndSorted().getValue()
- .iterator(Math.toIntExact(skip), Math.toIntExact(limit));
+ val visibleRows = getDataTableModel().getDataRowsFilteredAndSorted().getValue();
+ return sorted(visibleRows).iterator(Math.toIntExact(skip), Math.toIntExact(limit));
}
// -- HELPER
- private Optional<DataTableInteractive.ColumnSort> columnSort() {
- val sortParam = getSort();
- return lookupColumnIndexFor(sortParam).stream()
- .mapToObj(columnIndex->
- new DataTableInteractive.ColumnSort(columnIndex, sortDirection(sortParam)))
- .findFirst();
- }
-
- private OptionalInt lookupColumnIndexFor(final @Nullable SortParam<String> sortParam) {
- if(sortParam==null) return OptionalInt.empty();
- int columnIndex = 0;
- for(var column : getDataTableModel().getDataColumns().getValue()) {
- if(column.getAssociationMetaModel().getId().equals(sortParam.getProperty())) {
- return OptionalInt.of(columnIndex);
- }
- ++columnIndex;
+ private Can<DataRow> sorted(final Can<DataRow> dataRows) {
+ val sort = getSort();
+ val sortProperty = lookupPropertyFor(sort).orElse(null);
+ if(sortProperty != null) {
+ val objComparator = MmSortUtils.orderingBy(sortProperty, sort.isAscending());
+ return dataRows.sorted((a, b)-> {
+ ManagedObject managedObjectA = a.getRowElement();
+ if(managedObjectA.getSpecialization().isViewmodel()) {
+ // make sure any referenced entities are made live if currently hollow
+ ManagedObjects.refreshViewmodel(managedObjectA, /*bookmark supplier*/ null);
+ }
+ ManagedObject managedObjectB = b.getRowElement();
+ if(managedObjectA.getSpecialization().isViewmodel()) {
+ // make sure any referenced entities are made live if currently hollow
+ ManagedObjects.refreshViewmodel(managedObjectB, /*bookmark supplier*/ null);
+ }
+ return objComparator.compare(managedObjectA, managedObjectB);
+ });
}
- return OptionalInt.empty();
+ return dataRows;
}
- private static MmSortUtils.SortDirection sortDirection(final @NonNull SortParam<String> sortParam) {
- return sortParam.isAscending()
- ? MmSortUtils.SortDirection.ASCENDING
- : MmSortUtils.SortDirection.DESCENDING;
+ private Optional<OneToOneAssociation> lookupPropertyFor(final SortParam<String> sort) {
+ return Optional.ofNullable(sort)
+ .map(SortParam::getProperty)
+ .flatMap(getDataTableModel().getElementType()::getProperty);
}
}
\ No newline at end of file
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ToggleboxColumn.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ToggleboxColumn.java
index de0862a1a8..fc2bfa382f 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ToggleboxColumn.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/ToggleboxColumn.java
@@ -26,8 +26,10 @@ import org.apache.wicket.model.IModel;
import org.apache.causeway.commons.internal.collections._Lists;
import org.apache.causeway.core.metamodel.tabular.DataTableInteractive;
+import org.apache.causeway.core.metamodel.tabular.DataTableInteractive.TableImplementation;
import org.apache.causeway.viewer.wicket.model.models.interaction.coll.DataRowToggleWkt;
import org.apache.causeway.viewer.wicket.model.models.interaction.coll.DataRowWkt;
+import org.apache.causeway.viewer.wicket.model.models.interaction.coll.DataRowWktO;
import org.apache.causeway.viewer.wicket.ui.components.widgets.checkbox.ContainedToggleboxPanel;
import org.apache.causeway.viewer.wicket.ui.util.Wkt;
@@ -61,7 +63,9 @@ extends GenericColumnAbstract {
@Override
protected Component createCellComponent(final String componentId, final DataRowWkt dataRowWkt) {
- val dataRowToggle = new DataRowToggleWkt(dataRowWkt);
+ final IModel<Boolean> dataRowToggle = TableImplementation.getSelected().isOptimistic()
+ ? ((DataRowWktO)dataRowWkt).getDataRowToggle()
+ : new DataRowToggleWkt(dataRowWkt);
val rowToggle = new ContainedToggleboxPanel(componentId, dataRowToggle);
rowToggles.add(rowToggle);
return rowToggle.setOutputMarkupId(true);
@@ -72,7 +76,9 @@ extends GenericColumnAbstract {
val bulkToggle = new ContainedToggleboxPanel(
componentId,
new BulkToggleWkt(dataTableModelHolder),
- this::onBulkUpdate);
+ TableImplementation.getSelected().isOptimistic()
+ ? this::onBulkUpdateOptimistic
+ : this::onBulkUpdate);
Wkt.cssAppend(bulkToggle, "togglebox-column");
return bulkToggle;
}
@@ -91,4 +97,11 @@ extends GenericColumnAbstract {
}
+ private void onBulkUpdateOptimistic(final Boolean isChecked, final AjaxRequestTarget target) {
+ val bulkToggle = BulkToggle.valueOf(isChecked);
+ for (ContainedToggleboxPanel rowToggle : rowToggles) {
+ rowToggle.set(bulkToggle, target);
+ }
+ }
+
}
\ No newline at end of file
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/panels/FormExecutorDefault.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/panels/FormExecutorDefault.java
index dfdecb59f1..bbbea14947 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/panels/FormExecutorDefault.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/panels/FormExecutorDefault.java
@@ -161,7 +161,7 @@ implements FormExecutor, HasCommonContext {
//XXX triggers ManagedObject.getBookmarkRefreshed()
val resultResponse = actionOrPropertyModel.fold(
act->ActionResultResponseType
- .determineAndInterpretResult(act, ajaxTarget, resultAdapter),
+ .determineAndInterpretResult(act, ajaxTarget, resultAdapter, act.snapshotArgs()),
prop->ActionResultResponseType
.toEntityPage(resultAdapter));