001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.request.mapper.parameter; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Locale; 024import java.util.Set; 025import java.util.TreeSet; 026 027import org.apache.commons.collections4.CollectionUtils; 028import org.apache.wicket.request.IRequestMapper; 029import org.apache.wicket.util.io.IClusterable; 030import org.apache.wicket.util.lang.Args; 031import org.apache.wicket.util.lang.Objects; 032import org.apache.wicket.util.string.StringValue; 033import org.apache.wicket.util.string.Strings; 034 035/** 036 * Mutable class that holds parameters of a Page. Page parameters consist of indexed parameters and 037 * named parameters. Indexed parameters are URL segments before the query string. Named parameters 038 * are usually represented as query string params (i.e. ?arg1=var1&arg2=val) 039 * <p> 040 * <strong>Indexed vs Named Parameters</strong>: Suppose we mounted a page on {@code /user} and the 041 * following url was accessed {@code /user/profile/bob?action=view&redirect=false}. In this example 042 * {@code profile} and {@code bob} are indexed parameters with respective indexes 0 and 1. 043 * {@code action} and {@code redirect} are named parameters. 044 * </p> 045 * <p> 046 * How those parameters are populated depends on the {@link IRequestMapper}s 047 * 048 * @author Matej Knopp 049 */ 050public class PageParameters implements IClusterable, IIndexedParameters, INamedParameters 051{ 052 private static final long serialVersionUID = 1L; 053 054 private List<String> indexedParameters; 055 056 private List<NamedPair> namedParameters; 057 058 private Locale locale = Locale.getDefault(Locale.Category.DISPLAY); 059 060 /** 061 * Constructor. 062 */ 063 public PageParameters() 064 { 065 } 066 067 /** 068 * Copy constructor. 069 * 070 * @param copy 071 * The parameters to copy from 072 */ 073 public PageParameters(final PageParameters copy) 074 { 075 if (copy != null) 076 { 077 mergeWith(copy); 078 setLocale(copy.locale); 079 } 080 } 081 082 /** 083 * @return count of indexed parameters 084 */ 085 public int getIndexedCount() 086 { 087 return indexedParameters != null ? indexedParameters.size() : 0; 088 } 089 090 /** 091 * @return count of named parameters 092 */ 093 public int getNamedCount() 094 { 095 return namedParameters != null ? namedParameters.size() : 0; 096 } 097 098 /** 099 * @see org.apache.wicket.request.mapper.parameter.IIndexedParameters#set(int, java.lang.Object) 100 */ 101 @Override 102 public PageParameters set(final int index, final Object object) 103 { 104 if (indexedParameters == null) 105 { 106 indexedParameters = new ArrayList<>(index); 107 } 108 109 for (int i = indexedParameters.size(); i <= index; ++i) 110 { 111 indexedParameters.add(null); 112 } 113 114 indexedParameters.set(index, Strings.toString(object)); 115 return this; 116 } 117 118 @Override 119 public StringValue get(final int index) 120 { 121 if (indexedParameters != null) 122 { 123 if ((index >= 0) && (index < indexedParameters.size())) 124 { 125 return StringValue.valueOf(indexedParameters.get(index), locale); 126 } 127 } 128 return StringValue.valueOf((String)null); 129 } 130 131 @Override 132 public PageParameters remove(final int index) 133 { 134 if (indexedParameters != null) 135 { 136 if ((index >= 0) && (index < indexedParameters.size())) 137 { 138 indexedParameters.remove(index); 139 } 140 } 141 return this; 142 } 143 144 @Override 145 public Set<String> getNamedKeys() 146 { 147 if ((namedParameters == null) || namedParameters.isEmpty()) 148 { 149 return Collections.emptySet(); 150 } 151 Set<String> set = new TreeSet<>(); 152 for (NamedPair entry : namedParameters) 153 { 154 set.add(entry.getKey()); 155 } 156 return Collections.unmodifiableSet(set); 157 } 158 159 /** 160 * Checks if the parameter with the given name exists 161 * 162 * @param name the parameter name 163 * @return {@code true} if the parameter exists, {@code false} otherwise 164 */ 165 public boolean contains(final String name) 166 { 167 Args.notNull(name, "name"); 168 169 if (namedParameters != null) 170 { 171 for (NamedPair entry : namedParameters) 172 { 173 if (entry.getKey().equals(name)) 174 { 175 return true; 176 } 177 } 178 } 179 return false; 180 } 181 182 @Override 183 public StringValue get(final String name) 184 { 185 Args.notNull(name, "name"); 186 187 if (namedParameters != null) 188 { 189 for (NamedPair entry : namedParameters) 190 { 191 if (entry.getKey().equals(name)) 192 { 193 return StringValue.valueOf(entry.getValue(), locale); 194 } 195 } 196 } 197 return StringValue.valueOf((String)null); 198 } 199 200 @Override 201 public List<StringValue> getValues(final String name) 202 { 203 Args.notNull(name, "name"); 204 205 if (namedParameters != null) 206 { 207 List<StringValue> result = new ArrayList<>(); 208 for (NamedPair entry : namedParameters) 209 { 210 if (entry.getKey().equals(name)) 211 { 212 result.add(StringValue.valueOf(entry.getValue(), locale)); 213 } 214 } 215 return Collections.unmodifiableList(result); 216 } 217 else 218 { 219 return Collections.emptyList(); 220 } 221 } 222 223 @Override 224 public List<NamedPair> getAllNamed() 225 { 226 return namedParameters != null ? Collections.unmodifiableList(namedParameters) : Collections.<NamedPair>emptyList(); 227 } 228 229 @Override 230 public List<NamedPair> getAllNamedByType(Type type) 231 { 232 List<NamedPair> allNamed = getAllNamed(); 233 if (type == null || allNamed.isEmpty()) 234 { 235 return allNamed; 236 } 237 238 List<NamedPair> parametersByType = new ArrayList<>(); 239 for (NamedPair pair : allNamed) { 240 if (type == pair.getType()) { 241 parametersByType.add(pair); 242 } 243 } 244 return Collections.unmodifiableList(parametersByType); 245 } 246 247 @Override 248 public int getPosition(final String name) 249 { 250 int index = -1; 251 if (namedParameters != null) 252 { 253 for (int i = 0; i < namedParameters.size(); i++) 254 { 255 NamedPair entry = namedParameters.get(i); 256 if (entry.getKey().equals(name)) 257 { 258 index = i; 259 break; 260 } 261 } 262 } 263 return index; 264 } 265 266 @Override 267 public PageParameters remove(final String name, final String... values) 268 { 269 Args.notNull(name, "name"); 270 271 if (namedParameters != null) 272 { 273 for (Iterator<NamedPair> i = namedParameters.iterator(); i.hasNext();) 274 { 275 NamedPair e = i.next(); 276 if (e.getKey().equals(name)) 277 { 278 if (values != null && values.length > 0) 279 { 280 for (String value : values) 281 { 282 if (e.getValue().equals(value)) 283 { 284 i.remove(); 285 break; 286 } 287 } 288 } 289 else 290 { 291 i.remove(); 292 } 293 } 294 } 295 } 296 return this; 297 } 298 299 /** 300 * Adds a page parameter to these with {@code name} and {@code value} 301 * 302 * @param name 303 * @param value 304 * @return these 305 */ 306 public PageParameters add(final String name, final Object value) 307 { 308 return add(name, value, Type.MANUAL); 309 } 310 311 @Override 312 public PageParameters add(final String name, final Object value, Type type) 313 { 314 return add(name, value, -1, type); 315 } 316 317 @Override 318 public PageParameters add(final String name, final Object value, final int index, Type type) 319 { 320 Args.notEmpty(name, "name"); 321 Args.notNull(value, "value"); 322 323 if (value instanceof String[]) 324 { 325 addNamed(name, (String[]) value, index, type); 326 } 327 else 328 { 329 addNamed(name, value.toString(), index, type); 330 } 331 332 return this; 333 } 334 335 private void addNamed(String name, String[] values, int index, Type type) 336 { 337 if (namedParameters == null && values.length > 0) 338 { 339 namedParameters = new ArrayList<>(values.length); 340 } 341 342 for (String val : values) 343 { 344 addNamed(name, val, index, type); 345 } 346 } 347 348 private void addNamed(String name, String value, int index, Type type) 349 { 350 if (namedParameters == null) 351 { 352 namedParameters = new ArrayList<>(1); 353 } 354 355 NamedPair entry = new NamedPair(name, value, type); 356 357 if (index < 0 || index > namedParameters.size()) 358 { 359 namedParameters.add(entry); 360 } 361 else 362 { 363 namedParameters.add(index, entry); 364 } 365 } 366 367 /** 368 * Sets the page parameter with {@code name} and {@code value} at the given {@code index} 369 * 370 * @param name 371 * @param value 372 * @param index 373 * @return this 374 */ 375 public PageParameters set(final String name, final Object value, final int index) 376 { 377 return set(name, value, index, Type.MANUAL); 378 } 379 380 @Override 381 public PageParameters set(final String name, final Object value, final int index, Type type) 382 { 383 remove(name); 384 385 if (value != null) 386 { 387 add(name, value, index, type); 388 } 389 return this; 390 } 391 392 /** 393 * Sets the page parameter with {@code name} and {@code value} 394 * 395 * @param name 396 * @param value 397 * @return this 398 */ 399 public PageParameters set(final String name, final Object value) 400 { 401 return set(name, value, Type.MANUAL); 402 } 403 404 @Override 405 public PageParameters set(final String name, final Object value, Type type) 406 { 407 int position = getPosition(name); 408 set(name, value, position, type); 409 return this; 410 } 411 412 @Override 413 public PageParameters clearIndexed() 414 { 415 indexedParameters = null; 416 return this; 417 } 418 419 @Override 420 public PageParameters clearNamed() 421 { 422 namedParameters = null; 423 return this; 424 } 425 426 /** 427 * Copy the page parameters 428 * 429 * @param other 430 * The new parameters 431 * @return this instance, for chaining 432 */ 433 public PageParameters overwriteWith(final PageParameters other) 434 { 435 if (this != other) 436 { 437 indexedParameters = other.indexedParameters; 438 namedParameters = other.namedParameters; 439 locale = other.locale; 440 } 441 return this; 442 } 443 444 /** 445 * Merges the page parameters into this, overwriting existing values 446 * 447 * @param other 448 * The parameters to merge 449 * @return this instance, for chaining 450 */ 451 public PageParameters mergeWith(final PageParameters other) 452 { 453 if (other != null && this != other) 454 { 455 mergeIndexed(other); 456 mergeNamed(other); 457 } 458 return this; 459 } 460 461 private void mergeIndexed(PageParameters other) 462 { 463 final int otherIndexedCount = other.getIndexedCount(); 464 for (int index = 0; index < otherIndexedCount; index++) 465 { 466 final StringValue value = other.get(index); 467 if (!value.isNull()) 468 { 469 set(index, value); 470 } 471 } 472 } 473 474 private void mergeNamed(PageParameters other) 475 { 476 final List<NamedPair> otherNamed = other.namedParameters; 477 if (otherNamed == null || otherNamed.isEmpty()) 478 { 479 return; 480 } 481 482 for (NamedPair curNamed : otherNamed) 483 { 484 remove(curNamed.getKey()); 485 } 486 487 if (this.namedParameters == null) 488 { 489 this.namedParameters = new ArrayList<>(otherNamed.size()); 490 } 491 492 for (NamedPair curNamed : otherNamed) 493 { 494 add(curNamed.getKey(), curNamed.getValue(), curNamed.getType()); 495 } 496 } 497 498 @Override 499 public int hashCode() 500 { 501 final int prime = 31; 502 int result = 1; 503 result = prime * result + ((indexedParameters == null) ? 0 : indexedParameters.hashCode()); 504 result = prime * result + ((namedParameters == null) ? 0 : namedParameters.hashCode()); 505 return result; 506 } 507 508 @Override 509 public boolean equals(Object obj) 510 { 511 if (this == obj) 512 return true; 513 if (obj == null) 514 return false; 515 if (getClass() != obj.getClass()) 516 return false; 517 PageParameters other = (PageParameters)obj; 518 if (indexedParameters == null) 519 { 520 if (other.indexedParameters != null) 521 return false; 522 } 523 else if (!indexedParameters.equals(other.indexedParameters)) 524 return false; 525 if (namedParameters == null) 526 { 527 if (other.namedParameters != null) 528 return false; 529 } 530 else if (other.namedParameters == null) 531 return false; 532 else if (!CollectionUtils.isEqualCollection(namedParameters, other.namedParameters)) 533 return false; 534 return true; 535 } 536 537 /** 538 * Compares two {@link PageParameters} objects. 539 * 540 * @param p1 541 * The first parameters 542 * @param p2 543 * The second parameters 544 * @return <code>true</code> if the objects are equal, <code>false</code> otherwise. 545 */ 546 public static boolean equals(final PageParameters p1, final PageParameters p2) 547 { 548 if (Objects.equal(p1, p2)) 549 { 550 return true; 551 } 552 if ((p1 == null) && (p2.getIndexedCount() == 0) && p2.getNamedCount() == 0) 553 { 554 return true; 555 } 556 if ((p2 == null) && (p1.getIndexedCount() == 0) && p1.getNamedCount() == 0) 557 { 558 return true; 559 } 560 return false; 561 } 562 563 /** 564 * @return <code>true</code> if the parameters are empty, <code>false</code> otherwise. 565 */ 566 public boolean isEmpty() 567 { 568 return getIndexedCount() == 0 && getNamedCount() == 0; 569 } 570 571 public PageParameters setLocale(Locale locale) 572 { 573 this.locale = locale != null ? locale : Locale.getDefault(Locale.Category.DISPLAY); 574 return this; 575 } 576 577 @Override 578 public String toString() 579 { 580 StringBuilder str = new StringBuilder(); 581 582 if (indexedParameters != null) 583 { 584 for (int i = 0; i < indexedParameters.size(); i++) 585 { 586 if (i > 0) 587 { 588 str.append(", "); 589 } 590 591 str.append(i); 592 str.append('='); 593 str.append('[').append(indexedParameters.get(i)).append(']'); 594 } 595 } 596 597 if (str.length() > 0) 598 { 599 str.append(", "); 600 } 601 602 if (namedParameters != null) 603 { 604 for (int i = 0; i < namedParameters.size(); i++) 605 { 606 NamedPair entry = namedParameters.get(i); 607 608 if (i > 0) 609 { 610 str.append(", "); 611 } 612 613 str.append(entry.getKey()); 614 str.append('='); 615 str.append('[').append(entry.getValue()).append(']'); 616 } 617 } 618 return str.toString(); 619 } 620}