LinkArgs.java

/*
 * #%L
 * wcm.io
 * %%
 * Copyright (C) 2014 wcm.io
 * %%
 * Licensed 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.
 * #L%
 */
package io.wcm.handler.link;

import java.util.HashMap;
import java.util.Map;

import io.wcm.handler.url.VanityMode;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.wrappers.ValueMapDecorator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osgi.annotation.versioning.ProviderType;

import io.wcm.handler.url.UrlHandler;
import io.wcm.handler.url.UrlMode;
import io.wcm.wcm.commons.util.ToStringStyle;

/**
 * Holds parameters to influence the link resolving process.
 */
@ProviderType
public final class LinkArgs implements Cloneable {

  private UrlMode urlMode;
  private VanityMode vanityMode;
  private boolean dummyLink;
  private String dummyLinkUrl;
  private String selectors;
  private String extension;
  private String suffix;
  private String queryString;
  private String fragment;
  private String windowTarget;
  private boolean disableSuffixSelector;
  private ValueMap properties;
  private String[] linkTargetUrlFallbackProperty;
  private String[] linkTargetWindowTargetFallbackProperty;


  /**
   * @return URL mode for externalizing the URL
   */
  public @Nullable UrlMode getUrlMode() {
    return this.urlMode;
  }

  /**
   * @return Vanity mode for building the URL
   */
  public @Nullable VanityMode getVanityMode() {
    return this.vanityMode;
  }

  /**
   * @param value URL mode for externalizing the URL
   * @return this
   */
  public @NotNull LinkArgs urlMode(@Nullable UrlMode value) {
    this.urlMode = value;
    return this;
  }

  /**
   * @param value Vanity mode for building the URL
   * @return this
   */
  public @NotNull LinkArgs vanityMode(@Nullable VanityMode value) {
    this.vanityMode = value;
    return this;
  }

  /**
   * @return If set to true, link handler returns a dummy link in edit mode when link is invalid.
   */
  public boolean isDummyLink() {
    return this.dummyLink;
  }

  /**
   * @param value If set to true, link handler returns a dummy link in edit mode when link is invalid.
   * @return this
   */
  public @NotNull LinkArgs dummyLink(boolean value) {
    this.dummyLink = value;
    return this;
  }

  /**
   * @return Custom dummy link url. If null default dummy url is used.
   */
  public @Nullable String getDummyLinkUrl() {
    return this.dummyLinkUrl;
  }

  /**
   * @param value Custom dummy link url. If null default dummy url is used.
   * @return this
   */
  public @NotNull LinkArgs dummyLinkUrl(@Nullable String value) {
    this.dummyLinkUrl = value;
    return this;
  }

  /**
   * @return Selector string
   */
  public @Nullable String getSelectors() {
    return this.selectors;
  }

  /**
   * @param value Selector string
   * @return this
   */
  public @NotNull LinkArgs selectors(@Nullable String value) {
    this.selectors = value;
    return this;
  }

  /**
   * @return File extension
   */
  public @Nullable String getExtension() {
    return this.extension;
  }

  /**
   * @param value File extension
   * @return this
   */
  public @NotNull LinkArgs extension(@Nullable String value) {
    this.extension = value;
    return this;
  }

  /**
   * @return Suffix string
   */
  public @Nullable String getSuffix() {
    return this.suffix;
  }

  /**
   * @param value Suffix string
   * @return this
   */
  public @NotNull LinkArgs suffix(@Nullable String value) {
    this.suffix = value;
    return this;
  }

  /**
   * @return Query parameters string (properly url-encoded)
   */
  public @Nullable String getQueryString() {
    return this.queryString;
  }

  /**
   * @param value Query parameters string (properly url-encoded)
   * @return this
   */
  public @NotNull LinkArgs queryString(@Nullable String value) {
    this.queryString = value;
    return this;
  }

  /**
   * @return Fragment identifier
   */
  public @Nullable String getFragment() {
    return this.fragment;
  }

  /**
   * @param value Fragment identifier
   * @return this
   */
  public @NotNull LinkArgs fragment(@Nullable String value) {
    this.fragment = value;
    return this;
  }

  /**
   * @return Link window target
   */
  public @Nullable String getWindowTarget() {
    return this.windowTarget;
  }

  /**
   * @param value Link window target
   * @return this
   */
  public @NotNull LinkArgs windowTarget(@Nullable String value) {
    this.windowTarget = value;
    return this;
  }

  /**
   * Disable the automatic addition of an additional selector {@link UrlHandler#SELECTOR_SUFFIX}
   * in case a suffix is present for building the URL. Although recommended as best practice, this can
   * be omitted if you are sure your URLs are always either include a suffix or never do, so there is no risk
   * for file name clashes in dispatcher cache.
   * @return If set to true, no additional suffix selector is added
   */
  public boolean isDisableSuffixSelector() {
    return this.disableSuffixSelector;
  }

