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 GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, 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.relations.CmsLink;
033import org.opencms.relations.CmsRelationType;
034import org.opencms.staticexport.CmsLinkManager;
035import org.opencms.util.CmsFileUtil;
036import org.opencms.util.CmsMacroResolver;
037import org.opencms.util.CmsStringUtil;
038import org.opencms.util.CmsUUID;
039import org.opencms.util.I_CmsMacroResolver;
040
041import java.util.ArrayList;
042import java.util.Arrays;
043import java.util.Collections;
044import java.util.Iterator;
045import java.util.List;
046
047/**
048 * Resolves link macros for jsp pages.<p>
049 *
050 * The only supported macro is the 'link' macro.<p>
051 *
052 * @since 6.5.4
053 */
054public class CmsJspLinkMacroResolver implements I_CmsMacroResolver {
055
056    /** Identifier for the link macro separator. */
057    public static final char KEY_SEPARATOR = ':';
058
059    /** Identifier for the link macro name. */
060    public static final String MACRO_LINK = "link:";
061
062    /** Identifier for the link macro name. */
063    public static final String MACRO_LINK_STRONG = "link.strong:";
064
065    /** Identifier for the link macro name. */
066    public static final String MACRO_LINK_WEAK = "link.weak:";
067
068    /** Identifier for link commands. */
069    public static final String[] VALUE_NAME_ARRAY = {MACRO_LINK, MACRO_LINK_WEAK, MACRO_LINK_STRONG};
070
071    /** The link commands wrapped in a List. */
072    public static final List<String> VALUE_NAMES = Collections.unmodifiableList(Arrays.asList(VALUE_NAME_ARRAY));
073
074    /** The cms context. */
075    private CmsObject m_cms;
076
077    /**
078     * If <code>true</code> the macros get really resolved to valid vfs paths,
079     * otherwise only the path/id in the macros are updated.
080     */
081    private boolean m_forRfs;
082
083    /** The jsp root path. */
084    private String m_jspRootPath;
085
086    /** The list of links. */
087    private List<CmsLink> m_links = new ArrayList<CmsLink>();
088
089    /**
090     * Default constructor.<p>
091     *
092     * @param cms the cms context
093     * @param jspRootPath the (optional) jsp root path, needed for saving from the editor to resolve relative links
094     * @param forRfs Only if <code>true</code> the macros get really resolved to valid vfs paths
095     */
096    public CmsJspLinkMacroResolver(CmsObject cms, String jspRootPath, boolean forRfs) {
097
098        m_cms = cms;
099        m_forRfs = forRfs;
100        m_jspRootPath = jspRootPath;
101    }
102
103    /**
104     * Returns the links.<p>
105     *
106     * @return the links
107     */
108    public List<CmsLink> getLinks() {
109
110        return m_links;
111    }
112
113    /**
114     * @see org.opencms.util.I_CmsMacroResolver#getMacroValue(java.lang.String)
115     */
116    public String getMacroValue(String macro) {
117
118        String path = null;
119        String id = null;
120
121        // validate macro command
122        Iterator<String> it = VALUE_NAMES.iterator();
123        while (it.hasNext()) {
124            String cmd = it.next().toString();
125            if (macro.startsWith(cmd)) {
126                // a macro was found
127                path = macro.substring(cmd.length());
128                macro = cmd;
129                break;
130            }
131        }
132        if (path == null) {
133            // this is an unknown macro, ignore it
134            return null;
135        }
136
137        // we do have a valid link macro now, parse path and id
138        int pos = path.indexOf(KEY_SEPARATOR);
139        if ((pos > -1) && (path.length() > (pos + 1))) {
140            id = path.substring(pos + 1);
141        }
142        if (pos > -1) {
143            path = path.substring(0, pos);
144        }
145
146        // check the id
147        CmsUUID uuid = null;
148        if (CmsStringUtil.isEmptyOrWhitespaceOnly(id)) {
149            if (path != null) {
150                // try to use the path as an id (in case there is only an id)
151                id = path;
152            } else {
153                id = null;
154            }
155        }
156        if (id != null) {
157            try {
158                uuid = new CmsUUID(id);
159            } catch (Exception e) {
160                // ignore
161            }
162        }
163
164        // rewrite the path
165        if ((path == null) || (path.trim().length() == 0)) {
166            path = null;
167        } else {
168            boolean isAbsolute = (path.charAt(0) == '/');
169            path = CmsFileUtil.normalizePath(path, '/');
170            if (!isAbsolute) {
171                path = path.substring(1);
172            }
173            if (isAbsolute && !path.startsWith(m_cms.getRequestContext().getSiteRoot())) {
174                // add the site root if needed
175                path = m_cms.getRequestContext().addSiteRoot(path);
176            } else if (m_jspRootPath != null) {
177                // get the site aware absolute path
178                path = CmsLinkManager.getAbsoluteUri(path, CmsResource.getParentFolder(m_jspRootPath));
179            }
180        }
181
182        // check the relation type
183        CmsRelationType type = CmsRelationType.JSP_WEAK;
184        if (macro == MACRO_LINK_STRONG) {
185            type = CmsRelationType.JSP_STRONG;
186        }
187
188        // get the link object
189        CmsLink link = new CmsLink("link0", type, uuid, path, true);
190        link.checkConsistency(m_cms); // update id/path
191        m_links.add(link);
192
193        if (m_forRfs) {
194            // return the current correct link path
195            return m_cms.getRequestContext().removeSiteRoot(link.getTarget());
196        } else {
197            // rewrite the macro with the right absolute path and id
198            StringBuffer newMacro = new StringBuffer(128);
199            newMacro.append(I_CmsMacroResolver.MACRO_DELIMITER);
200            newMacro.append(I_CmsMacroResolver.MACRO_START);
201            newMacro.append(macro);
202            newMacro.append(link.getSitePath(m_cms));
203            if ((link.getStructureId() != null) && !link.getStructureId().isNullUUID()) {
204                newMacro.append(KEY_SEPARATOR).append(link.getStructureId());
205            }
206            newMacro.append(I_CmsMacroResolver.MACRO_END);
207            return newMacro.toString();
208        }
209    }
210
211    /**
212     * @see org.opencms.util.I_CmsMacroResolver#isKeepEmptyMacros()
213     */
214    public boolean isKeepEmptyMacros() {
215
216        return true;
217    }
218
219    /**
220     * Resolves the JSP link management macros in the given input.<p>
221     *
222     * Calls <code>{@link #resolveMacros(String)}</code> once for each macro in the input.
223     * This means "nested" macros are not supported in this implementation, which is fine since
224     * it can't happen in JSP link management anyway.<p>
225     *
226     * @see org.opencms.util.I_CmsMacroResolver#resolveMacros(java.lang.String)
227     */
228    public String resolveMacros(String input) {
229
230        // clear the list of links
231        m_links.clear();
232
233        // parse the input string
234        String result;
235        if (input != null) {
236            // resolve the macros
237            result = CmsMacroResolver.resolveMacros(input, this);
238        } else {
239            // nothing to resolve
240            result = null;
241        }
242        // return the result
243        return result;
244    }
245}