blob: bb9719aafa6756ee659cb76ef39de929e4afe552 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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
33package sun.rmi.rmic;
34
35import java.util.Vector;
36import java.util.Enumeration;
37import java.util.ResourceBundle;
38import java.util.StringTokenizer;
39import java.util.MissingResourceException;
40
41import java.io.OutputStream;
42import java.io.PrintStream;
43import java.io.IOException;
44import java.io.File;
45import java.io.FileNotFoundException;
46import java.io.FileOutputStream;
47import java.io.ByteArrayOutputStream;
48
49import sun.tools.java.ClassFile;
50import sun.tools.java.ClassDefinition;
51import sun.tools.java.ClassDeclaration;
52import sun.tools.java.ClassNotFound;
53import sun.tools.java.Identifier;
54import sun.tools.java.ClassPath;
55
56import sun.tools.javac.SourceClass;
57import sun.tools.util.CommandLine;
58import java.lang.reflect.Constructor;
59import 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 */
68public 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 classes;
77 boolean nowrite;
78 boolean nocompile;
79 boolean keepGenerated;
80 boolean status;
81 String[] generatorArgs;
82 Vector 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();
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();
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((String)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 = (Generator)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 */
686 public boolean compileClass (ClassDeclaration c,
687 ByteArrayOutputStream buf,
688 BatchEnvironment env)
689 throws ClassNotFound,
690 IOException,
691 InterruptedException {
692 boolean done = true;
693 env.flushErrors();
694 SourceClass src;
695
696 switch (c.getStatus()) {
697 case CS_UNDEFINED:
698 {
699 if (!env.dependencies()) {
700 break;
701 }
702 // fall through
703 }
704
705 case CS_SOURCE:
706 {
707 done = false;
708 env.loadDefinition(c);
709 if (c.getStatus() != CS_PARSED) {
710 break;
711 }
712 // fall through
713 }
714
715 case CS_PARSED:
716 {
717 if (c.getClassDefinition().isInsideLocal()) {
718 break;
719 }
720 // If we get to here, then compilation is going
721 // to occur. If the -Xnocompile switch is set
722 // then fail. Note that this check is required
723 // here because this method is called from
724 // generators, not just from within this class...
725
726 if (nocompile) {
727 throw new IOException("Compilation required, but -Xnocompile option in effect");
728 }
729
730 done = false;
731
732 src = (SourceClass)c.getClassDefinition(env);
733 src.check(env);
734 c.setDefinition(src, CS_CHECKED);
735 // fall through
736 }
737
738 case CS_CHECKED:
739 {
740 src = (SourceClass)c.getClassDefinition(env);
741 // bail out if there were any errors
742 if (src.getError()) {
743 c.setDefinition(src, CS_COMPILED);
744 break;
745 }
746 done = false;
747 buf.reset();
748 src.compile(buf);
749 c.setDefinition(src, CS_COMPILED);
750 src.cleanup(env);
751
752 if (src.getError() || nowrite) {
753 break;
754 }
755
756 String pkgName = c.getName().getQualifier().toString().replace('.', File.separatorChar);
757 String className = c.getName().getFlatName().toString().replace('.', SIGC_INNERCLASS) + ".class";
758
759 File file;
760 if (destDir != null) {
761 if (pkgName.length() > 0) {
762 file = new File(destDir, pkgName);
763 if (!file.exists()) {
764 file.mkdirs();
765 }
766 file = new File(file, className);
767 } else {
768 file = new File(destDir, className);
769 }
770 } else {
771 ClassFile classfile = (ClassFile)src.getSource();
772 if (classfile.isZipped()) {
773 env.error(0, "cant.write", classfile.getPath());
774 break;
775 }
776 file = new File(classfile.getPath());
777 file = new File(file.getParent(), className);
778 }
779
780 // Create the file
781 try {
782 FileOutputStream out = new FileOutputStream(file.getPath());
783 buf.writeTo(out);
784 out.close();
785 if (env.verbose()) {
786 output(getText("rmic.wrote", file.getPath()));
787 }
788 } catch (IOException ee) {
789 env.error(0, "cant.write", file.getPath());
790 }
791 }
792 }
793 return done;
794 }
795
796 /**
797 * Main program
798 */
799 public static void main(String argv[]) {
800 Main compiler = new Main(System.out, "rmic");
801 System.exit(compiler.compile(argv) ? 0 : 1);
802 }
803
804 /**
805 * Return the string value of a named resource in the rmic.properties
806 * resource bundle. If the resource is not found, null is returned.
807 */
808 public static String getString(String key) {
809 if (!resourcesInitialized) {
810 initResources();
811 }
812
813 // To enable extensions, search the 'resourcesExt'
814 // bundle first, followed by the 'resources' bundle...
815
816 if (resourcesExt != null) {
817 try {
818 return resourcesExt.getString(key);
819 } catch (MissingResourceException e) {}
820 }
821
822 try {
823 return resources.getString(key);
824 } catch (MissingResourceException ignore) {
825 }
826 return null;
827 }
828
829 private static boolean resourcesInitialized = false;
830 private static ResourceBundle resources;
831 private static ResourceBundle resourcesExt = null;
832
833 private static void initResources() {
834 try {
835 resources =
836 ResourceBundle.getBundle("sun.rmi.rmic.resources.rmic");
837 resourcesInitialized = true;
838 try {
839 resourcesExt =
840 ResourceBundle.getBundle("sun.rmi.rmic.resources.rmicext");
841 } catch (MissingResourceException e) {}
842 } catch (MissingResourceException e) {
843 throw new Error("fatal: missing resource bundle: " +
844 e.getClassName());
845 }
846 }
847
848 public static String getText(String key) {
849 String message = getString(key);
850 if (message == null) {
851 message = "no text found: \"" + key + "\"";
852 }
853 return message;
854 }
855
856 public static String getText(String key, int num) {
857 return getText(key, Integer.toString(num), null, null);
858 }
859
860 public static String getText(String key, String arg0) {
861 return getText(key, arg0, null, null);
862 }
863
864 public static String getText(String key, String arg0, String arg1) {
865 return getText(key, arg0, arg1, null);
866 }
867
868 public static String getText(String key,
869 String arg0, String arg1, String arg2)
870 {
871 String format = getString(key);
872 if (format == null) {
873 format = "no text found: key = \"" + key + "\", " +
874 "arguments = \"{0}\", \"{1}\", \"{2}\"";
875 }
876
877 String[] args = new String[3];
878 args[0] = (arg0 != null ? arg0.toString() : "null");
879 args[1] = (arg1 != null ? arg1.toString() : "null");
880 args[2] = (arg2 != null ? arg2.toString() : "null");
881
882 return java.text.MessageFormat.format(format, args);
883 }
884}