Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure consistent behaviour for path and fullPath #19

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
105 changes: 81 additions & 24 deletions src/main/java/org/webjars/WebJarVersionLocator.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,28 @@


/**
* Locate WebJar version. The class is thread safe.
* Helper Class to locate WebJar versions.
*
* <p>By default, this class only supports looking up official WebJars with the Maven group IDs of {@code org.webjars.npm} and {@code org.webjars}.
*
* <p>Custom WenJars can be registered by providing a {@code META-INF/resources/webjars-locator.properties} file.
*
* <p><b>Note:</b> It is recommended, to add this file directly to the custom WebJar to ease the usage.
* But for WebJars not providing the file, you can add a {@code webjars-locator.properties} to your project.
*
* <p>Example file (multiple WebJars can be provided, one per line):
* <pre>{@code
* mywebjar.version=3.2.1
* anotherwebjar.version=1.4.3
* }</pre>
*
* <p>As the lookup of all {@code webjars-locator.properties} files happens during the construction of the class
* and the found versions are directly added to the cache, these property files can and will override versions
* that otherwise would be looked up by {@link WebJarVersionLocator#version(String)}.
*
* <p>When multiple {@code webjars-locator.properties} files contain a version for the same WebJar, the one that has been found first wins.
*
* <p>The class is thread safe.
*/
@NullMarked
public class WebJarVersionLocator {
Expand Down Expand Up @@ -47,47 +68,83 @@ public WebJarVersionLocator() {
}

/**
* @param webJarName The name of the WebJar, i.e. bootstrap
* @param exactPath The path to the file within the WebJar, i.e. js/bootstrap.js
* @return The full path to the file in the classpath including the version, i.e. META-INF/resources/webjars/bootstrap/3.1.1/js/bootstrap.js
* Builds the versioned path for a file of a WebJar within the standard WebJar classpath location (see {@link WebJarVersionLocator#WEBJARS_PATH_PREFIX}).
*
* <p>The path is built by prefixing the versioned path built by {@link WebJarVersionLocator#path(String, String)} with the standard WebJars location classpath.
*
* <p>See {@link WebJarVersionLocator#path(String, String)} for a detailed explanation of how the versioned file path is built.
*
* <p><b>Note:</b> This method does not perform any checks if the resulting path references an existing file.
*
* @param webJarName The name of the WebJar, this is the directory in the standard WebJar classpath location, usually the same as the Maven artifact ID
* @param filePath The path to the file within the WebJar
* @return The versioned path to the file in the classpath, if a version has been found, otherwise {@code null}
* @see WebJarVersionLocator#path(String, String)
* @see WebJarVersionLocator#WEBJARS_PATH_PREFIX
*/
@Nullable
public String fullPath(final String webJarName, final String exactPath) {
final String version = version(webJarName);
public String fullPath(final String webJarName, final String filePath) {
final String path = path(webJarName, filePath);

if (!isEmpty(version)) {
// todo: not sure why we check this
if (!exactPath.startsWith(version)) {
return String.format("%s/%s/%s/%s", WEBJARS_PATH_PREFIX, webJarName, version, exactPath);
}
else {
return String.format("%s/%s/%s", WEBJARS_PATH_PREFIX, webJarName, exactPath);
}
if (notEmpty(path)) {
return String.format("%s/%s", WEBJARS_PATH_PREFIX, path);
}

return null;
}

/**
* Builds the versioned path for a file of a WebJar relative to the standard WebJar classpath location (see {@link WebJarVersionLocator#WEBJARS_PATH_PREFIX}).
*
* <p>The path is built by joining the {@code webJarName}, the known version and the {@code filePath}, if no version (from classpath checking) is known for the WebJar this method returns {@code null}.
*
* <p><b>Note:</b> In cases where the {@code filePath} parameter already starts with the known version of the WebJar, the version will not be added again. But it is recommended that you do NOT include a hard-coded version when looking up WebJar file paths.
*
* @param webJarName The name of the WebJar, i.e. bootstrap
* @param exactPath The path to the file within the WebJar, i.e. js/bootstrap.js
* @return The path to the file in the standard WebJar classpath location, including the version, i.e. bootstrap/3.1.1/js/bootstrap.js
* <pre>{@code
* // returns "bootstrap/3.1.1/css/bootstrap.css"
* locator.path("bootstrap", "css/bootstrap.css");
*
* // returns "bootstrap/3.1.1/css/bootstrap.css" as well
* locator.path("bootstrap", "3.1.1/css/bootstrap.css");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo? Is it ".js" or ".css"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep typos. Will fix that

*
* // returns null, assuming there is no "unknown" WebJar
* locator.path("unknown", "some/file.css");
* }</pre>
*
* <p><b>Note:</b> This method does not perform any checks if the resulting path references an existing file.
*
* @param webJarName The name of the WebJar, this is the directory in the standard WebJar classpath location, usually the same as the Maven artifact ID
* @param filePath The path to the file within the WebJar
* @return The versioned path relative to the standard WebJar classpath location, if a version has been found, otherwise {@code null}
* @see WebJarVersionLocator#fullPath(String, String)
* @see WebJarVersionLocator#WEBJARS_PATH_PREFIX
*/
@Nullable
public String path(final String webJarName, final String exactPath) {
public String path(final String webJarName, final String filePath) {
final String version = version(webJarName);

if (!isEmpty(version)) {
return String.format("%s/%s/%s", webJarName, version, exactPath);
if (notEmpty(version)) {
if (filePath.startsWith(version)) {
return String.format("%s/%s", webJarName, filePath);
} else {
return String.format("%s/%s/%s", webJarName, version, filePath);
}
}

return null;
}

/**
* @param webJarName The name of the WebJar, i.e. bootstrap
* @return The version of the WebJar, i.e 3.1.1
* This method tries to determine the available version for a WebJar in the classpath.
*
* <p>For official WebJars, the version lookup is performed by checking for a {@code pom.properties} file for either {@link WebJarVersionLocator#NPM}
* or {@link WebJarVersionLocator#PLAIN} WebJars within {@code META-INF/maven}. The lookup result is cached.
*
* <p>Custom WebJars can be registered by using a {@code webjars-locator.properties} file. See {@link WebJarVersionLocator} for details.
*
* @param webJarName The name of the WebJar, this is the directory in the standard WebJar classpath location, usually the same as the Maven artifact ID
* @return The version of the WebJar, if found, otherwise {@code null}
* @see WebJarVersionLocator
*/
@Nullable
public String version(final String webJarName) {
Expand Down Expand Up @@ -168,8 +225,8 @@ private boolean hasResourcePath(final String webJarName, final String path) {
return LOADER.getResource(WEBJARS_PATH_PREFIX + "/" + webJarName + "/" + path) != null;
}

private boolean isEmpty(@Nullable final String str) {
return str == null || str.trim().isEmpty();
private boolean notEmpty(@Nullable final String str) {
return str != null && !str.trim().isEmpty();
}

}
7 changes: 6 additions & 1 deletion src/test/java/org/webjars/WebJarVersionLocatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,15 @@ void path_exists_version_not_supplied() {
}

@Test
void full_path_exists_version_supplied() {
void full_path_exists_with_version_supplied() {
assertEquals(WebJarVersionLocator.WEBJARS_PATH_PREFIX + "/bootstrap/3.1.1/js/bootstrap.js", new WebJarVersionLocator().fullPath("bootstrap", "3.1.1/js/bootstrap.js"));
}

@Test
void path_exists_with_version_supplied() {
assertEquals("bootstrap/3.1.1/js/bootstrap.js", new WebJarVersionLocator().path("bootstrap", "3.1.1/js/bootstrap.js"));
}

@Test
void cache_is_populated_on_lookup() {
AtomicBoolean shouldInspect = new AtomicBoolean(false);
Expand Down