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

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/master by this push:
     new 09637c38cf CAUSEWAY-3051: string cutting util: separation of concerns
09637c38cf is described below

commit 09637c38cfc4c3421668fab913c96263377c5e84
Author: Andi Huber <[email protected]>
AuthorDate: Mon May 8 18:58:32 2023 +0200

    CAUSEWAY-3051: string cutting util: separation of concerns
    
    - move String cutting utils from _Refs to its own class and simplify
    semantics
---
 .../causeway/commons/internal/base/_Refs.java      | 118 -------------
 .../commons/internal/base/_StringCutter.java       | 184 +++++++++++++++++++++
 .../commons/internal/exceptions/_Exceptions.java   |   8 +-
 .../commons/resource/ResourceCoordinates.java      |   8 +-
 .../commons/internal/base/StringCutterTest.java    |  44 +++++
 .../actions/action/invocation/IdentifierUtil.java  |  12 +-
 .../testdomain/value/ValueSemanticsTester.java     |   9 +-
 .../model/valuetypes/ValueTypeExample.java         |  10 +-
 .../model4adoc/include/IncludeStatements.java      |  40 +++--
 .../viewer/resources/ResourceAbstract.java         |   8 +-
 10 files changed, 281 insertions(+), 160 deletions(-)

diff --git 
a/commons/src/main/java/org/apache/causeway/commons/internal/base/_Refs.java 
b/commons/src/main/java/org/apache/causeway/commons/internal/base/_Refs.java
index 86f651a748..d64ac205b9 100644
--- a/commons/src/main/java/org/apache/causeway/commons/internal/base/_Refs.java
+++ b/commons/src/main/java/org/apache/causeway/commons/internal/base/_Refs.java
@@ -38,7 +38,6 @@ import lombok.EqualsAndHashCode;
 import lombok.NonNull;
 import lombok.Setter;
 import lombok.ToString;
