• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / displaytag / 1753

12 Feb 2026 03:17AM UTC coverage: 77.321% (-0.01%) from 77.334%
1753

push

github

web-flow
Merge pull request #1102 from hazendaz/renovate/javax-support-logback-monorepo

Update dependency ch.qos.logback:logback-classic to v1.5.29 (javax-support)

1438 of 2003 branches covered (71.79%)

Branch coverage included in aggregate %.

4034 of 5074 relevant lines covered (79.5%)

0.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.65
/displaytag/src/main/java/org/displaytag/util/DefaultHref.java
1
/*
2
 * SPDX-License-Identifier: MIT
3
 * See LICENSE file for details.
4
 *
5
 * Copyright 2002-2026 Fabrizio Giustina, the Displaytag team
6
 */
7
package org.displaytag.util;
8

9
import java.net.URLDecoder;
10
import java.net.URLEncoder;
11
import java.nio.charset.StandardCharsets;
12
import java.util.HashMap;
13
import java.util.Iterator;
14
import java.util.LinkedHashMap;
15
import java.util.Map;
16
import java.util.Map.Entry;
17
import java.util.Set;
18
import java.util.StringTokenizer;
19

20
import org.apache.commons.lang3.StringUtils;
21
import org.apache.commons.lang3.builder.EqualsBuilder;
22
import org.apache.commons.lang3.builder.HashCodeBuilder;
23

24
/**
25
 * The Class DefaultHref.
26
 */
