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.CollectionFactory;
019    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
020    import org.apache.tapestry5.services.BaseURLSource;
021    import org.apache.tapestry5.services.ContextPathEncoder;
022    import org.apache.tapestry5.services.Response;
023    
024    import java.util.List;
025    import java.util.Map;
026    
027    public class LinkImpl implements Link
028    {
029        private Map<String, String> parameters;
030    
031        private final String basePath;
032    
033        private final boolean forForm;
034    
035        private LinkSecurity defaultSecurity;
036    
037        private final Response response;
038    
039        private final ContextPathEncoder contextPathEncoder;
040    
041        private final BaseURLSource baseURLSource;
042    
043        private String anchor;
044    
045        public LinkImpl(String basePath, boolean forForm, LinkSecurity defaultSecurity, Response response,
046                        ContextPathEncoder contextPathEncoder, BaseURLSource baseURLSource)
047        {
048            assert basePath != null;
049    
050            this.basePath = basePath;
051            this.forForm = forForm;
052            this.defaultSecurity = defaultSecurity;
053            this.response = response;
054            this.contextPathEncoder = contextPathEncoder;
055            this.baseURLSource = baseURLSource;
056        }
057    
058        public Link copyWithBasePath(String basePath)
059        {
060            LinkImpl copy = new LinkImpl(basePath, forForm, defaultSecurity, response, contextPathEncoder, baseURLSource);
061    
062            copy.anchor = anchor;
063    
064            for (String name : getParameterNames())
065            {
066                copy.addParameter(name, parameters.get(name));
067            }
068    
069            return copy;
070        }
071    
072        public void addParameter(String parameterName, String value)
073        {
074            assert InternalUtils.isNonBlank(parameterName);
075    
076            if (parameters == null)
077                parameters = CollectionFactory.newMap();
078    
079            parameters.put(parameterName, value == null ? "" : value);
080        }
081    
082        public String getBasePath()
083        {
084            return basePath;
085        }
086    
087        public void removeParameter(String parameterName)
088        {
089            assert InternalUtils.isNonBlank(parameterName);
090            if (parameters != null)
091                parameters.remove(parameterName);
092        }
093    
094        public String getAnchor()
095        {
096            return anchor;
097        }
098    
099        public List<String> getParameterNames()
100        {
101            return InternalUtils.sortedKeys(parameters);
102        }
103    
104        public String getParameterValue(String name)
105        {
106            return InternalUtils.get(parameters, name);
107        }
108    
109        public void setAnchor(String anchor)
110        {
111            this.anchor = anchor;
112        }
113    
114        public String toAbsoluteURI()
115        {
116            return buildAnchoredURI(defaultSecurity.promote());
117        }
118    
119        public String toAbsoluteURI(boolean secure)
120        {
121            return buildAnchoredURI(secure ? LinkSecurity.FORCE_SECURE : LinkSecurity.FORCE_INSECURE);
122        }
123    
124        public void setSecurity(LinkSecurity newSecurity)
125        {
126            assert newSecurity != null;
127    
128            defaultSecurity = newSecurity;
129        }
130    
131        public LinkSecurity getSecurity()
132        {
133            return defaultSecurity;
134        }
135    
136        public String toRedirectURI()
137        {
138            return appendAnchor(response.encodeRedirectURL(buildURI(defaultSecurity)));
139        }
140    
141        public String toURI()
142        {
143            return buildAnchoredURI(defaultSecurity);
144        }
145    
146        private String appendAnchor(String path)
147        {
148            return InternalUtils.isBlank(anchor) ? path : path + "#" + anchor;
149        }
150    
151        private String buildAnchoredURI(LinkSecurity security)
152        {
153            return appendAnchor(response.encodeURL(buildURI(security)));
154        }
155    
156        /**
157         * Returns the value from {@link #toURI()}
158         */
159        @Override
160        public String toString()
161        {
162            return toURI();
163        }
164    
165        /**
166         * Extends the absolute path with any query parameters. Query parameters are never added to a forForm link.
167         *
168         * @return absoluteURI appended with query parameters
169         */
170        private String buildURI(LinkSecurity security)
171        {
172    
173            if (!security.isAbsolute() && (forForm || parameters == null))
174                return basePath;
175    
176            StringBuilder builder = new StringBuilder(basePath.length() * 2);
177    
178            switch (security)
179            {
180                case FORCE_SECURE:
181                    builder.append(baseURLSource.getBaseURL(true));
182                    break;
183                case FORCE_INSECURE:
184                    builder.append(baseURLSource.getBaseURL(false));
185                    break;
186                default:
187            }
188    
189            // The base URL (from BaseURLSource) does not end with a slash.
190            // The basePath does (the context path begins with a slash or is blank, then there's
191            // always a slash before the local name or page name.
192    
193            builder.append(basePath);
194    
195            if (!forForm)
196            {
197                String sep = basePath.contains("?") ? "&" : "?";
198    
199                for (String name : getParameterNames())
200                {
201                    String value = parameters.get(name);
202    
203                    builder.append(sep);
204    
205                    // We assume that the name is URL safe and that the value will already have been URL
206                    // encoded if it is not known to be URL safe.
207    
208                    builder.append(name);
209                    builder.append("=");
210                    builder.append(value);
211    
212                    sep = "&";
213                }
214            }
215    
216            return builder.toString();
217        }
218    
219        public Link addParameterValue(String parameterName, Object value)
220        {
221            addParameter(parameterName, contextPathEncoder.encodeValue(value));
222    
223            return this;
224        }
225    
226    }