1 /** 2 * Licensed under the Artistic License; you may not use this file 3 * except in compliance with the License. 4 * You may obtain a copy of the License at 5 * 6 * http://displaytag.sourceforge.net/license.html 7 * 8 * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 10 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 11 */ 12 package org.displaytag.util; 13 14 import java.util.HashMap; 15 import java.util.Iterator; 16 import java.util.Map; 17 import java.util.Set; 18 import java.util.StringTokenizer; 19 20 import org.apache.commons.lang.ObjectUtils; 21 import org.apache.commons.lang.StringEscapeUtils; 22 import org.apache.commons.lang.StringUtils; 23 import org.apache.commons.lang.UnhandledException; 24 import org.apache.commons.lang.builder.EqualsBuilder; 25 import org.apache.commons.lang.builder.HashCodeBuilder; 26 27 28 /** 29 * @author Fabrizio Giustina 30 * @version $Revision$ ($Author$) 31 */ 32 public class DefaultHref implements Href 33 { 34 35 /** 36 * D1597A17A6. 37 */ 38 private static final long serialVersionUID = 899149338534L; 39 40 /** 41 * Base url for the href. 42 */ 43 private String url; 44 45 /** 46 * Url parameters. 47 */ 48 private Map parameters; 49 50 /** 51 * Anchor (to be added at the end of URL). 52 */ 53 private String anchor; 54 55 /** 56 * Construct a new Href parsing a URL. Parameters are stripped from the base url and saved in the parameters map. 57 * @param baseUrl String 58 */ 59 public DefaultHref(String baseUrl) 60 { 61 this.parameters = new HashMap(); 62 setFullUrl(baseUrl); 63 } 64 65 /** 66 * @see org.displaytag.util.Href#setFullUrl(java.lang.String) 67 */ 68 public void setFullUrl(String baseUrl) 69 { 70 this.url = null; 71 this.anchor = null; 72 String noAnchorUrl; 73 int anchorposition = baseUrl.indexOf('#'); 74 75 // extract anchor from url 76 if (anchorposition != -1) 77 { 78 noAnchorUrl = baseUrl.substring(0, anchorposition); 79 this.anchor = baseUrl.substring(anchorposition + 1); 80 } 81 else 82 { 83 noAnchorUrl = baseUrl; 84 } 85 86 if (noAnchorUrl.indexOf('?') == -1) 87 { 88 // simple url, no parameters 89 this.url = noAnchorUrl; 90 return; 91 } 92 93 // the Url already has parameters, put them in the parameter Map 94 StringTokenizer tokenizer = new StringTokenizer(noAnchorUrl, "?"); //$NON-NLS-1$ 95 96 if (baseUrl.startsWith("?")) //$NON-NLS-1$ 97 { 98 // support fake URI's which are just parameters to use with the current uri 99 url = TagConstants.EMPTY_STRING; 100 } 101 else 102 { 103 // base url (before "?") 104 url = tokenizer.nextToken(); 105 } 106 107 if (!tokenizer.hasMoreTokens()) 108 { 109 return; 110 } 111 112 // process parameters 113 StringTokenizer paramTokenizer = new StringTokenizer(tokenizer.nextToken(), "&"); //$NON-NLS-1$ 114 115 // split parameters (key=value) 116 while (paramTokenizer.hasMoreTokens()) 117 { 118 // split key and value ... 119 String[] keyValue = StringUtils.split(paramTokenizer.nextToken(), '='); 120 121 // encode name/value to prevent css 122 String escapedKey = StringEscapeUtils.escapeHtml(keyValue[0]); 123 String escapedValue = keyValue.length > 1 124 ? StringEscapeUtils.escapeHtml(keyValue[1]) 125 : TagConstants.EMPTY_STRING; 126 127 if (!this.parameters.containsKey(escapedKey)) 128 { 129 // ... and add it to the map 130 this.parameters.put(escapedKey, escapedValue); 131 } 132 else 133 { 134 // additional value for an existing parameter 135 Object previousValue = this.parameters.get(escapedKey); 136 if (previousValue != null && previousValue.getClass().isArray()) 137 { 138 Object[] previousArray = (Object[]) previousValue; 139 Object[] newArray = new Object[previousArray.length + 1]; 140 141 int j; 142 143 for (j = 0; j < previousArray.length; j++) 144 { 145 newArray[j] = previousArray[j]; 146 } 147 148 newArray[j] = escapedValue; 149 this.parameters.put(escapedKey, newArray); 150 } 151 else 152 { 153 this.parameters.put(escapedKey, new Object[]{previousValue, escapedValue}); 154 } 155 } 156 } 157 } 158 159 /** 160 * Adds a parameter to the href. 161 * @param name String 162 * @param value Object 163 * @return this Href instance, useful for concatenation. 164 */ 165 public Href addParameter(String name, Object value) 166 { 167 this.parameters.put(name, ObjectUtils.toString(value, null)); 168 return this; 169 } 170 171 /** 172 * Removes a parameter from the href. 173 * @param name String 174 */ 175 public void removeParameter(String name) 176 { 177 // warning, param names are escaped 178 this.parameters.remove(StringEscapeUtils.escapeHtml(name)); 179 } 180 181 /** 182 * Adds an int parameter to the href. 183 * @param name String 184 * @param value int 185 * @return this Href instance, useful for concatenation. 186 */ 187 public Href addParameter(String name, int value) 188 { 189 this.parameters.put(name, new Integer(value)); 190 return this; 191 } 192 193 /** 194 * Getter for the map containing link parameters. The returned map is always a copy and not the original instance. 195 * @return parameter Map (copy) 196 */ 197 public Map getParameterMap() 198 { 199 Map copyMap = new HashMap(this.parameters.size()); 200 copyMap.putAll(this.parameters); 201 return copyMap; 202 } 203 204 /** 205 * Adds all the parameters contained in the map to the Href. The value in the given Map will be escaped before 206 * added. Any parameter already present in the href object is removed. 207 * @param parametersMap Map containing parameters 208 */ 209 public void setParameterMap(Map parametersMap) 210 { 211 // create a new HashMap 212 this.parameters = new HashMap(parametersMap.size()); 213 214 // copy the parameters 215 addParameterMap(parametersMap); 216 } 217 218 /** 219 * Adds all the parameters contained in the map to the Href. The value in the given Map will be escaped before 220 * added. Parameters in the original href are kept and not overridden. 221 * @param parametersMap Map containing parameters 222 */ 223 public void addParameterMap(Map parametersMap) 224 { 225 // handle nulls 226 if (parametersMap == null) 227 { 228 return; 229 } 230 231 // copy value, escaping html 232 Iterator mapIterator = parametersMap.entrySet().iterator(); 233 while (mapIterator.hasNext()) 234 { 235 Map.Entry entry = (Map.Entry) mapIterator.next(); 236 String key = StringEscapeUtils.escapeHtml((String) entry.getKey()); 237 238 // don't overwrite parameters 239 if (!this.parameters.containsKey(key)) 240 { 241 Object value = entry.getValue(); 242 243 if (value != null) 244 { 245 if (value.getClass().isArray()) 246 { 247 String[] values = (String[]) value; 248 for (int i = 0; i < values.length; i++) 249 { 250 values[i] = StringEscapeUtils.escapeHtml(values[i]); 251 } 252 } 253 else 254 { 255 value = StringEscapeUtils.escapeHtml(value.toString()); 256 } 257 } 258 259 this.parameters.put(key, value); 260 } 261 } 262 } 263 264 /** 265 * Getter for the base url (without parameters). 266 * @return String 267 */ 268 public String getBaseUrl() 269 { 270 return this.url; 271 } 272 273 /** 274 * Returns the URI anchor. 275 * @return anchor or <code>null</code> if no anchor has been set. 276 */ 277 public String getAnchor() 278 { 279 return this.anchor; 280 } 281 282 /** 283 * Setter for the URI anchor. 284 * @param name string to be used as anchor name (without #). 285 */ 286 public void setAnchor(String name) 287 { 288 this.anchor = name; 289 } 290 291 /** 292 * toString: output the full url with parameters. 293 * @return String 294 */ 295 public String toString() 296 { 297 StringBuffer buffer = new StringBuffer(30); 298 299 buffer.append(this.url); 300 301 if (this.parameters.size() > 0) 302 { 303 buffer.append('?'); 304 Set parameterSet = this.parameters.entrySet(); 305 306 Iterator iterator = parameterSet.iterator(); 307 308 while (iterator.hasNext()) 309 { 310 Map.Entry entry = (Map.Entry) iterator.next(); 311 312 Object key = entry.getKey(); 313 Object value = entry.getValue(); 314 315 if (value == null) 316 { 317 buffer.append(key).append('='); // no value 318 } 319 else if (value.getClass().isArray()) 320 { 321 Object[] values = (Object[]) value; 322 for (int i = 0; i < values.length; i++) 323 { 324 if (i > 0) 325 { 326 buffer.append(TagConstants.AMPERSAND); 327 } 328 329 buffer.append(key).append('=').append(values[i]); 330 } 331 } 332 else 333 { 334 buffer.append(key).append('=').append(value); 335 } 336 337 if (iterator.hasNext()) 338 { 339 buffer.append(TagConstants.AMPERSAND); 340 } 341 } 342 } 343 344 if (this.anchor != null) 345 { 346 buffer.append('#'); 347 buffer.append(this.anchor); 348 } 349 350 return buffer.toString(); 351 } 352 353 /** 354 * @see java.lang.Object#clone() 355 */ 356 public Object clone() 357 { 358 final DefaultHref href; 359 try 360 { 361 href = (DefaultHref) super.clone(); 362 } 363 catch (CloneNotSupportedException e) 364 { 365 throw new UnhandledException(e); 366 } 367 368 href.parameters = new HashMap(this.parameters); 369 return href; 370 } 371 372 /** 373 * @see java.lang.Object#equals(Object) 374 */ 375 public boolean equals(Object object) 376 { 377 if (!(object instanceof DefaultHref)) 378 { 379 return false; 380 } 381 DefaultHref rhs = (DefaultHref) object; 382 return new EqualsBuilder().append(this.parameters, rhs.parameters).append(this.url, rhs.url).append( 383 this.anchor, 384 rhs.anchor).isEquals(); 385 } 386 387 /** 388 * @see java.lang.Object#hashCode() 389 */ 390 public int hashCode() 391 { 392 return new HashCodeBuilder(1313733113, -431360889) 393 .append(this.parameters) 394 .append(this.url) 395 .append(this.anchor) 396 .toHashCode(); 397 } 398 }