All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.agilecoders.wicket.sass.UrlImporter Maven / Gradle / Ivy

The newest version!
package de.agilecoders.wicket.sass;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import java.util.stream.Stream;

import jakarta.servlet.ServletContext;

import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.Application;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.core.util.lang.WicketObjects;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.util.io.IOUtils;
import org.apache.wicket.util.string.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.agilecoders.wicket.webjars.WicketWebjars;
import de.agilecoders.wicket.webjars.util.WebJarAssetLocator;
import io.bit3.jsass.importer.Import;
import io.bit3.jsass.importer.Importer;





/**
 * SASS importer that knows how to load dependencies (imports) from:
 * 
    *
  • WebJars
  • *
  • classpath
  • *
  • same package, different jar
  • *
*/ class UrlImporter implements Importer { private static final Logger LOG = LoggerFactory.getLogger(SassSource.class); private static final String SCSS_EXT = ".scss"; private static final String SASS_EXT = ".sass"; /** * A mediator class that loads a class from wicket-webjars only when a dependency * (an import) with scheme "webjars!" needs to be resolved */ private static final class Holder { private static final WebJarAssetLocator locator = new WebJarAssetLocator(WicketWebjars.settings()); } public static final String CLASSPATH_SCHEME = "classpath!"; public static final String PACKAGE_SCHEME = "package!"; public static final String WEBJARS_SCHEME = "webjars!"; public static final String WEB_CONTEXT_SCHEME = "webcontext!"; public static final String JAR_SCHEME = "jar!"; /** * The scope class used with SassResourceReference. * Used to resolve dependencies with scheme "package!" */ private final String scopeClass; /** * @param scopeClass The scope class used with SassResourceReference. */ public UrlImporter(String scopeClass) { this.scopeClass = scopeClass; } @Override public Collection apply(String url, Import previous) { return Stream.concat(withExtension(url), cssUri(url)) .map(candidate -> tryResolveUrl(previous.getAbsoluteUri(), candidate)) .filter(Optional::isPresent) .map(Optional::get) .findFirst() .map(Collections::singletonList) .orElse(null); } private Optional tryResolveUrl(URI base, String url) { final Optional newImport; if (StringUtils.startsWith(url, WEBJARS_SCHEME)) { newImport = resolveWebJarsDependency(url); } else if (StringUtils.startsWith(url, CLASSPATH_SCHEME)) { newImport = resolveClasspathDependency(url); } else if (StringUtils.startsWith(url, WEB_CONTEXT_SCHEME)) { newImport = resolveWebContextDependency(url); } else if (scopeClass != null && StringUtils.startsWith(url, PACKAGE_SCHEME)) { newImport = resolvePackageDependency(url); } else { newImport = resolveLocalDependency(base, url); } return newImport; } private Stream withExtension(String url) { String withScssExt = url + SCSS_EXT; String withSassExt = url + SASS_EXT; return url.endsWith(SCSS_EXT) || url.endsWith(SASS_EXT) ? Stream.of(url, addUnderscore(url)) : Stream.of(withScssExt, addUnderscore(withScssExt), withSassExt, addUnderscore(withSassExt)); } private Stream cssUri(String url) { return Stream.of(url).filter(u -> u.endsWith(".css")); } private String addUnderscore(String url) { int lastSlash = url.lastIndexOf('/'); return new StringBuilder(url) .insert(lastSlash + 1, '_') .toString(); } private Optional resolveWebJarsDependency(String url) { LOG.debug("Going to resolve an import from WebJars: {}", url); try { String file = UrlImporter.Holder.locator.getFullPath(url.replaceFirst(WEBJARS_SCHEME, "/webjars/")); URL importUrl = Thread.currentThread().getContextClassLoader().getResource(file); return Optional.ofNullable(importUrl).map(this::buildImport); } catch (WebJarAssetLocator.ResourceException e) { LOG.debug("Webjar resource [{}] wasn't found", url); } catch (RuntimeException e) { throw new WicketRuntimeException(e); } return Optional.empty(); } private Optional resolveClasspathDependency(String url) { LOG.debug("Going to resolve an import from the classpath: {}", url); String resourceName = url.substring(CLASSPATH_SCHEME.length() + 1); if (resourceName.indexOf(0) != '/') { resourceName = '/' + resourceName; } URL importUrl = SassCacheManager.class.getResource(resourceName); return Optional.ofNullable(importUrl).map(this::buildImport); } private Optional resolveWebContextDependency(String url) { LOG.debug("Going to resolve an import from the web context: {}", url); String resourceName = url.substring(WEB_CONTEXT_SCHEME.length()); if (resourceName.indexOf(0) == '/') { resourceName = resourceName.substring(1); } final ServletContext context = ((WebApplication) Application.get()).getServletContext(); try { return Optional.of(buildImport(context.getResource(resourceName))); } catch (MalformedURLException ex) { throw new IllegalArgumentException("Cannot create a URL to a resource in the web context", ex); } } private Optional resolvePackageDependency(String url) { if (Strings.isEmpty(scopeClass)) { throw new IllegalStateException("Cannot resolve dependency '" + url + "' without a scope class!"); } LOG.debug("Going to resolve an import from the package: {}", url); String resourceName = url.startsWith(PACKAGE_SCHEME) ? url.substring(PACKAGE_SCHEME.length()) : url; if (resourceName.indexOf(0) == '/') { resourceName = resourceName.substring(1); } Class scope = WicketObjects.resolveClass(scopeClass); URL importUrl = scope.getResource(resourceName); return Optional.ofNullable(importUrl).map(this::buildImport); } private Optional resolveLocalDependency(URI base, String url) { LOG.debug("Going to resolve an import from local dependency: {}", url); String importUrl = getAbsolutePath(base, url); Optional localImport = resolveLocalFileDependency(importUrl); // local resource maybe inside jar, webjar return localImport.isPresent() ? localImport : resolveJarDependency(importUrl); } private Optional resolveJarDependency(String url) { LOG.debug("Going to resolve an import from jar file: {}", url); // Using the last index here because in FAT JARs (e.g Spring Boot), there can be multiple // nested JARs int jarSchemeIndex = url.lastIndexOf(JAR_SCHEME); if (jarSchemeIndex == -1) { return Optional.empty(); } int resourceNameIndex = jarSchemeIndex + JAR_SCHEME.length(); String resourceName = url.substring(resourceNameIndex); if (!resourceName.startsWith("/")) { resourceName = "/" + resourceName; } URL importUrl = SassCacheManager.class.getResource(resourceName); return Optional.ofNullable(importUrl).map(this::buildImport); } private Optional resolveLocalFileDependency(String url) { LOG.debug("Going to resolve an import from local file: {}", url); File file = new File(url); if (file.exists()) { try { return Optional.of(buildImport(file.toURI().toURL())); } catch (MalformedURLException e) { throw new IllegalArgumentException( String.format("Cannot resolve local dependency at path '%s'", url)); } } return Optional.empty(); } private String getAbsolutePath(URI base, String url) { String basePath = base.toString(); String parentBasePath = basePath.substring(0, basePath.lastIndexOf('/')); return parentBasePath + '/' + url; } private Import buildImport(URL importUri) { try { final String contents = read(importUri); return new Import(importUri.toURI(), importUri.toURI(), contents); } catch (URISyntaxException e) { throw new RuntimeException(e); } } private String read(URL url) { try (InputStream is = url.openStream()) { return IOUtils.toString(is, StandardCharsets.UTF_8.name()); } catch (IOException ex) { throw new WicketRuntimeException(ex); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy