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 }