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 }