You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by gh...@apache.org on 2020/09/10 22:12:42 UTC
[sling-org-apache-sling-api] 01/01: SLING-9662 Introduce Resource
Mapping SPI incl. ResourceUri
This is an automated email from the ASF dual-hosted git repository.
ghenzler pushed a commit to branch feature/SLING-9662-Introduce-Resource-Mapping-SPI-v2
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-api.git
commit c27db47a07ce7d85896e1c761f462653a7532e79
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Mon Sep 7 16:01:00 2020 +0200
SLING-9662 Introduce Resource Mapping SPI incl. ResourceUri
---
pom.xml | 2 +-
.../sling/api/resource/ResourceResolver.java | 6 +-
.../resource/mapping/PathToUriMappingService.java | 69 +++
.../sling/api/resource/mapping/package-info.java | 2 +-
.../apache/sling/api/resource/package-info.java | 2 +-
.../apache/sling/api/resource/uri/ResourceUri.java | 122 ++++
.../sling/api/resource/uri/ResourceUriBuilder.java | 607 ++++++++++++++++++
.../sling/api/resource/{ => uri}/package-info.java | 5 +-
.../spi/resource/mapping/MappingChainContext.java | 51 ++
.../spi/resource/mapping/ResourceUriMapper.java | 56 ++
.../resource/mapping/package-info.java | 4 +-
.../sling/api/resource/uri/ResourceUriTest.java | 679 +++++++++++++++++++++
...UriToSlingRequestPathInfoCompatibilityTest.java | 271 ++++++++
13 files changed, 1866 insertions(+), 10 deletions(-)
diff --git a/pom.xml b/pom.xml
index 8e02cb7..10f3109 100644
--- a/pom.xml
+++ b/pom.xml
@@ -105,7 +105,7 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.2</version>
- <scope>test</scope>
+
</dependency>
<dependency>
<groupId>org.osgi</groupId>
diff --git a/src/main/java/org/apache/sling/api/resource/ResourceResolver.java b/src/main/java/org/apache/sling/api/resource/ResourceResolver.java
index 8a2b27d..e92abe0 100644
--- a/src/main/java/org/apache/sling/api/resource/ResourceResolver.java
+++ b/src/main/java/org/apache/sling/api/resource/ResourceResolver.java
@@ -22,12 +22,14 @@ import java.io.Closeable;
import java.util.Iterator;
import java.util.Map;
-import org.jetbrains.annotations.Nullable;
-import org.jetbrains.annotations.NotNull;
import javax.servlet.http.HttpServletRequest;
import org.apache.sling.api.adapter.Adaptable;
import org.apache.sling.api.resource.mapping.ResourceMapper;
+import org.apache.sling.api.resource.uri.ResourceUri;
+import org.apache.sling.api.resource.uri.ResourceUriBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.osgi.annotation.versioning.ProviderType;
/**
diff --git a/src/main/java/org/apache/sling/api/resource/mapping/PathToUriMappingService.java b/src/main/java/org/apache/sling/api/resource/mapping/PathToUriMappingService.java
new file mode 100644
index 0000000..8134699
--- /dev/null
+++ b/src/main/java/org/apache/sling/api/resource/mapping/PathToUriMappingService.java
@@ -0,0 +1,69 @@
+/*
+ * 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.sling.api.resource.mapping;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.resource.uri.ResourceUri;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.annotation.versioning.ProviderType;
+
+/** Provides a way to resolve URIs to resource paths and map resource paths to URIs. */
+@ProviderType
+public interface PathToUriMappingService {
+
+ /** A context hint for a consumer of the map/resolve result */
+ @ProviderType
+ public interface ContextHint {
+ String getName();
+ }
+
+ /** The result of a map or resolve operation */
+ @ProviderType
+ public interface Result {
+ /** @return the ResourceUri */
+ @NotNull
+ ResourceUri getResourceUri();
+
+ /** @return context hints (e.g. 'invalid link' for map(), or 'requires authentication' for resolve()) */
+ @NotNull
+ Set<ContextHint> getContextHints();
+
+ /** @return all intermediate mappings as produced by {@link org.apache.sling.spi.resource.mapping.ResourceUriMapper} services. */
+ @NotNull
+ Map<String, ResourceUri> getIntermediateMappings();
+ }
+
+ /** Resolves a path relative to the given request.
+ *
+ * @param request
+ * @param path
+ * @return a @{link PathToUriMappingService.Result} */
+ Result resolve(@Nullable HttpServletRequest request, @NotNull String path);
+
+ /** @param request
+ * @param resourcePath
+ * @return a @{link PathToUriMappingService.Result} */
+ Result map(@Nullable HttpServletRequest request, @NotNull String resourcePath);
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/api/resource/mapping/package-info.java b/src/main/java/org/apache/sling/api/resource/mapping/package-info.java
index e871225..00a1ea1 100644
--- a/src/main/java/org/apache/sling/api/resource/mapping/package-info.java
+++ b/src/main/java/org/apache/sling/api/resource/mapping/package-info.java
@@ -17,7 +17,7 @@
* under the License.
*/
-@Version("1.0.1")
+@Version("1.1.0")
package org.apache.sling.api.resource.mapping;
import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/api/resource/package-info.java b/src/main/java/org/apache/sling/api/resource/package-info.java
index 7bd85e6..ac05b61 100644
--- a/src/main/java/org/apache/sling/api/resource/package-info.java
+++ b/src/main/java/org/apache/sling/api/resource/package-info.java
@@ -17,7 +17,7 @@
* under the License.
*/
-@Version("2.12.2")
+@Version("2.13.0")
package org.apache.sling.api.resource;
import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/api/resource/uri/ResourceUri.java b/src/main/java/org/apache/sling/api/resource/uri/ResourceUri.java
new file mode 100644
index 0000000..858ac31
--- /dev/null
+++ b/src/main/java/org/apache/sling/api/resource/uri/ResourceUri.java
@@ -0,0 +1,122 @@
+/*
+ * 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.sling.api.resource.uri;
+
+import static org.apache.commons.lang3.StringUtils.isBlank;
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import org.apache.sling.api.request.RequestPathInfo;
+import org.apache.sling.api.resource.Resource;
+
+/** Represents an immutable URI that points to a resource or alternatively, can contain special URIs like {@code mailto:} or
+ * {@code javascript:}. */
+public interface ResourceUri extends RequestPathInfo {
+
+ /** @return returns the URI. */
+ public URI toUri();
+
+ /** @return returns the URI as String. */
+ public String toString();
+
+ /** @return returns the scheme of the link */
+ public String getScheme();
+
+ /** @return returns the user info of the link */
+ public String getUserInfo();
+
+ /** @return returns the host of the link */
+ public String getHost();
+
+ /** @return returns the port of the link */
+ public int getPort();
+
+ /** @return returns the resource path or null if link does not contain path. */
+ @Override
+ public String getResourcePath();
+
+ /** @return returns the selector string */
+ @Override
+ public String getSelectorString();
+
+ /** @return returns the selector array */
+ @Override
+ public String[] getSelectors();
+
+ /** @return returns the extension of the link */
+ @Override
+ public String getExtension();
+
+ /** @return returns the path parameters */
+ public Map<String, String> getPathParameters();
+
+ /** @return returns the suffix of the link */
+ @Override
+ public String getSuffix();
+
+ /** @return returns the query part of the link */
+ public String getQuery();
+
+ /** @return returns the url fragment of the link */
+ public String getFragment();
+
+ /** @return scheme specific part of the URL */
+ public String getSchemeSpecificPart();
+
+ /** @return returns the corresponding */
+ @Override
+ public Resource getSuffixResource();
+
+ /** @return returns true if the link is either a relative or absolute path (this is the case if scheme and host is empty and the URI
+ * path is set) */
+ default boolean isPath() {
+ return isBlank(getScheme())
+ && isBlank(getHost())
+ && isNotBlank(getResourcePath());
+ }
+
+ /** @return true if the link is a absolute path starting with a slash ('/'). This is the default case for all links to pages and assets
+ * in AEM. */
+ default boolean isAbsolutePath() {
+ return isPath() && getResourcePath().startsWith(ResourceUriBuilder.CHAR_SLASH);
+ }
+
+ /** @return true if link is relative (not an URL and not starting with '/') */
+ default boolean isRelativePath() {
+ return isPath() && !getResourcePath().startsWith(ResourceUriBuilder.CHAR_SLASH);
+ }
+
+ /** @return true if the link is an absolute URI containing a scheme. */
+ default boolean isFullUri() {
+ return isNotBlank(getScheme())
+ && isNotBlank(getHost());
+ }
+
+ /** @param builderConsumer
+ * @return the adjusted ResourceUri (new instance) */
+ default ResourceUri adjust(Consumer<ResourceUriBuilder> builderConsumer) {
+ ResourceUriBuilder builder = ResourceUriBuilder.createFrom(this);
+ builderConsumer.accept(builder);
+ return builder.build();
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/api/resource/uri/ResourceUriBuilder.java b/src/main/java/org/apache/sling/api/resource/uri/ResourceUriBuilder.java
new file mode 100644
index 0000000..bc9c4c6
--- /dev/null
+++ b/src/main/java/org/apache/sling/api/resource/uri/ResourceUriBuilder.java
@@ -0,0 +1,607 @@
+/*
+ * 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.sling.api.resource.uri;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.request.RequestPathInfo;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+
+public class ResourceUriBuilder {
+
+ static final String CHAR_HASH = "#";
+ static final String CHAR_QM = "?";
+ static final String CHAR_DOT = ".";
+ static final String CHAR_SLASH = "/";
+ static final String CHAR_AT = "@";
+ static final String SELECTOR_DOT_REGEX = "\\.(?!\\.?/)"; // (?!\\.?/) to avoid matching ./ and ../
+ static final String CHAR_COLON = ":";
+ static final String CHAR_SEMICOLON = ";";
+ static final String CHAR_EQUALS = "=";
+ static final String CHAR_SINGLEQUOTE = "'";
+
+ public static ResourceUriBuilder create() {
+ return new ResourceUriBuilder();
+ }
+
+ /** Creates a builder from another ResourceUri.
+ *
+ * @param resourceUri
+ * @return a ResourceUriBuilder */
+ public static ResourceUriBuilder createFrom(ResourceUri resourceUri) {
+ return create()
+ .setScheme(resourceUri.getScheme())
+ .setUserInfo(resourceUri.getUserInfo())
+ .setHost(resourceUri.getHost())
+ .setPort(resourceUri.getPort())
+ .setResourcePath(resourceUri.getResourcePath())
+ .setPathParameters(resourceUri.getPathParameters())
+ .setSelectors(resourceUri.getSelectors())
+ .setExtension(resourceUri.getExtension())
+ .setSuffix(resourceUri.getSuffix())
+ .setQuery(resourceUri.getQuery())
+ .setFragment(resourceUri.getFragment())
+ .setSchemeSpecificPart(resourceUri.getSchemeSpecificPart())
+ .setResourceResolver(resourceUri instanceof ImmutableResourceUri
+ ? ((ImmutableResourceUri) resourceUri).getBuilder().resourceResolver
+ : null);
+ }
+
+ /** Creates a builder from a Resource (only taking the resource path into account).
+ *
+ * @param resource
+ * @return a ResourceUriBuilder */
+ public static ResourceUriBuilder createFrom(Resource resource) {
+ return create()
+ .setResourcePath(resource.getPath())
+ .setResourceResolver(resource.getResourceResolver());
+ }
+
+ /** Creates a builder from a RequestPathInfo instance .
+ *
+ * @param requestPathInfo
+ * @return a ResourceUriBuilder */
+ public static ResourceUriBuilder createFrom(RequestPathInfo requestPathInfo) {
+ return create()
+ .setResourcePath(requestPathInfo.getResourcePath())
+ .setSelectors(requestPathInfo.getSelectors())
+ .setExtension(requestPathInfo.getExtension())
+ .setSuffix(requestPathInfo.getSuffix());
+ }
+
+ /** Creates a builder from a request.
+ *
+ * @param request
+ * @return a ResourceUriBuilder */
+ public static ResourceUriBuilder createFrom(SlingHttpServletRequest request) {
+ return createFrom(request.getRequestPathInfo())
+ .setResourceResolver(request.getResourceResolver())
+ .setScheme(request.getScheme())
+ .setHost(request.getServerName())
+ .setPort(request.getServerPort());
+ }
+
+ /** Creates a builder from a URI.
+ *
+ * @param uri
+ * @return a ResourceUriBuilder */
+ public static ResourceUriBuilder createFrom(URI uri, ResourceResolver resourceResolver) {
+ String path = uri.getPath();
+ boolean pathExists = !StringUtils.isBlank(path);
+ boolean schemeSpecificRelevant = !pathExists && uri.getQuery() == null;
+ return create()
+ .setResourceResolver(resourceResolver)
+ .setScheme(uri.getScheme())
+ .setUserInfo(uri.getUserInfo())
+ .setHost(uri.getHost())
+ .setPort(uri.getPort())
+ .setPath(pathExists ? path : null)
+ .setQuery(uri.getQuery())
+ .setFragment(uri.getFragment())
+ .setSchemeSpecificPart(schemeSpecificRelevant ? uri.getSchemeSpecificPart() : null);
+ }
+
+ /** Creates a builder from an arbitrary URI string.
+ *
+ * @param resourceUriStr
+ * @return a ResourceUriBuilder */
+ public static ResourceUriBuilder parse(String resourceUriStr, ResourceResolver resourceResolver) {
+ URI uri;
+ try {
+ uri = new URI(resourceUriStr);
+ return createFrom(uri, resourceResolver);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Invalid URI " + resourceUriStr + ": " + e.getMessage(), e);
+ }
+ }
+
+ /** Creates a builder from a resource path.
+ *
+ * @param resourcePathStr
+ * @return a ResourceUriBuilder */
+ public static ResourceUriBuilder forPath(String resourcePathStr) {
+ return new ResourceUriBuilder().setPath(resourcePathStr);
+ }
+
+ private String scheme = null;
+
+ private String userInfo = null;
+ private String host = null;
+ private int port = -1;
+
+ private String resourcePath = null;
+ private final List<String> selectors = new LinkedList<>();
+ private String extension = null;
+ private final Map<String, String> pathParameters = new LinkedHashMap<>();
+ private String suffix = null;
+ private String schemeSpecificPart = null;
+ private String query = null;
+ private String fragment = null;
+
+ // only needed for getSuffixResource() from interface RequestPathInfo
+ private ResourceResolver resourceResolver = null;
+
+ private ResourceUriBuilder() {
+ }
+
+ /** @param userInfo
+ * @return the builder for method chaining */
+ public ResourceUriBuilder setUserInfo(String userInfo) {
+ if (schemeSpecificPart != null) {
+ return this;
+ }
+ this.userInfo = userInfo;
+ return this;
+ }
+
+ /** @param host
+ * @return the builder for method chaining */
+ public ResourceUriBuilder setHost(String host) {
+ if (schemeSpecificPart != null) {
+ return this;
+ }
+ this.host = host;
+ return this;
+ }
+
+ /** @param port
+ * @return the builder for method chaining */
+ public ResourceUriBuilder setPort(int port) {
+ if (schemeSpecificPart != null) {
+ return this;
+ }
+ this.port = port;
+ return this;
+ }
+
+ /** @param path
+ * @return the builder for method chaining */
+ public ResourceUriBuilder setPath(String path) {
+ if (schemeSpecificPart != null) {
+ return this;
+ }
+
+ // path parameters
+ Map<String, String> currentPathParameters = null;
+ if (path != null) {
+ Pattern pathParameterRegex = Pattern.compile(";([a-zA-z0-9]+)=(?:\\'([^']*)\\'|([^/]+))");
+
+ StringBuffer resultString = null;
+ Matcher regexMatcher = pathParameterRegex.matcher(path);
+ while (regexMatcher.find()) {
+ if (resultString == null) {
+ resultString = new StringBuffer();
+ }
+ if (currentPathParameters == null) {
+ currentPathParameters = new LinkedHashMap<>();
+ }
+ regexMatcher.appendReplacement(resultString, "");
+ String key = regexMatcher.group(1);
+ String value = StringUtils.defaultIfEmpty(regexMatcher.group(2), regexMatcher.group(3));
+ currentPathParameters.put(key, value);
+ }
+ if (resultString != null) {
+ regexMatcher.appendTail(resultString);
+ path = resultString.toString();
+ pathParameters.putAll(currentPathParameters);
+ }
+ }
+
+ // regular RequestPathInfo
+ Matcher dotMatcher;
+ if (path != null && (dotMatcher = Pattern.compile(SELECTOR_DOT_REGEX).matcher(path)).find()) {
+ int firstDotPosition = dotMatcher.start();
+ int firstSlashAfterFirstDotPosition = path.indexOf(CHAR_SLASH, firstDotPosition);
+ String pathWithoutSuffix = firstSlashAfterFirstDotPosition > -1 ? path.substring(0, firstSlashAfterFirstDotPosition) : path;
+ String[] pathBits = pathWithoutSuffix.split(SELECTOR_DOT_REGEX);
+ setResourcePath(pathBits[0]);
+ if (pathBits.length > 2) {
+ setSelectors(Arrays.copyOfRange(pathBits, 1, pathBits.length - 1));
+ }
+ setExtension(pathBits.length > 1 ? pathBits[pathBits.length - 1] : null);
+ setSuffix(firstSlashAfterFirstDotPosition > -1 ? path.substring(firstSlashAfterFirstDotPosition) : null);
+ } else {
+ setResourcePath(path);
+ }
+
+ if (resourceResolver != null) {
+ balanceResourcePath();
+ }
+
+ return this;
+ }
+
+ public ResourceUriBuilder balanceResourcePath() {
+ if (schemeSpecificPart != null) {
+ return this;
+ }
+ if (resourceResolver == null) {
+ throw new IllegalStateException("setResourceResolver() needs to be called before balanceResourcePath()");
+ }
+ List<String> potentialResourcePathBits = new ArrayList<>();
+ potentialResourcePathBits.add(resourcePath);
+ potentialResourcePathBits.addAll(selectors);
+ if (extension != null) {
+ potentialResourcePathBits.add(extension);
+ }
+ String fullPathWithSuffix = String.join(".", potentialResourcePathBits) + suffix;
+ if (resourceResolver.getResource(fullPathWithSuffix) != null) {
+ this.resourcePath = fullPathWithSuffix;
+ selectors.clear();
+ extension = null;
+ suffix = null;
+ } else {
+ for (int i = potentialResourcePathBits.size(); i > 1; i--) {
+ String potentialResourcePath = String.join(".", potentialResourcePathBits.subList(0, i));
+ if (resourceResolver.getResource(potentialResourcePath) != null) {
+ this.resourcePath = potentialResourcePath;
+ selectors.clear();
+ extension = null;
+ List<String> remainingList = potentialResourcePathBits.subList(i, potentialResourcePathBits.size());
+ if (!remainingList.isEmpty()) {
+ extension = remainingList.get(remainingList.size() - 1);
+ selectors.addAll(remainingList.subList(0, remainingList.size() - 1));
+ }
+ break;
+ }
+ }
+ }
+ return this;
+ }
+
+ /** @param resourcePath
+ * @return the builder for method chaining */
+ public ResourceUriBuilder setResourcePath(String resourcePath) {
+ if (schemeSpecificPart != null) {
+ return this;
+ }
+ this.resourcePath = resourcePath;
+ return this;
+ }
+
+ /** @param selectors
+ * @return the builder for method chaining */
+ public ResourceUriBuilder setSelectors(String[] selectors) {
+ if (schemeSpecificPart != null || resourcePath == null) {
+ return this;
+ }
+ this.selectors.clear();
+ Arrays.stream(selectors).forEach(this.selectors::add);
+ return this;
+ }
+
+ /** @param selector
+ * @return the builder for method chaining */
+ public ResourceUriBuilder addSelector(String selector) {
+ if (schemeSpecificPart != null || resourcePath == null) {
+ return this;
+ }
+ this.selectors.add(selector);
+ return this;
+ }
+
+ /** @param extension
+ * @return the builder for method chaining */
+ public ResourceUriBuilder setExtension(String extension) {
+ if (schemeSpecificPart != null || resourcePath == null) {
+ return this;
+ }
+ this.extension = extension;
+ return this;
+ }
+
+ /** @return returns the path parameters */
+ public ResourceUriBuilder setPathParameter(String key, String value) {
+ if (schemeSpecificPart != null || resourcePath == null) {
+ return this;
+ }
+ this.pathParameters.put(key, value);
+ return this;
+ }
+
+ public ResourceUriBuilder setPathParameters(Map<String, String> pathParameters) {
+ this.pathParameters.clear();
+ this.pathParameters.putAll(pathParameters);
+ return this;
+ }
+
+ /** @param suffix
+ * @return the builder for method chaining */
+ public ResourceUriBuilder setSuffix(String suffix) {
+ if (schemeSpecificPart != null || resourcePath == null) {
+ return this;
+ }
+ if (suffix != null && !StringUtils.startsWith(suffix, "/")) {
+ throw new IllegalArgumentException("Suffix needs to start with slash");
+ }
+ this.suffix = suffix;
+ return this;
+ }
+
+ /** @param query
+ * @return the builder for method chaining */
+ public ResourceUriBuilder setQuery(String query) {
+ if (schemeSpecificPart != null) {
+ return this;
+ }
+ this.query = query;
+ return this;
+ }
+
+ /** @param urlFragment
+ * @return the builder for method chaining */
+ public ResourceUriBuilder setFragment(String urlFragment) {
+ if (schemeSpecificPart != null) {
+ return this;
+ }
+ this.fragment = urlFragment;
+ return this;
+ }
+
+ /** @param scheme
+ * @return the builder for method chaining */
+ public ResourceUriBuilder setScheme(String scheme) {
+ this.scheme = scheme;
+ return this;
+ }
+
+ /** @param schemeSpecificPart
+ * @return the builder for method chaining */
+ public ResourceUriBuilder setSchemeSpecificPart(String schemeSpecificPart) {
+ if (schemeSpecificPart != null && schemeSpecificPart.isEmpty()) {
+ return this;
+ }
+ this.schemeSpecificPart = schemeSpecificPart;
+ return this;
+ }
+
+ /** Will remove scheme and authority (that is user info, host and port).
+ *
+ * @return the builder for method chaining */
+ public ResourceUriBuilder removeSchemeAndAuthority() {
+ setScheme(null);
+ setUserInfo(null);
+ setHost(null);
+ setPort(-1);
+ return this;
+ }
+
+ /** Will take over scheme and authority (user info, host and port) from provided resourceUri.
+ *
+ * @param resourceUri
+ * @return the builder for method chaining */
+ public ResourceUriBuilder useSchemeAndAuthority(ResourceUri resourceUri) {
+ setScheme(resourceUri.getScheme());
+ setUserInfo(resourceUri.getUserInfo());
+ setHost(resourceUri.getHost());
+ setPort(resourceUri.getPort());
+ return this;
+ }
+
+ // only to support getSuffixResource() from interface RequestPathInfo
+ private ResourceUriBuilder setResourceResolver(ResourceResolver resourceResolver) {
+ this.resourceResolver = resourceResolver;
+ return this;
+ }
+
+ /** Will take over scheme and authority (user info, host and port) from provided uri.
+ *
+ * @param uri
+ * @return the builder for method chaining */
+ public ResourceUriBuilder useSchemeAndAuthority(URI uri) {
+ useSchemeAndAuthority(createFrom(uri, resourceResolver).build());
+ return this;
+ }
+
+ /** Builds the immutable ResourceUri from this builder.
+ *
+ * @return the builder for method chaining */
+ public ResourceUri build() {
+ return new ImmutableResourceUri();
+ }
+
+ /** @return string representation of builder */
+ public String toString() {
+ return build().toString();
+ }
+
+ // read-only view on the builder data (to avoid another copy of the data into a new object)
+ private class ImmutableResourceUri implements ResourceUri {
+
+ private static final String HTTPS_SCHEME = "https";
+ private static final int HTTPS_DEFAULT_PORT = 443;
+ private static final String HTTP_SCHEME = "http";
+ private static final int HTTP_DEFAULT_PORT = 80;
+
+ @Override
+ public String getResourcePath() {
+ return resourcePath;
+ }
+
+ // returns null in line with
+ // https://sling.apache.org/apidocs/sling11/org/apache/sling/api/request/RequestPathInfo.html#getSelectorString--
+ @Override
+ public String getSelectorString() {
+ return !selectors.isEmpty() ? String.join(CHAR_DOT, selectors) : null;
+ }
+
+ @Override
+ public String[] getSelectors() {
+ return selectors.toArray(new String[selectors.size()]);
+ }
+
+ @Override
+ public String getExtension() {
+ return extension;
+ }
+
+ @Override
+ public Map<String, String> getPathParameters() {
+ return pathParameters;
+ }
+
+ @Override
+ public String getSuffix() {
+ return suffix;
+ }
+
+ @Override
+ public String getSchemeSpecificPart() {
+ return schemeSpecificPart;
+ }
+
+ @Override
+ public String getQuery() {
+ return query;
+ }
+
+ @Override
+ public String getFragment() {
+ return fragment;
+ }
+
+ @Override
+ public String getScheme() {
+ return scheme;
+ }
+
+ @Override
+ public String getHost() {
+ return host;
+ }
+
+ @Override
+ public int getPort() {
+ return port;
+ }
+
+ @Override
+ public Resource getSuffixResource() {
+ if (StringUtils.isNotBlank(suffix) && resourceResolver != null) {
+ return resourceResolver.resolve(suffix);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public String getUserInfo() {
+ return userInfo;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder requestUri = new StringBuilder();
+
+ if (StringUtils.isNotBlank(scheme)) {
+ requestUri.append(scheme + CHAR_COLON);
+ }
+ if (isFullUri()) {
+ requestUri.append(CHAR_SLASH + CHAR_SLASH);
+ if (StringUtils.isNotBlank(userInfo)) {
+ requestUri.append(userInfo + CHAR_AT);
+ }
+ requestUri.append(host);
+ if (port > 0
+ && !(scheme.equals(HTTP_SCHEME) && port == HTTP_DEFAULT_PORT)
+ && !(scheme.equals(HTTPS_SCHEME) && port == HTTPS_DEFAULT_PORT)) {
+ requestUri.append(CHAR_COLON + port);
+ }
+ }
+ if (resourcePath != null) {
+ requestUri.append(resourcePath);
+ }
+ if (!pathParameters.isEmpty()) {
+ for (Map.Entry<String, String> pathParameter : pathParameters.entrySet()) {
+ requestUri.append(CHAR_SEMICOLON + pathParameter.getKey() + CHAR_EQUALS +
+ CHAR_SINGLEQUOTE + pathParameter.getValue() + CHAR_SINGLEQUOTE);
+ }
+ }
+
+ if (!selectors.isEmpty()) {
+ requestUri.append(CHAR_DOT + String.join(CHAR_DOT, selectors));
+ }
+ if (!StringUtils.isBlank(extension)) {
+ requestUri.append(CHAR_DOT + extension);
+ }
+
+ if (!StringUtils.isBlank(suffix)) {
+ requestUri.append(suffix);
+ }
+ if (schemeSpecificPart != null) {
+ requestUri.append(schemeSpecificPart);
+ }
+ if (query != null) {
+ requestUri.append(CHAR_QM + query);
+ }
+ if (fragment != null) {
+ requestUri.append(CHAR_HASH + fragment);
+ }
+ return requestUri.toString();
+ }
+
+ @Override
+ public URI toUri() {
+ String uriString = toString();
+ try {
+ return new URI(uriString);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Invalid Sling URI: " + uriString, e);
+ }
+ }
+
+ private ResourceUriBuilder getBuilder() {
+ return ResourceUriBuilder.this;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/api/resource/package-info.java b/src/main/java/org/apache/sling/api/resource/uri/package-info.java
similarity index 93%
copy from src/main/java/org/apache/sling/api/resource/package-info.java
copy to src/main/java/org/apache/sling/api/resource/uri/package-info.java
index 7bd85e6..dd21e49 100644
--- a/src/main/java/org/apache/sling/api/resource/package-info.java
+++ b/src/main/java/org/apache/sling/api/resource/uri/package-info.java
@@ -16,9 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
-
-@Version("2.12.2")
-package org.apache.sling.api.resource;
+@Version("1.0.0")
+package org.apache.sling.api.resource.uri;
import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/spi/resource/mapping/MappingChainContext.java b/src/main/java/org/apache/sling/spi/resource/mapping/MappingChainContext.java
new file mode 100644
index 0000000..cf6d541
--- /dev/null
+++ b/src/main/java/org/apache/sling/spi/resource/mapping/MappingChainContext.java
@@ -0,0 +1,51 @@
+/*
+ * 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.sling.spi.resource.mapping;
+
+import java.util.Map;
+
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.mapping.PathToUriMappingService.ContextHint;
+import org.apache.sling.api.resource.uri.ResourceUri;
+import org.osgi.annotation.versioning.ProviderType;
+
+/** Provides ResourceToUriMapper instances with additional context. */
+@ProviderType
+public interface MappingChainContext {
+
+ /** May be called by any ResourceUriMapper in the chain to indicate that the rest of the chain should be skipped. */
+ void skipRemainingChain();
+
+ /** Add @{link ContextHint} (e.g. 'invalid link' for map(), or 'requires authentication' for resolve()) */
+ void addContextHint(ContextHint contextHint);
+
+ /** The resource resolver that was used to call map() or resolve(). */
+ ResourceResolver getResourceResolver();
+
+ /** Allows to share state between ResourceToUriMapper instances in the chain.
+ *
+ * @return a mutable map to share state (never null). */
+ Map<String, Object> getAttributes();
+
+ /** Provides access to intermediate mappings.
+ *
+ * @return the resource mappings */
+ Map<String, ResourceUri> getIntermediateMappings();
+
+}
diff --git a/src/main/java/org/apache/sling/spi/resource/mapping/ResourceUriMapper.java b/src/main/java/org/apache/sling/spi/resource/mapping/ResourceUriMapper.java
new file mode 100644
index 0000000..d3fed13
--- /dev/null
+++ b/src/main/java/org/apache/sling/spi/resource/mapping/ResourceUriMapper.java
@@ -0,0 +1,56 @@
+/*
+ * 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.sling.spi.resource.mapping;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.resource.uri.ResourceUri;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.annotation.versioning.ConsumerType;
+
+/** SPI interface that contributes to resource mapping and resolving of the resource resolver's map() and resolve() methods.
+ *
+ * All registered services build a conceptual chain sorted by service ranking. The resource URI is passed through the chain while any
+ * ResourceUriMapper chain member may or may not make adjustments to the resource URI.
+ *
+ * rr.resolve() passes through the chain starting at the ResourceUriMapper with the <strong>highest</strong> service ranking and rr.map()
+ * passes through the chain starting at the ResourceUriMapper with the <strong>lowest</strong> service ranking */
+@ConsumerType
+public interface ResourceUriMapper {
+
+ /** Contributes to the map process, may or may not make adjustments to the resource URI.
+ *
+ * @param resourceUri the URI to be mapped
+ * @param request the request to be taken as example
+ * @param context can be used to skip further processing of the chain or for sharing state between instances of ResourceUriMapper
+ * services
+ * @return the adjusted ResourceUri or if no adjustments are necessary, just return resourceUri as passed in by first parameter */
+ ResourceUri map(@NotNull ResourceUri resourceUri, @Nullable HttpServletRequest request, @NotNull MappingChainContext context);
+
+ /** Contributes to the resolve process, may or may not make adjustments to the resource URI
+ *
+ * @param resourceUri the URI to be resolved
+ * @param request the request context that may or may not influence the resolution process (request may be null)
+ * @param context can be used to skip further processing of the chain or for sharing state between instances of ResourceUriMapper
+ * services
+ * @return the adjusted ResourceUri or if no adjustments are necessary, just return resourceUri as passed in by first parameter */
+ ResourceUri resolve(@NotNull ResourceUri resourceUri, @Nullable HttpServletRequest request, @NotNull MappingChainContext context);
+
+}
diff --git a/src/main/java/org/apache/sling/api/resource/mapping/package-info.java b/src/main/java/org/apache/sling/spi/resource/mapping/package-info.java
similarity index 92%
copy from src/main/java/org/apache/sling/api/resource/mapping/package-info.java
copy to src/main/java/org/apache/sling/spi/resource/mapping/package-info.java
index e871225..c8c07af 100644
--- a/src/main/java/org/apache/sling/api/resource/mapping/package-info.java
+++ b/src/main/java/org/apache/sling/spi/resource/mapping/package-info.java
@@ -17,8 +17,8 @@
* under the License.
*/
-@Version("1.0.1")
-package org.apache.sling.api.resource.mapping;
+@Version("1.0.0")
+package org.apache.sling.spi.resource.mapping;
import org.osgi.annotation.versioning.Version;
diff --git a/src/test/java/org/apache/sling/api/resource/uri/ResourceUriTest.java b/src/test/java/org/apache/sling/api/resource/uri/ResourceUriTest.java
new file mode 100644
index 0000000..3711631
--- /dev/null
+++ b/src/test/java/org/apache/sling/api/resource/uri/ResourceUriTest.java
@@ -0,0 +1,679 @@
+/*
+ * 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.sling.api.resource.uri;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import java.util.function.Consumer;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.Silent.class)
+public class ResourceUriTest {
+
+ @Mock
+ ResourceResolver resourceResolver;
+
+ @Mock
+ Resource resource;
+
+ @Test
+ public void testFullResourceUri() {
+
+ String testUriStr = "http://host.com/test/to/path.html";
+ testUri(testUriStr, false, false, false, true, resourceUri -> {
+ assertEquals("http", resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals("host.com", resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals("/test/to/path", resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals("html", resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals(null, resourceUri.getQuery());
+ assertEquals(null, resourceUri.getFragment());
+ });
+
+ }
+
+ @Test
+ public void testFullResourceUriComplex() {
+
+ String testUriStr = "https://test:pw@host.com:888/test/to/path.sel1.json/suffix/path?p1=2&p2=3#frag3939";
+ testUri(testUriStr, false, false, false, true, resourceUri -> {
+ assertEquals("https", resourceUri.getScheme());
+ assertEquals("test:pw", resourceUri.getUserInfo());
+ assertEquals("host.com", resourceUri.getHost());
+ assertEquals(888, resourceUri.getPort());
+ assertEquals("/test/to/path", resourceUri.getResourcePath());
+ assertEquals("sel1", resourceUri.getSelectorString());
+ assertEquals("json", resourceUri.getExtension());
+ assertEquals("/suffix/path", resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals("p1=2&p2=3", resourceUri.getQuery());
+ assertEquals("frag3939", resourceUri.getFragment());
+ });
+
+ }
+
+ @Test
+ public void testAbsolutePathResourceUri() {
+ String testUriStr = "/test/to/path.sel1.json/suffix/path?p1=2&p2=3#frag3939";
+
+ testUri(testUriStr, true, true, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals("/test/to/path", resourceUri.getResourcePath());
+ assertEquals("sel1", resourceUri.getSelectorString());
+ assertEquals("json", resourceUri.getExtension());
+ assertEquals("/suffix/path", resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals("p1=2&p2=3", resourceUri.getQuery());
+ assertEquals("frag3939", resourceUri.getFragment());
+ });
+ }
+
+ @Test
+ public void testResourceUriSuffixWithDots() {
+
+ String testUriStr = "/test/to/path.min.js/suffix/app.nodesbrowser.js";
+ testUri(testUriStr, true, true, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals("/test/to/path", resourceUri.getResourcePath());
+ assertEquals("min", resourceUri.getSelectorString());
+ assertEquals("js", resourceUri.getExtension());
+ assertEquals("/suffix/app.nodesbrowser.js", resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals(null, resourceUri.getQuery());
+ assertEquals(null, resourceUri.getFragment());
+ });
+ }
+
+ @Test
+ public void testResourceUriMultipleDots() {
+
+ String testUriStr = "/test/to/path.sel1.sel2..sel4.js";
+ testUri(testUriStr, true, true, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals("/test/to/path", resourceUri.getResourcePath());
+ assertEquals(4, resourceUri.getSelectors().length);
+ assertEquals("sel1.sel2..sel4", resourceUri.getSelectorString());
+ assertEquals("js", resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals(null, resourceUri.getQuery());
+ assertEquals(null, resourceUri.getFragment());
+ });
+
+ String testUriStr2 = "/test/to/path.sel1.sel2../sel4.js";
+ testUri(testUriStr2, true, true, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals("/test/to/path", resourceUri.getResourcePath());
+ assertEquals(1, resourceUri.getSelectors().length);
+ assertEquals("sel1", resourceUri.getSelectorString());
+ assertEquals("sel2", resourceUri.getExtension());
+ assertEquals("/sel4.js", resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals(null, resourceUri.getQuery());
+ assertEquals(null, resourceUri.getFragment());
+ }, true);
+ }
+
+ @Test
+ public void testRelativePathResourceUri() {
+ String testUriStr = "../path.html#frag1";
+
+ testUri(testUriStr, true, false, true, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals("../path", resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals("html", resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals(null, resourceUri.getQuery());
+ assertEquals("frag1", resourceUri.getFragment());
+ });
+ }
+
+ @Test
+ public void testRelativePathResourceUriComplex() {
+ String testUriStr = "../path/./deep/path/../path.sel1.sel2.html?test=1#frag1";
+
+ testUri(testUriStr, true, false, true, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals("../path/./deep/path/../path", resourceUri.getResourcePath());
+ assertEquals("sel1.sel2", resourceUri.getSelectorString());
+ assertEquals("html", resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals("test=1", resourceUri.getQuery());
+ assertEquals("frag1", resourceUri.getFragment());
+ });
+ }
+
+ @Test
+ public void testAbsolutePathWithPathParameter() {
+ String testUriStr = "/test/to/path;v='1.0'.sel1.html/suffix/path?p1=2&p2=3#frag3939";
+
+ testUri(testUriStr, true, true, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals("/test/to/path", resourceUri.getResourcePath());
+ assertEquals("sel1", resourceUri.getSelectorString());
+ assertEquals("html", resourceUri.getExtension());
+ assertEquals(1, resourceUri.getPathParameters().size());
+ assertEquals("1.0", resourceUri.getPathParameters().get("v"));
+ assertEquals("/suffix/path", resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals("p1=2&p2=3", resourceUri.getQuery());
+ assertEquals("frag3939", resourceUri.getFragment());
+ });
+
+ String testUriStr2 = "/test/to/file;foo='bar'.sel1.sel2.json/suffix/path";
+ testUri(testUriStr2, true, true, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals("/test/to/file", resourceUri.getResourcePath());
+ assertEquals("sel1.sel2", resourceUri.getSelectorString());
+ assertEquals("json", resourceUri.getExtension());
+ assertEquals(1, resourceUri.getPathParameters().size());
+ assertEquals("bar", resourceUri.getPathParameters().get("foo"));
+ assertEquals("/suffix/path", resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals(null, resourceUri.getQuery());
+ assertEquals(null, resourceUri.getFragment());
+ });
+ }
+
+ @Test
+ public void testAbsolutePathWithPathParameterMultiple() {
+ String testUriStr = "/test/to/path;v='1.0';antotherParam='test/nested';antotherParam2='7'.sel1.html/suffix/path?p1=2&p2=3#frag3939";
+
+ testUri(testUriStr, true, true, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals("/test/to/path", resourceUri.getResourcePath());
+ assertEquals("sel1", resourceUri.getSelectorString());
+ assertEquals("html", resourceUri.getExtension());
+
+ assertEquals(3, resourceUri.getPathParameters().size());
+ assertEquals("1.0", resourceUri.getPathParameters().get("v"));
+ assertEquals("test/nested", resourceUri.getPathParameters().get("antotherParam"));
+ assertEquals("7", resourceUri.getPathParameters().get("antotherParam2"));
+
+ assertEquals("/suffix/path", resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals("p1=2&p2=3", resourceUri.getQuery());
+ assertEquals("frag3939", resourceUri.getFragment());
+ });
+ }
+
+ @Test
+ public void testAbsolutePathWithPathParameterAfterExtension() {
+ String testUriStr = "/test/to/path.sel1.html;v='1.0'/suffix/path?p1=2&p2=3#frag3939";
+
+ ResourceUri testUri = testUri(testUriStr, true, true, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals("/test/to/path", resourceUri.getResourcePath());
+ assertEquals("sel1", resourceUri.getSelectorString());
+ assertEquals("html", resourceUri.getExtension());
+ assertEquals(1, resourceUri.getPathParameters().size());
+ assertEquals("1.0", resourceUri.getPathParameters().get("v"));
+ assertEquals("/suffix/path", resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals("p1=2&p2=3", resourceUri.getQuery());
+ assertEquals("frag3939", resourceUri.getFragment());
+ }, true /* URL is restructured (parameter moved to end), assertion below */);
+
+ assertEquals("/test/to/path;v='1.0'.sel1.html/suffix/path?p1=2&p2=3#frag3939", testUri.toString());
+
+ }
+
+ @Test
+ public void testJavascriptUri() {
+ String testUriStr = "javascript:void(0)";
+
+ testUri(testUriStr, false, false, false, false, resourceUri -> {
+ assertEquals("javascript", resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(null, resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ assertEquals("void(0)", resourceUri.getSchemeSpecificPart());
+ assertEquals(null, resourceUri.getQuery());
+ assertEquals(null, resourceUri.getFragment());
+ });
+ }
+
+ @Test
+ public void testMailtotUri() {
+ String testUriStr = "mailto:jon.doe@example.com";
+
+ testUri(testUriStr, false, false, false, false, resourceUri -> {
+ assertEquals("mailto", resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(null, resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ assertEquals("jon.doe@example.com", resourceUri.getSchemeSpecificPart());
+ assertEquals(null, resourceUri.getQuery());
+ assertEquals(null, resourceUri.getFragment());
+ });
+ }
+
+ @Test
+ public void testHashOnlyUri() {
+
+ testUri("#", false, false, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals(null, resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(null, resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals(null, resourceUri.getQuery());
+ assertEquals("", resourceUri.getFragment());
+ });
+
+ testUri("#fragment", false, false, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals(null, resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(null, resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals(null, resourceUri.getQuery());
+ assertEquals("fragment", resourceUri.getFragment());
+ });
+ }
+
+ @Test
+ public void testQueryOnlyUri() {
+
+ testUri("?", false, false, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals(null, resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(null, resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals("", resourceUri.getQuery());
+ assertEquals(null, resourceUri.getFragment());
+ });
+
+ testUri("?test=test", false, false, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ assertEquals(-1, resourceUri.getPort());
+ assertEquals(null, resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(null, resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ assertEquals(null, resourceUri.getSchemeSpecificPart());
+ assertEquals("test=test", resourceUri.getQuery());
+ assertEquals(null, resourceUri.getFragment());
+ });
+ }
+
+ @Test
+ public void testBalanceResourcePathSimpleCases() {
+ // simple case
+ String testUriStrSimple = "/test/to/file";
+ when(resourceResolver.getResource("/test/to/file")).thenReturn(resource);
+ testUri(testUriStrSimple, true, true, false, false, resourceUri -> {
+ assertEquals("/test/to/file", resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(null, resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ });
+
+ // simple file case
+ String testUriStrSimpleFile = "/test/to/file.css";
+ when(resourceResolver.getResource("/test/to/file.css")).thenReturn(resource);
+ when(resourceResolver.getResource("/test/to/file")).thenReturn(null);
+ testUri(testUriStrSimpleFile, true, true, false, false, resourceUri -> {
+ assertEquals("/test/to/file.css", resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(null, resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ });
+
+ // simple html rendering case
+ String testUriStrSimplePage = "/path/to/page.html";
+ when(resourceResolver.getResource("/path/to/page.html")).thenReturn(null);
+ when(resourceResolver.getResource("/path/to/page")).thenReturn(resource);
+ testUri(testUriStrSimplePage, true, true, false, false, resourceUri -> {
+ assertEquals("/path/to/page", resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals("html", resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ });
+ }
+
+ @Test
+ public void testBalanceResourcePathWithSelectorsAndExtension() {
+
+ String testUriStr = "/test/to/file.ext.sel1.json/suffix/path.js";
+
+ // pull path with suffix is resource path
+ when(resourceResolver.getResource("/test/to/file.ext.sel1.json/suffix/path.js")).thenReturn(resource);
+ when(resourceResolver.getResource("/test/to/file.ext.sel1.json")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext.sel1")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file")).thenReturn(null);
+ testUri(testUriStr, true, true, false, false, resourceUri -> {
+ assertEquals("/test/to/file.ext.sel1.json/suffix/path.js", resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(null, resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ });
+
+ // full path without suffix is resource path
+ when(resourceResolver.getResource("/test/to/file.ext.sel1.json/suffix/path.js")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext.sel1.json")).thenReturn(resource);
+ when(resourceResolver.getResource("/test/to/file.ext.sel1")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file")).thenReturn(null);
+ testUri(testUriStr, true, true, false, false, resourceUri -> {
+ assertEquals("/test/to/file.ext.sel1.json", resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(null, resourceUri.getExtension());
+ assertEquals("/suffix/path.js", resourceUri.getSuffix());
+ });
+
+ // mix of extension and resource path with dots
+ when(resourceResolver.getResource("/test/to/file.ext.sel1.json/suffix/path.js")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext.sel1.json")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext.sel1")).thenReturn(resource);
+ when(resourceResolver.getResource("/test/to/file.ext")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file")).thenReturn(null);
+ testUri(testUriStr, true, true, false, false, resourceUri -> {
+ assertEquals("/test/to/file.ext.sel1", resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals("json", resourceUri.getExtension());
+ assertEquals("/suffix/path.js", resourceUri.getSuffix());
+ });
+
+ when(resourceResolver.getResource("/test/to/file.ext.sel1.json/suffix/path.js")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext.sel1.json")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext.sel1")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext")).thenReturn(resource);
+ when(resourceResolver.getResource("/test/to/file")).thenReturn(null);
+ testUri(testUriStr, true, true, false, false, resourceUri -> {
+ assertEquals("/test/to/file.ext", resourceUri.getResourcePath());
+ assertEquals("sel1", resourceUri.getSelectorString());
+ assertEquals("json", resourceUri.getExtension());
+ assertEquals("/suffix/path.js", resourceUri.getSuffix());
+ });
+
+ // usual case: resource path does not contain dot
+ when(resourceResolver.getResource("/test/to/file.ext.sel1.json/suffix/path.js")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext.sel1.json")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext.sel1")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file")).thenReturn(resource);
+ testUri(testUriStr, true, true, false, false, resourceUri -> {
+ assertEquals("/test/to/file", resourceUri.getResourcePath());
+ assertEquals("ext.sel1", resourceUri.getSelectorString());
+ assertEquals("json", resourceUri.getExtension());
+ assertEquals("/suffix/path.js", resourceUri.getSuffix());
+ });
+
+ // side by side resources in same folder: the longest path wins
+ when(resourceResolver.getResource("/test/to/file.ext.sel1.json/suffix/path.js")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext.sel1.json")).thenReturn(null);
+ when(resourceResolver.getResource("/test/to/file.ext.sel1")).thenReturn(resource);
+ when(resourceResolver.getResource("/test/to/file.ext")).thenReturn(resource);
+ when(resourceResolver.getResource("/test/to/file")).thenReturn(resource);
+ testUri(testUriStr, true, true, false, false, resourceUri -> {
+ assertEquals("/test/to/file.ext.sel1", resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals("json", resourceUri.getExtension());
+ assertEquals("/suffix/path.js", resourceUri.getSuffix());
+ });
+ }
+
+ @Test
+ public void testUnusualQueryFragmentCombinations() {
+ testUri("?#", false, false, false, false, resourceUri -> {
+ assertEquals("", resourceUri.getQuery());
+ assertEquals("", resourceUri.getFragment());
+ });
+ testUri("?t=2#", false, false, false, false, resourceUri -> {
+ assertEquals("t=2", resourceUri.getQuery());
+ assertEquals("", resourceUri.getFragment());
+ });
+ testUri("?#t=3", false, false, false, false, resourceUri -> {
+ assertEquals("", resourceUri.getQuery());
+ assertEquals("t=3", resourceUri.getFragment());
+ });
+ testUri("", false, false, false, false, resourceUri -> {
+ assertEquals(null, resourceUri.getQuery());
+ assertEquals(null, resourceUri.getFragment());
+ });
+ }
+
+ // -- adjustment test cases
+
+ @Test
+ public void testAdjustAddSelectorFullUrl() {
+
+ testAdjustUri(
+ "http://host.com/test/to/path.html",
+ resourceUriBuilder -> {
+ resourceUriBuilder.addSelector("test");
+ },
+ "http://host.com/test/to/path.test.html",
+ resourceUri -> {
+ assertEquals("test", resourceUri.getSelectorString());
+ });
+ }
+
+ @Test
+ public void testAdjustAddSelectorAndSuffixPath() {
+
+ testAdjustUri(
+ "/test/to/path.html",
+ resourceUriBuilder -> {
+ resourceUriBuilder.addSelector("test");
+ resourceUriBuilder.setSuffix("/suffix/path/to/file");
+ },
+ "/test/to/path.test.html/suffix/path/to/file",
+ resourceUri -> {
+ assertArrayEquals(new String[] { "test" }, resourceUri.getSelectors());
+ assertEquals("/suffix/path/to/file", resourceUri.getSuffix());
+ });
+ }
+
+ @Test
+ public void testExtendSimplePathToFullUrl() {
+
+ testAdjustUri(
+ "/test/to/path.html",
+ resourceUriBuilder -> {
+ resourceUriBuilder.setScheme("https");
+ resourceUriBuilder.setHost("example.com");
+ resourceUriBuilder.setSuffix("/suffix/path/to/file");
+ },
+ "https://example.com/test/to/path.html/suffix/path/to/file",
+ resourceUri -> {
+ assertEquals("https", resourceUri.getScheme());
+ assertEquals("example.com", resourceUri.getHost());
+ assertEquals("/suffix/path/to/file", resourceUri.getSuffix());
+ });
+ }
+
+ @Test
+ public void testFullUrltoSimplePath() {
+
+ testAdjustUri(
+ "https://user:pw@example.com/test/to/path.html/suffix/path/to/file",
+ resourceUriBuilder -> {
+ resourceUriBuilder.removeSchemeAndAuthority();
+ },
+ "/test/to/path.html/suffix/path/to/file",
+ resourceUri -> {
+ assertEquals(null, resourceUri.getScheme());
+ assertEquals(null, resourceUri.getUserInfo());
+ assertEquals(null, resourceUri.getHost());
+ });
+ }
+
+ @Test
+ public void testAdjustPathInSpecialUriWithoutEffect() {
+
+ testAdjustUri(
+ "mailto:jon.doe@example.com",
+ resourceUriBuilder -> {
+ resourceUriBuilder.setPath("/path/to/resource");
+ resourceUriBuilder.setResourcePath("/path/to/resource");
+ resourceUriBuilder.addSelector("test");
+ resourceUriBuilder.setExtension("html");
+ resourceUriBuilder.setSuffix("/suffix");
+ },
+ "mailto:jon.doe@example.com",
+ resourceUri -> {
+ assertEquals(null, resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(null, resourceUri.getExtension());
+ assertEquals(null, resourceUri.getSuffix());
+ });
+ }
+
+ @Test
+ public void testAdjustSelectorsInFragmentOnlyUrlWithoutEffect() {
+
+ testAdjustUri(
+ "#fragment",
+ resourceUriBuilder -> {
+ resourceUriBuilder.addSelector("test");
+ resourceUriBuilder.setSuffix("/suffix");
+ },
+ "#fragment",
+ resourceUri -> {
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(null, resourceUri.getSuffix());
+ });
+ }
+
+ @Test
+ public void testAjustFtpUrl() {
+
+ testAdjustUri(
+ "sftp://user:pw@example.com:9090/some/path",
+ resourceUriBuilder -> {
+ resourceUriBuilder.setPath("/some/other/path");
+ resourceUriBuilder.setPort(9091);
+ },
+ "sftp://user:pw@example.com:9091/some/other/path",
+ resourceUri -> {
+ assertEquals("/some/other/path", resourceUri.getResourcePath());
+ assertEquals(null, resourceUri.getSelectorString());
+ assertEquals(9091, resourceUri.getPort());
+ });
+ }
+
+ // -- helper methods
+
+
+ public ResourceUri testUri(String testUri, boolean isPath, boolean isAbsolutePath, boolean isRelativePath, boolean isFullUri,
+ Consumer<ResourceUri> additionalAssertions) {
+ return testUri(testUri, isPath, isAbsolutePath, isRelativePath, isFullUri, additionalAssertions, false);
+ }
+
+ public ResourceUri testUri(String testUri, boolean isPath, boolean isAbsolutePath, boolean isRelativePath, boolean isFullUri,
+ Consumer<ResourceUri> additionalAssertions, boolean urlIsRestructured) {
+ ResourceUri resourceUri = ResourceUriBuilder.parse(testUri, resourceResolver).build();
+
+ if (!urlIsRestructured) {
+ assertEquals(testUri, resourceUri.toString());
+ assertEquals(testUri, resourceUri.toUri().toString());
+ }
+
+ assertEquals("isPath()", isPath, resourceUri.isPath());
+ assertEquals("isAbsolutePath()", isAbsolutePath, resourceUri.isAbsolutePath());
+ assertEquals("isRelativePath()", isRelativePath, resourceUri.isRelativePath());
+ assertEquals("isFullUri()", isFullUri, resourceUri.isFullUri());
+
+ additionalAssertions.accept(resourceUri);
+
+ return resourceUri;
+ }
+
+ public void testAdjustUri(String testUri, Consumer<ResourceUriBuilder> adjuster, String testUriAfterEdit,
+ Consumer<ResourceUri> additionalAssertions) {
+ ResourceUri resourceUri = ResourceUriBuilder.parse(testUri, resourceResolver).build();
+
+ ResourceUri adjustedResourceUri = resourceUri.adjust(adjuster);
+
+ assertEquals(testUriAfterEdit, adjustedResourceUri.toString());
+ assertEquals(testUriAfterEdit, adjustedResourceUri.toUri().toString());
+
+ additionalAssertions.accept(adjustedResourceUri);
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/api/resource/uri/ResourceUriToSlingRequestPathInfoCompatibilityTest.java b/src/test/java/org/apache/sling/api/resource/uri/ResourceUriToSlingRequestPathInfoCompatibilityTest.java
new file mode 100644
index 0000000..b452be4
--- /dev/null
+++ b/src/test/java/org/apache/sling/api/resource/uri/ResourceUriToSlingRequestPathInfoCompatibilityTest.java
@@ -0,0 +1,271 @@
+/*
+ * 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.sling.api.resource.uri;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.when;
+
+import org.apache.sling.api.request.RequestPathInfo;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ResourceUriToSlingRequestPathInfoCompatibilityTest {
+
+ @Mock
+ ResourceResolver resourceResolver;
+
+ @Mock
+ Resource resource;
+
+ private RequestPathInfo createResourceUri(String resolutionPath, String resolutionPathInfo) {
+ when(resourceResolver.getResource(resolutionPath)).thenReturn(resource);
+ return ResourceUriBuilder.parse(resolutionPath + (resolutionPathInfo != null ? resolutionPathInfo : ""), resourceResolver).build();
+ }
+
+ @Test
+ public void testTrailingDot() {
+ RequestPathInfo p = createResourceUri("/some/path", ".");
+ assertEquals("/some/path", p.getResourcePath());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals(0, p.getSelectors().length);
+ assertNull("Extension is null", p.getExtension());
+ assertNull("Suffix is null", p.getSuffix());
+ }
+
+ @Test
+ @Ignore
+ public void testTrailingDotWithSuffix() {
+ RequestPathInfo p = createResourceUri("/some/path", "./suffix");
+ assertEquals("/some/path", p.getResourcePath());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals(0, p.getSelectors().length);
+ assertNull("Extension is null", p.getExtension());
+ assertEquals("/suffix", p.getSuffix());
+ }
+
+ @Test
+ public void testTrailingDotDot() {
+ RequestPathInfo p = createResourceUri("/some/path", "..");
+ assertEquals("/some/path", p.getResourcePath());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals(0, p.getSelectors().length);
+ assertNull("Extension is null", p.getExtension());
+ assertNull("Suffix is null", p.getSuffix());
+ }
+
+ @Test
+ @Ignore
+ public void testTrailingDotDotWithSuffix() {
+ RequestPathInfo p = createResourceUri("/some/path", "../suffix");
+ assertEquals("/some/path", p.getResourcePath());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals(0, p.getSelectors().length);
+ assertNull("Extension is null", p.getExtension());
+ assertEquals("/suffix", p.getSuffix());
+ }
+
+ @Test
+ public void testTrailingDotDotDot() {
+ RequestPathInfo p = createResourceUri("/some/path", "...");
+ assertEquals("/some/path", p.getResourcePath());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals(0, p.getSelectors().length);
+ assertNull("Extension is null", p.getExtension());
+ assertNull("Suffix is null", p.getSuffix());
+ }
+
+ @Test
+ public void testTrailingDotDotDotWithSuffix() {
+ RequestPathInfo p = createResourceUri("/some/path", ".../suffix");
+ assertEquals("/some/path", p.getResourcePath());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals(0, p.getSelectors().length);
+ assertNull("Extension is null", p.getExtension());
+ assertEquals("/suffix", p.getSuffix());
+ }
+
+ @Test
+ public void testAllOptions() {
+ RequestPathInfo p = createResourceUri("/some/path", ".print.a4.html/some/suffix");
+ assertEquals("/some/path", p.getResourcePath());
+ assertEquals("print.a4", p.getSelectorString());
+ assertEquals(2, p.getSelectors().length);
+ assertEquals("print", p.getSelectors()[0]);
+ assertEquals("a4", p.getSelectors()[1]);
+ assertEquals("html", p.getExtension());
+ assertEquals("/some/suffix", p.getSuffix());
+ }
+
+ @Test
+ public void testAllEmpty() {
+ RequestPathInfo p = createResourceUri("/", null);
+ assertEquals("/", p.getResourcePath());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals(0, p.getSelectors().length);
+ assertNull("Extension is null", p.getExtension());
+ assertNull("Suffix is null", p.getSuffix());
+ }
+
+ @Test
+ public void testPathOnly() {
+ RequestPathInfo p = createResourceUri("/some/path/here", "");
+ assertEquals("/some/path/here", p.getResourcePath());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals(0, p.getSelectors().length);
+ assertNull("Extension is null", p.getExtension());
+ assertNull("Suffix is null", p.getSuffix());
+ }
+
+ @Test
+ public void testPathWithExtensionOnly() {
+ RequestPathInfo p = createResourceUri("/some/path/here.html", "");
+ assertEquals("/some/path/here.html", p.getResourcePath());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals(0, p.getSelectors().length);
+ assertNull("Extension is null", p.getExtension());
+ assertNull("Suffix is null", p.getSuffix());
+ }
+
+ @Test
+ public void testPathAndExtensionOnly() {
+ RequestPathInfo p = createResourceUri("/some/path/here", ".html");
+ assertEquals("/some/path/here", p.getResourcePath());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals(0, p.getSelectors().length);
+ assertEquals("html", p.getExtension());
+ assertNull("Suffix is null", p.getSuffix());
+ }
+
+ @Test
+ public void testPathAndOneSelectorOnly() {
+ RequestPathInfo p = createResourceUri("/some/path/here", ".print.html");
+ assertEquals("/some/path/here", p.getResourcePath());
+ assertEquals("print", p.getSelectorString());
+ assertEquals(1, p.getSelectors().length);
+ assertEquals("print", p.getSelectors()[0]);
+ assertEquals("html", p.getExtension());
+ assertNull("Suffix is null", p.getSuffix());
+ }
+
+ @Test
+ public void testPathExtAndSuffix() {
+ RequestPathInfo p = createResourceUri("/some/path/here", ".html/something");
+ assertEquals("/some/path/here", p.getResourcePath());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals(0, p.getSelectors().length);
+ assertEquals("html", p.getExtension());
+ assertEquals("/something", p.getSuffix());
+ }
+
+ @Test
+ public void testSelectorsSplit() {
+ RequestPathInfo p = createResourceUri("/some/path", ".print.a4.html/some/suffix");
+ assertEquals("/some/path", p.getResourcePath());
+ assertEquals(2, p.getSelectors().length);
+ assertEquals("print", p.getSelectors()[0]);
+ assertEquals("a4", p.getSelectors()[1]);
+ assertEquals("html", p.getExtension());
+ assertEquals("/some/suffix", p.getSuffix());
+ }
+
+ @Test
+ public void testPartialResolutionB() {
+ RequestPathInfo p = createResourceUri("/some/path", ".print.a4.html/some/suffix");
+ assertEquals("/some/path", p.getResourcePath());
+ assertEquals("print.a4", p.getSelectorString());
+ assertEquals(2, p.getSelectors().length);
+ assertEquals("print", p.getSelectors()[0]);
+ assertEquals("a4", p.getSelectors()[1]);
+ assertEquals("html", p.getExtension());
+ assertEquals("/some/suffix", p.getSuffix());
+ }
+
+ @Test
+ public void testPartialResolutionC() {
+ RequestPathInfo p = createResourceUri("/some/path.print", ".a4.html/some/suffix");
+ assertEquals("/some/path.print", p.getResourcePath());
+ assertEquals("a4", p.getSelectorString());
+ assertEquals(1, p.getSelectors().length);
+ assertEquals("a4", p.getSelectors()[0]);
+ assertEquals("html", p.getExtension());
+ assertEquals("/some/suffix", p.getSuffix());
+ }
+
+ @Test
+ public void testPartialResolutionD() {
+ RequestPathInfo p = createResourceUri("/some/path.print.a4", ".html/some/suffix");
+ assertEquals("/some/path.print.a4", p.getResourcePath());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals(0, p.getSelectors().length);
+ assertEquals("html", p.getExtension());
+ assertEquals("/some/suffix", p.getSuffix());
+ }
+
+ @Test
+ public void testDotsAroundSuffix() {
+ RequestPathInfo p = createResourceUri("/libs/foo/content/something/formitems", ".json/image/vnd/xnd/knd.xml");
+ assertEquals("/libs/foo/content/something/formitems", p.getResourcePath());
+ assertEquals("json", p.getExtension());
+ assertNull("Selectors are null", p.getSelectorString());
+ assertEquals("/image/vnd/xnd/knd.xml", p.getSuffix());
+ }
+
+ @Test
+ public void testJIRA_250_a() {
+ RequestPathInfo p = createResourceUri("/bunkai", ".1.json");
+ assertEquals("/bunkai", p.getResourcePath());
+ assertEquals("json", p.getExtension());
+ assertEquals("1", p.getSelectorString());
+ }
+
+ @Test
+ public void testJIRA_250_b() {
+ RequestPathInfo p = createResourceUri("/", ".1.json");
+ assertEquals("/", p.getResourcePath());
+ assertEquals("json", p.getExtension());
+ assertNull("Suffix is null", p.getSuffix());
+ assertEquals("Selector string must not be null", "1",
+ p.getSelectorString());
+ }
+
+ @Test
+ public void testJIRA_250_c() {
+ RequestPathInfo p = createResourceUri("/", ".1.json/my/suffix");
+ assertEquals("/", p.getResourcePath());
+ assertEquals("json", p.getExtension());
+ assertEquals("/my/suffix", p.getSuffix());
+ assertEquals("Selector string must not be null", "1",
+ p.getSelectorString());
+ }
+
+ @Test
+ public void testJIRA_250_d() {
+ RequestPathInfo p = createResourceUri("/", ".json");
+ assertEquals("/", p.getResourcePath());
+ assertEquals("json", p.getExtension());
+ assertNull("Suffix is null", p.getSuffix());
+ assertNull("Selectors are null", p.getSelectorString());
+ }
+
+}