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 2023/11/23 07:47:13 UTC

(causeway) 01/01: CAUSEWAY-3646: support fa-stacks via quick-notation

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch 3646-fa.quick.notation
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit ec6c68296cf3fa57cb1b6b90b043089321265194
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Nov 23 08:47:05 2023 +0100

    CAUSEWAY-3646: support fa-stacks via quick-notation
---
 .../causeway/applib/fa/FontAwesomeLayers.java      |   8 ++
 .../fa/FontAwesomeQuickNotationGenerator.java      |  57 +++++++++++
 .../applib/fa/FontAwesomeQuickNotationParser.java  |  14 ++-
 .../causeway/applib/fa/FontAwesomeLayersTest.java  |   4 +-
 .../facets/members/iconfa/FaStaticFacet.java       |  35 +------
 .../members/iconfa/FaStaticFacetAbstract.java      | 106 ++-------------------
 ...tract.java => FaStaticFacetAbstractLegacy.java} |  61 ++----------
 .../core/metamodel/layout/LayoutFacetUtil.java     |   2 +-
 .../ActionLayoutAnnotationFacetFactoryTest.java    |   6 +-
 .../facets/members/iconfa/FaFacetAbstractTest.java |  45 ---------
 .../DomainObjectLayoutFactoryTest.java             |   2 +-
 .../testdomain/domainmodel/SpecLoaderTest.java     |  46 ++++-----
 12 files changed, 118 insertions(+), 268 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/causeway/applib/fa/FontAwesomeLayers.java b/api/applib/src/main/java/org/apache/causeway/applib/fa/FontAwesomeLayers.java
index df1e6c243c..a5c9e8c59b 100644
--- a/api/applib/src/main/java/org/apache/causeway/applib/fa/FontAwesomeLayers.java
+++ b/api/applib/src/main/java/org/apache/causeway/applib/fa/FontAwesomeLayers.java
@@ -228,6 +228,14 @@ public class FontAwesomeLayers implements Serializable {
         return JsonUtils.toStringUtf8(this, JsonUtils::indentedOutput);
     }
 
+    /**
+     * If this instance was not created from a quick-notation,
+     * the result may loose style information.
+     */
+    public String toQuickNotation() {
+        return FontAwesomeQuickNotationGenerator.generate(this);
+    }
+
     // -- UTILITY
 
     //TODO[CAUSEWAY-3646] how to determine position when empty
