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

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


The following commit(s) were added to refs/heads/main by this push:
     new 607b7b2c63f CAUSEWAY-3948: adds WebjarEnumerator utility
607b7b2c63f is described below

commit 607b7b2c63f068fe5486da0a7e980320c2282937
Author: andi-huber <[email protected]>
AuthorDate: Thu Jan 8 14:03:49 2026 +0100

    CAUSEWAY-3948: adds WebjarEnumerator utility
    
    Task-Url: https://issues.apache.org/jira/browse/CAUSEWAY-3948
---
 .../commons/model/src/main/java/module-info.java   |   1 +
 .../commons/model/webjar/WebjarEnumerator.java     | 103 +++++++++++++++++++++
 2 files changed, 104 insertions(+)

diff --git a/viewers/commons/model/src/main/java/module-info.java 
b/viewers/commons/model/src/main/java/module-info.java
index 74e17e28196..d962f328310 100644
--- a/viewers/commons/model/src/main/java/module-info.java
+++ b/viewers/commons/model/src/main/java/module-info.java
@@ -28,6 +28,7 @@
     exports org.apache.causeway.viewer.commons.model.layout;
     exports org.apache.causeway.viewer.commons.model.mixin;
     exports org.apache.causeway.viewer.commons.model.object;
+    exports org.apache.causeway.viewer.commons.model.webjar;
 
     requires static lombok;
     requires transitive org.apache.causeway.applib;
diff --git 
a/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/webjar/WebjarEnumerator.java
 
b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/webjar/WebjarEnumerator.java
new file mode 100644
index 00000000000..eac34885f4f
--- /dev/null
+++ 
b/viewers/commons/model/src/main/java/org/apache/causeway/viewer/commons/model/webjar/WebjarEnumerator.java
@@ -0,0 +1,103 @@
+/*
+ *  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.viewer.commons.model.webjar;
+
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
+
+import org.apache.causeway.commons.internal.base._Lazy;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
+import org.apache.causeway.commons.io.TextUtils;
+
+import lombok.SneakyThrows;
+
+/**
+ * Utility to scan webjar version strings dynamically from class-path under 
{@code META-INF/resources/webjars}.
+ *
+ * <p>Removes the need to hard code version strings for webjar resource 
lookups.
+ *
+ * @since 3.6.0, 4.0
+ */
+public record WebjarEnumerator() {
+
+    private final static _Lazy<Map<String, WebjarResource>> WEBJARS = 
_Lazy.threadSafe(WebjarEnumerator::scanClassPath);
+
+    public static Optional<WebjarResource> lookup(final String path) {
+        return Optional.ofNullable(WEBJARS.get().get(path));
+    }
+
+    public static WebjarResource lookupElseFail(final String path) {
+        return lookup(path).orElseThrow(()->_Exceptions
+                .noSuchElement("no webjar found on class-path under 
META-INF/resources/webjars matching sub-path '%s'", path));
+    }
+
+    public record WebjarResource(
+        String path,
+        String version) {
+
+        static Optional<WebjarResource> parseFromURL(final String url) {
+            if(url==null
+                    || !url.startsWith("jar:file:")
+                    || !url.contains("/org/webjars/")
+                    || !url.contains(".jar!"))
+                return Optional.empty();
+            // datatables/2.3.6
+            // npm/bootstrap-select/1.14.0-beta3
+            // npm/inputmask/5.0.9
+            // jquery-ui/1.14.1
+            var pathAndVersion = TextUtils.cutter(url)
+                .keepAfter("/org/webjars/")
+                .keepBefore(".jar!")
+                .keepBeforeLast("/");
+            return Optional.of(new WebjarResource(
+                pathAndVersion.keepBeforeLast("/").getValue(),
+                pathAndVersion.keepAfterLast("/").getValue()));
+        }
+    }
+
+    // -- HELPER
+
+    private static Map<String, WebjarResource> scanClassPath() {
+        return webjarURLs().stream()
+            .map(WebjarResource::parseFromURL)
+            .filter(Optional::isPresent)
+            .map(Optional::get)
+            .collect(Collectors.toMap(WebjarResource::path, 
UnaryOperator.identity()));
+    }
+
+    @SneakyThrows
+    private static Set<String> webjarURLs() {
+        Set<String> webjarURLs = new TreeSet<>();
+        Enumeration<URL> resources = WebjarEnumerator.class.getClassLoader()
+                .getResources("META-INF/resources/webjars");
+
+        while (resources.hasMoreElements()) {
+            URL resource = resources.nextElement();
+            webjarURLs.add(resource.toString());
+        }
+        return webjarURLs;
+    }
+
+}

Reply via email to