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.resource; 018 019import java.io.Serializable; 020import java.util.ArrayList; 021import java.util.List; 022import java.util.Locale; 023 024import org.apache.wicket.Application; 025import org.apache.wicket.core.util.lang.WicketObjects; 026import org.apache.wicket.markup.head.HeaderItem; 027import org.apache.wicket.util.io.IClusterable; 028import org.apache.wicket.util.lang.Args; 029import org.apache.wicket.util.lang.Objects; 030import org.danekja.java.util.function.serializable.SerializableSupplier; 031 032/** 033 * Reference to a resource. Can be used to reference global resources. 034 * <p> 035 * Even though resource reference is just a factory for resources, it still needs to be identified 036 * by a globally unique identifier, combination of <code>scope</code> and <code>name</code>. Those 037 * are used to generate URLs for resource references. <code>locale</code>, <code>style</code> and 038 * <code>variation</code> are optional fields to allow having specific references for individual 039 * locales, styles and variations. 040 * 041 * @author Matej Knopp 042 * @author Juergen Donnerstag 043 */ 044public abstract class ResourceReference implements IClusterable 045{ 046 private static final long serialVersionUID = 1L; 047 048 private final Key data; 049 050 /** 051 * Creates new {@link ResourceReference} instance. 052 * 053 * @param key 054 * The data making up the resource reference 055 */ 056 public ResourceReference(final Key key) 057 { 058 Args.notNull(key, "key"); 059 060 data = key; 061 } 062 063 /** 064 * Creates new {@link ResourceReference} instance. 065 * 066 * @param scope 067 * mandatory parameter 068 * @param name 069 * mandatory parameter 070 * @param locale 071 * resource locale 072 * @param style 073 * resource style 074 * @param variation 075 * resource variation 076 */ 077 public ResourceReference(Class<?> scope, String name, Locale locale, String style, 078 String variation) 079 { 080 Args.notNull(scope, "scope"); 081 Args.notNull(name, "name"); 082 083 data = new Key(scope.getName(), name, locale, style, variation); 084 } 085 086 /** 087 * Creates new {@link ResourceReference} instance. 088 * 089 * @param scope 090 * mandatory parameter 091 * @param name 092 * mandatory parameter 093 */ 094 public ResourceReference(Class<?> scope, String name) 095 { 096 this(scope, name, null, null, null); 097 } 098 099 /** 100 * Construct. 101 * 102 * @param name 103 * resource name 104 */ 105 public ResourceReference(String name) 106 { 107 this(Application.class, name, null, null, null); 108 } 109 110 /** 111 * @return Gets the data making up the resource reference. They'll be use by 112 * ResourceReferenceRegistry to make up the key under which the resource reference gets 113 * stored. 114 */ 115 public final Key getKey() 116 { 117 return data; 118 } 119 120 /** 121 * @return name 122 */ 123 public String getName() 124 { 125 return data.getName(); 126 } 127 128 /** 129 * returns extension of the resource reference 130 * 131 * @return extension of the resource's name in lower-case or <code>null</code> if there is no 132 * extension 133 */ 134 public final String getExtension() 135 { 136 String name = getName(); 137 138 final int queryAt = name.indexOf('?'); 139 140 // remove query string part 141 if (queryAt != -1) 142 { 143 name = name.substring(0, queryAt); 144 } 145 146 // get start of extension 147 final int extPos = name.lastIndexOf('.'); 148 149 if (extPos == -1) 150 { 151 return null; 152 } 153 154 // return extension 155 return name.substring(extPos + 1).toLowerCase(Locale.ROOT); 156 } 157 158 /** 159 * @return scope 160 */ 161 public Class<?> getScope() 162 { 163 return WicketObjects.resolveClass(data.getScope()); 164 } 165 166 /** 167 * @return locale 168 */ 169 public Locale getLocale() 170 { 171 return data.getLocale(); 172 } 173 174 /** 175 * @return style 176 */ 177 public String getStyle() 178 { 179 return data.getStyle(); 180 } 181 182 /** 183 * @return variation 184 */ 185 public String getVariation() 186 { 187 return data.getVariation(); 188 } 189 190 /** 191 * Can be used to disable registering certain resource references in 192 * {@link ResourceReferenceRegistry}. 193 * 194 * @return <code>true</code> if this reference can be registered, <code>false</code> otherwise. 195 */ 196 public boolean canBeRegistered() 197 { 198 return true; 199 } 200 201 /** 202 * @see java.lang.Object#equals(java.lang.Object) 203 */ 204 @Override 205 public boolean equals(Object obj) 206 { 207 if (this == obj) 208 { 209 return true; 210 } 211 if (obj instanceof ResourceReference == false) 212 { 213 return false; 214 } 215 ResourceReference that = (ResourceReference)obj; 216 return Objects.equal(data, that.data); 217 } 218 219 /** 220 * @see java.lang.Object#hashCode() 221 */ 222 @Override 223 public int hashCode() 224 { 225 return data.hashCode(); 226 } 227 228 /** 229 * Returns the resource. 230 * 231 * @return resource instance 232 */ 233 public abstract IResource getResource(); 234 235 /** 236 * Allows to specify which locale, style and variation values will the generated URL for this 237 * resource reference have. 238 * 239 * @return url attributes 240 */ 241 public UrlAttributes getUrlAttributes() 242 { 243 return new UrlAttributes(getLocale(), getStyle(), getVariation()); 244 } 245 246 /** 247 * Factory method to build a resource reference that uses the provided supplier to return 248 * the resource. 249 * 250 * @param name 251 * The name to use with the resource 252 * @param resourceSupplier 253 * Lambda supplier to build the resource 254 * @return the new resource reference 255 */ 256 public static final ResourceReference of(String name, SerializableSupplier<IResource> resourceSupplier) 257 { 258 return new LambdaResourceReference(name, resourceSupplier); 259 } 260 261 /** 262 * Factory method to build a resource reference that uses the provided supplier to return 263 * the resource. 264 * 265 * @param key 266 * The {@link Key} to use with the resource 267 * @param resourceSupplier 268 * Lambda supplier to build the resource 269 * @return the new resource reference 270 */ 271 public static final ResourceReference of(Key key, SerializableSupplier<IResource> resourceSupplier) 272 { 273 return new LambdaResourceReference(key, resourceSupplier); 274 } 275 276 public static final class LambdaResourceReference extends ResourceReference 277 { 278 private static final long serialVersionUID = 1826862147241009289L; 279 280 final SerializableSupplier<IResource> resourceBuilder; 281 282 public LambdaResourceReference(String name, SerializableSupplier<IResource> resourceBuilder) 283 { 284 super(name); 285 this.resourceBuilder = Args.notNull(resourceBuilder, "resourceBuilder"); 286 } 287 288 public LambdaResourceReference(Key key, SerializableSupplier<IResource> resourceBuilder) 289 { 290 super(key); 291 this.resourceBuilder = Args.notNull(resourceBuilder, "resourceBuilder"); 292 } 293 294 @Override 295 public IResource getResource() 296 { 297 return resourceBuilder.get(); 298 } 299 } 300 301 /** 302 * @see ResourceReference#getUrlAttributes() 303 * 304 * @author Matej Knopp 305 */ 306 public static class UrlAttributes 307 { 308 private final Locale locale; 309 private final String style; 310 private final String variation; 311 312 /** 313 * Construct. 314 * 315 * @param locale 316 * resource locale 317 * @param style 318 * resource style 319 * @param variation 320 * resource variation 321 */ 322 public UrlAttributes(Locale locale, String style, String variation) 323 { 324 this.locale = locale; 325 this.style = style; 326 this.variation = variation; 327 } 328 329 /** 330 * @return locale 331 */ 332 public Locale getLocale() 333 { 334 return locale; 335 } 336 337 /** 338 * @return style 339 */ 340 public String getStyle() 341 { 342 return style; 343 } 344 345 /** 346 * @return variation 347 */ 348 public String getVariation() 349 { 350 return variation; 351 } 352 353 /** 354 * @see java.lang.Object#equals(java.lang.Object) 355 */ 356 @Override 357 public boolean equals(Object obj) 358 { 359 if (this == obj) 360 { 361 return true; 362 } 363 if (obj instanceof UrlAttributes == false) 364 { 365 return false; 366 } 367 UrlAttributes that = (UrlAttributes)obj; 368 return Objects.equal(getLocale(), that.getLocale()) && 369 Objects.equal(getStyle(), that.getStyle()) && 370 Objects.equal(getVariation(), that.getVariation()); 371 } 372 373 /** 374 * @see java.lang.Object#hashCode() 375 */ 376 @Override 377 public int hashCode() 378 { 379 return Objects.hashCode(getLocale(), getStyle(), getVariation()); 380 } 381 382 /** 383 * @see java.lang.Object#toString() 384 */ 385 @Override 386 public String toString() 387 { 388 return "locale: " + locale + "; style: " + style + "; variation: " + variation; 389 } 390 } 391 392 /** 393 * A (re-usable) data store for all relevant ResourceReference data 394 */ 395 public static class Key implements Serializable 396 { 397 private static final long serialVersionUID = 1L; 398 399 private final String scope; 400 private final String name; 401 private final Locale locale; 402 private final String style; 403 private final String variation; 404 405 /** 406 * Construct. 407 * 408 * @param reference 409 * resource reference 410 */ 411 public Key(final ResourceReference reference) 412 { 413 this(reference.getScope().getName(), reference.getName(), reference.getLocale(), 414 reference.getStyle(), reference.getVariation()); 415 } 416 417 /** 418 * Construct. 419 * 420 * @param scope 421 * resource scope 422 * @param name 423 * resource name 424 * @param locale 425 * resource locale 426 * @param style 427 * resource style 428 * @param variation 429 * resource variation 430 */ 431 public Key(final String scope, final String name, final Locale locale, final String style, 432 final String variation) 433 { 434 Args.notNull(scope, "scope"); 435 Args.notNull(name, "name"); 436 437 this.scope = scope.intern(); 438 this.name = name.intern(); 439 this.locale = locale; 440 this.style = style != null ? style.intern() : null; 441 this.variation = variation != null ? variation.intern() : null; 442 } 443 444 /** 445 * @see java.lang.Object#equals(java.lang.Object) 446 */ 447 @Override 448 public boolean equals(final Object obj) 449 { 450 if (this == obj) 451 { 452 return true; 453 } 454 if (obj instanceof Key == false) 455 { 456 return false; 457 } 458 Key that = (Key)obj; 459 return Objects.equal(scope, that.scope) && // 460 Objects.equal(name, that.name) && // 461 Objects.equal(locale, that.locale) && // 462 Objects.equal(style, that.style) && // 463 Objects.equal(variation, that.variation); 464 } 465 466 /** 467 * @see java.lang.Object#hashCode() 468 */ 469 @Override 470 public int hashCode() 471 { 472 return Objects.hashCode(scope, name, locale, style, variation); 473 } 474 475 /** 476 * Gets scope. 477 * 478 * @return scope 479 */ 480 public final String getScope() 481 { 482 return scope; 483 } 484 485 /** 486 * @return Assuming scope ist a fully qualified class name, than get the associated class 487 */ 488 public final Class<?> getScopeClass() 489 { 490 return WicketObjects.resolveClass(scope); 491 } 492 493 /** 494 * Gets name. 495 * 496 * @return name 497 */ 498 public final String getName() 499 { 500 return name; 501 } 502 503 /** 504 * Gets locale. 505 * 506 * @return locale 507 */ 508 public final Locale getLocale() 509 { 510 return locale; 511 } 512 513 /** 514 * Gets style. 515 * 516 * @return style 517 */ 518 public final String getStyle() 519 { 520 return style; 521 } 522 523 /** 524 * Gets variation. 525 * 526 * @return variation 527 */ 528 public final String getVariation() 529 { 530 return variation; 531 } 532 533 /** 534 * @see java.lang.Object#toString() 535 */ 536 @Override 537 public String toString() 538 { 539 return "scope: " + scope + "; name: " + name + "; locale: " + locale + "; style: " + 540 style + "; variation: " + variation; 541 } 542 } 543 544 @Override 545 public String toString() 546 { 547 return data.toString(); 548 } 549 550 /** 551 * @return the resources this ResourceReference depends on. 552 */ 553 public List<HeaderItem> getDependencies() 554 { 555 return new ArrayList<>(); 556 } 557}