diff --git a/api/applib/src/main/java/org/apache/causeway/applib/fa/FontAwesomeQuickNotationGenerator.java b/api/applib/src/main/java/org/apache/causeway/applib/fa/FontAwesomeQuickNotationGenerator.java
new file mode 100644
index 0000000000..9a337733c7
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/causeway/applib/fa/FontAwesomeQuickNotationGenerator.java
@@ -0,0 +1,57 @@
+/*
+ *  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.applib.fa;
+
+import java.util.stream.Collectors;
+
+import org.apache.causeway.commons.internal.base._NullSafe;
+import org.apache.causeway.commons.internal.base._Strings;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+class FontAwesomeQuickNotationGenerator {
+
+    String generate(final FontAwesomeLayers model) {
+        return _NullSafe.stream(model.getIconEntries())
+            .map(iconEntry->processPrefixes(iconEntry.getCssClasses()))
+            .collect(Collectors.joining(", "));
+    }
+
+    private String processPrefixes(final String cssClasses) {
+        return _Strings.splitThenStream(cssClasses, " ")
+            .map(String::trim)
+            .filter(_Strings::isNotEmpty)
+            .map(FontAwesomeQuickNotationGenerator::processPrefix)
+            .filter(_Strings::isNotEmpty)
+            .collect(Collectors.joining(" "));
+    }
+
+    private String processPrefix(final String cssQuickClass) {
+        if(cssQuickClass.equals("fa")
+                || cssQuickClass.equals("fa-fw")) {
+            return null;
+        }
+        if(cssQuickClass.startsWith("fa-")) {
+            return cssQuickClass.substring(3);
+        }
+        return "." + cssQuickClass;
+    }
+
+}
diff --git a/api/applib/src/main/java/org/apache/causeway/applib/fa/FontAwesomeQuickNotationParser.java b/api/applib/src/main/java/org/apache/causeway/applib/fa/FontAwesomeQuickNotationParser.java
index 9f6790ebe9..324b57f432 100644
--- a/api/applib/src/main/java/org/apache/causeway/applib/fa/FontAwesomeQuickNotationParser.java
+++ b/api/applib/src/main/java/org/apache/causeway/applib/fa/FontAwesomeQuickNotationParser.java
@@ -22,6 +22,7 @@ import java.util.TreeSet;
 import java.util.stream.Collectors;
 
 import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.internal.base._NullSafe;
 import org.apache.causeway.commons.internal.base._Strings;
 
 import lombok.experimental.UtilityClass;
@@ -33,27 +34,30 @@ class FontAwesomeQuickNotationParser {
         var iconEntryCssClasses = _Strings.splitThenStream(quickNotation, ",")
             .map(String::trim)
             .map(_Strings::emptyToNull)
-            .map(FontAwesomeQuickNotationParser::processPrefixes)
             .collect(Can.toCan());
         if(iconEntryCssClasses.isEmpty()) {
             return FontAwesomeLayers.empty();
         }
         if(iconEntryCssClasses.isCardinalityOne()) {
-            return FontAwesomeLayers.singleIcon(iconEntryCssClasses.getFirstElseFail());
+            return FontAwesomeLayers
+                .singleIcon(processPrefixes(iconEntryCssClasses.getFirstElseFail(), "fa-fw"));
         }
         var stackBuilder = FontAwesomeLayers.stackBuilder()
             .containerCssClasses("fa-lg")
             .containerCssStyle("width:1.25em");
-        iconEntryCssClasses.forEach(stackBuilder::addIconEntry);
+        iconEntryCssClasses.stream()
+            .map(x->processPrefixes(x, "fa-stack-1x"))
+            .forEach(stackBuilder::addIconEntry);
         return stackBuilder.build();
     }
 
-    private String processPrefixes(final String cssQuickClasses) {
+    private String processPrefixes(final String cssQuickClasses, final String... mandatory) {
         var elements = _Strings.splitThenStream(cssQuickClasses, " ")
             .map(String::trim)
             .filter(_Strings::isNotEmpty)
             .collect(Collectors.toCollection(TreeSet::new));
-        elements.add("fa-stack-1x"); // mandatory
+        _NullSafe.stream(mandatory)
+            .forEach(elements::add);
         return elements.stream()
                 .map(FontAwesomeQuickNotationParser::processPrefix)
                 .collect(Collectors.joining(" "));
diff --git a/api/applib/src/test/java/org/apache/causeway/applib/fa/FontAwesomeLayersTest.java b/api/applib/src/test/java/org/apache/causeway/applib/fa/FontAwesomeLayersTest.java
index 5af00892a6..85a37117db 100644
--- a/api/applib/src/test/java/org/apache/causeway/applib/fa/FontAwesomeLayersTest.java
+++ b/api/applib/src/test/java/org/apache/causeway/applib/fa/FontAwesomeLayersTest.java
@@ -92,8 +92,8 @@ class FontAwesomeLayersTest {
                 .addIconEntry("fa-solid fa-circle-chevron-down fa-stack-1x my-color bottom-right-overlay")
                 .build();
 
-
-        System.err.printf("%s%n", iconStackFromQuick.toJson());
+        //debug
+        //System.err.printf("%s%n", iconStackFromQuick.toJson());
 
         assertEquals(iconStackReference.toJson(), iconStackFromQuick.toJson());
     }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacet.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacet.java
index 4ec11d1444..bbf3241336 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacet.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacet.java
@@ -18,48 +18,15 @@
  */
 package org.apache.causeway.core.metamodel.facets.members.iconfa;
 
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
 import org.apache.causeway.applib.layout.component.CssClassFaPosition;