27
public class DefaultHref implements Href {
28

29
    /**
30
     * Serial ID.
31
     */
32
    private static final long serialVersionUID = 899149338534L;
33

34
    /**
35
     * Base url for the href.
36
     */
37
    private String url;
38

39
    /**
40
     * Url parameters.
41
     */
42
    private Map<String, String[]> parameters;
43

44
    /**
45
     * Anchor (to be added at the end of URL).
46
     */
47
    private String anchor;
48

49
    /**
50
     * Construct a new Href parsing a URL. Parameters are stripped from the base url and saved in the parameters map.
51
     *
52
     * @param baseUrl
53
     *            String
54
     */
55
    public DefaultHref(final String baseUrl) {
1✔
56
        this.parameters = new LinkedHashMap<>();
1✔
57
        this.setFullUrl(baseUrl);
1✔
58
    }
1✔
59

60
    /**
61
     * Sets the full url.
62
     *
63
     * @param baseUrl
64
     *            the new full url
65
     *
66
     * @see org.displaytag.util.Href#setFullUrl(java.lang.String)
67
     */
68
    @Override
69
    public void setFullUrl(final String baseUrl) {
70
        this.url = null;
1✔
71
        this.anchor = null;
1✔
72
        String noAnchorUrl;
73

74
        final int anchorposition = baseUrl.indexOf('#');
1✔
75

76
        // extract anchor from url
77
        if (anchorposition != -1) {
1✔
78
            noAnchorUrl = baseUrl.substring(0, anchorposition);
1✔
79
            this.anchor = baseUrl.substring(anchorposition + 1);
1✔
80
        } else {
81
            noAnchorUrl = baseUrl;
1✔
82
        }
83

84
        if (noAnchorUrl.indexOf('?') == -1) {
1✔
85
            // simple url, no parameters
86
            this.url = noAnchorUrl;
1✔
87
            return;
1✔
88
        }
89

90
        // the Url already has parameters, put them in the parameter Map
91
        final StringTokenizer tokenizer = new StringTokenizer(noAnchorUrl, "?"); //$NON-NLS-1$
1✔
92

93
        if (baseUrl.startsWith("?")) //$NON-NLS-1$
1✔
94
        {
95
            // support fake URI's which are just parameters to use with the current uri
96
            this.url = TagConstants.EMPTY_STRING;
1✔
97
        } else {
98
            // base url (before "?")
99
            this.url = tokenizer.nextToken();
1✔
100
        }
101

102
        if (!tokenizer.hasMoreTokens()) {
1!
103
            return;
×
104
        }
105

106
        // process parameters
107
        final StringTokenizer paramTokenizer = new StringTokenizer(tokenizer.nextToken(), "&"); //$NON-NLS-1$
1✔
108

109
        // split parameters (key=value)
110
        while (paramTokenizer.hasMoreTokens()) {
1✔
111
            // split key and value ...
112
            final String[] keyValue = StringUtils.split(paramTokenizer.nextToken(), '=');
1✔
113

114
            // encode name/value to prevent css
115
            final String decodedkey = this.decodeParam(keyValue[0]);
1✔
116
            final String decodedvalue = this.decodeParam(keyValue.length > 1 ? keyValue[1] : StringUtils.EMPTY);
1✔
117

118
            if (!this.parameters.containsKey(decodedkey)) {
1✔
119
                // ... and add it to the map
120
                this.parameters.put(decodedkey, new String[] { decodedvalue });
1✔
121
            } else {
122
                // additional value for an existing parameter
123
                final String[] previousValue = this.parameters.get(decodedkey);
1✔
124

125
                final String[] newArray = new String[previousValue.length + 1];
1✔
126

127
                int j;
128

129
                for (j = 0; j < previousValue.length; j++) {
1✔
130
                    newArray[j] = previousValue[j];
1✔
131
                }
132

133
                newArray[j] = decodedvalue;
1✔
134
                this.parameters.put(decodedkey, newArray);
1✔
135

136
            }
137
        }
1✔
138
    }
1✔
139

140
    /**
141
     * Adds a parameter to the href.
142
     *
143
     * @param key
144
     *            String
145
     * @param value
146
     *            Object
147
     *
148
     * @return this Href instance, useful for concatenation.
149
     */
150
    @Override
151
    public Href addParameter(final String key, final Object value) {
152
        this.parameters.put(this.decodeParam(key), new String[] { this.decodeParam(value) });
1✔
153
        return this;
1✔
154
    }
155

156
    /**
157
     * Removes a parameter from the href.
158
     *
159
     * @param key
160
     *            String
161
     */
162
    @Override
163
    public void removeParameter(final String key) {
164
        this.parameters.remove(this.decodeParam(key));
1✔
165
    }
1✔
166

167
    /**
168
     * Adds an int parameter to the href.
169
     *
170
     * @param key
171
     *            String
172
     * @param value
173
     *            int
174
     *
175
     * @return this Href instance, useful for concatenation.
176
     */
177
    @Override
178
    public Href addParameter(final String key, final int value) {
179
        this.parameters.put(this.decodeParam(key), new String[] { Integer.toString(value) });
1✔
180
        return this;
1✔
181
    }
182

183
    /**
184
     * Getter for the map containing link parameters. The returned map is always a copy and not the original instance.
185
     *
186
     * @return parameter Map (copy)
187
     */
188
    @Override
189
    public Map<String, String[]> getParameterMap() {
190
        final Map<String, String[]> copyMap = new LinkedHashMap<>(this.parameters);
1✔
191
        return copyMap;
1✔
192
    }
193

194
    /**
195
     * Adds all the parameters contained in the map to the Href. The value in the given Map will be escaped before
196
     * added. Any parameter already present in the href object is removed.
197
     *
198
     * @param parametersMap
199
     *            Map containing parameters
200
     */
201
    @Override
202
    public void setParameterMap(final Map<String, String[]> parametersMap) {
203
        // create a new HashMap
204
        this.parameters = new HashMap<>(parametersMap.size());
1✔
205

206
        // copy the parameters
207
        this.addParameterMap(parametersMap);
1✔
208
    }
1✔
209

210
    /**
211
     * Adds all the parameters contained in the map to the Href. The value in the given Map will be escaped before
212
     * added. Parameters in the original href are kept and not overridden.
213
     *
214
     * @param parametersMap
215
     *            Map containing parameters
216
     */
217
    @Override
218
    public void addParameterMap(final Map<String, String[]> parametersMap) {
219
        // handle nulls
220
        if (parametersMap == null) {
1!
221
            return;
×
222
        }
223

224
        // copy value, escaping html
225
        final Iterator<Entry<String, String[]>> mapIterator = parametersMap.entrySet().iterator();
1✔
226
        while (mapIterator.hasNext()) {
1✔
227
            final Entry<String, String[]> entry = mapIterator.next();
1✔
228
            final String key = this.decodeParam(entry.getKey());
1✔
229

230
            // don't overwrite parameters
231
            if (!this.parameters.containsKey(key)) {
1✔
232
                final String[] value = entry.getValue();
1✔
233

234
                if (value != null) {
1✔
235
                    String[] values;
236
                    // check mantained for binary compatibility with displaytag 1.2
237
                    if (value.getClass().isArray()) {
1!
238
                        values = value;
1✔
239
                        for (int i = 0; i < values.length; i++) {
1✔
240
                            values[i] = this.decodeParam(values[i]);
1✔
241
                        }
242
                    } else {
243
                        values = new String[] { this.decodeParam(value) };
×
244
                    }
245

246
                    this.parameters.put(key, values);
1✔
247
                } else {
1✔
248
                    this.parameters.put(key, new String[0]);
1✔
249
                }
250
            }
251
        }
1✔
252
    }
1✔
253

254
    /**
255
     * Getter for the base url (without parameters).
256
     *
257
     * @return String
258
     */
259
    @Override
260
    public String getBaseUrl() {
261
        return this.url;
1✔
262
    }
263

264
    /**
265
     * Returns the URI anchor.
266
     *
267
     * @return anchor or <code>null</code> if no anchor has been set.
268
     */
269
    @Override
270
    public String getAnchor() {
271
        return this.anchor;
×
272
    }
273

274
    /**
275
     * Setter for the URI anchor.
276
     *
277
     * @param name
278
     *            string to be used as anchor name (without #).
279
     */
280
    @Override
281
    public void setAnchor(final String name) {
282
        this.anchor = name;
×
283
    }
×
284

285
    /**
286
     * toString: output the full url with parameters.
287
     *
288
     * @return String
289
     */
290
    @Override
291
    public String toString() {
292
        final StringBuilder buffer = new StringBuilder(30);
1✔
293

294
        buffer.append(this.url);
1✔
295

296
        if (this.parameters.size() > 0) {
1✔
297
            buffer.append('?');
1✔
298
            final Set<Entry<String, String[]>> parameterSet = this.parameters.entrySet();
1✔
299

300
            final Iterator<Entry<String, String[]>> iterator = parameterSet.iterator();
1✔
301

302
            while (iterator.hasNext()) {
1✔
303
                final Entry<String, String[]> entry = iterator.next();
1✔
304

305
                final Object key = entry.getKey();
1✔
306
                final Object value = entry.getValue();
1✔
307

308
                if (value == null) {
1!
309
                    buffer.append(this.encodeParam(key)).append('='); // no value
×
310
                } else if (value.getClass().isArray()) {
1!
311
                    final Object[] values = (Object[]) value;
1✔
312
                    if (values.length == 0) {
1✔
313
                        buffer.append(this.encodeParam(key)).append('='); // no value
1✔
314
                    } else {
315
                        for (int i = 0; i < values.length; i++) {
1✔
316
                            if (i > 0) {
1✔
317
                                buffer.append(TagConstants.AMPERSAND);
1✔
318
                            }
319

320
                            buffer.append(this.encodeParam(key)).append('=').append(this.encodeParam(values[i]));
1✔
321
                        }
322
                    }
323
                } else {
1✔
324
                    buffer.append(this.encodeParam(key)).append('=').append(this.encodeParam(value));
×
325
                }
326

327
                if (iterator.hasNext()) {
1✔
328
                    buffer.append(TagConstants.AMPERSAND);
1✔
329
                }
330
            }
1✔
331
        }
332

333
        if (this.anchor != null) {
1✔
334
            buffer.append('#');
1✔
335
            buffer.append(this.anchor);
1✔
336
        }
337

338
        return buffer.toString();
1✔
339
    }
340

341
    /**
342
     * Encode param.
343
     *
344
     * @param param
345
     *            the param
346
     *
347
     * @return the string
348
     */
349
    private String encodeParam(final Object param) {
350
        if (param == null) {
1!
351
            return StringUtils.EMPTY;
×
352
        }
353
        return URLEncoder.encode(param.toString(), StandardCharsets.UTF_8);
1✔
354
    }
355

356
    /**
357
     * Decode param.
358
     *
359
     * @param param
360
     *            the param
361
     *
362
     * @return the string
363
     */
364
    private String decodeParam(final Object param) {
365
        if (param == null) {
1✔
366
            return StringUtils.EMPTY;
1✔
367
        }
368
        return URLDecoder.decode(param.toString(), StandardCharsets.UTF_8);
1✔
369
    }
370

371
    /**
372
     * Clone.
373
     *
374
     * @return the object
375
     *
376
     * @see java.lang.Object#clone()
377
     */
378
    @Override
379
    public Object clone() {
380
        final DefaultHref href;
381
        try {
382
            href = (DefaultHref) super.clone();
1✔
383
        } catch (final CloneNotSupportedException e) {
×
384
            throw new RuntimeException(e); // should never happen
×
385
        }
1✔
386

387
        href.parameters = new LinkedHashMap<>(this.parameters);
1✔
388
        return href;
1✔
389
    }
390

391
    /**
392
     * Equals.
393
     *
394
     * @param object
395
     *            the object
396
     *
397
     * @return true, if successful
398
     *
399
     * @see java.lang.Object#equals(Object)
400
     */
401
    @Override
402
    public boolean equals(final Object object) {
403
        if (!(object instanceof DefaultHref)) {
1!
404
            return false;
×
405
        }
406
        final DefaultHref rhs = (DefaultHref) object;
1✔
407

408
        // "parameters" can't be added directly, since equals on HashMap doesn't return true with equal key/values
409
        return new EqualsBuilder().append(this.parameters.keySet(), rhs.parameters.keySet())
1✔
410
                .append(this.parameters.values().toArray(), rhs.parameters.values().toArray()).append(this.url, rhs.url)
1✔
411
                .append(this.anchor, rhs.anchor).isEquals();
1✔
412
    }
413

414
    /**
415
     * Hash code.
416
     *
417
     * @return the int
418
     *
419
     * @see java.lang.Object#hashCode()
420
     */
421
    @Override
422
    public int hashCode() {
423
        return new HashCodeBuilder(1313733113, -431360889).append(this.parameters.keySet())
×
424
                .append(this.parameters.values().toArray()).append(this.url).append(this.anchor).toHashCode();
×
425
    }
426
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc