1 /* 2 * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * Licensed Materials - Property of IBM 28 * RMI-IIOP v1.0 29 * Copyright IBM Corp. 1998 1999 All Rights Reserved 30 * 31 */ 32 33 package sun.rmi.rmic; 34 35 import java.util.Vector; 36 import java.util.Enumeration; 37 import java.util.ResourceBundle; 38 import java.util.StringTokenizer; 39 import java.util.MissingResourceException; 40 41 import java.io.OutputStream; 42 import java.io.PrintStream; 43 import java.io.IOException; 44 import java.io.File; 45 import java.io.FileNotFoundException; 46 import java.io.FileOutputStream; 47 import java.io.ByteArrayOutputStream; 48 49 import sun.tools.java.ClassFile; 50 import sun.tools.java.ClassDefinition; 51 import sun.tools.java.ClassDeclaration; 52 import sun.tools.java.ClassNotFound; 53 import sun.tools.java.Identifier; 54 import sun.tools.java.ClassPath; 55 56 import sun.tools.javac.SourceClass; 57 import sun.tools.util.CommandLine; 58 import java.lang.reflect.Constructor; 59 import java.util.Properties; 60 61 /** 62 * Main "rmic" program. 63 * 64 * WARNING: The contents of this source file are not part of any 65 * supported API. Code that depends on them does so at its own risk: 66 * they are subject to change or removal without notice. 67 */ 68 public class Main implements sun.rmi.rmic.Constants { 69 String sourcePathArg; 70 String sysClassPathArg; 71 String extDirsArg; 72 String classPathString; 73 File destDir; 74 int flags; 75 long tm; 76 Vector<String> classes; 77 boolean nowrite; 78 boolean nocompile; 79 boolean keepGenerated; 80 boolean status; 81 String[] generatorArgs; 82 Vector<Generator> generators; 83 Class<? extends BatchEnvironment> environmentClass = 84 BatchEnvironment.class; 85 boolean iiopGeneration = false; 86 87 /** 88 * Name of the program. 89 */ 90 String program; 91 92 /** 93 * The stream where error message are printed. 94 */ 95 OutputStream out; 96 97 /** 98 * Constructor. 99 */ 100 public Main(OutputStream out, String program) { 101 this.out = out; 102 this.program = program; 103 } 104 105 /** 106 * Output a message. 107 */ 108 public void output(String msg) { 109 PrintStream out = 110 this.out instanceof PrintStream ? (PrintStream)this.out 111 : new PrintStream(this.out, true); 112 out.println(msg); 113 } 114 115 /** 116 * Top level error message. This method is called when the 117 * environment could not be set up yet. 118 */ 119 public void error(String msg) { 120 output(getText(msg)); 121 } 122 123 public void error(String msg, String arg1) { 124 output(getText(msg, arg1)); 125 } 126 127 public void error(String msg, String arg1, String arg2) { 128 output(getText(msg, arg1, arg2)); 129 } 130 131 /** 132 * Usage 133 */ 134 public void usage() { 135 error("rmic.usage", program); 136 } 137 138 /** 139 * Run the compiler 140 */ 141 public synchronized boolean compile(String argv[]) { 142 143 /* 144 * Handle internal option to use the new (and incomplete) rmic 145 * implementation. This option is handled here, rather than 146 * in parseArgs, so that none of the arguments will be nulled 147 * before delegating to the new implementation. 148 */ 149 for (int i = 0; i < argv.length; i++) { 150 if (argv[i].equals("-Xnew")) { 151 return (new sun.rmi.rmic.newrmic.Main(out, 152 program)).compile(argv); 153 } 154 } 155 156 if (!parseArgs(argv)) { 157 return false; 158 } 159 160 if (classes.size() == 0) { 161 usage(); 162 return false; 163 } 164 165 return doCompile(); 166 } 167 168 /** 169 * Get the destination directory. 170 */ 171 public File getDestinationDir() { 172 return destDir; 173 } 174 175 /** 176 * Parse the arguments for compile. 177 */ 178 public boolean parseArgs(String argv[]) { 179 sourcePathArg = null; 180 sysClassPathArg = null; 181 extDirsArg = null; 182 183 classPathString = null; 184 destDir = null; 185 flags = F_WARNINGS; 186 tm = System.currentTimeMillis(); 187 classes = new Vector<>(); 188 nowrite = false; 189 nocompile = false; 190 keepGenerated = false; 191 generatorArgs = getArray("generator.args",true); 192 if (generatorArgs == null) { 193 return false; 194 } 195 generators = new Vector<>(); 196 197 // Pre-process command line for @file arguments 198 try { 199 argv = CommandLine.parse(argv); 200 } catch (FileNotFoundException e) { 201 error("rmic.cant.read", e.getMessage()); 202 return false; 203 } catch (IOException e) { 204 e.printStackTrace(out instanceof PrintStream ? 205 (PrintStream) out : 206 new PrintStream(out, true)); 207 return false; 208 } 209 210 // Parse arguments 211 for (int i = 0 ; i < argv.length ; i++) { 212 if (argv[i] != null) { 213 if (argv[i].equals("-g")) { 214 flags &= ~F_OPT; 215 flags |= F_DEBUG_LINES | F_DEBUG_VARS; 216 argv[i] = null; 217 } else if (argv[i].equals("-O")) { 218 flags &= ~F_DEBUG_LINES; 219 flags &= ~F_DEBUG_VARS; 220 flags |= F_OPT | F_DEPENDENCIES; 221 argv[i] = null; 222 } else if (argv[i].equals("-nowarn")) { 223 flags &= ~F_WARNINGS; 224 argv[i] = null; 225 } else if (argv[i].equals("-debug")) { 226 flags |= F_DUMP; 227 argv[i] = null; 228 } else if (argv[i].equals("-depend")) { 229 flags |= F_DEPENDENCIES; 230 argv[i] = null; 231 } else if (argv[i].equals("-verbose")) { 232 flags |= F_VERBOSE; 233 argv[i] = null; 234 } else if (argv[i].equals("-nowrite")) { 235 nowrite = true; 236 argv[i] = null; 237 } else if (argv[i].equals("-Xnocompile")) { 238 nocompile = true; 239 keepGenerated = true; 240 argv[i] = null; 241 } else if (argv[i].equals("-keep") || 242 argv[i].equals("-keepgenerated")) { 243 keepGenerated = true; 244 argv[i] = null; 245 } else if (argv[i].equals("-show")) { 246 error("rmic.option.unsupported", "-show"); 247 usage(); 248 return false; 249 } else if (argv[i].equals("-classpath")) { 250 if ((i + 1) < argv.length) { 251 if (classPathString != null) { 252 error("rmic.option.already.seen", "-classpath"); 253 usage(); 254 return false; 255 } 256 argv[i] = null; 257 classPathString = argv[++i]; 258 argv[i] = null; 259 } else { 260 error("rmic.option.requires.argument", "-classpath"); 261 usage(); 262 return false; 263 } 264 } else if (argv[i].equals("-sourcepath")) { 265 if ((i + 1) < argv.length) { 266 if (sourcePathArg != null) { 267 error("rmic.option.already.seen", "-sourcepath"); 268 usage(); 269 return false; 270 } 271 argv[i] = null; 272 sourcePathArg = argv[++i]; 273 argv[i] = null; 274 } else { 275 error("rmic.option.requires.argument", "-sourcepath"); 276 usage(); 277 return false; 278 } 279 } else if (argv[i].equals("-bootclasspath")) { 280 if ((i + 1) < argv.length) { 281 if (sysClassPathArg != null) { 282 error("rmic.option.already.seen", "-bootclasspath"); 283 usage(); 284 return false; 285 } 286 argv[i] = null; 287 sysClassPathArg = argv[++i]; 288 argv[i] = null; 289 } else { 290 error("rmic.option.requires.argument", "-bootclasspath"); 291 usage(); 292 return false; 293 } 294 } else if (argv[i].equals("-extdirs")) { 295 if ((i + 1) < argv.length) { 296 if (extDirsArg != null) { 297 error("rmic.option.already.seen", "-extdirs"); 298 usage(); 299 return false; 300 } 301 argv[i] = null; 302 extDirsArg = argv[++i]; 303 argv[i] = null; 304 } else { 305 error("rmic.option.requires.argument", "-extdirs"); 306 usage(); 307 return false; 308 } 309 } else if (argv[i].equals("-d")) { 310 if ((i + 1) < argv.length) { 311 if (destDir != null) { 312 error("rmic.option.already.seen", "-d"); 313 usage(); 314 return false; 315 } 316 argv[i] = null; 317 destDir = new File(argv[++i]); 318 argv[i] = null; 319 if (!destDir.exists()) { 320 error("rmic.no.such.directory", destDir.getPath()); 321 usage(); 322 return false; 323 } 324 } else { 325 error("rmic.option.requires.argument", "-d"); 326 usage(); 327 return false; 328 } 329 } else { 330 if (!checkGeneratorArg(argv,i)) { 331 usage(); 332 return false; 333 } 334 } 335 } 336 } 337 338 339 // Now that all generators have had a chance at the args, 340 // scan what's left for classes and illegal args... 341 342 for (int i = 0; i < argv.length; i++) { 343 if (argv[i] != null) { 344 if (argv[i].startsWith("-")) { 345 error("rmic.no.such.option", argv[i]); 346 usage(); 347 return false; 348 } else { 349 classes.addElement(argv[i]); 350 } 351 } 352 } 353 354 355 // If the generators vector is empty, add the default generator... 356 357 if (generators.size() == 0) { 358 addGenerator("default"); 359 } 360 361 return true; 362 } 363 364 /** 365 * If this argument is for a generator, instantiate it, call 366 * parseArgs(...) and add generator to generators vector. 367 * Returns false on error. 368 */ 369 protected boolean checkGeneratorArg(String[] argv, int currentIndex) { 370 boolean result = true; 371 if (argv[currentIndex].startsWith("-")) { 372 String arg = argv[currentIndex].substring(1).toLowerCase(); // Remove '-' 373 for (int i = 0; i < generatorArgs.length; i++) { 374 if (arg.equalsIgnoreCase(generatorArgs[i])) { 375 // Got a match, add Generator and call parseArgs... 376 Generator gen = addGenerator(arg); 377 if (gen == null) { 378 return false; 379 } 380 result = gen.parseArgs(argv,this); 381 break; 382 } 383 } 384 } 385 return result; 386 } 387 388 /** 389 * Instantiate and add a generator to the generators array. 390 */ 391 protected Generator addGenerator(String arg) { 392 393 Generator gen; 394 395 // Create an instance of the generator and add it to 396 // the array... 397 398 String className = getString("generator.class." + arg); 399 if (className == null) { 400 error("rmic.missing.property",arg); 401 return null; 402 } 403 404 try { 405 gen = (Generator) Class.forName(className).newInstance(); 406 } catch (Exception e) { 407 error("rmic.cannot.instantiate",className); 408 return null; 409 } 410 411 generators.addElement(gen); 412 413 // Get the environment required by this generator... 414 415 Class<?> envClass = BatchEnvironment.class; 416 String env = getString("generator.env." + arg); 417 if (env != null) { 418 try { 419 envClass = Class.forName(env); 420 421 // Is the new class a subclass of the current one? 422 423 if (environmentClass.isAssignableFrom(envClass)) { 424 425 // Yes, so switch to the new one... 426 427 environmentClass = envClass.asSubclass(BatchEnvironment.class); 428 429 } else { 430 431 // No. Is the current class a subclass of the 432 // new one? 433 434 if (!envClass.isAssignableFrom(environmentClass)) { 435 436 // No, so it's a conflict... 437 438 error("rmic.cannot.use.both",environmentClass.getName(),envClass.getName()); 439 return null; 440 } 441 } 442 } catch (ClassNotFoundException e) { 443 error("rmic.class.not.found",env); 444 return null; 445 } 446 } 447 448 // If this is the iiop stub generator, cache 449 // that fact for the jrmp generator... 450 451 if (arg.equals("iiop")) { 452 iiopGeneration = true; 453 } 454 return gen; 455 } 456 457 /** 458 * Grab a resource string and parse it into an array of strings. Assumes 459 * comma separated list. 460 * @param name The resource name. 461 * @param mustExist If true, throws error if resource does not exist. If 462 * false and resource does not exist, returns zero element array. 463 */ 464 protected String[] getArray(String name, boolean mustExist) { 465 String[] result = null; 466 String value = getString(name); 467 if (value == null) { 468 if (mustExist) { 469 error("rmic.resource.not.found",name); 470 return null; 471 } else { 472 return new String[0]; 473 } 474 } 475 476 StringTokenizer parser = new StringTokenizer(value,", \t\n\r", false); 477 int count = parser.countTokens(); 478 result = new String[count]; 479 for (int i = 0; i < count; i++) { 480 result[i] = parser.nextToken(); 481 } 482 483 return result; 484 } 485 486 /** 487 * Get the correct type of BatchEnvironment 488 */ 489 public BatchEnvironment getEnv() { 490 491 ClassPath classPath = 492 BatchEnvironment.createClassPath(classPathString, 493 sysClassPathArg, 494 extDirsArg); 495 BatchEnvironment result = null; 496 try { 497 Class[] ctorArgTypes = {OutputStream.class,ClassPath.class,Main.class}; 498 Object[] ctorArgs = {out,classPath,this}; 499 Constructor<? extends BatchEnvironment> constructor = 500 environmentClass.getConstructor(ctorArgTypes); 501 result = constructor.newInstance(ctorArgs); 502 result.reset(); 503 } 504 catch (Exception e) { 505 error("rmic.cannot.instantiate",environmentClass.getName()); 506 } 507 return result; 508 } 509 510 511 /** 512 * Do the compile with the switches and files already supplied 513 */ 514 public boolean doCompile() { 515 // Create batch environment 516 BatchEnvironment env = getEnv(); 517 env.flags |= flags; 518 519 // Set the classfile version numbers 520 // Compat and 1.1 stubs must retain the old version number. 521 env.majorVersion = 45; 522 env.minorVersion = 3; 523 524 // Preload the "out of memory" error string just in case we run 525 // out of memory during the compile. 526 String noMemoryErrorString = getText("rmic.no.memory"); 527 String stackOverflowErrorString = getText("rmic.stack.overflow"); 528 529 try { 530 /** Load the classes on the command line 531 * Replace the entries in classes with the ClassDefinition for the class 532 */ 533 for (int i = classes.size()-1; i >= 0; i-- ) { 534 Identifier implClassName = 535 Identifier.lookup(classes.elementAt(i)); 536 537 /* 538 * Fix bugid 4049354: support using '.' as an inner class 539 * qualifier on the command line (previously, only mangled 540 * inner class names were understood, like "pkg.Outer$Inner"). 541 * 542 * The following method, also used by "javap", resolves the 543 * given unmangled inner class name to the appropriate 544 * internal identifier. For example, it translates 545 * "pkg.Outer.Inner" to "pkg.Outer. Inner". 546 */ 547 implClassName = env.resolvePackageQualifiedName(implClassName); 548 /* 549 * But if we use such an internal inner class name identifier 550 * to load the class definition, the Java compiler will notice 551 * if the impl class is a "private" inner class and then deny 552 * skeletons (needed unless "-v1.2" is used) the ability to 553 * cast to it. To work around this problem, we mangle inner 554 * class name identifiers to their binary "outer" class name: 555 * "pkg.Outer. Inner" becomes "pkg.Outer$Inner". 556 */ 557 implClassName = Names.mangleClass(implClassName); 558 559 ClassDeclaration decl = env.getClassDeclaration(implClassName); 560 try { 561 ClassDefinition def = decl.getClassDefinition(env); 562 for (int j = 0; j < generators.size(); j++) { 563 Generator gen = generators.elementAt(j); 564 gen.generate(env, def, destDir); 565 } 566 } catch (ClassNotFound ex) { 567 env.error(0, "rmic.class.not.found", implClassName); 568 } 569 570 } 571 572 // compile all classes that need compilation 573 if (!nocompile) { 574 compileAllClasses(env); 575 } 576 } catch (OutOfMemoryError ee) { 577 // The compiler has run out of memory. Use the error string 578 // which we preloaded. 579 env.output(noMemoryErrorString); 580 return false; 581 } catch (StackOverflowError ee) { 582 env.output(stackOverflowErrorString); 583 return false; 584 } catch (Error ee) { 585 // We allow the compiler to take an exception silently if a program 586 // error has previously been detected. Presumably, this makes the 587 // compiler more robust in the face of bad error recovery. 588 if (env.nerrors == 0 || env.dump()) { 589 env.error(0, "fatal.error"); 590 ee.printStackTrace(out instanceof PrintStream ? 591 (PrintStream) out : 592 new PrintStream(out, true)); 593 } 594 } catch (Exception ee) { 595 if (env.nerrors == 0 || env.dump()) { 596 env.error(0, "fatal.exception"); 597 ee.printStackTrace(out instanceof PrintStream ? 598 (PrintStream) out : 599 new PrintStream(out, true)); 600 } 601 } 602 603 env.flushErrors(); 604 605 boolean status = true; 606 if (env.nerrors > 0) { 607 String msg = ""; 608 if (env.nerrors > 1) { 609 msg = getText("rmic.errors", env.nerrors); 610 } else { 611 msg = getText("rmic.1error"); 612 } 613 if (env.nwarnings > 0) { 614 if (env.nwarnings > 1) { 615 msg += ", " + getText("rmic.warnings", env.nwarnings); 616 } else { 617 msg += ", " + getText("rmic.1warning"); 618 } 619 } 620 output(msg); 621 status = false; 622 } else { 623 if (env.nwarnings > 0) { 624 if (env.nwarnings > 1) { 625 output(getText("rmic.warnings", env.nwarnings)); 626 } else { 627 output(getText("rmic.1warning")); 628 } 629 } 630 } 631 632 // last step is to delete generated source files 633 if (!keepGenerated) { 634 env.deleteGeneratedFiles(); 635 } 636 637 // We're done 638 if (env.verbose()) { 639 tm = System.currentTimeMillis() - tm; 640 output(getText("rmic.done_in", Long.toString(tm))); 641 } 642 643 // Shutdown the environment object and release our resources. 644 // Note that while this is unneccessary when rmic is invoked 645 // the command line, there are environments in which rmic 646 // from is invoked within a server process, so resource 647 // reclamation is important... 648 649 env.shutdown(); 650 651 sourcePathArg = null; 652 sysClassPathArg = null; 653 extDirsArg = null; 654 classPathString = null; 655 destDir = null; 656 classes = null; 657 generatorArgs = null; 658 generators = null; 659 environmentClass = null; 660 program = null; 661 out = null; 662 663 return status; 664 } 665 666 /* 667 * Compile all classes that need to be compiled. 668 */ 669 public void compileAllClasses (BatchEnvironment env) 670 throws ClassNotFound, 671 IOException, 672 InterruptedException { 673 ByteArrayOutputStream buf = new ByteArrayOutputStream(4096); 674 boolean done; 675 676 do { 677 done = true; 678 for (Enumeration<?> e = env.getClasses() ; e.hasMoreElements() ; ) { 679 ClassDeclaration c = (ClassDeclaration)e.nextElement(); 680 done = compileClass(c,buf,env); 681 } 682 } while (!done); 683 } 684 685 /* 686 * Compile a single class. 687 * Fallthrough is intentional 688 */ 689 @SuppressWarnings("fallthrough") 690 public boolean compileClass (ClassDeclaration c, 691 ByteArrayOutputStream buf, 692 BatchEnvironment env) 693 throws ClassNotFound, 694 IOException, 695 InterruptedException { 696 boolean done = true; 697 env.flushErrors(); 698 SourceClass src; 699 700 switch (c.getStatus()) { 701 case CS_UNDEFINED: 702 { 703 if (!env.dependencies()) { 704 break; 705 } 706 // fall through 707 } 708 709 case CS_SOURCE: 710 { 711 done = false; 712 env.loadDefinition(c); 713 if (c.getStatus() != CS_PARSED) { 714 break; 715 } 716 // fall through 717 } 718 719 case CS_PARSED: 720 { 721 if (c.getClassDefinition().isInsideLocal()) { 722 break; 723 } 724 // If we get to here, then compilation is going 725 // to occur. If the -Xnocompile switch is set 726 // then fail. Note that this check is required 727 // here because this method is called from 728 // generators, not just from within this class... 729 730 if (nocompile) { 731 throw new IOException("Compilation required, but -Xnocompile option in effect"); 732 } 733 734 done = false; 735 736 src = (SourceClass)c.getClassDefinition(env); 737 src.check(env); 738 c.setDefinition(src, CS_CHECKED); 739 // fall through 740 } 741 742 case CS_CHECKED: 743 { 744 src = (SourceClass)c.getClassDefinition(env); 745 // bail out if there were any errors 746 if (src.getError()) { 747 c.setDefinition(src, CS_COMPILED); 748 break; 749 } 750 done = false; 751 buf.reset(); 752 src.compile(buf); 753 c.setDefinition(src, CS_COMPILED); 754 src.cleanup(env); 755 756 if (src.getError() || nowrite) { 757 break; 758 } 759 760 String pkgName = c.getName().getQualifier().toString().replace('.', File.separatorChar); 761 String className = c.getName().getFlatName().toString().replace('.', SIGC_INNERCLASS) + ".class"; 762 763 File file; 764 if (destDir != null) { 765 if (pkgName.length() > 0) { 766 file = new File(destDir, pkgName); 767 if (!file.exists()) { 768 file.mkdirs(); 769 } 770 file = new File(file, className); 771 } else { 772 file = new File(destDir, className); 773 } 774 } else { 775 ClassFile classfile = (ClassFile)src.getSource(); 776 if (classfile.isZipped()) { 777 env.error(0, "cant.write", classfile.getPath()); 778 break; 779 } 780 file = new File(classfile.getPath()); 781 file = new File(file.getParent(), className); 782 } 783 784 // Create the file 785 try { 786 FileOutputStream out = new FileOutputStream(file.getPath()); 787 buf.writeTo(out); 788 out.close(); 789 if (env.verbose()) { 790 output(getText("rmic.wrote", file.getPath())); 791 } 792 } catch (IOException ee) { 793 env.error(0, "cant.write", file.getPath()); 794 } 795 } 796 } 797 return done; 798 } 799 800 /** 801 * Main program 802 */ 803 public static void main(String argv[]) { 804 Main compiler = new Main(System.out, "rmic"); 805 System.exit(compiler.compile(argv) ? 0 : 1); 806 } 807 808 /** 809 * Return the string value of a named resource in the rmic.properties 810 * resource bundle. If the resource is not found, null is returned. 811 */ 812 public static String getString(String key) { 813 if (!resourcesInitialized) { 814 initResources(); 815 } 816 817 // To enable extensions, search the 'resourcesExt' 818 // bundle first, followed by the 'resources' bundle... 819 820 if (resourcesExt != null) { 821 try { 822 return resourcesExt.getString(key); 823 } catch (MissingResourceException e) {} 824 } 825 826 try { 827 return resources.getString(key); 828 } catch (MissingResourceException ignore) { 829 } 830 return null; 831 } 832 833 private static boolean resourcesInitialized = false; 834 private static ResourceBundle resources; 835 private static ResourceBundle resourcesExt = null; 836 837 private static void initResources() { 838 try { 839 resources = 840 ResourceBundle.getBundle("sun.rmi.rmic.resources.rmic"); 841 resourcesInitialized = true; 842 try { 843 resourcesExt = 844 ResourceBundle.getBundle("sun.rmi.rmic.resources.rmicext"); 845 } catch (MissingResourceException e) {} 846 } catch (MissingResourceException e) { 847 throw new Error("fatal: missing resource bundle: " + 848 e.getClassName()); 849 } 850 } 851 852 public static String getText(String key) { 853 String message = getString(key); 854 if (message == null) { 855 message = "no text found: \"" + key + "\""; 856 } 857 return message; 858 } 859 860 public static String getText(String key, int num) { 861 return getText(key, Integer.toString(num), null, null); 862 } 863 864 public static String getText(String key, String arg0) { 865 return getText(key, arg0, null, null); 866 } 867 868 public static String getText(String key, String arg0, String arg1) { 869 return getText(key, arg0, arg1, null); 870 } 871 872 public static String getText(String key, 873 String arg0, String arg1, String arg2) 874 { 875 String format = getString(key); 876 if (format == null) { 877 format = "no text found: key = \"" + key + "\", " + 878 "arguments = \"{0}\", \"{1}\", \"{2}\""; 879 } 880 881 String[] args = new String[3]; 882 args[0] = (arg0 != null ? arg0 : "null"); 883 args[1] = (arg1 != null ? arg1 : "null"); 884 args[2] = (arg2 != null ? arg2 : "null"); 885 886 return java.text.MessageFormat.format(format, (Object[]) args); 887 } 888 }