Link.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.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.jdom2.Attribute;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osgi.annotation.versioning.ProviderType;
import com.day.cq.wcm.api.Page;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import io.wcm.handler.commons.dom.Anchor;
import io.wcm.handler.link.spi.LinkType;
import io.wcm.handler.media.Asset;
import io.wcm.handler.media.Rendition;
/**
* Holds information about a link processed and resolved by {@link LinkHandler}.
*/
@ProviderType
@JsonInclude(Include.NON_NULL)
public final class Link {
private final @NotNull LinkType linkType;
private @NotNull LinkRequest linkRequest;
private boolean linkReferenceInvalid;
private Anchor anchor;
private Function<Link, Anchor> anchorBuilder;
private String url;
private Page targetPage;
private Asset targetAsset;
private Rendition targetRendition;
private List<Page> redirectPages;
/**
* @param linkType Link type
* @param linkRequest Processed link reference
*/
public Link(@NotNull LinkType linkType, @NotNull LinkRequest linkRequest) {
this.linkRequest = linkRequest;
this.linkType = linkType;
}
/**
* @return Link type
*/
@JsonUnwrapped
public @NotNull LinkType getLinkType() {
return this.linkType;
}
/**
* @return Link request
*/
@JsonIgnore
public @NotNull LinkRequest getLinkRequest() {
return this.linkRequest;
}
/**
* @param linkRequest Link request
*/
public void setLinkRequest(@NotNull LinkRequest linkRequest) {
this.linkRequest = linkRequest;
}
/**
* @return true if a link reference was set, but the reference was invalid and could not be resolved
*/
@JsonIgnore
public boolean isLinkReferenceInvalid() {
return this.linkReferenceInvalid;
}
/**
* @param linkReferenceInvalid true if a link reference was set, but the reference was invalid and could not be
* resolved
*/
public void setLinkReferenceInvalid(boolean linkReferenceInvalid) {
this.linkReferenceInvalid = linkReferenceInvalid;
}
/**
* @return Anchor element
*/
@JsonIgnore
public @Nullable Anchor getAnchor() {
if (this.anchor == null && this.anchorBuilder != null) {
this.anchor = this.anchorBuilder.apply(this);
this.anchorBuilder = null;
}
return this.anchor;
}
/**
* @return Map with all attributes of the anchor element. Returns null if anchor element is null.
*/
@JsonIgnore
@SuppressWarnings("java:S1168")
public @Nullable Map<String, String> getAnchorAttributes() {
Anchor a = getAnchor();
if (a == null) {
return null;
}
Map<String, String> attributes = new HashMap<>();
for (Attribute attribute : a.getAttributes()) {
attributes.put(attribute.getName(), attribute.getValue());
}
return attributes;
}
/**
* @param anchorBuilder Function that builds an anchor representation on demand
*/
public void setAnchorBuilder(@NotNull Function<Link, Anchor> anchorBuilder) {
this.anchorBuilder = anchorBuilder;
}
/**
* @return Link markup (only the opening anchor tag) or null if resolving was not successful.
*/
@JsonIgnore
public @Nullable String getMarkup() {
Anchor a = getAnchor();
if (a != null) {
return StringUtils.removeEnd(a.toString(), "</a>");
}
else {
return null;
}
}
/**
* @return Link URL
*/
public @Nullable String getUrl() {
return this.url;
}
/**
* @param url Link URL
*/
public void setUrl(@Nullable String url) {
this.url = url;
}
/**
* @return Target page referenced by the link (applies only for internal links)
*/
@JsonIgnore
public @Nullable Page getTargetPage() {
return this.targetPage;
}
/**
* @param targetPage Target page referenced by the link (applies only for internal links)
*/
public void setTargetPage(@Nullable Page targetPage) {
this.targetPage = targetPage;
}
/**
* @return Target media item (applies only for media links)
*/
@JsonIgnore
public @Nullable Asset getTargetAsset() {
return this.targetAsset;
}
/**
* @param targetAsset Target media item (applies only for media links)
*/
public void setTargetAsset(@Nullable Asset targetAsset) {
this.targetAsset = targetAsset;
}
/**
* @return Target media rendition (applies only for media links)
*/
@JsonIgnore
public @Nullable Rendition getTargetRendition() {
return this.targetRendition;
}
/**
* @param targetRendition Target media rendition (applies only for media links)
*/
public void setTargetRendition(@Nullable Rendition targetRendition) {
this.targetRendition = targetRendition;
}
/**
* During link resolution one or multiple redirect pages may get resolved and replaced by the referenced
* link target. This page list gives access to all redirect pages that where visited and resolved
* during the link resolution process.
* @return List of links in the "resolve history".
*/
@JsonIgnore
public @NotNull List<Page> getRedirectPages() {
if (redirectPages == null) {
return Collections.emptyList();
}
else {
return Collections.unmodifiableList(redirectPages);
}
}
/**
* Add page to list of redirect pages (at first position of the list).
* @param redirectPage Redirect page
*/
public void addRedirectPage(@NotNull Page redirectPage) {
if (redirectPages == null) {
redirectPages = new LinkedList<>();
}
redirectPages.add(0, redirectPage);
}
/**
* @return true if link is valid and was resolved successfully
*/
@SuppressWarnings({ "null", "java:S2589" }) // extra null checks for backward compatibility
public boolean isValid() {
return getLinkType() != null
&& getUrl() != null
&& !StringUtils.equals(getUrl(), LinkHandler.INVALID_LINK);
}
@Override
public String toString() {
ToStringBuilder sb = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
sb.append("valid", isValid());
if (isValid()) {
sb.append("url", getUrl());
}
else {
sb.append("linkReferenceInvalid", this.linkReferenceInvalid);
}
sb.append("linkType", getLinkType());
if (this.anchor != null) {
sb.append("anchor", this.anchor.toString());
}
if (targetPage != null) {
sb.append("targetPage", targetPage.getPath());
}
if (targetAsset != null) {
sb.append("targetAsset", targetAsset.getPath());
}
if (targetRendition != null) {
sb.append("targetRendition", targetRendition);
}
if (redirectPages != null && !redirectPages.isEmpty()) {
sb.append("redirectPages", redirectPages.stream().map(Page::getPath).toArray());
}
sb.append("linkRequest", linkRequest);
return sb.build();
}
}