    001    // Copyright 2009, 2010, 2011 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // You may obtain a copy of the License at
    006    //
    007    // http://www.apache.org/licenses/LICENSE-2.0
    008    //
    009    // Unless required by applicable law or agreed to in writing, software
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.internal.services;
    016
    017    import org.apache.tapestry5.Link;
    018    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    019    import org.apache.tapestry5.services.BaseURLSource;
    020    import org.apache.tapestry5.services.ContextPathEncoder;
    021    import org.apache.tapestry5.services.Response;
    022
    023    import java.util.Arrays;
    024    import java.util.List;
    025    import java.util.Map;
    026    import java.util.TreeMap;
    027
    028    public class LinkImpl implements Link
    029    {
    030            private Map<String, List<String>> parameters;
    031
    032        private final String basePath;
    033
    034        private final boolean forForm;
    035
    036        private LinkSecurity defaultSecurity;
    037
    038        private final Response response;
    039
    040        private final ContextPathEncoder contextPathEncoder;
    041
    042        private final BaseURLSource baseURLSource;
    043
    044        private String anchor;
    045
    046        public LinkImpl(String basePath, boolean forForm, LinkSecurity defaultSecurity, Response response,
    047                        ContextPathEncoder contextPathEncoder, BaseURLSource baseURLSource)
    048        {
    049            assert basePath != null;
    050
    051            this.basePath = basePath;
    052            this.forForm = forForm;
    053            this.defaultSecurity = defaultSecurity;
    054            this.response = response;
    055            this.contextPathEncoder = contextPathEncoder;
    056            this.baseURLSource = baseURLSource;
    057        }
    058
    059        public Link copyWithBasePath(String basePath)
    060        {
    061            LinkImpl copy = new LinkImpl(basePath, forForm, defaultSecurity, response, contextPathEncoder, baseURLSource);
    062
    063            copy.anchor = anchor;
    064
    065            for (String name : getParameterNames())
    066            {
    067                copy.addParameter(name, getParameterValues(name));
    068            }
    069
    070            return copy;
    071        }
    072
    073        private void addParameter(String parameterName, String[] value)
    074        {
    075            assert InternalUtils.isNonBlank(parameterName);
    076            if (parameters == null)
    077                parameters = new TreeMap<String, List<String>>();
    078
    079            parameters.put(parameterName, Arrays.asList(value));
    080        }
    081
    082        public void addParameter(String parameterName, String value)
    083        {
    084            assert InternalUtils.isNonBlank(parameterName);
    085
    086            if (parameters == null)
    087                parameters = new TreeMap<String, List<String>>();
    088
    089            InternalUtils.addToMapList(parameters, parameterName, value == null ? "" : value);
    090        }
    091
    092        public String getBasePath()
    093        {
    094            return basePath;
    095        }
    096
    097        public void removeParameter(String parameterName)
    098        {
    099            assert InternalUtils.isNonBlank(parameterName);
    100            if (parameters != null)
    101                parameters.remove(parameterName);
    102        }
    103
    104        public String getAnchor()
    105        {
    106            return anchor;
    107        }
    108
    109        public List<String> getParameterNames()
    110        {
    111            return InternalUtils.sortedKeys(parameters);
    112        }
    113
    114        public String getParameterValue(String name)
    115        {
    116            List<String> values = InternalUtils.get(parameters, name);
    117            return values != null && !values.isEmpty() ? values.get(0) : null;
    118        }
    119
    120        public void setAnchor(String anchor)
    121        {
    122            this.anchor = anchor;
    123        }
    124
    125        public String toAbsoluteURI()
    126        {
    127            return buildAnchoredURI(defaultSecurity.promote());
    128        }
    129
    130        public String toAbsoluteURI(boolean secure)
    131        {
    132            return buildAnchoredURI(secure ? LinkSecurity.FORCE_SECURE : LinkSecurity.FORCE_INSECURE);
    133        }
    134
    135        public void setSecurity(LinkSecurity newSecurity)
    136        {
    137            assert newSecurity != null;
    138
    139            defaultSecurity = newSecurity;
    140        }
    141
    142        public LinkSecurity getSecurity()
    143        {
    144            return defaultSecurity;
    145        }
    146
    147        public String toRedirectURI()
    148        {
    149            return appendAnchor(response.encodeRedirectURL(buildURI(defaultSecurity)));
    150        }
    151
    152        public String toURI()
    153        {
    154            return buildAnchoredURI(defaultSecurity);
    155        }
    156
    157        private String appendAnchor(String path)
    158        {
    159            return InternalUtils.isBlank(anchor) ? path : path + "#" + anchor;
    160        }
    161
    162        private String buildAnchoredURI(LinkSecurity security)
    163        {
    164            return appendAnchor(response.encodeURL(buildURI(security)));
    165        }
    166
    167        /**
    168         * Returns the value from {@link #toURI()}
    169         */
    170        @Override
    171        public String toString()
    172        {
    173            return toURI();
    174        }
    175
    176        /**
    177         * Extends the absolute path with any query parameters. Query parameters are never added to a forForm link.
    178         *
    179         * @return absoluteURI appended with query parameters
    180         */
    181        private String buildURI(LinkSecurity security)
    182        {
    183
    184            if (!security.isAbsolute() && (forForm || parameters == null))
    185                return basePath;
    186
    187            StringBuilder builder = new StringBuilder(basePath.length() * 2);
    188
    189            switch (security)
    190            {
    191                case FORCE_SECURE:
    192                    builder.append(baseURLSource.getBaseURL(true));
    193                    break;
    194                case FORCE_INSECURE:
    195                    builder.append(baseURLSource.getBaseURL(false));
    196                    break;
    197                default:
    198            }
    199
    200            // The base URL (from BaseURLSource) does not end with a slash.
    201            // The basePath does (the context path begins with a slash or is blank, then there's
    202            // always a slash before the local name or page name.
    203
    204            builder.append(basePath);
    205
    206            if (!forForm)
    207            {
    208                String sep = basePath.contains("?") ? "&" : "?";
    209
    210                for (String name : getParameterNames())
    211                {
    212                    List<String> values = parameters.get(name);
    213
    214                    for (String value : values)
    215                    {
    216                        builder.append(sep);
    217
    218                        // We assume that the name is URL safe and that the value will already have been URL
    219                        // encoded if it is not known to be URL safe.
    220
    221                        builder.append(name);
    222                        builder.append("=");
    223                        builder.append(value);
    224
    225                        sep = "&";
    226                    }
    227                }
    228            }
    229
    230            return builder.toString();
    231        }
    232
    233        public Link addParameterValue(String parameterName, Object value)
    234        {
    235            addParameter(parameterName, contextPathEncoder.encodeValue(value));
    236
    237            return this;
    238        }
    239
    240        public String[] getParameterValues(String parameterName)
    241        {
    242            List<String> values = InternalUtils.get(parameters, parameterName);
    243            return values.toArray(new String[values.size()]);
    244        }
    245
    246    }