001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNUAbstractCollection<String>
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.jsp.util;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.jsp.CmsJspResourceWrapper;
033import org.opencms.main.CmsLog;
034import org.opencms.main.OpenCms;
035import org.opencms.relations.CmsLink;
036import org.opencms.relations.CmsRelationType;
037import org.opencms.util.CmsStringUtil;
038import org.opencms.xml.types.CmsXmlVarLinkValue;
039
040import java.net.URI;
041import java.net.URISyntaxException;
042import java.util.AbstractCollection;
043import java.util.Collections;
044import java.util.Iterator;
045import java.util.Map;
046import java.util.Optional;
047import java.util.concurrent.ConcurrentHashMap;
048
049import org.apache.commons.logging.Log;
050
051/**
052 * Wrapper for handling links in template/formatter JSP EL.
053 */
054public class CmsJspLinkWrapper extends AbstractCollection<String> {
055
056    /** Logger instance for this class. */
057    private static final Log LOG = CmsLog.getLog(CmsJspLinkWrapper.class);
058
059    /** Stored CMS context. */
060    protected CmsObject m_cms;
061
062    /** The link literal from which this wrapper was created. */
063    protected String m_link;
064
065    /** Cached internal/external state. */
066    protected Boolean m_internal;
067
068    /** Cached link target resource. */
069    protected Optional<CmsResource> m_resource;
070
071    /** Cached links (online, perma, server). */
072    protected Map<String, String> m_stringCache = new ConcurrentHashMap<>();
073
074    /** If <code>true</code> then empty links are allowed. */
075    private boolean m_allowEmpty;
076
077    /**
078     * Creates a new link wrapper.<p>
079     *
080     * The link parameter should be in the same format that you enter in an XML content of field of type OpenCmsVarLink, i.e.
081     * either a full external URL or a site path with a query string attached.
082     *
083     * @param cms the CMS context
084     * @param link the link to wrap
085     */
086    public CmsJspLinkWrapper(CmsObject cms, String link) {
087
088        this(cms, link, false);
089    }
090
091    /**
092     * Creates a new link wrapper.<p>
093     *
094     * The link parameter should be in the same format that you enter in an XML content of field of type OpenCmsVarLink, i.e.
095     * either a full external URL or a site path with a query string attached.
096     *
097     * @param cms the CMS context
098     * @param link the link to wrap
099     * @param allowEmpty if <code>true</code> then empty links are allowed
100     */
101    public CmsJspLinkWrapper(CmsObject cms, String link, boolean allowEmpty) {
102
103        m_cms = cms;
104        m_link = link;
105        m_allowEmpty = allowEmpty;
106    }
107
108    /**
109     * @see java.lang.Object#equals(java.lang.Object)
110     */
111    @Override
112    public boolean equals(Object obj) {
113
114        if (obj == this) {
115            return true;
116        }
117        if (obj instanceof CmsJspLinkWrapper) {
118            return obj.toString().equals(toString());
119        }
120        return false;
121    }
122
123    /**
124     * Returns <code>true</code> if the link is internal.
125     *
126     * @return <code>true</code> if the link is internal
127     */
128    public boolean getIsInternal() {
129
130        if (m_internal == null) {
131            if (isEmpty()) {
132                m_internal = Boolean.FALSE;
133            } else {
134                m_internal = Boolean.valueOf(
135                    null != CmsXmlVarLinkValue.getInternalPathAndQuery(m_cms, getServerLink()));
136            }
137        }
138        return m_internal.booleanValue();
139    }
140
141    /**
142     * Performs normal link substitution.
143     *
144     * @return the substituted link
145     */
146    public String getLink() {
147
148        return m_stringCache.computeIfAbsent(
149            "link",
150            k -> (!isEmpty() ? A_CmsJspValueWrapper.substituteLink(m_cms, m_link) : ""));
151    }
152
153    /**
154     * Gets the literal from which this wrapper was constructed.
155     *
156     * @return the original link literal
157     */
158    public String getLiteral() {
159
160        return m_link;
161    }
162
163    /**
164     * Performs online link substitution.
165     *
166     * @return the online link
167     */
168    public String getOnlineLink() {
169
170        return m_stringCache.computeIfAbsent(
171            "online",
172            k -> (!isEmpty() ? OpenCms.getLinkManager().getOnlineLink(m_cms, m_link) : ""));
173    }
174
175    /**
176     * Performs permalink substitution.
177     *
178     * @return the permalink
179     */
180    public String getPermaLink() {
181
182        return m_stringCache.computeIfAbsent(
183            "perma",
184            k -> (!isEmpty() ? OpenCms.getLinkManager().getPermalink(m_cms, m_link) : ""));
185    }
186
187    /**
188     * Gets the resource wrapper for the link target.
189     *
190     * @return the resource wrapper for the target
191     */
192    public CmsJspResourceWrapper getResource() {
193
194        if (m_resource == null) {
195            try {
196                String link = CmsXmlVarLinkValue.getInternalPathAndQuery(m_cms, getServerLink());
197                if (link == null) {
198                    m_resource = Optional.empty();
199                } else {
200                    CmsLink linkObj = new CmsLink(/*name=*/null, CmsRelationType.HYPERLINK, link, true);
201                    linkObj.checkConsistency(m_cms);
202                    m_resource = Optional.ofNullable(linkObj.getResource());
203                }
204            } catch (Exception e) {
205                LOG.warn(e.getLocalizedMessage(), e);
206                m_resource = Optional.empty();
207            }
208        }
209        if (m_resource.isPresent()) {
210            return CmsJspResourceWrapper.wrap(m_cms, m_resource.get());
211        } else {
212            return null;
213        }
214
215    }
216
217    /**
218     * Performs server link substitution.
219     *
220     * @return the server link
221     */
222    public String getServerLink() {
223
224        return m_stringCache.computeIfAbsent(
225            "server",
226            k -> (!isEmpty() ? OpenCms.getLinkManager().getServerLink(m_cms, m_link) : ""));
227    }
228
229    /**
230     * Returns the wrapped link as a String as in {@link #toString()}.<p>
231     *
232     * @return the wrapped link as a String
233     */
234    public String getToString() {
235
236        return toString();
237    }
238
239    /**
240     * Converts the wrapped string to an URI object and returns it.
241     *
242     * <p>If the wrapped string cannont be converted, returns null.
243     *
244     * @return the URI object for the wrapped string, or null if conversion fails
245     */
246    public URI getToURI() {
247        return toURI();
248    }
249
250    /**
251     * @see org.opencms.jsp.util.A_CmsJspValueWrapper#hashCode()
252     */
253    @Override
254    public int hashCode() {
255
256        if (m_link == null) {
257            return 0;
258        }
259        return toString().hashCode();
260    }
261
262    /**
263     * Returns <code>true</code> if the wrapped link has been initialized.<p>
264     *
265     * @return <code>true</code> if the wrapped link has been initialized
266     */
267    @Override
268    public boolean isEmpty() {
269
270        if (m_allowEmpty) {
271            return m_link == null;
272        }
273        return CmsStringUtil.isEmptyOrWhitespaceOnly(m_link);
274    }
275
276    /**
277     * @see java.util.AbstractCollection#iterator()
278     */
279    @Override
280    public Iterator<String> iterator() {
281
282        return isEmpty() ? Collections.emptyIterator() : Collections.singletonList(toString()).iterator();
283    }
284
285    /**
286     * @see java.util.AbstractCollection#size()
287     */
288    @Override
289    public int size() {
290
291        return isEmpty() ? 0 : 1;
292    }
293
294    /**
295     * Returns the wrapped link as a String as in {@link #getLink()}.<p>
296     *
297     * @return the wrapped link as a String
298     *
299     * @see #getLiteral()
300     */
301    @Override
302    public String toString() {
303
304        return getLink();
305    }
306
307    /**
308     * Converts the wrapped string to an URI object and returns it.
309     *
310     * <p>If the wrapped string cannont be converted, returns null.
311     *
312     * @return the URI object for the wrapped string, or null if conversion fails
313     */
314    public URI toURI() {
315
316        if (m_link == null) {
317            return null;
318        }
319        try {
320            return new URI(m_link);
321        } catch (URISyntaxException e) {
322            return null;
323        }
324    }
325}