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 }