  /**
   * Disable the automatic addition of an additional selector {@link UrlHandler#SELECTOR_SUFFIX}
   * in case a suffix is present for building the URL. Although recommended as best practice, this can
   * be omitted if you are sure your URLs are always either include a suffix or never do, so there is no risk
   * for file name clashes in dispatcher cache.
   * @param value If set to true, no additional suffix selector is added
   * @return this
   */
  public @NotNull LinkArgs disableSuffixSelector(boolean value) {
    this.disableSuffixSelector = value;
    return this;
  }

  /**
   * Custom properties that my be used by application-specific markup builders or processors.
   * @param map Property map. Is merged with properties already set.
   * @return this
   */
  @SuppressWarnings({ "null", "unused" })
  public @NotNull LinkArgs properties(@NotNull Map<String, Object> map) {
    if (map == null) {
      throw new IllegalArgumentException("Map argument must not be null.");
    }
    getProperties().putAll(map);
    return this;
  }

  /**
   * Custom properties that my be used by application-specific markup builders or processors.
   * @param key Property key
   * @param value Property value
   * @return this
   */
  @SuppressWarnings({ "null", "unused" })
  public @NotNull LinkArgs property(@NotNull String key, @Nullable Object value) {
    if (key == null) {
      throw new IllegalArgumentException("Key argument must not be null.");
    }
    getProperties().put(key, value);
    return this;
  }

  /**
   * Custom properties that my be used by application-specific markup builders or processors.
   * @return Value map
   */
  public @NotNull ValueMap getProperties() {
    if (this.properties == null) {
      this.properties = new ValueMapDecorator(new HashMap<>());
    }
    return this.properties;
  }


  /**
   * Defines a "fallback" property name that is used to load link target information from a single property
   * instead of the link type + link type depending property name. This property is used for migration
   * from components that do not support Link Handler. It is only used for reading, and never written back to.
   * When opened and saved in the link dialog, the property is removed and instead the dedicated properties are used.
   * @param propertyNames Property name(s)
   * @return this
   */
  public @NotNull LinkArgs linkTargetUrlFallbackProperty(@NotNull String @Nullable... propertyNames) {
    this.linkTargetUrlFallbackProperty = propertyNames;
    return this;
  }

  /**
   * @return Property name(s)
   */
  public @Nullable String[] getLinkTargetUrlFallbackProperty() {
    return this.linkTargetUrlFallbackProperty;
  }

  /**
   * Defines a "fallback" property name that is used to load the "windows target" information from
   * instead of the the default property. This property is used for migration
   * from components that do not support Link Handler. It is only used for reading, and never written back to.
   * When opened and saved in the link dialog, the property is removed and instead the dedicated properties are used.
   * @param propertyNames Property name(s)
   * @return this
   */
  public @NotNull LinkArgs linkTargetWindowTargetFallbackProperty(@NotNull String @Nullable... propertyNames) {
    this.linkTargetWindowTargetFallbackProperty = propertyNames;
    return this;
  }

  /**
   * @return Property name(s)
   */
  public @Nullable String[] getLinkTargetWindowTargetFallbackProperty() {
    return this.linkTargetWindowTargetFallbackProperty;
  }


  @Override
  public int hashCode() {
    return HashCodeBuilder.reflectionHashCode(this);
  }

  @Override
  public boolean equals(Object obj) {
    return EqualsBuilder.reflectionEquals(this, obj);
  }

  @Override
  public String toString() {
    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_OMIT_NULL_STYLE);
  }

  /**
   * Custom clone-method for {@link LinkArgs}
   * @return the cloned {@link LinkArgs}
   */
  @Override
  @SuppressWarnings({ "java:S2975", "java:S1182", "checkstyle:SuperCloneCheck" }) // ignore clone warnings
  public LinkArgs clone() { //NOPMD
    LinkArgs clone = new LinkArgs();

    clone.urlMode = this.urlMode;
    clone.dummyLink = this.dummyLink;
    clone.dummyLinkUrl = this.dummyLinkUrl;
    clone.selectors = this.selectors;
    clone.extension = this.extension;
    clone.suffix = this.suffix;
    clone.queryString = this.queryString;
    clone.fragment = this.fragment;
    clone.windowTarget = this.windowTarget;
    clone.disableSuffixSelector = this.disableSuffixSelector;
    clone.linkTargetUrlFallbackProperty = ArrayUtils.clone(this.linkTargetUrlFallbackProperty);
    clone.linkTargetWindowTargetFallbackProperty = ArrayUtils.clone(this.linkTargetWindowTargetFallbackProperty);
    if (this.properties != null) {
      clone.properties = new ValueMapDecorator(new HashMap<>(this.properties));
    }

    return clone;
  }

}