Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2fc2ee1
CAUSEWAY-3997: [Layout] In-memory Patching of Table Column Order (stubs)
andi-huber Apr 23, 2026
1f6525f
CAUSEWAY-3997: flesh out collection-id choices
andi-huber Apr 28, 2026
e910398
CAUSEWAY-3997: test approvals
andi-huber Apr 29, 2026
8658941
CAUSEWAY-3997: brings in Listing from causewaystuff
andi-huber Apr 29, 2026
8cefc8d
CAUSEWAY-3997: new methods for MetaModelService
andi-huber Apr 29, 2026
7b14a93
CAUSEWAY-3997: adds converter for ApplicationFeatureId -> Identifier
andi-huber Apr 29, 2026
d624f3a
CAUSEWAY-3997: todo remarks
andi-huber Apr 29, 2026
031cae7
CAUSEWAY-3997: formal API into MM in the context of column ordering
andi-huber May 11, 2026
bb4f30f
CAUSEWAY-3997: improves design of Object_patchColumnOrder
andi-huber May 12, 2026
cfd6622
CAUSEWAY-3997: refining MetaModelService; no wildcard support for
andi-huber May 12, 2026
e743bac
CAUSEWAY-3997: prepares quick-icons for AppFeatIds
andi-huber May 12, 2026
2f8e09e
Revert "CAUSEWAY-3997: prepares quick-icons for AppFeatIds"
andi-huber May 14, 2026
d844d02
CAUSEWAY-3997: ApplicationFeatureIdValueSemantics CSS fix
andi-huber May 14, 2026
62902c2
CAUSEWAY-3997: ApplicationFeatureIdValueSemantics better icons
andi-huber May 15, 2026
8e911ee
CAUSEWAY-3997: find a place to hold column order patches
andi-huber May 16, 2026
1bd392f
CAUSEWAY-3997: logic to apply patches
andi-huber May 16, 2026
760f9f8
CAUSEWAY-3997: wire up Listing; flesh out patching
andi-huber May 16, 2026
19fb3e7
CAUSEWAY-3997: test approvals
andi-huber May 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.apache.causeway.applib.services.bookmark.BookmarkHolder_object;
import org.apache.causeway.applib.services.clock.ClockService;
import org.apache.causeway.applib.services.columnorder.Object_downloadColumnOrderTxtFilesAsZip;
import org.apache.causeway.applib.services.columnorder.Object_patchColumnOrder;
import org.apache.causeway.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto;
import org.apache.causeway.applib.services.commanddto.conmap.ContentMappingServiceForCommandsDto;
import org.apache.causeway.applib.services.commanddto.processor.spi.CommandDtoProcessorServiceIdentity;
Expand Down Expand Up @@ -111,6 +112,7 @@
Dto_downloadXml.class,
Dto_downloadXsd.class,
Object_downloadColumnOrderTxtFilesAsZip.class,
Object_patchColumnOrder.class,
Object_downloadLayout.class,
Object_patchLayout.class,
Object_downloadMetamodelXml.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ default String getFullyQualifiedName() {

default SortedSet<ApplicationFeatureId> getMembersOfSort(final ApplicationMemberSort memberSort) {
return switch (memberSort) {
case PROPERTY -> getProperties();
case COLLECTION -> getCollections();
case ACTION -> getActions();
default -> Collections.emptySortedSet();
case PROPERTY -> getProperties();
case COLLECTION -> getCollections();
case ACTION -> getActions();
default -> Collections.emptySortedSet();
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsFirst;

import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;

import org.apache.causeway.applib.Identifier;
Expand All @@ -45,21 +46,20 @@
import org.apache.causeway.commons.internal.exceptions._Exceptions;

import lombok.Getter;
import org.jspecify.annotations.NonNull;
import lombok.Synchronized;

/**
* Value type representing a namespace, type or member.
* <p>
* This value is {@link Comparable}, the implementation of which considers
*
* <p> This value is {@link Comparable}, the implementation of which considers
* {@link #getSort() (feature) sort}, {@link #getNamespace() namespace},
* {@link #getTypeSimpleName() type simple name} and {@link #getLogicalMemberName() member-logical-name}.
* <p>
* If the represented member is an <i>action</i>, then {@link #getLogicalMemberName() member-logical-name}
*
* <p> If the represented member is an <i>action</i>, then {@link #getLogicalMemberName() member-logical-name}
* must <b>not</b> include any parameter list or parentheses.
* Consequently method overloading is not supported.
* <p>
* If there is a member name clash involving an <i>action</i> and an <i>association</i>,
*
* <p> If there is a member name clash involving an <i>action</i> and an <i>association</i>,
* then consequently any permissions defined automatically apply to both and one cannot separate
* these.
*
Expand All @@ -79,43 +79,40 @@ public class ApplicationFeatureId

// -- FACTORY METHODS

/**
* @apiNote the inverse conversion is available via
* {@link ApplicationFeatureRepository#asIdentifier(ApplicationFeatureId)}
*/
public static ApplicationFeatureId fromIdentifier(final @NonNull Identifier identifier) {

var logicalTypeName = identifier.logicalTypeName();

if(identifier.type().isClass()) {
if(identifier.type().isClass())
return newType(logicalTypeName);
}
return newMember(logicalTypeName, identifier.memberLogicalName());
}

public static ApplicationFeatureId newFeature(
final @NonNull ApplicationFeatureSort featureSort,
final @NonNull String qualifiedLogicalName) {

switch (featureSort) {
case NAMESPACE:
return newNamespace(qualifiedLogicalName);
case TYPE:
return newType(qualifiedLogicalName);
case MEMBER:
return newMember(qualifiedLogicalName);
}
throw _Exceptions.illegalArgument("Unknown feature sort '%s'", featureSort);
return switch (featureSort) {
case NAMESPACE -> newNamespace(qualifiedLogicalName);
case TYPE -> newType(qualifiedLogicalName);
case MEMBER -> newMember(qualifiedLogicalName);
};
}

public static ApplicationFeatureId newFeature(
final @NonNull String namespace,
final @Nullable String logicalTypeSimpleName,
final @Nullable String memberName) {

if(logicalTypeSimpleName == null) {
if(logicalTypeSimpleName == null)
return newNamespace(namespace);
}
var logicalTypeName = namespace + "." + logicalTypeSimpleName;
if(memberName == null) {
if(memberName == null)
return newType(logicalTypeName);
}
return newMember(logicalTypeName, memberName);
}

Expand Down Expand Up @@ -161,9 +158,8 @@ public static ApplicationFeatureId newMember(final String logicalTypeName, final
public static ApplicationFeatureId newMember(final String fullyQualifiedLogicalName) {
var featureId = new ApplicationFeatureId(ApplicationFeatureSort.MEMBER);
var i = fullyQualifiedLogicalName.lastIndexOf("#");
if(i == -1) {
if(i == -1)
throw new IllegalArgumentException("Malformed, expected a '#': " + fullyQualifiedLogicalName);
}
var logicalTypeName = fullyQualifiedLogicalName.substring(0, i);
var memberName = fullyQualifiedLogicalName.substring(i + 1);
initType(featureId, logicalTypeName);
Expand All @@ -184,10 +180,9 @@ private static void initType(final ApplicationFeatureId featureId, final String
}

// guard against empty namespace; there should be a meta-model validator that already catched that
if(_Strings.isEmpty(featureId.namespace)) {
if(_Strings.isEmpty(featureId.namespace))
throw _Exceptions.illegalArgument(
"fullyQualifiedName '%s' must include a non-empty namespace", fullyQualifiedName);
}

featureId.logicalMemberName = null;
}
Expand All @@ -197,9 +192,8 @@ private static void initMember(final ApplicationFeatureId featureId, final @Null
}

private static String stripOffParamsIfAny(final @Nullable String name) {
if(_Strings.isEmpty(name)) {
if(_Strings.isEmpty(name))
return name;
}
final int paramListStartIndex = name.indexOf('(');
return paramListStartIndex>-1
? name.substring(0, paramListStartIndex)
Expand Down Expand Up @@ -284,9 +278,8 @@ public String getFullyQualifiedName() {

@Programmatic
public String getLogicalTypeName() {
if (getTypeSimpleName() == null) {
if (getTypeSimpleName() == null)
return null;
}
var buf = new StringBuilder();
if(!_Strings.isNullOrEmpty(getNamespace())) {
buf.append(getNamespace()).append(".");
Expand All @@ -305,13 +298,12 @@ public ApplicationFeatureId getParentNamespaceFeatureId() {

_Assert.assertFalse(sort.isMember());

if(sort.isType()) {
if(sort.isType())
return ApplicationFeatureId.newNamespace(getNamespace());
} else {
else {
var namespace = getNamespace(); // eg aaa.bbb.ccc
if(!namespace.contains(".")) {
if(!namespace.contains("."))
return null; // parent is root
}
final int cutOffPos = namespace.lastIndexOf('.');
final String parentPackageName = namespace.substring(0, cutOffPos);
return newNamespace(parentPackageName);
Expand Down Expand Up @@ -479,15 +471,12 @@ public ApplicationFeatureId withNamespace(final @NonNull String namespace) {
* @param logicalTypeName
*/
public ApplicationFeatureId withLogicalTypeName(final @NonNull String logicalTypeName) {
switch (getSort()) {
case MEMBER:
return newMember(logicalTypeName, this.getLogicalMemberName());
case TYPE:
return newType(logicalTypeName);
case NAMESPACE:
default:
return this;
}
return switch (getSort()) {
case MEMBER -> newMember(logicalTypeName, this.getLogicalMemberName());
case TYPE -> newType(logicalTypeName);
case NAMESPACE -> this;
default -> this;
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@

import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.SortedSet;

import org.jspecify.annotations.Nullable;

import org.apache.causeway.applib.Identifier;
import org.apache.causeway.applib.id.LogicalType;

/**
Expand Down Expand Up @@ -52,4 +56,6 @@ public interface ApplicationFeatureRepository {
Collection<ApplicationFeature> allMembers();

SortedSet<ApplicationFeatureId> propertyIdsFor(LogicalType logicalType);

Optional<Identifier> asIdentifier(@Nullable ApplicationFeatureId applicationFeatureId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,24 @@
* This is a utility service to support the usage of {@link TableColumnOrderService}, providing the ability to obtain
* a zip of each of the <code>Xxx.columnOrder.txt</code> files for the specified domain object.
*
* <p>
* The zip contains:
* <p> The zip contains:
* <ul>
* <li>DomainClass.columnOrder.txt</li> - as used for standalone collections of <code>DomainClass</code> itself
* <li>DomainClass#collection1.columnOrder.txt</li> - for <code>DomainClass</code>' collection with id <code>collection1</code>.
* <li>...</li>
* <li>DomainClass#collectionN.columnOrder.txt</li> - for <code>DomainClass</code>' collection with id <code>collectionN</code>.
* </ul>
* </p>
*
* <p>
* These should be unzipped and copied in the domain class' package, and then their contents updated to specify the
* <p> These should be un-zipped and copied in the domain class' package, and then their contents updated to specify the
* order in which the respective object's properties will be shown in the standalone or parented collections.
* </p>
*
* @see Object_downloadColumnOrderTxtFilesAsZip
* @see Object_patchColumnOrder
* @see TableColumnOrderService
*
* @since 2.0 {@index}
*/
public interface ColumnOrderTxtFileService {

byte[] toZip(final Object domainObject);

byte[] toZip(Object domainObject);
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
@RequiredArgsConstructor
public class Object_downloadColumnOrderTxtFilesAsZip {

@Inject ColumnOrderTxtFileService columnOrderTxtFileService;

private final Object domainObject; // mixee

public static class ActionDomainEvent
Expand All @@ -88,6 +90,4 @@ public static class ActionDomainEvent
return String.format("%s.columnOrder.zip", domainObject.getClass().getSimpleName());
}

@Inject ColumnOrderTxtFileService columnOrderTxtFileService;

}
Loading