This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
The following commit(s) were added to refs/heads/v4 by this push:
new 0e8d2221fed CAUSEWAY-3889: adds support for Domain Object Icon Image
Embedding
0e8d2221fed is described below
commit 0e8d2221fede798e0361039412c1e4c811cd4620
Author: a.huber <[email protected]>
AuthorDate: Mon Aug 18 12:03:55 2025 +0200
CAUSEWAY-3889: adds support for Domain Object Icon Image Embedding
---
.../commons/internal/resources/_Resources.java | 20 ++++-
.../org/apache/causeway/commons/net/DataUri.java | 3 +-
.../core/metamodel/commons/ClassExtensions.java | 4 +-
.../metamodel/facets/object/icon/ObjectIcon.java | 52 ++++---------
.../facets/object/icon/ObjectIconEmbedded.java | 31 ++++----
.../{ObjectIcon.java => ObjectIconUrlBased.java} | 62 +++++-----------
.../icons/ObjectIconServiceDefault.java | 23 ++++--
.../DemoFixture_extending_ExcelFixture2.java | 2 +-
.../DemoToDoItem_create_usingExcelFixture.java | 10 +--
.../ExcelModuleDemoUploadService_IntegTest.java | 2 +-
.../resources/DomainObjectResourceServerside.java | 4 +-
.../wicket/model/models/BookmarkTreeNode.java | 86 +++++-----------------
.../wicket/model/models/ImageResourceCache.java | 12 +--
.../viewer/wicket/model/models/UiObjectWkt.java | 29 ++++++--
.../bookmarkedpages/BookmarkedPagesPanel.java | 18 +++--
.../object/icontitle/ObjectIconAndTitlePanel.java | 13 +++-
.../apache/causeway/viewer/wicket/ui/util/Wkt.java | 17 +++++
.../services/ImageResourceCacheClassPath.java | 17 +++--
18 files changed, 189 insertions(+), 216 deletions(-)
diff --git
a/commons/src/main/java/org/apache/causeway/commons/internal/resources/_Resources.java
b/commons/src/main/java/org/apache/causeway/commons/internal/resources/_Resources.java
index 5de2fe28bd7..d98218ca3f6 100644
---
a/commons/src/main/java/org/apache/causeway/commons/internal/resources/_Resources.java
+++
b/commons/src/main/java/org/apache/causeway/commons/internal/resources/_Resources.java
@@ -27,14 +27,15 @@
import java.util.function.Predicate;
import java.util.regex.Pattern;
+import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
+import org.apache.causeway.commons.functional.Try;
import org.apache.causeway.commons.internal.base._Bytes;
import org.apache.causeway.commons.internal.base._Strings;
import org.apache.causeway.commons.internal.context._Context;
import org.apache.causeway.commons.internal.exceptions._Exceptions;
-import org.jspecify.annotations.NonNull;
import lombok.SneakyThrows;
/**
@@ -141,7 +142,7 @@ public static String loadAsStringUtf8ElseFail(
* @param resourceName
* @return The resource location as an URL, or null if the resource could
not be found.
*/
- public static @Nullable URL getResourceUrl(
+ public static Optional<URL> lookupResourceUrl(
final @NonNull Class<?> contextClass,
final @NonNull String resourceName) {
@@ -149,8 +150,19 @@ public static String loadAsStringUtf8ElseFail(
return Optional
.ofNullable(contextClass.getResource(absoluteResourceName))
- .orElseGet(()->_Context.getDefaultClassLoader()
- .getResource(absoluteResourceName));
+ .or(()->Optional
+ .ofNullable(_Context.getDefaultClassLoader()
+ .getResource(absoluteResourceName)));
+ }
+
+ @SneakyThrows
+ public static Optional<URI> lookupResourceUri(
+ final @NonNull Class<?> contextClass,
+ final @NonNull String resourceName) {
+ return lookupResourceUrl(contextClass, resourceName)
+ .map(url->Try
+ .call(url::toURI)
+ .valueAsNonNullElseFail());
}
// -- LOCAL vs EXTERNAL resource path
diff --git a/commons/src/main/java/org/apache/causeway/commons/net/DataUri.java
b/commons/src/main/java/org/apache/causeway/commons/net/DataUri.java
index 6199cda8e2f..164550043a9 100644
--- a/commons/src/main/java/org/apache/causeway/commons/net/DataUri.java
+++ b/commons/src/main/java/org/apache/causeway/commons/net/DataUri.java
@@ -18,6 +18,7 @@
*/
package org.apache.causeway.commons.net;
+import java.io.Serializable;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@@ -44,7 +45,7 @@ public record DataUri(
String mediaType,
List<String> parameters,
Encoding encoding,
- byte[] data) {
+ byte[] data) implements Serializable {
public enum Encoding {
NONE,
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/commons/ClassExtensions.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/commons/ClassExtensions.java
index 66cd7883623..163a92ac622 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/commons/ClassExtensions.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/commons/ClassExtensions.java
@@ -22,7 +22,6 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.net.URL;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -121,8 +120,7 @@ public static Method getMethodElseNull(final Class<?>
clazz, final String method
}
public static boolean exists(final Class<?> cls, final String
resourceName) {
- final URL url = _Resources.getResourceUrl(cls, resourceName);
- return url != null;
+ return _Resources.lookupResourceUrl(cls, resourceName).isPresent();
}
static Class<?> asWrapped(final Class<?> primitiveClassExtendee) {
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIcon.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIcon.java
index 09656132270..e062125270e 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIcon.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIcon.java
@@ -18,30 +18,21 @@
*/
package org.apache.causeway.core.metamodel.facets.object.icon;
-import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
-import java.util.Objects;
import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
-import org.apache.causeway.commons.internal._Constants;
-import org.apache.causeway.commons.internal.base._Bytes;
import org.apache.causeway.commons.internal.base._StableValue;
-import org.apache.causeway.commons.internal.base._Strings;
+import org.apache.causeway.commons.net.DataUri;
/**
* Icon image data class-path resource reference.
*
* @see ObjectIconService
- * @since 2.0
+ * @since 2.0 revised for 4.0
*/
-public record ObjectIcon(
- String shortName,
- URL url,
- CommonMimeType mimeType,
- String identifier,
- _StableValue<byte[]> iconData
- ) implements Serializable {
+public sealed interface ObjectIcon extends Serializable
+permits ObjectIconEmbedded, ObjectIconUrlBased {
// -- FACTORIES
@@ -53,9 +44,8 @@ public static ObjectIcon eager(
final String shortName,
final URL url,
final CommonMimeType mimeType) {
- var id = _Strings.base64UrlEncode(url.getPath());
- var objectIcon = new ObjectIcon(shortName, url, mimeType, id, new
_StableValue<>());
- objectIcon.asBytes(); // memoize
+ var objectIcon = lazy(shortName, url, mimeType);
+ ((ObjectIconUrlBased) objectIcon).iconData(); // memoize
return objectIcon;
}
@@ -67,31 +57,17 @@ public static ObjectIcon lazy(
final String shortName,
final URL url,
final CommonMimeType mimeType) {
- var id = _Strings.base64UrlEncode(url.getPath());
- return new ObjectIcon(shortName, url, mimeType, id, new
_StableValue<>());
+ return new ObjectIconUrlBased(shortName, url, mimeType, new
_StableValue<>());
}
- // -- EQUALITY
-
- @Override
- public final boolean equals(Object o) {
- if(this == o) return true;
- return o instanceof ObjectIcon other
- ? Objects.equals(this.shortName, other.shortName)
- && Objects.equals(this.url, other.url)
- && Objects.equals(this.mimeType, other.mimeType)
- && Objects.equals(this.identifier, other.identifier)
- : false;
+ public static ObjectIcon embedded(String shortName, DataUri dataUri) {
+ return new ObjectIconEmbedded(shortName, dataUri);
}
- public byte[] asBytes() {
- return iconData.orElseSet(()->{
- try(final InputStream is = url.openStream()){
- return _Bytes.of(is);
- } catch (Exception e) {
- return _Constants.emptyBytes;
- }
- });
- }
+ // --
+
+ String shortName();
+ String mediaType();
+ byte[] iconData();
}
diff --git
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ImageResourceCache.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconEmbedded.java
similarity index 53%
copy from
viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ImageResourceCache.java
copy to
core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconEmbedded.java
index a36d27a1c12..29f44cb2070 100644
---
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ImageResourceCache.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconEmbedded.java
@@ -16,24 +16,29 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.causeway.viewer.wicket.model.models;
+package org.apache.causeway.core.metamodel.facets.object.icon;
-import java.io.Serializable;
-
-import org.apache.wicket.request.resource.ResourceReference;
-
-import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
+import org.apache.causeway.commons.net.DataUri;
/**
- * Ideally I'd like to move this to the
<tt>org.apache.causeway.viewer.wicket.model.causeway</tt>
- * package, however to do so would break existing API (gmap3 has a dependency
on this, for example).
+ * Icon image based on {@link DataUri}
+ *
+ * @see ObjectIconService
+ * @since 4.0
*/
-public interface ImageResourceCache extends Serializable {
+public record ObjectIconEmbedded(
+ String shortName,
+ DataUri dataUri
+ ) implements ObjectIcon {
- //ResourceReference resourceReferenceFor(ManagedObject adapter);
+ @Override
+ public String mediaType() {
+ return dataUri.mediaType();
+ }
- //ResourceReference resourceReferenceForSpec(ObjectSpecification
objectSpecification);
+ @Override
+ public byte[] iconData() {
+ return dataUri.data();
+ }
- ResourceReference resourceReferenceForObjectIcon(final ObjectIcon
objectIcon);
-
}
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIcon.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconUrlBased.java
similarity index 58%
copy from
core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIcon.java
copy to
core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconUrlBased.java
index 09656132270..1c31a4893f4 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIcon.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/icon/ObjectIconUrlBased.java
@@ -19,7 +19,6 @@
package org.apache.causeway.core.metamodel.facets.object.icon;
import java.io.InputStream;
-import java.io.Serializable;
import java.net.URL;
import java.util.Objects;
@@ -35,40 +34,30 @@
* @see ObjectIconService
* @since 2.0
*/
-public record ObjectIcon(
+public record ObjectIconUrlBased(
String shortName,
URL url,
CommonMimeType mimeType,
- String identifier,
- _StableValue<byte[]> iconData
- ) implements Serializable {
+ _StableValue<byte[]> iconDataRef
+ ) implements ObjectIcon {
- // -- FACTORIES
+ public String cacheId() {
+ return _Strings.base64UrlEncode(url.getPath());
+ }
- /**
- * Create an ObjectIcon and eagerly read in image data from
- * class-path resources.
- */
- public static ObjectIcon eager(
- final String shortName,
- final URL url,
- final CommonMimeType mimeType) {
- var id = _Strings.base64UrlEncode(url.getPath());
- var objectIcon = new ObjectIcon(shortName, url, mimeType, id, new
_StableValue<>());
- objectIcon.asBytes(); // memoize
- return objectIcon;
+ public byte[] iconData() {
+ return iconDataRef.orElseSet(()->{
+ try(final InputStream is = url.openStream()){
+ return _Bytes.of(is);
+ } catch (Exception e) {
+ return _Constants.emptyBytes;
+ }
+ });
}
- /**
- * Create an ObjectIcon and not yet read in image data from
- * class-path resources.
- */
- public static ObjectIcon lazy(
- final String shortName,
- final URL url,
- final CommonMimeType mimeType) {
- var id = _Strings.base64UrlEncode(url.getPath());
- return new ObjectIcon(shortName, url, mimeType, id, new
_StableValue<>());
+ @Override
+ public String mediaType() {
+ return mimeType.mimeType().getBaseType();
}
// -- EQUALITY
@@ -76,22 +65,11 @@ public static ObjectIcon lazy(
@Override
public final boolean equals(Object o) {
if(this == o) return true;
- return o instanceof ObjectIcon other
+ return o instanceof ObjectIconUrlBased other
? Objects.equals(this.shortName, other.shortName)
- && Objects.equals(this.url, other.url)
- && Objects.equals(this.mimeType, other.mimeType)
- && Objects.equals(this.identifier, other.identifier)
+ && Objects.equals(this.url, other.url)
+ && Objects.equals(this.mimeType, other.mimeType)
: false;
}
- public byte[] asBytes() {
- return iconData.orElseSet(()->{
- try(final InputStream is = url.openStream()){
- return _Bytes.of(is);
- } catch (Exception e) {
- return _Constants.emptyBytes;
- }
- });
- }
-
}
diff --git
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/icons/ObjectIconServiceDefault.java
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/icons/ObjectIconServiceDefault.java
index 720abe71b3e..5b1b3c040c6 100644
---
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/icons/ObjectIconServiceDefault.java
+++
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/icons/ObjectIconServiceDefault.java
@@ -33,6 +33,7 @@
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
import org.apache.causeway.applib.annotation.PriorityPrecedence;
import org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
@@ -41,6 +42,7 @@
import org.apache.causeway.commons.internal.base._Strings;
import org.apache.causeway.commons.internal.exceptions._Exceptions;
import org.apache.causeway.commons.internal.resources._Resources;
+import org.apache.causeway.commons.net.DataUri;
import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconService;
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
@@ -82,9 +84,16 @@ public ObjectIcon getObjectIcon(
final @Nullable String iconNameModifier) {
var domainClass = spec.getCorrespondingClass();
- var iconResourceKey = _Strings.isNotEmpty(iconNameModifier)
- ? domainClass.getName() + "-" + iconNameModifier
- : domainClass.getName();
+
+ var suffix = "";
+ if(StringUtils.hasLength(iconNameModifier)) {
+ suffix = "-" + iconNameModifier;
+ if(iconNameModifier.startsWith("data:")) {
+ return ObjectIcon.embedded(domainClass.getSimpleName(),
DataUri.parse(iconNameModifier));
+ }
+ }
+
+ var iconResourceKey = domainClass.getName() + suffix;
// also memoize unsuccessful icon lookups (as fallback), so we don't
search repeatedly
@@ -105,9 +114,10 @@ public ObjectIcon getObjectIcon(
private ObjectIcon getObjectFallbackIcon() {
return fallbackIcon.orElseSet(()->ObjectIcon.eager(
"ObjectIconFallback",
- _Resources.getResourceUrl(
+ _Resources.lookupResourceUrl(
ObjectIconServiceDefault.class,
- "ObjectIconFallback.png"),
+ "ObjectIconFallback.png")
+ .orElse(null),
CommonMimeType.PNG));
}
@@ -195,8 +205,7 @@ private static Optional<URL> classPathResource(
throw _Exceptions
.illegalArgument("invalid relative resourceName %s",
relativeResourceName);
}
- var resourceUrl = _Resources.getResourceUrl(contextClass,
relativeResourceName);
- return Optional.ofNullable(resourceUrl);
+ return _Resources.lookupResourceUrl(contextClass,
relativeResourceName);
}
}
diff --git
a/extensions/core/excel/fixture/src/main/java/org/apache/causeway/extensions/excel/fixtures/demoapp/demomodule/fixturescripts/DemoFixture_extending_ExcelFixture2.java
b/extensions/core/excel/fixture/src/main/java/org/apache/causeway/extensions/excel/fixtures/demoapp/demomodule/fixturescripts/DemoFixture_extending_ExcelFixture2.java
index cbbba40e658..93bf85f8097 100644
---
a/extensions/core/excel/fixture/src/main/java/org/apache/causeway/extensions/excel/fixtures/demoapp/demomodule/fixturescripts/DemoFixture_extending_ExcelFixture2.java
+++
b/extensions/core/excel/fixture/src/main/java/org/apache/causeway/extensions/excel/fixtures/demoapp/demomodule/fixturescripts/DemoFixture_extending_ExcelFixture2.java
@@ -47,7 +47,7 @@ public DemoFixture_extending_ExcelFixture2(){
@Override
protected void execute(final ExecutionContext executionContext) {
- setExcelResource(_Resources.getResourceUrl(getClass(),
getResourceName()));
+ setExcelResource(_Resources.lookupResourceUrl(getClass(),
getResourceName()).orElse(null));
setMatcher(sheetName -> {
diff --git
a/extensions/core/excel/fixture/src/main/java/org/apache/causeway/extensions/excel/fixtures/demoapp/demomodule/fixturescripts/DemoToDoItem_create_usingExcelFixture.java
b/extensions/core/excel/fixture/src/main/java/org/apache/causeway/extensions/excel/fixtures/demoapp/demomodule/fixturescripts/DemoToDoItem_create_usingExcelFixture.java
index 297ec945e58..d9aac500f91 100644
---
a/extensions/core/excel/fixture/src/main/java/org/apache/causeway/extensions/excel/fixtures/demoapp/demomodule/fixturescripts/DemoToDoItem_create_usingExcelFixture.java
+++
b/extensions/core/excel/fixture/src/main/java/org/apache/causeway/extensions/excel/fixtures/demoapp/demomodule/fixturescripts/DemoToDoItem_create_usingExcelFixture.java
@@ -42,7 +42,7 @@ public DemoToDoItem_create_usingExcelFixture() {
this(null);
}
- public DemoToDoItem_create_usingExcelFixture(String ownedBy) {
+ public DemoToDoItem_create_usingExcelFixture(final String ownedBy) {
this.user = ownedBy;
}
@@ -50,7 +50,7 @@ public DemoToDoItem_create_usingExcelFixture(String ownedBy) {
private List<ExcelDemoToDoItem> todoItems = _Lists.newArrayList();
@Override
- public void execute(ExecutionContext executionContext) {
+ public void execute(final ExecutionContext executionContext) {
final String ownedBy = this.user != null ? this.user :
userService.currentUserNameElseNobody();
@@ -59,7 +59,7 @@ public void execute(ExecutionContext executionContext) {
transactionService.flushTransaction();
}
- private void installFor(String user, ExecutionContext ec) {
+ private void installFor(final String user, final ExecutionContext ec) {
ec.setParameter("user", user);
@@ -73,12 +73,12 @@ private List<ExcelDemoToDoItem> load(
final ExecutionContext executionContext,
final String resourceName) {
- final URL excelResource = _Resources.getResourceUrl(getClass(),
resourceName);
+ final URL excelResource = _Resources.lookupResourceUrl(getClass(),
resourceName).orElse(null);
final ExcelFixture excelFixture = new ExcelFixture(excelResource,
DemoToDoItemRowHandler.class);
excelFixture.setExcelResourceName(resourceName);
executionContext.executeChild(this, excelFixture);
- return (List<ExcelDemoToDoItem>) excelFixture.getObjects();
+ return excelFixture.getObjects();
}
@Inject UserService userService;
diff --git
a/extensions/core/excel/integtests/src/test/java/org/apache/causeway/extensions/excel/integtests/tests/ExcelModuleDemoUploadService_IntegTest.java
b/extensions/core/excel/integtests/src/test/java/org/apache/causeway/extensions/excel/integtests/tests/ExcelModuleDemoUploadService_IntegTest.java
index 994b02538dd..06517532f70 100644
---
a/extensions/core/excel/integtests/src/test/java/org/apache/causeway/extensions/excel/integtests/tests/ExcelModuleDemoUploadService_IntegTest.java
+++
b/extensions/core/excel/integtests/src/test/java/org/apache/causeway/extensions/excel/integtests/tests/ExcelModuleDemoUploadService_IntegTest.java
@@ -52,7 +52,7 @@ public void setUpData() throws Exception {
public void uploadSpreadsheet() throws Exception{
// Given
- final URL excelResource = _Resources.getResourceUrl(getClass(),
"ToDoItemsWithMultipleSheets.xlsx");
+ final URL excelResource = _Resources.lookupResourceUrl(getClass(),
"ToDoItemsWithMultipleSheets.xlsx").orElse(null);
final Blob blob = new ExcelFileBlobConverter().toBlob("unused",
excelResource);
// When
diff --git
a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
index 2b5a1600e1d..615c30f048f 100644
---
a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
+++
b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/DomainObjectResourceServerside.java
@@ -222,8 +222,8 @@ public ResponseEntity<Object> image(
return _EndpointLogging.response(log, "GET
/objects/{}/{}/object-icon", domainType, instanceId,
responseFactory.ok(
- objectIcon.asBytes(),
-
MediaType.parseMediaType(objectIcon.mimeType().baseType())));
+ objectIcon.iconData(),
+ MediaType.parseMediaType(objectIcon.mediaType())));
}
@Override
diff --git
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkTreeNode.java
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkTreeNode.java
index 9453bdb4a99..52fb4a6696f 100644
---
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkTreeNode.java
+++
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkTreeNode.java
@@ -22,19 +22,20 @@
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.LongAdder;
+import java.util.function.Consumer;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.resource.ResourceReference;
+import org.jspecify.annotations.NonNull;
import org.apache.causeway.applib.fa.FontAwesomeLayers;
import org.apache.causeway.applib.services.bookmark.Bookmark;
-import org.apache.causeway.commons.functional.Either;
import org.apache.causeway.commons.internal.base._Casts;
import org.apache.causeway.commons.internal.collections._Lists;
import org.apache.causeway.commons.internal.functions._Functions;
+import
org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconEmbedded;
import lombok.Getter;
-import org.jspecify.annotations.NonNull;
public class BookmarkTreeNode
implements
@@ -49,12 +50,9 @@ public class BookmarkTreeNode
@Getter private String title;
- /** its either a iconResourceReference or a iconFaClass or neither
(decomposed for easy serialization) */
private ResourceReference iconResourceReference;
- /** its either a iconResourceReference or a FontAwesomeLayers or neither
(decomposed for easy serialization) */
private FontAwesomeLayers faLayers;
-
- //private final Set<Bookmark> propertyBookmarks; ... in support of parents
referencing their child
+ private ObjectIconEmbedded embedded;
// -- FACTORIES
@@ -73,30 +71,27 @@ private BookmarkTreeNode(
this.pageParameters =
bookmarkableModel.getPageParametersWithoutUiHints();
this.bookmark = bookmark;
-// this.propertyBookmarks = bookmarkableModel.streamPropertyBookmarks()
-// .collect(Collectors.toCollection(HashSet::new));
-
this.title = bookmarkableModel.getTitle();
_Casts.castTo(UiObjectWkt.class, bookmarkableModel)
- .map(UiObjectWkt::getIconAsResourceReference)
- .ifPresent(either->either.accept(
- iconResourceReference->
- this.iconResourceReference = iconResourceReference,
- faLayers->
- this.faLayers = faLayers
- )
- );
+ .ifPresent(x->x.visitIconVariantOrElse(
+ rref->{this.iconResourceReference = rref;},
+ embedded->{this.embedded = embedded;},
+ faLayers->{this.faLayers = faLayers;},
+ ()->{}));
this.depth = depth;
}
- // -- ICON
-
- public Either<ResourceReference, FontAwesomeLayers> eitherIconOrFaClass() {
- return faLayers==null
- ? Either.left(iconResourceReference)
- : Either.right(faLayers);
+ public void visitIconVariantOrElse(
+ Consumer<ResourceReference> a,
+ Consumer<ObjectIconEmbedded> b,
+ Consumer<FontAwesomeLayers> c,
+ Runnable onNoMatch) {
+ if(this.iconResourceReference!=null) a.accept(iconResourceReference);
+ else if(this.embedded!=null) b.accept(embedded);
+ else if(this.faLayers!=null) c.accept(faLayers);
+ else onNoMatch.run();
}
// -- COMPARATOR
@@ -184,37 +179,6 @@ private boolean matchAndUpdateTitleFor(final UiObjectWkt
candidateEntityModel) {
return inGraph;
}
-// /**
-// * Whether or not the provided {@link ActionModelImpl} matches that
contained
-// * within this node (taking into account the action's arguments).
-// *
-// * If it does match, then the matched node's title is updated to that of
the provided
-// * {@link ActionModelImpl}.
-// * <p>
-// *
-// * @return - whether the provided candidate is found or was added to
this node's tree.
-// */
-// private boolean matchFor(final ActionModelImpl candidateActionModel) {
-//
-// var candidateBookmark =
candidateActionModel.toBookmark().orElse(null);
-//
-// // check if target object of the action is the same
-// if(!Objects.equals(getBookmark(), candidateBookmark)) {
-// return false;
-// }
-//
-// // check if args same
-// List<String> thisArgs =
PageParameterNames.ACTION_ARGS.getListFrom(pageParameters);
-// PageParameters candidatePageParameters =
candidateActionModel.getPageParameters();
-// List<String> candidateArgs =
PageParameterNames.ACTION_ARGS.getListFrom(candidatePageParameters);
-// if(!Objects.equals(thisArgs, candidateArgs)) {
-// return false;
-// }
-//
-// // ok, a match
-// return true;
-// }
-
/**
* For given candidate model look into its properties and see whether one
matches this node's bookmark.
* If so, we found a parent/child relation for the tree to populate
@@ -231,19 +195,7 @@ private boolean addToGraphIfParented(final
BookmarkableModel candidateBookmarkab
}
});
- if(addedCount.longValue()>0L) {
- return true;
- }
-
-// /* also check the other way around, that is,
-// * whether the child is referenced from one of the parent's
properties
-// */
-// if(candidateBookmarkableModel.toBookmark()
-// .map(propertyBookmarks::contains)
-// .orElse(false)) {
-// return this.addChild(candidateBookmarkableModel).isPresent();
-// }
- return false;
+ return addedCount.longValue()>0L;
}
}
diff --git
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ImageResourceCache.java
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ImageResourceCache.java
index a36d27a1c12..7562baf2441 100644
---
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ImageResourceCache.java
+++
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ImageResourceCache.java
@@ -22,18 +22,10 @@
import org.apache.wicket.request.resource.ResourceReference;
-import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
+import
org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconUrlBased;
-/**
- * Ideally I'd like to move this to the
<tt>org.apache.causeway.viewer.wicket.model.causeway</tt>
- * package, however to do so would break existing API (gmap3 has a dependency
on this, for example).
- */
public interface ImageResourceCache extends Serializable {
- //ResourceReference resourceReferenceFor(ManagedObject adapter);
-
- //ResourceReference resourceReferenceForSpec(ObjectSpecification
objectSpecification);
+ ResourceReference resourceReferenceForObjectIcon(final ObjectIconUrlBased
objectIcon);
- ResourceReference resourceReferenceForObjectIcon(final ObjectIcon
objectIcon);
-
}
diff --git
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/UiObjectWkt.java
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/UiObjectWkt.java
index 4dc97c318ba..f60aca4186e 100644
---
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/UiObjectWkt.java
+++
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/UiObjectWkt.java
@@ -21,12 +21,13 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
+import java.util.function.Consumer;
import java.util.stream.Stream;
import org.apache.wicket.Component;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.resource.ResourceReference;
-
+import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.apache.causeway.applib.Identifier;
@@ -40,6 +41,8 @@
import org.apache.causeway.core.metamodel.commons.ViewOrEditMode;
import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
+import
org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconEmbedded;
+import
org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconUrlBased;
import org.apache.causeway.core.metamodel.object.ManagedObject;
import org.apache.causeway.core.metamodel.object.ManagedObjects;
import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
@@ -55,7 +58,6 @@
import org.apache.causeway.viewer.wicket.model.util.PageParameterUtils;
import lombok.Getter;
-import org.jspecify.annotations.NonNull;
import lombok.Setter;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
@@ -188,10 +190,25 @@ public Either<ObjectIcon, FontAwesomeLayers> getIcon() {
return getManagedObject().eitherIconOrFaLayers();
}
- public Either<ResourceReference, FontAwesomeLayers>
getIconAsResourceReference() {
- return getIcon()
- .mapLeft(objectIcon->
-
imageResourceCache().resourceReferenceForObjectIcon(objectIcon));
+ public void visitIconVariantOrElse(
+ Consumer<ResourceReference> a,
+ Consumer<ObjectIconEmbedded> b,
+ Consumer<FontAwesomeLayers> c,
+ Runnable onNoMatch) {
+ getIcon().accept(
+ objectIcon->{
+ if(objectIcon instanceof ObjectIconUrlBased urlBased){
+ var rref =
imageResourceCache().resourceReferenceForObjectIcon(urlBased);
+ if(rref!=null) {
+ a.accept(rref);
+ } else {
+ onNoMatch.run();
+ }
+ } else if(objectIcon instanceof ObjectIconEmbedded embedded){
+ b.accept(embedded);
+ }
+ },
+ fontAwesomeLayers->c.accept(fontAwesomeLayers));
}
@Override
diff --git
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.java
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.java
index 01a75b71cac..6a06a14e5e1 100644
---
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.java
+++
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/bookmarkedpages/BookmarkedPagesPanel.java
@@ -19,7 +19,6 @@
package org.apache.causeway.viewer.wicket.ui.components.bookmarkedpages;
import java.util.List;
-import java.util.Optional;
import jakarta.inject.Inject;
@@ -35,6 +34,7 @@
import org.apache.wicket.util.string.Strings;
import
org.apache.causeway.applib.exceptions.unrecoverable.ObjectNotFoundException;
+import org.apache.causeway.applib.value.Markup;
import org.apache.causeway.viewer.wicket.model.models.BookmarkTreeNode;
import org.apache.causeway.viewer.wicket.model.models.BookmarkedPagesModel;
import org.apache.causeway.viewer.wicket.model.models.PageType;
@@ -138,17 +138,23 @@ public void renderHead(final IHeaderResponse response) {
bookmarkNode.getPageParameters(),
pageClassRegistry.getPageClass(PageType.DOMAIN_OBJECT)));
- bookmarkNode.eitherIconOrFaClass()
- .accept(
+ bookmarkNode.visitIconVariantOrElse(
iconResourceRef->{
- Optional.ofNullable(iconResourceRef)
- .ifPresent(icon->
- Wkt.imageAddCachable(link,
ID_BOOKMARKED_PAGE_ICON, icon));
+ Wkt.imageAddCachable(link,
ID_BOOKMARKED_PAGE_ICON, iconResourceRef);
+ WktComponents.permanentlyHide(link,
ID_BOOKMARKED_PAGE_ICON_FA);
+ },
+ embedded->{
+ Wkt.markupAdd(link, ID_BOOKMARKED_PAGE_ICON,
Markup.embeddedImage(embedded.dataUri()).html());
WktComponents.permanentlyHide(link,
ID_BOOKMARKED_PAGE_ICON_FA);
},
faLayers->{
WktComponents.permanentlyHide(link,
ID_BOOKMARKED_PAGE_ICON);
Wkt.faIconLayersAdd(link,
ID_BOOKMARKED_PAGE_ICON_FA, faLayers);
+ },
+ ()->{
+ // on no match: hide all
+ WktComponents.permanentlyHide(link,
ID_BOOKMARKED_PAGE_ICON_FA);
+ WktComponents.permanentlyHide(link,
ID_BOOKMARKED_PAGE_ICON);
});
Wkt.labelAdd(link, ID_BOOKMARKED_PAGE_TITLE,
bookmarkNode.getTitle());
diff --git
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconAndTitlePanel.java
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconAndTitlePanel.java
index 295dd510b5f..d8021f913c6 100644
---
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconAndTitlePanel.java
+++
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/object/icontitle/ObjectIconAndTitlePanel.java
@@ -28,6 +28,8 @@
import org.apache.causeway.commons.internal.assertions._Assert;
import org.apache.causeway.commons.internal.base._Strings;
import org.apache.causeway.commons.internal.base._Text;
+import
org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconEmbedded;
+import
org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconUrlBased;
import org.apache.causeway.core.metamodel.object.ManagedObject;
import org.apache.causeway.core.metamodel.object.ManagedObjects;
import org.apache.causeway.core.metamodel.object.MmTitleUtils;
@@ -112,8 +114,15 @@ private AbstractLink createLinkWithIconAndTitle() {
linkedDomainObject.eitherIconOrFaLayers()
.accept(
objectIcon->{
- Wkt.imageAddCachable(link, ID_OBJECT_ICON,
-
getImageResourceCache().resourceReferenceForObjectIcon(objectIcon));
+ if(objectIcon instanceof ObjectIconEmbedded
iconEmbedded) {
+ Wkt.imageAddEmbedded(link, ID_OBJECT_ICON,
iconEmbedded.dataUri());
+ } else if(objectIcon instanceof ObjectIconUrlBased
iconUrlBased) {
+ Wkt.imageAddCachable(link, ID_OBJECT_ICON,
+
getImageResourceCache().resourceReferenceForObjectIcon(iconUrlBased));
+ } else {
+ throw new IllegalArgumentException("Unexpected
value: " + objectIcon);
+ }
+
WktComponents.permanentlyHide(link,
ID_OBJECT_FONT_AWESOME_LEFT);
WktComponents.permanentlyHide(link,
ID_OBJECT_FONT_AWESOME_RIGHT);
},
diff --git
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/util/Wkt.java
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/util/Wkt.java
index 699b18d5c43..ddfc45b352b 100644
---
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/util/Wkt.java
+++
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/util/Wkt.java
@@ -110,6 +110,7 @@
import org.apache.causeway.commons.internal.exceptions._Exceptions;
import
org.apache.causeway.commons.internal.functions._Functions.SerializableFunction;
import
org.apache.causeway.commons.internal.functions._Functions.SerializableSupplier;
+import org.apache.causeway.commons.net.DataUri;
import org.apache.causeway.core.config.CausewayConfiguration.Viewer.Wicket;
import org.apache.causeway.core.metamodel.tabular.DataTableInteractive;
import org.apache.causeway.viewer.commons.model.components.UiString;
@@ -827,10 +828,26 @@ public Image imageCachable(final String id, final
ResourceReference imageResourc
};
}
+ public Image imageEmbedded(final String id, final DataUri dataUri) {
+ return new Image(id, "embedded") {
+ private static final long serialVersionUID = 1L;
+ @Override protected boolean shouldAddAntiCacheParameter() { return
false; }
+
+ @Override
+ protected String buildSrcAttribute(ComponentTag tag) {
+ return dataUri.toExternalForm();
+ }
+ };
+ }
+
public Image imageAddCachable(final MarkupContainer container, final
String id, final ResourceReference imageResource) {
return add(container, imageCachable(id, imageResource));
}
+ public Image imageAddEmbedded(final MarkupContainer container, final
String id, final DataUri imageResource) {
+ return add(container, imageEmbedded(id, imageResource));
+ }
+
// -- LABEL
public Label label(final String id, final String label) {
diff --git
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/services/ImageResourceCacheClassPath.java
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/services/ImageResourceCacheClassPath.java
index b21ce1176ff..84d5c09fb8f 100644
---
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/services/ImageResourceCacheClassPath.java
+++
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/services/ImageResourceCacheClassPath.java
@@ -37,6 +37,7 @@
import org.apache.causeway.applib.annotation.PriorityPrecedence;
import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
+import
org.apache.causeway.core.metamodel.facets.object.icon.ObjectIconUrlBased;
import org.apache.causeway.viewer.wicket.model.models.ImageResourceCache;
import
org.apache.causeway.viewer.wicket.viewer.CausewayModuleViewerWicketViewer;
@@ -54,7 +55,7 @@ public class ImageResourceCacheClassPath
public static final String LOGICAL_TYPE_NAME =
CausewayModuleViewerWicketViewer.NAMESPACE +
".ImageResourceCacheClassPath";
- @Configuration
+ @Configuration(proxyBeanMethods = false)
public static class AutoConfiguration {
@Bean
@Named(LOGICAL_TYPE_NAME)
@@ -67,7 +68,7 @@ public ImageResourceCacheClassPath
imageResourceCacheClassPath() {
private static final long serialVersionUID = 1L;
@Override
- public ResourceReference resourceReferenceForObjectIcon(final ObjectIcon
objectIcon) {
+ public ResourceReference resourceReferenceForObjectIcon(final
ObjectIconUrlBased objectIcon) {
return new ObjectIconResourceReference(objectIcon);
}
@@ -80,8 +81,8 @@ private static class ObjectIconResourceReference
private final @NonNull ObjectIconResource objectIconResource;
- public ObjectIconResourceReference(final ObjectIcon objectIcon) {
- super(new Key(Application.class.getName(),
objectIcon.identifier(), null, null, null));
+ public ObjectIconResourceReference(final ObjectIconUrlBased
objectIcon) {
+ super(new Key(Application.class.getName(), objectIcon.cacheId(),
null, null, null));
this.objectIconResource = new ObjectIconResource(objectIcon);
}
@@ -103,14 +104,14 @@ private static class ObjectIconResource
@Override
protected ResourceResponse newResourceResponse(final Attributes
attributes) {
- var imageDataBytes = objectIcon.asBytes();
+ var imageDataBytes = objectIcon.iconData();
final long size = imageDataBytes.length;
- ResourceResponse resourceResponse = new ResourceResponse();
- resourceResponse.setContentType(objectIcon.mimeType().baseType());
+ var resourceResponse = new ResourceResponse();
+ resourceResponse.setContentType(objectIcon.mediaType());
resourceResponse.setAcceptRange(ContentRangeType.BYTES);
resourceResponse.setContentLength(size);
resourceResponse.setFileName(objectIcon.shortName());
- RequestCycle cycle = RequestCycle.get();
+ var cycle = RequestCycle.get();
Long startbyte = cycle.getMetaData(CONTENT_RANGE_STARTBYTE);
Long endbyte = cycle.getMetaData(CONTENT_RANGE_ENDBYTE);
resourceResponse.setWriteCallback(