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