-import lombok.val;
 
 /**
  * <h1>- internal use only -</h1>
@@ -304,123 +303,6 @@ public final class _Refs {
             return value.contains(s);
         }
 
-        /**
-         * At given {@code index} cuts the held {@link String} value into 
<i>left</i> and <i>right</i>
-         * parts, returns the <i>left</i> part and replaces the held string 
value with the <i>right</i>
-         * part.
-         * <ul>
-         * <li>Index underflow returns an empty string and leaves the held 
value unmodified.</li>
-         * <li>Index overflow returns the currently held value 'then' assigns 
the held value an empty string.</li>
-         * </ul>
-         * @param index - zero based cutting point
-         * @return left - cut off - part of held value (non-null)
-         */
-        public String cutAtIndex(final int index) {
-            if(index<=0) {
-                return "";
-            }
-            if(index>=value.length()) {
-                val left = value;
-                value = "";
-                return left;
-            }
-            val left = value.substring(0, index);
-            value = value.substring(index);
-            return left;
-        }
-
-        /**
-         * Shortcut to {@code cutAtIndex(value.indexOf(s))}.
-         * <p>
-         * At calculated {@code value.indexOf(s)} cuts the held {@link String} 
value into
-         * <i>left</i> and <i>right</i> parts, returns the <i>left</i> part 
and replaces the held
-         * string value with the <i>right</i> part.
-         * <p>
-         * When the substring is not found, returns an empty string and leaves 
the held value unmodified.
-         *
-         * @param   s   the substring to search for.
-         * @return  left - cut off - part of held value (non-null) at the 
index of the first
-         *          occurrence of the specified substring,
-         *          or an empty string if there is no such occurrence.
-         * @see #cutAtIndex(int)
-         * @see String#indexOf(String)
-         */
-        public String cutAtIndexOf(final String s) {
-            return cutAtIndex(value.indexOf(s));
-        }
-
-        /**
-         * Shortcut to {@code cutAtIndex(value.lastIndexOf(s))}.
-         * <p>
-         * At calculated {@code value.lastIndexOf(s)} cuts the held {@link 
String} value into
-         * <i>left</i> and <i>right</i> parts, returns the <i>left</i> part 
and replaces the held
-         * string value with the <i>right</i> part.
-         * <p>
-         * When the substring is not found, returns an empty string and leaves 
the held value unmodified.
-         *
-         * @param   s   the substring to search for.
-         * @return  left - cut off - part of held value (non-null) at the 
index of the last
-         *          occurrence of the specified substring,
-         *          or an empty string if there is no such occurrence.
-         * @see #cutAtIndex(int)
-         * @see String#lastIndexOf(String)
-         */
-        public String cutAtLastIndexOf(final String s) {
-            return cutAtIndex(value.lastIndexOf(s));
-        }
-
-        /**
-         * Variant of to {@link #cutAtIndexOf(String)}, that drops the 
specified substring {@code s}.
-         * <p>
-         * At calculated {@code value.indexOf(s)} cuts the held {@link String} 
value into
-         * <i>left</i>, <i>dropped</i> and <i>right</i> parts, returns the 
<i>left</i> part and
-         * replaces the held string value with the <i>right</i> part. Where 
the <i>dropped</i> part
-         * identifies as the matching part, that equals the specified 
substring {@code s}.
-         * <p>
-         * When the substring is not found, returns an empty string and leaves 
the held value unmodified.
-         *
-         * @param   s   the substring to search for.
-         * @return  left - cut off - part of held value (non-null) at the 
index of the first
-         *          occurrence of the specified substring,
-         *          or an empty string if there is no such occurrence.
-         * @see #cutAtIndex(int)
-         * @see String#indexOf(String)
-         */
-        public String cutAtIndexOfAndDrop(final String s) {
-            if(!value.contains(s)) {
-                return "";
-            }
-            val left = cutAtIndex(value.indexOf(s));
-            cutAtIndex(s.length());
-            return left;
-        }
-
-        /**
-         * Variant of to {@link #cutAtLastIndexOf(String)}, that drops the 
specified substring {@code s}.
-         * <p>
-         * At calculated {@code value.lastIndexOf(s)} cuts the held {@link 
String} value into
-         * <i>left</i>, <i>dropped</i> and <i>right</i> parts, returns the 
<i>left</i> part and
-         * replaces the held string value with the <i>right</i> part. Where 
the <i>dropped</i> part
-         * identifies as the matching part, that equals the specified 
substring {@code s}.
-         * <p>
-         * When the substring is not found, returns an empty string and leaves 
the held value unmodified.
-         *
-         * @param   s   the substring to search for.
-         * @return  left - cut off - part of held value (non-null) at the 
index of the last
-         *          occurrence of the specified substring,
-         *          or an empty string if there is no such occurrence.
-         * @see #cutAtIndex(int)
-         * @see String#lastIndexOf(String)
-         */
-        public String cutAtLastIndexOfAndDrop(final String s) {
-            if(!value.contains(s)) {
-                return "";
-            }
-            val left = cutAtIndex(value.lastIndexOf(s));
-            cutAtIndex(s.length());
-            return left;
-        }
-
     }
 
 }
diff --git 
a/commons/src/main/java/org/apache/causeway/commons/internal/base/_StringCutter.java
 
