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