RootPathResolver.java

  1. /*
  2.  * #%L
  3.  * wcm.io
  4.  * %%
  5.  * Copyright (C) 2019 wcm.io
  6.  * %%
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *      http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  * #L%
  19.  */
  20. package io.wcm.wcm.ui.granite.util;

  21. import java.util.HashMap;
  22. import java.util.Map;

  23. import org.apache.commons.lang3.StringUtils;
  24. import org.apache.sling.api.SlingHttpServletRequest;
  25. import org.apache.sling.api.resource.ResourceResolver;
  26. import org.jetbrains.annotations.NotNull;
  27. import org.osgi.annotation.versioning.ProviderType;

  28. import com.adobe.granite.ui.components.ComponentHelper;
  29. import com.adobe.granite.ui.components.Config;
  30. import com.adobe.granite.ui.components.ExpressionHelper;
  31. import com.day.text.Text;

  32. /**
  33.  * Helper class for path-based GraniteUI components to resolve the root path.
  34.  * <p>
  35.  * Resolution order for root path detection:
  36.  * </p>
  37.  * <ul>
  38.  * <li>Reads configured root path from <code>rootPath</code> property</li>
  39.  * <li>Calls the provided root path detector implementation to detect root path from context</li>
  40.  * <li>Reads fallback root path from <code>fallbackRootPath</code> property</li>
  41.  * <li>Uses fallback root path provided for this instance</li>
  42.  * <li>Fallback to "/"</li>
  43.  * </ul>
  44.  * <p>
  45.  * Additionally the root path is modified:
  46.  * </p>
  47.  * <ul>
  48.  * <li>If an <code>appendPath</code> property is configured it is appended to the detected root path</li>
  49.  * <li>Than it is checked if the root path is valid - if not the next-valid parent path is returned</li>
  50.  * </ul>
  51.  */
  52. @ProviderType
  53. public final class RootPathResolver {

  54.   static final String PN_ROOT_PATH = "rootPath";
  55.   static final String PN_APPEND_PATH = "appendPath";
  56.   static final String PN_FALLBACK_PATH = "fallbackRootPath";
  57.   static final String DEFAULT_FALLBACK_ROOT_PATH = "/";

  58.   private final ComponentHelper cmp;
  59.   private final Config cfg;
  60.   private final ExpressionHelper ex;
  61.   private final SlingHttpServletRequest request;
  62.   private final ResourceResolver resourceResolver;

  63.   private RootPathDetector rootPathDetector;
  64.   private String fallbackRootPath = DEFAULT_FALLBACK_ROOT_PATH;

  65.   /**
  66.    * @param cmp Component helper
  67.    * @param request Request
  68.    */
  69.   public RootPathResolver(@NotNull ComponentHelper cmp, @NotNull SlingHttpServletRequest request) {
  70.     this.cmp = cmp;
  71.     this.cfg = cmp.getConfig();
  72.     this.ex = cmp.getExpressionHelper();
  73.     this.request = request;
  74.     this.resourceResolver = request.getResourceResolver();
  75.   }

  76.   /**
  77.    * @param rootPathDetector For detecting root path from context
  78.    */
  79.   public void setRootPathDetector(@NotNull RootPathDetector rootPathDetector) {
  80.     this.rootPathDetector = rootPathDetector;
  81.   }

  82.   /**
  83.    * @param fallbackRootPath Fallback root path that is used if none is configured
  84.    */
  85.   public void setFallbackRootPath(@NotNull String fallbackRootPath) {
  86.     this.fallbackRootPath = fallbackRootPath;
  87.   }

  88.   /**
  89.    * Get the resolved and validated root path.
  90.    * @return Root path.
  91.    */
  92.   public @NotNull String get() {
  93.     // get configured or detected or fallback root path
  94.     String rootPath = getRootPath();

  95.     // append path if configured
  96.     rootPath = appendPath(rootPath);

  97.     // resolve to existing path
  98.     return getExistingPath(rootPath);
  99.   }

  100.   /**
  101.    * Get map of override properties for super component based on the wcm.io Granite UI Extensions PathField.
  102.    * @return Path properties
  103.    */
  104.   public Map<String, Object> getOverrideProperties() {
  105.     Map<String, Object> props = new HashMap<>();
  106.     props.put(PN_ROOT_PATH, get());
  107.     props.put(PN_APPEND_PATH, "");
  108.     props.put(PN_FALLBACK_PATH, "");
  109.     return props;
  110.   }

  111.   /**
  112.    * @return Configured or detected root path or fallback path
  113.    */
  114.   private @NotNull String getRootPath() {

  115.     // check for configured root path
  116.     String rootPath = ex.getString(cfg.get(PN_ROOT_PATH, String.class));
  117.     if (StringUtils.isNotBlank(rootPath)) {
  118.       return rootPath;
  119.     }

  120.     // call root path detector
  121.     if (rootPathDetector != null) {
  122.       rootPath = rootPathDetector.detectRootPath(cmp, request);
  123.       if (rootPath != null && StringUtils.isNotBlank(rootPath)) {
  124.         return rootPath;
  125.       }
  126.     }

  127.     // check for configured fallback path
  128.     rootPath = ex.getString(cfg.get(PN_FALLBACK_PATH, String.class));
  129.     if (StringUtils.isNotBlank(rootPath)) {
  130.       return rootPath;
  131.     }

  132.     // fallback to default fallback path
  133.     return fallbackRootPath;
  134.   }

  135.   /**
  136.    * Appends the "appendPath" if configured.
  137.    * @param rootPath Root path
  138.    * @return Path with appendix
  139.    */
  140.   private @NotNull String appendPath(@NotNull String rootPath) {
  141.     String appendPath = ex.getString(cfg.get(PN_APPEND_PATH, String.class));
  142.     if (StringUtils.isBlank(appendPath)) {
  143.       return rootPath;
  144.     }
  145.     StringBuilder combinedPath = new StringBuilder(rootPath);
  146.     if (!StringUtils.startsWith(appendPath, "/")) {
  147.       combinedPath.append("/");
  148.     }
  149.     combinedPath.append(appendPath);
  150.     return combinedPath.toString();
  151.   }

  152.   /**
  153.    * Make sure the root path exists. If it does not exist go up to parent hierarchy until it returns an
  154.    * existing resource path.
  155.    */
  156.   @NotNull
  157.   String getExistingPath(@NotNull String rootPath) {
  158.     if (resourceResolver.getResource(rootPath) == null) {
  159.       String parentPath = Text.getRelativeParent(rootPath, 1);
  160.       if (StringUtils.isBlank(parentPath)) {
  161.         return DEFAULT_FALLBACK_ROOT_PATH;
  162.       }
  163.       return getExistingPath(parentPath);
  164.     }
  165.     else {
  166.       return rootPath;
  167.     }
  168.   }

  169. }