001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyrighted [year] [name of copyright owner]". 013 * 014 * Copyright 2009 Sun Microsystems, Inc. 015 * Portions copyright 2011 ForgeRock AS 016 */ 017 018package org.forgerock.i18n; 019 020import java.util.Locale; 021import java.util.ResourceBundle; 022 023/** 024 * An opaque handle to a localizable message. 025 */ 026public final class LocalizableMessageDescriptor { 027 /** 028 * Subclass for creating messages with no arguments. 029 */ 030 public static final class Arg0 extends AbstractLocalizableMessageDescriptor { 031 032 /** 033 * Cached copy of the message created by this descriptor. We can get 034 * away with this for the zero argument message because it is immutable. 035 */ 036 private final LocalizableMessage message; 037 038 private final boolean requiresFormat; 039 040 /** 041 * Creates a parameterized instance. 042 * 043 * @param sourceClass 044 * The class in which this descriptor is defined. This class 045 * will be used to obtain the {@code ClassLoader} for 046 * retrieving the {@code ResourceBundle}. The class may also 047 * be retrieved in order to uniquely identify the source of a 048 * message, for example using 049 * {@code getClass().getPackage().getName()}. 050 * @param resourceName 051 * The name of the resource bundle containing the localizable 052 * message. 053 * @param key 054 * The resource bundle property key. 055 * @param ordinal 056 * The ordinal associated with this descriptor or {@code -1} 057 * if undefined. A message can be uniquely identified by its 058 * ordinal and class. 059 */ 060 public Arg0(final Class<?> sourceClass, final String resourceName, 061 final String key, final int ordinal) { 062 super(sourceClass, resourceName, key, ordinal); 063 final Object[] args = {}; 064 message = new LocalizableMessage(this, args); 065 requiresFormat = containsArgumentLiterals(getFormatString()); 066 } 067 068 /** 069 * Creates a localizable message. 070 * 071 * @return The localizable message. 072 */ 073 public LocalizableMessage get() { 074 return message; 075 } 076 077 /** 078 * {@inheritDoc} 079 */ 080 @Override 081 boolean requiresFormatter() { 082 return requiresFormat; 083 } 084 085 /** 086 * Indicates whether or not formatting should be applied to the given 087 * format string. Note that a format string might have literal 088 * specifiers (%% or %n for example) that require formatting but are not 089 * replaced by arguments. 090 * 091 * @param s 092 * Candidate for formatting. 093 * @return {@code true} if the format string requires formatting. 094 */ 095 private boolean containsArgumentLiterals(final String s) { 096 return s.contains("%%") || s.contains("%n"); // match Formatter 097 // literals 098 } 099 } 100 101 /** 102 * Subclass for creating messages with one argument. 103 * 104 * @param <T1> 105 * The type of the first message argument. 106 */ 107 public static final class Arg1<T1> extends 108 AbstractLocalizableMessageDescriptor { 109 110 /** 111 * Creates a parameterized instance. 112 * 113 * @param sourceClass 114 * The class in which this descriptor is defined. This class 115 * will be used to obtain the {@code ClassLoader} for 116 * retrieving the {@code ResourceBundle}. The class may also 117 * be retrieved in order to uniquely identify the source of a 118 * message, for example using 119 * {@code getClass().getPackage().getName()}. 120 * @param resourceName 121 * The name of the resource bundle containing the localizable 122 * message. 123 * @param key 124 * The resource bundle property key. 125 * @param ordinal 126 * The ordinal associated with this descriptor or {@code -1} 127 * if undefined. A message can be uniquely identified by its 128 * ordinal and class. 129 */ 130 public Arg1(final Class<?> sourceClass, final String resourceName, 131 final String key, final int ordinal) { 132 super(sourceClass, resourceName, key, ordinal); 133 } 134 135 /** 136 * Creates a message with arguments that will replace format specifiers 137 * in the associated format string when the message is rendered to 138 * string representation. 139 * 140 * @return The localizable message containing the provided arguments. 141 * @param a1 142 * A message argument. 143 */ 144 public LocalizableMessage get(final T1 a1) { 145 final Object[] args = { a1 }; 146 return new LocalizableMessage(this, args); 147 } 148 149 /** 150 * {@inheritDoc} 151 */ 152 @Override 153 boolean requiresFormatter() { 154 return true; 155 } 156 157 } 158 159 /** 160 * Subclass for creating messages with two arguments. 161 * 162 * @param <T1> 163 * The type of the first message argument. 164 * @param <T2> 165 * The type of the second message argument. 166 */ 167 public static final class Arg2<T1, T2> extends 168 AbstractLocalizableMessageDescriptor { 169 170 /** 171 * Creates a parameterized instance. 172 * 173 * @param sourceClass 174 * The class in which this descriptor is defined. This class 175 * will be used to obtain the {@code ClassLoader} for 176 * retrieving the {@code ResourceBundle}. The class may also 177 * be retrieved in order to uniquely identify the source of a 178 * message, for example using 179 * {@code getClass().getPackage().getName()}. 180 * @param resourceName 181 * The name of the resource bundle containing the localizable 182 * message. 183 * @param key 184 * The resource bundle property key. 185 * @param ordinal 186 * The ordinal associated with this descriptor or {@code -1} 187 * if undefined. A message can be uniquely identified by its 188 * ordinal and class. 189 */ 190 public Arg2(final Class<?> sourceClass, final String resourceName, 191 final String key, final int ordinal) { 192 super(sourceClass, resourceName, key, ordinal); 193 } 194 195 /** 196 * Creates a message with arguments that will replace format specifiers 197 * in the associated format string when the message is rendered to 198 * string representation. 199 * 200 * @return The localizable message containing the provided arguments. 201 * @param a1 202 * A message argument. 203 * @param a2 204 * A message argument. 205 */ 206 public LocalizableMessage get(final T1 a1, final T2 a2) { 207 final Object[] args = { a1, a2 }; 208 return new LocalizableMessage(this, args); 209 } 210 211 /** 212 * {@inheritDoc} 213 */ 214 @Override 215 boolean requiresFormatter() { 216 return true; 217 } 218 219 } 220 221 /** 222 * Subclass for creating messages with three arguments. 223 * 224 * @param <T1> 225 * The type of the first message argument. 226 * @param <T2> 227 * The type of the second message argument. 228 * @param <T3> 229 * The type of the third message argument. 230 */ 231 public static final class Arg3<T1, T2, T3> extends 232 AbstractLocalizableMessageDescriptor { 233 234 /** 235 * Creates a parameterized instance. 236 * 237 * @param sourceClass 238 * The class in which this descriptor is defined. This class 239 * will be used to obtain the {@code ClassLoader} for 240 * retrieving the {@code ResourceBundle}. The class may also 241 * be retrieved in order to uniquely identify the source of a 242 * message, for example using 243 * {@code getClass().getPackage().getName()}. 244 * @param resourceName 245 * The name of the resource bundle containing the localizable 246 * message. 247 * @param key 248 * The resource bundle property key. 249 * @param ordinal 250 * The ordinal associated with this descriptor or {@code -1} 251 * if undefined. A message can be uniquely identified by its 252 * ordinal and class. 253 */ 254 public Arg3(final Class<?> sourceClass, final String resourceName, 255 final String key, final int ordinal) { 256 super(sourceClass, resourceName, key, ordinal); 257 } 258 259 /** 260 * Creates a message with arguments that will replace format specifiers 261 * in the associated format string when the message is rendered to 262 * string representation. 263 * 264 * @return The localizable message containing the provided arguments. 265 * @param a1 266 * A message argument. 267 * @param a2 268 * A message argument. 269 * @param a3 270 * A message argument. 271 */ 272 public LocalizableMessage get(final T1 a1, final T2 a2, final T3 a3) { 273 final Object[] args = { a1, a2, a3 }; 274 return new LocalizableMessage(this, args); 275 } 276 277 /** 278 * {@inheritDoc} 279 */ 280 @Override 281 boolean requiresFormatter() { 282 return true; 283 } 284 285 } 286 287 /** 288 * Subclass for creating messages with four arguments. 289 * 290 * @param <T1> 291 * The type of the first message argument. 292 * @param <T2> 293 * The type of the second message argument. 294 * @param <T3> 295 * The type of the third message argument. 296 * @param <T4> 297 * The type of the fourth message argument. 298 */ 299 public static final class Arg4<T1, T2, T3, T4> extends 300 AbstractLocalizableMessageDescriptor { 301 302 /** 303 * Creates a parameterized instance. 304 * 305 * @param sourceClass 306 * The class in which this descriptor is defined. This class 307 * will be used to obtain the {@code ClassLoader} for 308 * retrieving the {@code ResourceBundle}. The class may also 309 * be retrieved in order to uniquely identify the source of a 310 * message, for example using 311 * {@code getClass().getPackage().getName()}. 312 * @param resourceName 313 * The name of the resource bundle containing the localizable 314 * message. 315 * @param key 316 * The resource bundle property key. 317 * @param ordinal 318 * The ordinal associated with this descriptor or {@code -1} 319 * if undefined. A message can be uniquely identified by its 320 * ordinal and class. 321 */ 322 public Arg4(final Class<?> sourceClass, final String resourceName, 323 final String key, final int ordinal) { 324 super(sourceClass, resourceName, key, ordinal); 325 } 326 327 /** 328 * Creates a message with arguments that will replace format specifiers 329 * in the associated format string when the message is rendered to 330 * string representation. 331 * 332 * @return The localizable message containing the provided arguments. 333 * @param a1 334 * A message argument. 335 * @param a2 336 * A message argument. 337 * @param a3 338 * A message argument. 339 * @param a4 340 * A message argument. 341 */ 342 public LocalizableMessage get(final T1 a1, final T2 a2, final T3 a3, 343 final T4 a4) { 344 final Object[] args = { a1, a2, a3, a4 }; 345 return new LocalizableMessage(this, args); 346 } 347 348 /** 349 * {@inheritDoc} 350 */ 351 @Override 352 boolean requiresFormatter() { 353 return true; 354 } 355 356 } 357 358 /** 359 * Subclass for creating messages with five arguments. 360 * 361 * @param <T1> 362 * The type of the first message argument. 363 * @param <T2> 364 * The type of the second message argument. 365 * @param <T3> 366 * The type of the third message argument. 367 * @param <T4> 368 * The type of the fourth message argument. 369 * @param <T5> 370 * The type of the fifth message argument. 371 */ 372 public static final class Arg5<T1, T2, T3, T4, T5> extends 373 AbstractLocalizableMessageDescriptor { 374 375 /** 376 * Creates a parameterized instance. 377 * 378 * @param sourceClass 379 * The class in which this descriptor is defined. This class 380 * will be used to obtain the {@code ClassLoader} for 381 * retrieving the {@code ResourceBundle}. The class may also 382 * be retrieved in order to uniquely identify the source of a 383 * message, for example using 384 * {@code getClass().getPackage().getName()}. 385 * @param resourceName 386 * The name of the resource bundle containing the localizable 387 * message. 388 * @param key 389 * The resource bundle property key. 390 * @param ordinal 391 * The ordinal associated with this descriptor or {@code -1} 392 * if undefined. A message can be uniquely identified by its 393 * ordinal and class. 394 */ 395 public Arg5(final Class<?> sourceClass, final String resourceName, 396 final String key, final int ordinal) { 397 super(sourceClass, resourceName, key, ordinal); 398 } 399 400 /** 401 * Creates a message with arguments that will replace format specifiers 402 * in the associated format string when the message is rendered to 403 * string representation. 404 * 405 * @return The localizable message containing the provided arguments. 406 * @param a1 407 * A message argument. 408 * @param a2 409 * A message argument. 410 * @param a3 411 * A message argument. 412 * @param a4 413 * A message argument. 414 * @param a5 415 * A message argument. 416 */ 417 public LocalizableMessage get(final T1 a1, final T2 a2, final T3 a3, 418 final T4 a4, final T5 a5) { 419 final Object[] args = { a1, a2, a3, a4, a5 }; 420 return new LocalizableMessage(this, args); 421 } 422 423 /** 424 * {@inheritDoc} 425 */ 426 @Override 427 boolean requiresFormatter() { 428 return true; 429 } 430 431 } 432 433 /** 434 * Subclass for creating messages with six arguments. 435 * 436 * @param <T1> 437 * The type of the first message argument. 438 * @param <T2> 439 * The type of the second message argument. 440 * @param <T3> 441 * The type of the third message argument. 442 * @param <T4> 443 * The type of the fourth message argument. 444 * @param <T5> 445 * The type of the fifth message argument. 446 * @param <T6> 447 * The type of the sixth message argument. 448 */ 449 public static final class Arg6<T1, T2, T3, T4, T5, T6> extends 450 AbstractLocalizableMessageDescriptor { 451 452 /** 453 * Creates a parameterized instance. 454 * 455 * @param sourceClass 456 * The class in which this descriptor is defined. This class 457 * will be used to obtain the {@code ClassLoader} for 458 * retrieving the {@code ResourceBundle}. The class may also 459 * be retrieved in order to uniquely identify the source of a 460 * message, for example using 461 * {@code getClass().getPackage().getName()}. 462 * @param resourceName 463 * The name of the resource bundle containing the localizable 464 * message. 465 * @param key 466 * The resource bundle property key. 467 * @param ordinal 468 * The ordinal associated with this descriptor or {@code -1} 469 * if undefined. A message can be uniquely identified by its 470 * ordinal and class. 471 */ 472 public Arg6(final Class<?> sourceClass, final String resourceName, 473 final String key, final int ordinal) { 474 super(sourceClass, resourceName, key, ordinal); 475 } 476 477 /** 478 * Creates a message with arguments that will replace format specifiers 479 * in the associated format string when the message is rendered to 480 * string representation. 481 * 482 * @return The localizable message containing the provided arguments. 483 * @param a1 484 * A message argument. 485 * @param a2 486 * A message argument. 487 * @param a3 488 * A message argument. 489 * @param a4 490 * A message argument. 491 * @param a5 492 * A message argument. 493 * @param a6 494 * A message argument. 495 */ 496 public LocalizableMessage get(final T1 a1, final T2 a2, final T3 a3, 497 final T4 a4, final T5 a5, final T6 a6) { 498 final Object[] args = { a1, a2, a3, a4, a5, a6 }; 499 return new LocalizableMessage(this, args); 500 } 501 502 /** 503 * {@inheritDoc} 504 */ 505 @Override 506 boolean requiresFormatter() { 507 return true; 508 } 509 510 } 511 512 /** 513 * Subclass for creating messages with seven arguments. 514 * 515 * @param <T1> 516 * The type of the first message argument. 517 * @param <T2> 518 * The type of the second message argument. 519 * @param <T3> 520 * The type of the third message argument. 521 * @param <T4> 522 * The type of the fourth message argument. 523 * @param <T5> 524 * The type of the fifth message argument. 525 * @param <T6> 526 * The type of the sixth message argument. 527 * @param <T7> 528 * The type of the seventh message argument. 529 */ 530 public static final class Arg7<T1, T2, T3, T4, T5, T6, T7> extends 531 AbstractLocalizableMessageDescriptor { 532 533 /** 534 * Creates a parameterized instance. 535 * 536 * @param sourceClass 537 * The class in which this descriptor is defined. This class 538 * will be used to obtain the {@code ClassLoader} for 539 * retrieving the {@code ResourceBundle}. The class may also 540 * be retrieved in order to uniquely identify the source of a 541 * message, for example using 542 * {@code getClass().getPackage().getName()}. 543 * @param resourceName 544 * The name of the resource bundle containing the localizable 545 * message. 546 * @param key 547 * The resource bundle property key. 548 * @param ordinal 549 * The ordinal associated with this descriptor or {@code -1} 550 * if undefined. A message can be uniquely identified by its 551 * ordinal and class. 552 */ 553 public Arg7(final Class<?> sourceClass, final String resourceName, 554 final String key, final int ordinal) { 555 super(sourceClass, resourceName, key, ordinal); 556 } 557 558 /** 559 * Creates a message with arguments that will replace format specifiers 560 * in the associated format string when the message is rendered to 561 * string representation. 562 * 563 * @return The localizable message containing the provided arguments. 564 * @param a1 565 * A message argument. 566 * @param a2 567 * A message argument. 568 * @param a3 569 * A message argument. 570 * @param a4 571 * A message argument. 572 * @param a5 573 * A message argument. 574 * @param a6 575 * A message argument. 576 * @param a7 577 * A message argument. 578 */ 579 public LocalizableMessage get(final T1 a1, final T2 a2, final T3 a3, 580 final T4 a4, final T5 a5, final T6 a6, final T7 a7) { 581 final Object[] args = { a1, a2, a3, a4, a5, a6, a7 }; 582 return new LocalizableMessage(this, args); 583 } 584 585 /** 586 * {@inheritDoc} 587 */ 588 @Override 589 boolean requiresFormatter() { 590 return true; 591 } 592 593 } 594 595 /** 596 * Subclass for creating messages with eight arguments. 597 * 598 * @param <T1> 599 * The type of the first message argument. 600 * @param <T2> 601 * The type of the second message argument. 602 * @param <T3> 603 * The type of the third message argument. 604 * @param <T4> 605 * The type of the fourth message argument. 606 * @param <T5> 607 * The type of the fifth message argument. 608 * @param <T6> 609 * The type of the sixth message argument. 610 * @param <T7> 611 * The type of the seventh message argument. 612 * @param <T8> 613 * The type of the eighth message argument. 614 */ 615 public static final class Arg8<T1, T2, T3, T4, T5, T6, T7, T8> extends 616 AbstractLocalizableMessageDescriptor { 617 618 /** 619 * Creates a parameterized instance. 620 * 621 * @param sourceClass 622 * The class in which this descriptor is defined. This class 623 * will be used to obtain the {@code ClassLoader} for 624 * retrieving the {@code ResourceBundle}. The class may also 625 * be retrieved in order to uniquely identify the source of a 626 * message, for example using 627 * {@code getClass().getPackage().getName()}. 628 * @param resourceName 629 * The name of the resource bundle containing the localizable 630 * message. 631 * @param key 632 * The resource bundle property key. 633 * @param ordinal 634 * The ordinal associated with this descriptor or {@code -1} 635 * if undefined. A message can be uniquely identified by its 636 * ordinal and class. 637 */ 638 public Arg8(final Class<?> sourceClass, final String resourceName, 639 final String key, final int ordinal) { 640 super(sourceClass, resourceName, key, ordinal); 641 } 642 643 /** 644 * Creates a message with arguments that will replace format specifiers 645 * in the associated format string when the message is rendered to 646 * string representation. 647 * 648 * @return The localizable message containing the provided arguments. 649 * @param a1 650 * A message argument. 651 * @param a2 652 * A message argument. 653 * @param a3 654 * A message argument. 655 * @param a4 656 * A message argument. 657 * @param a5 658 * A message argument. 659 * @param a6 660 * A message argument. 661 * @param a7 662 * A message argument. 663 * @param a8 664 * A message argument. 665 */ 666 public LocalizableMessage get(final T1 a1, final T2 a2, final T3 a3, 667 final T4 a4, final T5 a5, final T6 a6, final T7 a7, final T8 a8) { 668 final Object[] args = { a1, a2, a3, a4, a5, a6, a7, a8 }; 669 return new LocalizableMessage(this, args); 670 } 671 672 /** 673 * {@inheritDoc} 674 */ 675 @Override 676 boolean requiresFormatter() { 677 return true; 678 } 679 680 } 681 682 /** 683 * Subclass for creating messages with nine arguments. 684 * 685 * @param <T1> 686 * The type of the first message argument. 687 * @param <T2> 688 * The type of the second message argument. 689 * @param <T3> 690 * The type of the third message argument. 691 * @param <T4> 692 * The type of the fourth message argument. 693 * @param <T5> 694 * The type of the fifth message argument. 695 * @param <T6> 696 * The type of the sixth message argument. 697 * @param <T7> 698 * The type of the seventh message argument. 699 * @param <T8> 700 * The type of the eighth message argument. 701 * @param <T9> 702 * The type of the ninth message argument. 703 */ 704 public static final class Arg9<T1, T2, T3, T4, T5, T6, T7, T8, T9> extends 705 AbstractLocalizableMessageDescriptor { 706 707 /** 708 * Creates a parameterized instance. 709 * 710 * @param sourceClass 711 * The class in which this descriptor is defined. This class 712 * will be used to obtain the {@code ClassLoader} for 713 * retrieving the {@code ResourceBundle}. The class may also 714 * be retrieved in order to uniquely identify the source of a 715 * message, for example using 716 * {@code getClass().getPackage().getName()}. 717 * @param resourceName 718 * The name of the resource bundle containing the localizable 719 * message. 720 * @param key 721 * The resource bundle property key. 722 * @param ordinal 723 * The ordinal associated with this descriptor or {@code -1} 724 * if undefined. A message can be uniquely identified by its 725 * ordinal and class. 726 */ 727 public Arg9(final Class<?> sourceClass, final String resourceName, 728 final String key, final int ordinal) { 729 super(sourceClass, resourceName, key, ordinal); 730 } 731 732 /** 733 * Creates a message with arguments that will replace format specifiers 734 * in the associated format string when the message is rendered to 735 * string representation. 736 * 737 * @return The localizable message containing the provided arguments. 738 * @param a1 739 * A message argument. 740 * @param a2 741 * A message argument. 742 * @param a3 743 * A message argument. 744 * @param a4 745 * A message argument. 746 * @param a5 747 * A message argument. 748 * @param a6 749 * A message argument. 750 * @param a7 751 * A message argument. 752 * @param a8 753 * A message argument. 754 * @param a9 755 * A message argument. 756 */ 757 public LocalizableMessage get(final T1 a1, final T2 a2, final T3 a3, 758 final T4 a4, final T5 a5, final T6 a6, final T7 a7, 759 final T8 a8, final T9 a9) { 760 final Object[] args = { a1, a2, a3, a4, a5, a6, a7, a8, a9 }; 761 return new LocalizableMessage(this, args); 762 } 763 764 /** 765 * {@inheritDoc} 766 */ 767 @Override 768 boolean requiresFormatter() { 769 return true; 770 } 771 772 } 773 774 /** 775 * Subclass for creating messages with an any number of arguments. In 776 * general this class should be used when a message needs to be defined with 777 * more arguments that can be handled with the current number of subclasses 778 */ 779 public static final class ArgN extends AbstractLocalizableMessageDescriptor { 780 781 /** 782 * Creates a parameterized instance. 783 * 784 * @param sourceClass 785 * The class in which this descriptor is defined. This class 786 * will be used to obtain the {@code ClassLoader} for 787 * retrieving the {@code ResourceBundle}. The class may also 788 * be retrieved in order to uniquely identify the source of a 789 * message, for example using 790 * {@code getClass().getPackage().getName()}. 791 * @param resourceName 792 * The name of the resource bundle containing the localizable 793 * message. 794 * @param key 795 * The resource bundle property key. 796 * @param ordinal 797 * The ordinal associated with this descriptor or {@code -1} 798 * if undefined. A message can be uniquely identified by its 799 * ordinal and class. 800 */ 801 public ArgN(final Class<?> sourceClass, final String resourceName, 802 final String key, final int ordinal) { 803 super(sourceClass, resourceName, key, ordinal); 804 } 805 806 /** 807 * Creates a message with arguments that will replace format specifiers 808 * in the associated format string when the message is rendered to 809 * string representation. 810 * 811 * @return The localizable message containing the provided arguments. 812 * @param args 813 * The message arguments. 814 */ 815 public LocalizableMessage get(final Object... args) { 816 return new LocalizableMessage(this, args); 817 } 818 819 /** 820 * {@inheritDoc} 821 */ 822 @Override 823 boolean requiresFormatter() { 824 return true; 825 } 826 827 } 828 829 /** 830 * Base class for all message descriptors. 831 */ 832 abstract static class AbstractLocalizableMessageDescriptor { 833 /** 834 * Container for caching the last locale specific format string. 835 */ 836 private static final class CachedFormatString { 837 private final Locale locale; 838 839 private final String formatString; 840 841 private CachedFormatString(final Locale locale, 842 final String formatString) { 843 this.locale = locale; 844 this.formatString = formatString; 845 } 846 } 847 848 // Used for accessing format string from the resource bundle. 849 private final String key; 850 851 /* 852 * The class in which this descriptor is defined. This class will be 853 * used to obtain the ClassLoader for retrieving the ResourceBundle. The 854 * class may also be retrieved in order to uniquely identify the source 855 * of a message, for example using getClass().getPackage().getName(). 856 */ 857 private final Class<?> sourceClass; 858 859 /* 860 * The name of the resource bundle containing the localizable message. 861 */ 862 private final String resourceName; 863 864 /* 865 * The ordinal associated with this descriptor or -1 if undefined. A 866 * message can be uniquely identified by its ordinal and class. 867 */ 868 private final int ordinal; 869 870 // It's ok if there are race conditions. 871 private CachedFormatString cachedFormatString = null; 872 873 /** 874 * Creates a parameterized message descriptor. 875 * 876 * @param sourceClass 877 * The class in which this descriptor is defined. This class 878 * will be used to obtain the {@code ClassLoader} for 879 * retrieving the {@code ResourceBundle}. The class may also 880 * be retrieved in order to uniquely identify the source of a 881 * message, for example using 882 * {@code getClass().getPackage().getName()}. 883 * @param resourceName 884 * The name of the resource bundle containing the localizable 885 * message. 886 * @param key 887 * The resource bundle property key. 888 * @param ordinal 889 * The ordinal associated with this descriptor or {@code -1} 890 * if undefined. A message can be uniquely identified by its 891 * ordinal and class. 892 */ 893 private AbstractLocalizableMessageDescriptor( 894 final Class<?> sourceClass, final String resourceName, 895 final String key, final int ordinal) { 896 this.sourceClass = sourceClass; 897 this.resourceName = resourceName; 898 this.key = key; 899 this.ordinal = ordinal; 900 } 901 902 /** 903 * Returns the format string which should be used when creating the 904 * string representation of this message using the default locale. 905 * 906 * @return The format string. 907 */ 908 final String getFormatString() { 909 return getFormatString(Locale.getDefault()); 910 } 911 912 /** 913 * Returns the format string which should be used when creating the 914 * string representation of this message using the specified locale. 915 * 916 * @param locale 917 * The locale. 918 * @return The format string. 919 * @throws NullPointerException 920 * If {@code locale} was {@code null}. 921 */ 922 String getFormatString(final Locale locale) { 923 if (locale == null) { 924 throw new NullPointerException("locale was null"); 925 } 926 927 // Fast path. 928 final CachedFormatString cfs = cachedFormatString; 929 if (cfs != null && cfs.locale == locale) { 930 return cfs.formatString; 931 } 932 933 // There's a potential race condition here but it's benign - we'll 934 // just do a bit more work than needed. 935 final ResourceBundle bundle = getBundle(locale); 936 final String formatString = bundle.getString(key); 937 cachedFormatString = new CachedFormatString(locale, formatString); 938 939 return formatString; 940 } 941 942 /** 943 * Returns the ordinal associated with this message, or {@code -1} if 944 * undefined. A message can be uniquely identified by its resource name 945 * and ordinal. 946 * <p> 947 * This may be useful when an application wishes to identify the source 948 * of a message. For example, a logging implementation could log the 949 * resource name in addition to the ordinal in order to unambiguously 950 * identify a message in a locale independent way. 951 * 952 * @return The ordinal associated with this descriptor, or {@code -1} if 953 * undefined. 954 */ 955 public final int ordinal() { 956 return ordinal; 957 } 958 959 /** 960 * Indicates whether or not this descriptor format string should be 961 * processed by {@code Formatter} during string rendering. 962 * 963 * @return {@code true} if a {@code Formatter} should be used, otherwise 964 * {@code false}. 965 */ 966 abstract boolean requiresFormatter(); 967 968 /** 969 * Returns the name of the resource in which this message is defined. A 970 * message can be uniquely identified by its resource name and ordinal. 971 * <p> 972 * This may be useful when an application wishes to identify the source 973 * of a message. For example, a logging implementation could log the 974 * resource name in addition to the ordinal in order to unambiguously 975 * identify a message in a locale independent way. 976 * <p> 977 * The resource name may be used for obtaining named loggers, e.g. using 978 * SLF4J's {@code org.slf4j.LoggerFactory#getLogger(String name)}. 979 * 980 * @return The name of the resource in which this message is defined, or 981 * {@code null} if this message is a raw message and its source 982 * is undefined. 983 */ 984 public final String resourceName() { 985 return resourceName; 986 } 987 988 private ResourceBundle getBundle(final Locale locale) { 989 return ResourceBundle.getBundle(resourceName, 990 locale == null ? Locale.getDefault() : locale, 991 sourceClass.getClassLoader()); 992 } 993 } 994 995 /** 996 * A descriptor for creating a raw message from a {@code String}. In general 997 * this descriptor should NOT be used internally. 998 */ 999 static final class Raw extends AbstractLocalizableMessageDescriptor { 1000 1001 private final String formatString; 1002 1003 private final boolean requiresFormatter; 1004 1005 /** 1006 * Creates a parameterized instance. 1007 * 1008 * @param formatString 1009 * The format string. 1010 */ 1011 Raw(final CharSequence formatString) { 1012 super(Void.class, null, null, -1); 1013 this.formatString = formatString.toString(); 1014 this.requiresFormatter = this.formatString.matches(".*%.*"); 1015 } 1016 1017 /** 1018 * Creates a message with arguments that will replace format specifiers 1019 * in the associated format string when the message is rendered to 1020 * string representation. 1021 * 1022 * @return The localizable message containing the provided arguments. 1023 * @param args 1024 * The message arguments. 1025 */ 1026 LocalizableMessage get(final Object... args) { 1027 return new LocalizableMessage(this, args); 1028 } 1029 1030 /** 1031 * Overridden in order to bypass the resource bundle plumbing and return 1032 * the format string directly. 1033 */ 1034 @Override 1035 String getFormatString(final Locale locale) { 1036 return this.formatString; 1037 } 1038 1039 /** 1040 * {@inheritDoc} 1041 */ 1042 @Override 1043 boolean requiresFormatter() { 1044 return this.requiresFormatter; 1045 } 1046 1047 } 1048 1049 /** 1050 * Cached zero arg raw message descriptor. 1051 */ 1052 static final LocalizableMessageDescriptor.Raw RAW0 = new LocalizableMessageDescriptor.Raw( 1053 "%s"); 1054 1055 // Prevent instantiation. 1056 private LocalizableMessageDescriptor() { 1057 // Do nothing. 1058 } 1059 1060}