b/commons/src/main/java/org/apache/causeway/commons/internal/base/_StringCutter.java
new file mode 100644
index 0000000000..dc50cdd291
--- /dev/null
+++ 
b/commons/src/main/java/org/apache/causeway/commons/internal/base/_StringCutter.java
@@ -0,0 +1,184 @@
+/*
+ *  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.commons.internal.base;
+
+import java.util.function.UnaryOperator;
+
+import org.apache.causeway.commons.internal.assertions._Assert;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NonNull;
+
+/**
+ * <h1>- internal use only -</h1>
+ * <p>
+ * String cutting utility.
+ * </p>
+ * <p>
+ * <b>WARNING</b>: Do <b>NOT</b> use any of the classes provided by this 
package! <br/>
+ * These may be changed or removed without notice!
+ * </p>
+ *
+ * @since 2.0
+ */
+@AllArgsConstructor(staticName = "of")
+public final class _StringCutter {
+
+    @Getter
+    private final @NonNull String value;
+
+    /**
+     * Returns a new {@link _StringCutter} holding a the string value as 
returned by given {@code mapper}.
+     */
+    public _StringCutter map(final @NonNull UnaryOperator<String> mapper) {
+        return _StringCutter.of(mapper.apply(value));
+    }
+
+    /**
+     * Whether the held string value contains the specified
+     * sequence of char values.
+     *
+     * @param s the sequence to search for
+     * @return true if the held string value contains {@code s}, false 
otherwise
+     */
+    public boolean contains(final CharSequence s) {
+        return value.contains(s);
+    }
+
+    /**
+     * Searches for first occurrence of given {@code str} within
+     * held string value and drops any characters that come before
+     * (the matching part).
+     * <p>
+     * If no match acts as identity operator.
+     */
+    public _StringCutter dropBefore(final @NonNull String str) {
+        _Assert.assertNotEmpty(str, ()->"can only matches against non-empty 
string");
+        final int index = value.indexOf(str);
+        if(index>-1) {
+            return _StringCutter.of(value.substring(index));
+        }
+        return this;
+    }
+
+    /**
+     * Searches for first occurrence of given {@code str} within
+     * held string value and drops any characters that come after
+     * (the matching part).
+     */
+    public _StringCutter dropAfter(final @NonNull String str) {
+        _Assert.assertNotEmpty(str, ()->"can only matches against non-empty 
string");
+        final int index = value.indexOf(str);
+        if(index>-1) {
+            return _StringCutter.of(value.substring(0, index + str.length()));
+        }
+        return this;
+    }
+
+    /**
+     * Searches for last occurrence of given {@code str} within
+     * held string value and drops any characters that come before
+     * (the matching part).
+     */
+    public _StringCutter dropBeforeLast(final @NonNull String str) {
+        _Assert.assertNotEmpty(str, ()->"can only matches against non-empty 
string");
+        final int index = value.lastIndexOf(str);
+        if(index>-1) {
+            return _StringCutter.of(value.substring(index));
+        }
+        return this;
+    }
+
+    /**
+     * Searches for last occurrence of given {@code str} within
+     * held string value and drops any characters that come after
+     * (the matching part).
+     */
+    public _StringCutter dropAfterLast(final @NonNull String str) {
+        _Assert.assertNotEmpty(str, ()->"can only matches against non-empty 
string");
+        final int index = value.lastIndexOf(str);
+        if(index>-1) {
+            return _StringCutter.of(value.substring(0, index + str.length()));
+        }
+        return this;
+    }
+
+    /**
+     * Searches for first occurrence of given {@code str} within
+     * held string value, keeps any characters that come before
+     * (the matching part) and drops the rest.
+     * <p>
+     * If no match acts as identity operator.
+     */
+    public _StringCutter keepBefore(final @NonNull String str) {
+        _Assert.assertNotEmpty(str, ()->"can only matches against non-empty 
string");
+        final int index = value.indexOf(str);
+        if(index>-1) {
+            return _StringCutter.of(value.substring(0, index));
+        }
+        return this;
+    }
+
+    /**
+     * Searches for first occurrence of given {@code str} within
+     * held string value, keeps any characters that come after
+     * (the matching part) and drops the rest.
+     */
+    public _StringCutter keepAfter(final @NonNull String str) {
+        _Assert.assertNotEmpty(str, ()->"can only matches against non-empty 
string");
+        final int index = value.indexOf(str);
+        if(index>-1) {
+            return _StringCutter.of(value.substring(index + str.length()));
+        }
+        return this;
+    }
+
+    /**
+     * Searches for last occurrence of given {@code str} within
+     * held string value, keeps any characters that come before
+     * (the matching part) and drops the rest.
+     * <p>
+     * If no match acts as identity operator.
+     */
+    public _StringCutter keepBeforeLast(final @NonNull String str) {
+        _Assert.assertNotEmpty(str, ()->"can only matches against non-empty 
string");
+        final int index = value.indexOf(str);
+        if(index>-1) {
+            return _StringCutter.of(value.substring(0, index));
+        }
+        return this;
+    }
+
+    /**
+     * Searches for last occurrence of given {@code str} within
+     * held string value, keeps any characters that come after
+     * (the matching part) and drops the rest.
+     */
+    public _StringCutter keepAfterLast(final @NonNull String str) {
+        _Assert.assertNotEmpty(str, ()->"can only matches against non-empty 
string");
+        final int index = value.indexOf(str);
+        if(index>-1) {
+            return _StringCutter.of(value.substring(index + str.length()));
+        }
+        return this;
+    }
+
+
+}
diff --git 
a/commons/src/main/java/org/apache/causeway/commons/internal/exceptions/_Exceptions.java
 
b/commons/src/main/java/org/apache/causeway/commons/internal/exceptions/_Exceptions.java
index ea73143f0a..e8b70cac28 100644
--- 
a/commons/src/main/java/org/apache/causeway/commons/internal/exceptions/_Exceptions.java
+++ 
b/commons/src/main/java/org/apache/causeway/commons/internal/exceptions/_Exceptions.java
@@ -33,7 +33,7 @@ import org.springframework.lang.Nullable;
 
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.commons.internal.base._NullSafe;
-import org.apache.causeway.commons.internal.base._Refs;
+import org.apache.causeway.commons.internal.base._StringCutter;
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.internal.collections._Lists;
 
@@ -440,9 +440,9 @@ public final class _Exceptions {
                     val replacement = entry.getValue();
                     var s = str;
                     s = s.replace(entry.getKey() + ".", replacement.isEmpty() 
? "{" : replacement + ".");
-                    val ref = _Refs.stringRef(s);
-                    val left = ref.cutAtIndexOfAndDrop(".");
-                    val right = ref.getValue();
+                    val cutter = _StringCutter.of(s);
+                    val left = cutter.keepBefore(".").getValue();
+                    val right = cutter.keepAfter(".").getValue();
                     s = replacement.isEmpty()
                             ? left + "}." + right
                             : left + "." + right;
diff --git 
a/commons/src/main/java/org/apache/causeway/commons/resource/ResourceCoordinates.java
 
b/commons/src/main/java/org/apache/causeway/commons/resource/ResourceCoordinates.java
index 31a4c28cff..2bbd4a1fa3 100644
--- 
a/commons/src/main/java/org/apache/causeway/commons/resource/ResourceCoordinates.java
+++ 
b/commons/src/main/java/org/apache/causeway/commons/resource/ResourceCoordinates.java
@@ -24,7 +24,7 @@ import java.util.Comparator;
 import org.springframework.lang.Nullable;
 
 import org.apache.causeway.commons.collections.Can;
-import org.apache.causeway.commons.internal.base._Refs;
+import org.apache.causeway.commons.internal.base._StringCutter;
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.internal.collections._Lists;
 
@@ -51,11 +51,11 @@ implements Comparable<ResourceCoordinates> {
             next = next.getParentFile();
         }
 
-        val nameRef = _Refs.stringRef(file.getName());
+        val cutter = _StringCutter.of(file.getName());
 
         //XXX lombok issue, cannot use val
-        final String simpleName = nameRef.cutAtLastIndexOfAndDrop(".");
-        final String fileNameExtension = nameRef.getValue();
+        final String simpleName = cutter.keepBeforeLast(".").getValue();
+        final String fileNameExtension = cutter.keepAfterLast(".").getValue();
 
         return ResourceCoordinates.builder()
              // could semantically mean the mount-point, but we don't have 
that info in a file instance
diff --git 
a/commons/src/test/java/org/apache/causeway/commons/internal/base/StringCutterTest.java
 
b/commons/src/test/java/org/apache/causeway/commons/internal/base/StringCutterTest.java
new file mode 100644
index 0000000000..49e60c59e5
--- /dev/null
+++ 
b/commons/src/test/java/org/apache/causeway/commons/internal/base/StringCutterTest.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.commons.internal.base;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class StringCutterTest {
+
+    @Test
+    void dropBeforeAndAfter() {
+        assertEquals("(or)", _StringCutter.of("HalloW(or)ld!")
+                .dropBefore("(")
+                .dropAfter(")")
+                .getValue());
+    }
+
+    @Test
+    void keepBeforeAndAfter() {
+        assertEquals("or", _StringCutter.of("HalloW(or)ld!")
+                .keepAfter("(")
+                .keepBefore(")")
+                .getValue());
+
+    }
+
+}
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/action/invocation/IdentifierUtil.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/action/invocation/IdentifierUtil.java
index 2d13c2b961..26c3fe0875 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/action/invocation/IdentifierUtil.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/action/invocation/IdentifierUtil.java
@@ -23,7 +23,7 @@ import org.springframework.lang.Nullable;
 import org.apache.causeway.applib.Identifier;
 import org.apache.causeway.applib.id.LogicalType;
 import org.apache.causeway.applib.services.command.Command;
-import org.apache.causeway.commons.internal.base._Refs;
+import org.apache.causeway.commons.internal.base._StringCutter;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.metamodel.commons.StringExtensions;
 import org.apache.causeway.core.metamodel.interactions.InteractionHead;
@@ -59,9 +59,13 @@ public class IdentifierUtil {
             final @NonNull Identifier.Type identifierType,
             final @NonNull String logicalMemberIdentifier) {
 
-        val ref = _Refs.stringRef(logicalMemberIdentifier);
-        val logicalTypeName = ref.cutAtIndexOfAndDrop("#");
-        val memberId = ref.getValue();
+        val stringCutter = _StringCutter.of(logicalMemberIdentifier);
+        val logicalTypeName = stringCutter
+                .keepBefore("#")
+                .getValue();
+        val memberId = stringCutter
+                .keepAfter("#")
+                .getValue();
         val typeSpec = 
specLoader.specForLogicalTypeNameElseFail(logicalTypeName);
         val logicalType = LogicalType.eager(typeSpec.getCorrespondingClass(), 
logicalTypeName);
 
diff --git 
a/regressiontests/stable-value/src/test/java/org/apache/causeway/testdomain/value/ValueSemanticsTester.java
 
b/regressiontests/stable-value/src/test/java/org/apache/causeway/testdomain/value/ValueSemanticsTester.java
index 93f665e006..4e8774ec1a 100644
--- 
a/regressiontests/stable-value/src/test/java/org/apache/causeway/testdomain/value/ValueSemanticsTester.java
+++ 
b/regressiontests/stable-value/src/test/java/org/apache/causeway/testdomain/value/ValueSemanticsTester.java
@@ -36,7 +36,7 @@ import org.apache.causeway.applib.value.semantics.Renderer;
 import org.apache.causeway.applib.value.semantics.ValueSemanticsProvider;
 import org.apache.causeway.commons.functional.Try;
 import org.apache.causeway.commons.internal.base._Casts;
-import org.apache.causeway.commons.internal.base._Refs;
+import org.apache.causeway.commons.internal.base._StringCutter;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.commons.io.JaxbUtils;
 import org.apache.causeway.core.metamodel.facets.object.value.ValueFacet;
@@ -153,9 +153,10 @@ public class ValueSemanticsTester<T> {
                 .formattedOutput(true)))
         .getValue().orElseThrow();
 
-        val xmlRef = _Refs.stringRef(rawXml);
-        xmlRef.cutAtIndexOf("<ValueWithTypeDto");
-        return xmlRef.cutAtLastIndexOf("</ValueWithTypeDto>")
+        return _StringCutter.of(rawXml)
+                .dropBefore("<ValueWithTypeDto")
+                .keepBeforeLast("</ValueWithTypeDto>")
+                .getValue()
                 .replace(" null=\"false\" 
xmlns:com=\"https://causeway.apache.org/schema/common\"; 
xmlns:cmd=\"https://causeway.apache.org/schema/cmd\"";, "")
                 + "</ValueWithTypeDto>";
 
diff --git 
a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/model/valuetypes/ValueTypeExample.java
 
b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/model/valuetypes/ValueTypeExample.java
index a8ebdd8790..52d81fc471 100644
--- 
a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/model/valuetypes/ValueTypeExample.java
+++ 
b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/model/valuetypes/ValueTypeExample.java
@@ -38,6 +38,7 @@ import java.util.stream.Stream;
 import javax.inject.Named;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
 
 import org.apache.causeway.applib.annotation.Action;
 import org.apache.causeway.applib.annotation.Collection;
@@ -60,7 +61,7 @@ import 
org.apache.causeway.applib.value.NamedWithMimeType.CommonMimeType;
 import org.apache.causeway.applib.value.Password;
 import org.apache.causeway.applib.value.semantics.ValueSemanticsAbstract;
 import org.apache.causeway.commons.collections.Can;
-import org.apache.causeway.commons.internal.base._Refs;
+import org.apache.causeway.commons.internal.base._StringCutter;
 import org.apache.causeway.commons.internal.base._Temporals;
 import 
org.apache.causeway.core.metamodel.valuesemantics.ApplicationFeatureIdValueSemantics;
 import org.apache.causeway.core.metamodel.valuesemantics.MarkupValueSemantics;
@@ -72,7 +73,6 @@ import org.apache.causeway.schema.common.v2.OidDto;
 import org.apache.causeway.schema.ixn.v2.InteractionDto;
 import org.apache.causeway.valuetypes.vega.applib.value.Vega;
 import 
org.apache.causeway.valuetypes.vega.metamodel.semantics.VegaValueSemantics;
-import org.springframework.context.annotation.Scope;
 
 import lombok.Builder;
 import lombok.Getter;
@@ -163,9 +163,9 @@ public abstract class ValueTypeExample<T> {
         if(!name.contains("_")) {
             return Optional.empty();
         }
-        val ref = _Refs.stringRef(name);
-        ref.cutAtIndexOfAndDrop("_");
-        return Optional.of(ref.getValue());
+        return Optional.of(_StringCutter.of(name)
+                .keepBefore("_")
+                .getValue());
     }
 
     // -- EXAMPLES - BASIC
diff --git 
a/tooling/model4adoc/src/main/java/org/apache/causeway/tooling/model4adoc/include/IncludeStatements.java
 
b/tooling/model4adoc/src/main/java/org/apache/causeway/tooling/model4adoc/include/IncludeStatements.java
index d9c0ba0bfe..138b49d846 100644
--- 
a/tooling/model4adoc/src/main/java/org/apache/causeway/tooling/model4adoc/include/IncludeStatements.java
+++ 
b/tooling/model4adoc/src/main/java/org/apache/causeway/tooling/model4adoc/include/IncludeStatements.java
@@ -23,7 +23,7 @@ import java.util.function.BiConsumer;
 import java.util.function.UnaryOperator;
 
 import org.apache.causeway.commons.collections.Can;
-import org.apache.causeway.commons.internal.base._Refs;
+import org.apache.causeway.commons.internal.base._StringCutter;
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.internal.collections._Lists;
 
@@ -80,38 +80,44 @@ public final class IncludeStatements {
 
             if(line.startsWith("include::")) {
 
-                val acc = _Refs.stringRef(line);
-
-                acc.cutAtIndex("include::".length());
+                var cutter = _StringCutter.of(line)
+                        .keepAfter("include::");
 
                 val incl = IncludeStatement.builder();
                 incl.matchingLine(line);
                 incl.zeroBasedLineIndex(zeroBasedLineIndex);
 
-                if(acc.contains("@")) {
-                    incl.version(acc.cutAtIndexOfAndDrop("@"));
+                if(cutter.contains("@")) {
+                    incl.version(cutter.keepBefore("@").getValue());
+                    cutter = cutter.keepAfter("@");
                 }
 
-                incl.component(acc.cutAtIndexOfAndDrop(":"));
-                incl.module(acc.cutAtIndexOfAndDrop(":"));
-                incl.type(acc.cutAtIndexOfAndDrop("$"));
+                incl.component(cutter.keepBefore(":").getValue());
+                cutter = cutter.keepAfter(":");
+
+                incl.module(cutter.keepBefore(":").getValue());
+                cutter = cutter.keepAfter(":");
+
+                incl.type(cutter.keepBefore("$").getValue());
+                cutter = cutter.keepAfter(":");
 
                 final String referencePath;
-                if(acc.contains("[")) {
-                    referencePath = acc.cutAtIndexOf("[");
-                    incl.options(acc.getValue());
+                if(cutter.contains("[")) {
+                    referencePath = cutter.keepBefore("[").getValue();
+                    cutter = cutter.keepAfter("[");
+                    incl.options(cutter.getValue());
                 } else {
-                    referencePath = acc.getValue();
+                    referencePath = cutter.getValue();
                 }
 
-                acc.setValue(referencePath);
+                cutter = _StringCutter.of(referencePath);
 
-                val namespaceAsString = acc.cutAtLastIndexOfAndDrop("/");
+                val namespaceAsString = cutter.keepBeforeLast("/").getValue();
 
                 
incl.namespace(Can.ofStream(_Strings.splitThenStream(namespaceAsString, "/")));
 
-                incl.canonicalName(acc.cutAtLastIndexOfAndDrop("."));
-                incl.ext(acc.getValue());
+                incl.canonicalName(cutter.keepBeforeLast(".").getValue());
+                incl.ext(cutter.keepAfter(".").getValue());
 
                 onLine.accept(line, Optional.of(incl.build()));
             } else {
diff --git 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/ResourceAbstract.java
 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/ResourceAbstract.java
index 5f7abd08bc..85776cfd24 100644
--- 
a/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/ResourceAbstract.java
+++ 
b/viewers/restfulobjects/viewer/src/main/java/org/apache/causeway/viewer/restfulobjects/viewer/resources/ResourceAbstract.java
@@ -35,7 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 
 import org.apache.causeway.applib.annotation.Where;
 import org.apache.causeway.applib.services.bookmark.Bookmark;
-import org.apache.causeway.commons.internal.base._Refs;
+import org.apache.causeway.commons.internal.base._StringCutter;
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.internal.codec._UrlDecoderUtil;
 import org.apache.causeway.core.config.viewer.web.WebAppContextPath;
@@ -114,9 +114,9 @@ implements HasMetaModelContext {
         // eg. http://localhost:8080/
         val serverAbsoluteBase =
                 _Strings
-                .suffix(_Refs
-                        .stringRef(restfulAbsoluteBase)
-                        .cutAtLastIndexOfAndDrop(restfulRelativeBase),
+                .suffix(_StringCutter.of(restfulAbsoluteBase)
+                        .keepAfterLast(restfulRelativeBase)
+                        .getValue(),
                 "/");
 
         // eg. http://localhost:8080/ctx/

Reply via email to