-import org.apache.causeway.commons.internal.base._NullSafe;
-import org.apache.causeway.commons.internal.base._Strings;
 
 public interface FaStaticFacet
 extends FaFacet, FaLayersProvider {
 
+    //TODO[CAUSEWAY-3646] remove - can be directly queried from FA layers
     /**
      * Position of <i>Font Awesome</i> icon, relative to its accompanied title.
      */
     CssClassFaPosition getPosition();
 
-    Stream<String> streamCssClasses();
-
-    /**
-     * Space separated (distinct) CSS-class strings.
-     */
-    default String asSpaceSeparated() {
-        return streamCssClasses()
-                .collect(Collectors.joining(" "));
-    }
-
-    /**
-     * Space separated (distinct) CSS-class strings.
-     * @param additionalClasses - trimmed and filtered by non-empty, then added to the resulting string
-     */
-    default String asSpaceSeparatedWithAdditional(final String ... additionalClasses) {
-
-        if(_NullSafe.size(additionalClasses)==0) {
-            return asSpaceSeparated();
-        }
-
-        return Stream.concat(
-                streamCssClasses(),
-                _NullSafe.stream(additionalClasses)
-                .map(String::trim)
-                .filter(_Strings::isNotEmpty))
-                .distinct()
-                .collect(Collectors.joining(" "));
-    }
-
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacetAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacetAbstract.java
index 4d42e4b943..4f21310894 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacetAbstract.java
@@ -16,25 +16,15 @@
  * under the License. */
 package org.apache.causeway.core.metamodel.facets.members.iconfa;
 
-import java.util.List;
-import java.util.Set;
 import java.util.function.BiConsumer;
-import java.util.regex.Pattern;
-import java.util.stream.Stream;
 
 import org.apache.causeway.applib.fa.FontAwesomeLayers;
 import org.apache.causeway.applib.layout.component.CssClassFaPosition;
 import org.apache.causeway.commons.functional.Either;
-import org.apache.causeway.commons.internal.base._NullSafe;
-import org.apache.causeway.commons.internal.base._Strings;
-import org.apache.causeway.commons.internal.collections._Lists;
-import org.apache.causeway.commons.internal.collections._Sets;
-import org.apache.causeway.commons.internal.functions._Predicates;
 import org.apache.causeway.core.metamodel.facetapi.FacetAbstract;
 import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
 
 import lombok.Getter;
-import lombok.val;
 
 /**
  * One of two bases for the {@link FaFacet}.
@@ -50,15 +40,14 @@ implements FaStaticFacet {
         return FaFacet.class;
     }
 
-    private static final Pattern WHITESPACE = Pattern.compile("\\s+");
-    private static final String FIXED_WIDTH = "fa-fw";
-    private static final String DEFAULT_PRIMARY_PREFIX = "fa";
-
     @Getter(onMethod_ = {@Override})
     private final Either<FaStaticFacet, FaImperativeFacet> specialization = Either.left(this);
 
-    @Getter(onMethod_ = {@Override}) private CssClassFaPosition position;
-    private final List<String> cssClasses; // serializable list implementation
+    @Getter(onMethod_ = {@Override})
+    private CssClassFaPosition position;
+
+    @Getter(onMethod_ = {@Override})
+    private final FontAwesomeLayers layers; // serializable
 
     protected FaStaticFacetAbstract(
             final String value,
@@ -68,100 +57,21 @@ implements FaStaticFacet {
     }
 
     protected FaStaticFacetAbstract(
-            final String value,
+            final String quickNotation,
             final CssClassFaPosition position,
             final FacetHolder holder,
             final Precedence precedence) {
 
         super(type(), holder, precedence);
         this.position = position;
-        this.cssClasses = parse(value);
-    }
-
-    @Override
-    public final Stream<String> streamCssClasses() {
-        return cssClasses.stream();
-    }
-
-    @Override
-    public final FontAwesomeLayers getLayers() {
-        return FontAwesomeLayers.singleIcon(asSpaceSeparatedWithAdditional("fa-lg"));
+        this.layers = FontAwesomeLayers.fromQuickNotation(quickNotation);
     }
 
     @Override
     public void visitAttributes(final BiConsumer<String, Object> visitor) {
         super.visitAttributes(visitor);
         visitor.accept("position", position);
-        visitor.accept("classes", asSpaceSeparated());
-    }
-
-    // -- HELPER
-
-    /**
-     * Parses given value for CSS classes (space separated).
-     * <ul>
-     * <li>
-     * Adds the optional <em>fa-fw</em> fixed width FontAwesome class, if not provided.
-     * <li>
-     * Adds the default <em>fa</em> FontAwesome prefix class, if no other prefix class provided (fab, far or fas).
-     * </ul>
-     * @param parsedClasses
-     * @return The original CSS classes plus <em>fa</em> and <em>fa-fw</em> if not already provided
-     */
-    static List<String> parse(final String value) {
-        //XXX cannot use lombok val here
-        final Set<String> cssClassesSet = _Sets.<String>newLinkedHashSet(); // preserved order
-        _Strings.splitThenStreamTrimmed(value.trim(), WHITESPACE)
-        .map(FaStaticFacetAbstract::faPrefix)
-        .forEach(cssClass->cssClassesSet.add(faPrefix(cssClass)));
-
-        return sanitize(cssClassesSet);
-    }
-
-    private static List<String> sanitize(final Set<String> parsedClasses) {
-        val cssClasses = _Lists.<String>newArrayList();
-
-        val primaryPrefix = parsedClasses.stream()
-        .filter(FaStaticFacetAbstract::isFaPrimaryPrefix)
-        .findFirst()
-        .orElse(DEFAULT_PRIMARY_PREFIX);
-
-        cssClasses.add(primaryPrefix);
-        cssClasses.add(FIXED_WIDTH);
-
-        parsedClasses.stream()
-        .filter(_Predicates.not(FaStaticFacetAbstract::isFaPrimaryPrefix))
-        .filter(_Predicates.not(FaStaticFacetAbstract::isFixedWidth))
-        .forEach(cssClasses::add);
-
-        return cssClasses;
-    }
-
-    private static String faPrefix(final String cssClass) {
-        return cssClass.startsWith("fa-")
-                || isFaPrimaryPrefix(cssClass)
-                        ? cssClass
-                        : "fa-" + cssClass;
-    }
-
-    private static boolean isFixedWidth(final String cssClass) {
-        return FIXED_WIDTH.equals(cssClass);
-    }
-
-    private static boolean isFaPrimaryPrefix(final String cssClass) {
-        if(_NullSafe.isEmpty(cssClass)) {
-            return false;
-        }
-        switch(cssClass) {
-        case "fa":
-        case "far":
-        case "fab":
-        case "fas":
-            return true;
-        default:
-            return false;
-        }
-
+        visitor.accept("classes", layers.toQuickNotation());
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacetAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacetAbstractLegacy.java
similarity index 63%
copy from core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacetAbstract.java
copy to core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacetAbstractLegacy.java
index 4d42e4b943..492270e1d1 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaStaticFacetAbstractLegacy.java
@@ -18,22 +18,14 @@ package org.apache.causeway.core.metamodel.facets.members.iconfa;
 
 import java.util.List;
 import java.util.Set;
-import java.util.function.BiConsumer;
 import java.util.regex.Pattern;
-import java.util.stream.Stream;
 
-import org.apache.causeway.applib.fa.FontAwesomeLayers;
-import org.apache.causeway.applib.layout.component.CssClassFaPosition;
-import org.apache.causeway.commons.functional.Either;
 import org.apache.causeway.commons.internal.base._NullSafe;
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.internal.collections._Lists;
 import org.apache.causeway.commons.internal.collections._Sets;
 import org.apache.causeway.commons.internal.functions._Predicates;
-import org.apache.causeway.core.metamodel.facetapi.FacetAbstract;
-import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
 
-import lombok.Getter;
 import lombok.val;
 
 /**
@@ -42,9 +34,8 @@ import lombok.val;
  * @see FaImperativeFacetAbstract
  * @since 2.0
  */
-public abstract class FaStaticFacetAbstract
-extends FacetAbstract
-implements FaStaticFacet {
+@Deprecated //TODO[CAUSEWAY-3646] remove
+public abstract class FaStaticFacetAbstractLegacy {
 
     public static final Class<FaFacet> type() {
         return FaFacet.class;
@@ -54,46 +45,6 @@ implements FaStaticFacet {
     private static final String FIXED_WIDTH = "fa-fw";
     private static final String DEFAULT_PRIMARY_PREFIX = "fa";
 
-    @Getter(onMethod_ = {@Override})
-    private final Either<FaStaticFacet, FaImperativeFacet> specialization = Either.left(this);
-
-    @Getter(onMethod_ = {@Override}) private CssClassFaPosition position;
-    private final List<String> cssClasses; // serializable list implementation
-
-    protected FaStaticFacetAbstract(
-            final String value,
-            final CssClassFaPosition position,
-            final FacetHolder holder) {
-        this(value, position, holder, Precedence.DEFAULT);
-    }
-
-    protected FaStaticFacetAbstract(
-            final String value,
-            final CssClassFaPosition position,
-            final FacetHolder holder,
-            final Precedence precedence) {
-
-        super(type(), holder, precedence);
-        this.position = position;
-        this.cssClasses = parse(value);
-    }
-
-    @Override
-    public final Stream<String> streamCssClasses() {
-        return cssClasses.stream();
-    }
-
-    @Override
-    public final FontAwesomeLayers getLayers() {
-        return FontAwesomeLayers.singleIcon(asSpaceSeparatedWithAdditional("fa-lg"));
-    }
-
-    @Override
-    public void visitAttributes(final BiConsumer<String, Object> visitor) {
-        super.visitAttributes(visitor);
-        visitor.accept("position", position);
-        visitor.accept("classes", asSpaceSeparated());
-    }
 
     // -- HELPER
 
@@ -112,7 +63,7 @@ implements FaStaticFacet {
         //XXX cannot use lombok val here
         final Set<String> cssClassesSet = _Sets.<String>newLinkedHashSet(); // preserved order
         _Strings.splitThenStreamTrimmed(value.trim(), WHITESPACE)
-        .map(FaStaticFacetAbstract::faPrefix)
+        .map(FaStaticFacetAbstractLegacy::faPrefix)
         .forEach(cssClass->cssClassesSet.add(faPrefix(cssClass)));
 
         return sanitize(cssClassesSet);
@@ -122,7 +73,7 @@ implements FaStaticFacet {
         val cssClasses = _Lists.<String>newArrayList();
 
         val primaryPrefix = parsedClasses.stream()
-        .filter(FaStaticFacetAbstract::isFaPrimaryPrefix)
+        .filter(FaStaticFacetAbstractLegacy::isFaPrimaryPrefix)
         .findFirst()
         .orElse(DEFAULT_PRIMARY_PREFIX);
 
@@ -130,8 +81,8 @@ implements FaStaticFacet {
         cssClasses.add(FIXED_WIDTH);
 
         parsedClasses.stream()
-        .filter(_Predicates.not(FaStaticFacetAbstract::isFaPrimaryPrefix))
-        .filter(_Predicates.not(FaStaticFacetAbstract::isFixedWidth))
+        .filter(_Predicates.not(FaStaticFacetAbstractLegacy::isFaPrimaryPrefix))
+        .filter(_Predicates.not(FaStaticFacetAbstractLegacy::isFixedWidth))
         .forEach(cssClasses::add);
 
         return cssClasses;
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/layout/LayoutFacetUtil.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/layout/LayoutFacetUtil.java
index 3ea0bdf54a..d6ab32afbb 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/layout/LayoutFacetUtil.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/layout/LayoutFacetUtil.java
@@ -113,7 +113,7 @@ public class LayoutFacetUtil {
         .ifPresent(specialization->
             specialization.accept(
                     hasStaticFaIcon->{
-                        final String cssClassFa = hasStaticFaIcon.asSpaceSeparated();
+                        final String cssClassFa = hasStaticFaIcon.getLayers().toQuickNotation();
                         if(!_Strings.isNullOrEmpty(cssClassFa)) {
                             hasCssClassFa.setCssClassFa(cssClassFa);
                             hasCssClassFa.setCssClassFaPosition(hasStaticFaIcon.getPosition());
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/actions/layout/ActionLayoutAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/actions/layout/ActionLayoutAnnotationFacetFactoryTest.java
index 80ddab33fe..3af76b6b23 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/actions/layout/ActionLayoutAnnotationFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/actions/layout/ActionLayoutAnnotationFacetFactoryTest.java
@@ -121,7 +121,8 @@ extends FacetFactoryTestAbstract {
             assertThat(facet, is(notNullValue()));
             assertThat(facet, is(instanceOf(FaFacetForActionLayoutAnnotation.class)));
             val classFaFacetForActionLayoutAnnotation = (FaFacetForActionLayoutAnnotation) facet;
-            assertThat(classFaFacetForActionLayoutAnnotation.asSpaceSeparated(), is(equalTo("fa fa-fw fa-font-awesome")));
+            assertThat(classFaFacetForActionLayoutAnnotation.getLayers().getIconEntries().get(0).getCssClasses(),
+                    is(equalTo("fa fa-font-awesome fa-fw")));
             assertThat(classFaFacetForActionLayoutAnnotation.getPosition(), is(CssClassFaPosition.LEFT));
 
         });
@@ -142,7 +143,8 @@ extends FacetFactoryTestAbstract {
             assertThat(facet, is(notNullValue()));
             assertThat(facet, is(instanceOf(FaFacetForActionLayoutAnnotation.class)));
             val classFaFacetForActionLayoutAnnotation = (FaFacetForActionLayoutAnnotation) facet;
-            assertThat(classFaFacetForActionLayoutAnnotation.asSpaceSeparated(), is(equalTo("fa fa-fw fa-font-awesome")));
+            assertThat(classFaFacetForActionLayoutAnnotation.getLayers().getIconEntries().get(0).getCssClasses(),
+                    is(equalTo("fa fa-font-awesome fa-fw")));
             assertThat(classFaFacetForActionLayoutAnnotation.getPosition(), is(CssClassFaPosition.RIGHT));
 
         });
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaFacetAbstractTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaFacetAbstractTest.java
deleted file mode 100644
index b3f7ded909..0000000000
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/members/iconfa/FaFacetAbstractTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- *  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.facets.members.iconfa;
-
-import org.junit.jupiter.api.Test;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-public class FaFacetAbstractTest {
-
-    public static class Sanitize extends FaFacetAbstractTest {
-
-        @Test
-        public void present() throws Exception {
-            assertThat(FaStaticFacetAbstract.parse("fab foo").toString(), is("[fab, fa-fw, fa-foo]"));
-        }
-
-        @Test
-        public void presentAtEnd() throws Exception {
-            assertThat(FaStaticFacetAbstract.parse("foo far ").toString(), is("[far, fa-fw, fa-foo]"));
-        }
-
-        @Test
-        public void missing() throws Exception {
-            assertThat(FaStaticFacetAbstract.parse("foo").toString(), is("[fa, fa-fw, fa-foo]"));
-        }
-    }
-}
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFactoryTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFactoryTest.java
index 7c9f0419b7..9aa1dc96a2 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobjectlayout/DomainObjectLayoutFactoryTest.java
@@ -163,7 +163,7 @@ extends FacetFactoryTestAbstract {
                 assertTrue(facet instanceof FaFacetForDomainObjectLayoutAnnotation);
 
                 final FaFacetForDomainObjectLayoutAnnotation facetImpl = (FaFacetForDomainObjectLayoutAnnotation) facet;
-                assertThat(facetImpl.asSpaceSeparated(), equalTo("fa fa-fw fa-foo"));
+                assertThat(facetImpl.getLayers().getIconEntries().get(0).getCssClasses(), equalTo("fa fa-foo fa-fw"));
                 assertThat(facetImpl.getPosition(), is(CssClassFaPosition.RIGHT));
 
                 assertNoMethodsRemoved();
diff --git a/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/SpecLoaderTest.java b/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/SpecLoaderTest.java
index 394ed7a66b..b14a24afba 100644
--- a/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/SpecLoaderTest.java
+++ b/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/SpecLoaderTest.java
@@ -18,12 +18,10 @@
  */
 package org.apache.causeway.testdomain.domainmodel;
 
-import java.util.stream.Stream;
-
 import javax.inject.Inject;
 
 import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.TestPropertySource;
 
@@ -38,11 +36,11 @@ import org.apache.causeway.testdomain.model.good.Configuration_usingValidDomain;
 import lombok.val;
 
 @SpringBootTest(
-        classes = { 
+        classes = {
                 Configuration_headless.class,
                 Configuration_usingValidDomain.class,
-                
-        }, 
+
+        },
         properties = {
                 "causeway.core.meta-model.introspector.mode=FULL",
                 "causeway.applib.annotation.domain-object.editing=TRUE",
@@ -54,33 +52,31 @@ import lombok.val;
     CausewayPresets.SilenceProgrammingModel
 })
 class SpecLoaderTest {
-    
+
     @Inject private SpecificationLoader specificationLoader;
-    
+
     @ParameterizedTest
-    @MethodSource("providePrimitiveTypes")
-    void primitiveRoundtrip_shouldSucceed(Class<?> type) {
-        
+    @ValueSource(classes = {
+            boolean.class,
+            byte.class,
+            short.class,
+            int.class,
+            long.class,
+            float.class,
+            double.class,
+            char.class
+    })
+    void primitiveRoundtrip_shouldSucceed(final Class<?> type) {
+
         val spec1 = specificationLoader.loadSpecification(type);
         assertNotNull(spec1);
-        
+
         val logicalType = spec1.getLogicalType();
-        
+
         val spec2 = specificationLoader.specForLogicalType(logicalType).orElse(null);
         assertNotNull(spec2);
-        
+
         assertEquals(spec1.getLogicalType(), spec2.getLogicalType());
     }
-    
-    private static Stream<Class<?>> providePrimitiveTypes() {
-        return Stream.of(boolean.class,
-                byte.class,
-                short.class,
-                int.class,
-                long.class,
-                float.class,
-                double.class,
-                char.class);
-    }
 
 }