001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * @author Vasily Zakharov
021: * @version $Revision: 1.1.2.3 $
022: */package org.apache.harmony.rmi.compiler;
023:
024: import java.io.File;
025: import java.io.FileWriter;
026: import java.io.IOException;
027:
028: import java.util.ArrayList;
029: import java.util.HashMap;
030: import java.util.Iterator;
031: import java.util.StringTokenizer;
032:
033: import org.apache.harmony.rmi.common.JavaCompiler;
034: import org.apache.harmony.rmi.common.JavaCompilerException;
035: import org.apache.harmony.rmi.common.RMIUtil;
036: import org.apache.harmony.rmi.internal.nls.Messages;
037:
038: /**
039: * Core class of RMI Compiler.
040: *
041: * @author Vasily Zakharov
042: * @version $Revision: 1.1.2.3 $
043: *
044: * @todo Implement IDL/IIOP support.
045: */
046: public final class RMICompiler implements RmicConstants, RmicStrings {
047:
048: /**
049: * Version of stubs to generate.
050: */
051: private final int version;
052:
053: /**
054: * Option: do not delete generated source files
055: * (<code>-keep</code>).
056: */
057: private final boolean keepSources;
058:
059: /**
060: * Option: always regenerate IDL/IIOP stubs
061: * (<code>-always</code>).
062: */
063: private final boolean always;
064:
065: /**
066: * Option: use factory keyword in generated IDL stubs
067: * (<code>-factory</code>).
068: */
069: private final boolean factory;
070:
071: /**
072: * Option: valuetype methods and initializers in IDL stubs
073: * (<code>-noValueMethods</code>).
074: */
075: private final boolean valueMethods;
076:
077: /**
078: * Option: create stubs optimized for the same process
079: * (<code>-nolocalstubs</code>).
080: */
081: private final boolean localStubs;
082:
083: /**
084: * Option: change POA inheritance
085: * (<code>-poa</code>).
086: */
087: private final boolean poa;
088:
089: /**
090: * Option: generate debug information
091: * (<code>-debug</code>).
092: */
093: private final boolean debug;
094:
095: /**
096: * Option: notify about compiler warnings
097: * (<code>-nowarn</code>).
098: */
099: private final boolean warnings;
100:
101: /**
102: * Option: do not write compiled classes
103: * (<code>-nowrite</code>).
104: */
105: private final boolean writeClasses;
106:
107: /**
108: * Option: print detailed compilation log
109: * (<code>-verbose</code>).
110: */
111: private final boolean verbose;
112:
113: /**
114: * Option: recompile dependent classes.
115: * (<code>-depend</code>).
116: */
117: private final boolean depend;
118:
119: /**
120: * Option: destination directory for generated source and class files.
121: * (<code>-d</code>).
122: */
123: private final String destinationDir;
124:
125: /**
126: * If any options were specified.
127: */
128: private final boolean optionsPresent;
129:
130: /**
131: * Java compiler options.
132: */
133: private final String[] javacOptions;
134:
135: /**
136: * Warning flags.
137: */
138: private HashMap warningTags = new HashMap();
139:
140: /**
141: * Classes to generate stubs for.
142: *
143: * We use {@link Object} array to store either {@link String}
144: * or {@link Class} objects because we don't want to resolve all the class
145: * names in the beginning - to avoid one incorrectly spelled class name
146: * preventing generation of stubs for other specified classes.
147: */
148: private Object[] classes;
149:
150: /**
151: * Number of classes in {@link #classes} array.
152: */
153: private int numClasses;
154:
155: /**
156: * Constructs instance of RMI Compiler.
157: *
158: * @param args
159: * RMI Compiler options (see
160: * <a href="package-summary.html">package description</a>
161: * for details).
162: *
163: * @param classes
164: * Classes to process.
165: * Call {@link #run()} to process them.
166: *
167: * @throws RMICompilerException
168: * If some error occurs.
169: */
170: public RMICompiler(String[] args, Class[] classes)
171: throws RMICompilerException {
172: this (args, (Object[]) classes);
173: }
174:
175: /**
176: * Constructs instance of RMI Compiler.
177: *
178: * @param args
179: * RMI Compiler options (see
180: * <a href="package-summary.html">package description</a>
181: * for details).
182: *
183: * @param classNames
184: * Names of classes to process.
185: * Call {@link #run()} to process them.
186: *
187: * @throws RMICompilerException
188: * If some error occurs.
189: */
190: public RMICompiler(String[] args, String[] classNames)
191: throws RMICompilerException {
192: this (args, (Object[]) classNames);
193: }
194:
195: /**
196: * Constructs instance of RMI Compiler.
197: *
198: * @param args
199: * RMI Compiler options (see
200: * <a href="package-summary.html">package description</a>
201: * for details).
202: *
203: * @param classNames
204: * Names of classes to process (space separated).
205: * Call {@link #run()} to process them.
206: *
207: * @throws RMICompilerException
208: * If some error occurs.
209: */
210: public RMICompiler(String[] args, String classNames)
211: throws RMICompilerException {
212: this (args, (Object[]) stringToArray(classNames));
213: }
214:
215: /**
216: * Constructs instance of RMI Compiler.
217: *
218: * @param args
219: * RMI Compiler options (see
220: * <a href="package-summary.html">package description</a>
221: * for details).
222: * Call {@link #run(Class[])} or {@link #run(String[])}
223: * to specify classes to process and process them immediately.
224: *
225: * @throws RMICompilerException
226: * If some error occurs.
227: */
228: public RMICompiler(String[] args) throws RMICompilerException {
229: this (args, (Object[]) null);
230: }
231:
232: /**
233: * Constructs instance of RMI Compiler.
234: *
235: * @param args
236: * RMI Compiler options, space separated (see
237: * <a href="package-summary.html">package description</a>
238: * for details).
239: *
240: * @param classes
241: * Classes to process.
242: * Call {@link #run()} to process them.
243: *
244: * @throws RMICompilerException
245: * If some error occurs.
246: */
247: public RMICompiler(String args, Class[] classes)
248: throws RMICompilerException {
249: this (stringToArray(args), (Object[]) classes);
250: }
251:
252: /**
253: * Constructs instance of RMI Compiler.
254: *
255: * @param args
256: * RMI Compiler options, space separated (see
257: * <a href="package-summary.html">package description</a>
258: * for details).
259: *
260: * @param classNames
261: * Names of classes to process.
262: * Call {@link #run()} to process them.
263: *
264: * @throws RMICompilerException
265: * If some error occurs.
266: */
267: public RMICompiler(String args, String[] classNames)
268: throws RMICompilerException {
269: this (stringToArray(args), (Object[]) classNames);
270: }
271:
272: /**
273: * Constructs instance of RMI Compiler.
274: *
275: * @param args
276: * RMI Compiler options, space separated (see
277: * <a href="package-summary.html">package description</a>
278: * for details).
279: *
280: * @param classNames
281: * Names of classes to process (space separated).
282: * Call {@link #run()} to process them.
283: *
284: * @throws RMICompilerException
285: * If some error occurs.
286: */
287: public RMICompiler(String args, String classNames)
288: throws RMICompilerException {
289: this (stringToArray(args), (Object[]) stringToArray(classNames));
290: }
291:
292: /**
293: * Constructs instance of RMI Compiler.
294: *
295: * @param args
296: * RMI Compiler options, space separated (see
297: * <a href="package-summary.html">package description</a>
298: * for details).
299: * Call {@link #run(Class[])} or {@link #run(String[])}
300: * to specify classes to process and process them immediately.
301: *
302: * @throws RMICompilerException
303: * If some error occurs.
304: */
305: public RMICompiler(String args) throws RMICompilerException {
306: this (stringToArray(args), (Object[]) null);
307: }
308:
309: /**
310: * Private constructor, called from all other constructors.
311: *
312: * @param args
313: * RMI Compiler options (see
314: * <a href="package-summary.html">package description</a>
315: * for details).
316: *
317: * @param classes
318: * Classes to process ({@link Class} or {@link String}
319: * objects).
320: *
321: * @throws RMICompilerException
322: * If some error occurs.
323: */
324: private RMICompiler(String[] args, Object[] classes)
325: throws RMICompilerException {
326: int numArgs = args.length;
327:
328: int version = VERSION_NOT_SET;
329: boolean keepSources = false;
330: boolean always = false;
331: boolean factory = false;
332: boolean valueMethods = true;
333: boolean localStubs = true;
334: boolean poa = false;
335: boolean debug = false;
336: boolean warnings = true;
337: boolean writeClasses = true;
338: boolean verbose = false;
339: boolean depend = false;
340: boolean optionsPresent = (numArgs > 0);
341: String destinationDir = "."; //$NON-NLS-1$
342:
343: ArrayList javacOptionsList = new ArrayList();
344:
345: // User doesn't need any warnings on compiling stubs, as all possible
346: // issues that may appear concern RMI specification, not user classes.
347: javacOptionsList.add(optionNoWarnings);
348:
349: // Parse arguments, adjust values of option fields,
350: // add necessary options to javacOptionsList.
351: for (int i = 0; i < numArgs; i++) {
352: String arg = args[i].intern();
353:
354: if (arg == optionV11) {
355: if (version == VERSION_NOT_SET) {
356: version = VERSION_V11;
357: } else {
358: error(errorVersionText);
359: }
360: } else if (arg == optionV12) {
361: if (version == VERSION_NOT_SET) {
362: version = VERSION_V12;
363: } else {
364: error(errorVersionText);
365: }
366: } else if (arg == optionVCompat) {
367: if (version == VERSION_NOT_SET) {
368: version = VERSION_VCOMPAT;
369: } else {
370: error(errorVersionText);
371: }
372: } else if (arg == optionIDL) {
373: if (version == VERSION_NOT_SET) {
374: version = VERSION_IDL;
375: } else {
376: error(errorVersionText);
377: }
378: } else if (arg == optionIIOP) {
379: if (version == VERSION_NOT_SET) {
380: version = VERSION_IIOP;
381: } else {
382: error(errorVersionText);
383: }
384: } else if (arg == optionTarget) {
385: if (i < (numArgs - 1)) {
386: // If parameter is available,
387: // add options to javacOptionsList.
388: String target = args[++i].intern();
389: String source = (((target == "1.1") || (target == "1.2")) //$NON-NLS-1$ //$NON-NLS-2$
390: ? "1.3" : target); //$NON-NLS-1$
391:
392: javacOptionsList.add(optionSource);
393: javacOptionsList.add(source);
394: javacOptionsList.add(optionTarget);
395: javacOptionsList.add(target);
396: } else {
397: error(errorNeedParameterText, arg);
398: }
399: } else if ((arg == optionKeep)
400: || (arg == optionKeepGenerated)) {
401: keepSources = true;
402: } else if ((arg == optionAlways)
403: || (arg == optionAlwaysGenerate)) {
404: always = true;
405: } else if (arg == optionFactory) {
406: factory = true;
407: } else if (arg == optionNoValueMethods) {
408: valueMethods = false;
409: } else if (arg == optionNoLocalStubs) {
410: localStubs = false;
411: } else if (arg == optionPOA) {
412: poa = true;
413: } else if ((arg == optionDebug)
414: || arg.startsWith(optionDebugDetails)) {
415: javacOptionsList.add(arg);
416: debug = true;
417: } else if (arg == optionNoWarnings) {
418: warnings = false;
419: } else if (arg == optionNoWrite) {
420: writeClasses = false;
421: } else if (arg == optionVerbose) {
422: javacOptionsList.add(arg);
423: verbose = true;
424: } else if (arg == optionDepend) {
425: depend = true;
426: } else if ((arg == optionIdlModule)
427: || (arg == optionIdlFile)) {
428: if (i < (numArgs - 2)) {
429: // @ToDo: implement for IDL support.
430: i += 2;
431: } else {
432: error(errorNeedTwoParametersText, arg);
433: }
434: } else if ((arg == optionClassPath) || (arg == optionCP)
435: || (arg == optionBootClassPath)
436: || (arg == optionExtDirs)
437: || (arg == optionDestinationDir)) {
438: if (i < (numArgs - 1)) {
439: // If parameter is available,
440: // add option to javacOptionsList.
441: String option = ((arg == optionCP) ? optionClassPath
442: : arg);
443: javacOptionsList.add(option);
444: String param = args[++i];
445: javacOptionsList.add(param);
446:
447: if (arg == optionDestinationDir) {
448: destinationDir = param;
449: } else {
450: addWarning(option.substring(1),
451: warningClassPathText);
452: }
453: } else {
454: error(errorNeedParameterText, arg);
455: }
456: } else if (arg.startsWith(optionJava)
457: || arg.startsWith(optionX)) {
458: if (arg.length() > 2) {
459: // If parameter is available,
460: // add option to javacOptionsList.
461: javacOptionsList.add(arg);
462: } else {
463: error(errorNeedJVMParameterText, arg);
464: }
465: } else if (arg.startsWith(optionPrefix)) {
466: // What starts with dash is probably the non-specified option.
467: error(errorUnknownOptionText, arg);
468: } else {
469: // First arg that is not an option.
470: if (classes != null) {
471: // If classes are specified explicitly,
472: // then this is just an incorrect option.
473: error(errorUnknownOptionText, arg);
474: } else {
475: // If classes are not specified explicitly,
476: // extract them from args.
477: int numClasses = (numArgs - i);
478: classes = new Object[numClasses];
479: System.arraycopy(args, i, classes, 0, numClasses);
480:
481: if (i == 0) {
482: // Mark that no options were really specified.
483: optionsPresent = false;
484: }
485: }
486: break;
487: }
488: }
489:
490: // Print warnings.
491: if (warnings) {
492: for (Iterator i = warningTags.values().iterator(); i
493: .hasNext();) {
494: // rmi.console.1A=WARNING: {0}
495: System.err.println(Messages.getString(
496: "rmi.console.1A", i.next())); //$NON-NLS-1$
497: }
498: }
499:
500: // Check options compatibility.
501: if (always && (version != VERSION_IDL)
502: && (version != VERSION_IIOP)) {
503: error(errorUnusableExceptIDL_IIOP, optionAlways);
504: }
505:
506: if (factory && (version != VERSION_IDL)) {
507: error(errorUnusableExceptIDL, optionFactory);
508: }
509:
510: if (!valueMethods && (version != VERSION_IDL)) {
511: error(errorUnusableExceptIDL, optionNoValueMethods);
512: }
513:
514: if (!localStubs && (version != VERSION_IIOP)) {
515: error(errorUnusableExceptIIOP, optionNoLocalStubs);
516: }
517:
518: if (poa && (version != VERSION_IIOP)) {
519: error(errorUnusableExceptIIOP, optionPOA);
520: }
521:
522: if (version == VERSION_NOT_SET) {
523: version = VERSION_V12;
524: }
525:
526: // Save configuration.
527: this .classes = ((classes != null) ? classes : new Object[0]);
528: this .numClasses = this .classes.length;
529:
530: this .version = version;
531: this .keepSources = keepSources;
532: this .always = always;
533: this .factory = factory;
534: this .valueMethods = valueMethods;
535: this .localStubs = localStubs;
536: this .poa = poa;
537: this .debug = debug;
538: this .warnings = warnings;
539: this .writeClasses = writeClasses;
540: this .verbose = verbose;
541: this .depend = depend;
542: this .optionsPresent = optionsPresent;
543: this .destinationDir = destinationDir;
544:
545: // "Export" Javac options.
546: javacOptions = (String[]) javacOptionsList
547: .toArray(new String[javacOptionsList.size()]);
548: }
549:
550: /**
551: * Runs the compilation process.
552: *
553: * Note that to call this method classes to compile must be specified
554: * in constructor or in previous call to {@link #run(Class[])}
555: * or {@link #run(String[])}.
556: *
557: * @throws RMICompilerException
558: * If some error occurs.
559: */
560: public void run() throws RMICompilerException {
561: if (numClasses < 1) {
562: if (optionsPresent) {
563: error(errorNoClassesText);
564: } else {
565: usage();
566: }
567: }
568:
569: // Create files array for stub and skeleton files.
570: File[] stubFiles = new File[numClasses];
571: File[] skelFiles = new File[numClasses];
572: File[] skelClassFiles = new File[numClasses];
573: int filesNum = 0;
574:
575: try {
576: // Walk through the specified classes.
577: for (int i = 0; i < numClasses; i++) {
578:
579: // Find out the class to process.
580: Object obj = classes[i];
581: Class cls;
582:
583: if (obj instanceof Class) {
584: cls = (Class) obj;
585: } else { // (obj instanceof String)
586: String className = (String) obj;
587:
588: try {
589: cls = Class.forName(className);
590: classes[i] = cls;
591: } catch (ClassNotFoundException e) {
592: // rmi.55=Class not found: {0}
593: throw new RMICompilerException(Messages
594: .getString("rmi.55", e.getMessage()), e); //$NON-NLS-1$
595: } catch (LinkageError e) {
596: // rmi.57=Class loading error: {0}
597: throw new RMICompilerException(Messages
598: .getString("rmi.57", e.getMessage()), e); //$NON-NLS-1$
599: }
600: }
601:
602: // Create class stub.
603: ClassStub stub = new ClassStub(version, cls);
604:
605: String packageName = RMIUtil.getPackageName(cls);
606:
607: if (packageName != null) {
608: packageName = packageName.replace('.',
609: File.separatorChar);
610: }
611:
612: String stubClassName = stub.getStubClassName();
613: String skelClassName = stub.getSkeletonClassName();
614:
615: File dir = RmicUtil.getPackageDir(destinationDir,
616: packageName);
617:
618: // Generate stub source file name.
619: File stubFile = RmicUtil.getPackageFile(dir,
620: stubClassName + javaSuffix);
621: stubFiles[filesNum] = stubFile;
622:
623: // Generate skeleton source file name.
624: File skelFile = RmicUtil.getPackageFile(dir,
625: skelClassName + javaSuffix);
626: skelFiles[filesNum] = skelFile;
627:
628: // Generate skeleton class file name.
629: skelClassFiles[filesNum] = RmicUtil.getPackageFile(dir,
630: skelClassName + classSuffix);
631:
632: filesNum++;
633:
634: try {
635: // Write generated stub source to the file.
636: FileWriter writer = new FileWriter(stubFile);
637: writer.write(stub.getStubSource());
638: writer.close();
639: } catch (IOException e) {
640: // rmi.58=Can't write file {0}
641: throw new RMICompilerException(Messages.getString(
642: "rmi.58", stubFile.getName()), e); //$NON-NLS-1$
643: }
644:
645: if (version != VERSION_V12) {
646: try {
647: // Write generated skeleton source to the file.
648: FileWriter writer = new FileWriter(skelFile);
649: writer.write(stub.getSkeletonSource());
650: writer.close();
651: } catch (IOException e) {
652: // rmi.58=Can't write file {0}
653: throw new RMICompilerException(
654: Messages.getString(
655: "rmi.58", skelFile.getName()), e); //$NON-NLS-1$
656: }
657: }
658: }
659:
660: // Prepare files array for compilation.
661: File[] files;
662:
663: if (version == VERSION_V12) {
664: files = stubFiles;
665: } else {
666: files = new File[2 * numClasses];
667:
668: int j = 0;
669: for (int i = 0; i < numClasses; i++) {
670: files[j++] = stubFiles[i];
671: files[j++] = skelFiles[i];
672: }
673: }
674:
675: try {
676: // Invoke Java compiler for generated files.
677: int ret = JavaCompiler.locateJavaCompiler(verbose)
678: .compile(javacOptions, files);
679:
680: if (ret != 0) {
681: // rmi.59=Javac failed, code {0}
682: throw new RMICompilerException(Messages.getString(
683: "rmi.59", ret)); //$NON-NLS-1$
684: }
685: } catch (JavaCompilerException e) {
686: // rmi.5A=Can't run Javac: {0}
687: throw new RMICompilerException(Messages.getString(
688: "rmi.5A", e), e); //$NON-NLS-1$
689: }
690: } finally {
691:
692: // Remove generated stub and skeleton files even if exception arose
693: if (!keepSources) {
694: for (int i = 0; i < filesNum; i++) {
695: stubFiles[i].delete();
696: }
697: }
698:
699: // Remove skeleton files if version is set to 1.2.
700: if (!keepSources || (version == VERSION_V12)) {
701: for (int i = 0; i < filesNum; i++) {
702: skelFiles[i].delete();
703: }
704: }
705:
706: // Remove skeleton class files if version is set to 1.2.
707: if (version == VERSION_V12) {
708: for (int i = 0; i < filesNum; i++) {
709: skelClassFiles[i].delete();
710: }
711: }
712: }
713: }
714:
715: /**
716: * Runs the compilation process.
717: *
718: * @param classes
719: * Classes to compile. This classes list replace any previous
720: * specifications of classes to compile, made in constructor
721: * or in other calls to <code>run()</code> methods.
722: *
723: * @throws RMICompilerException
724: * If some error occurs.
725: */
726: public void run(Class[] classes) throws RMICompilerException {
727: this .classes = (Object[]) classes;
728: run();
729: }
730:
731: /**
732: * Runs the compilation process.
733: *
734: * @param classNames
735: * Names of classes to compile. This classes list replace any
736: * previous specifications of classes to compile, made in
737: * constructor or in other calls to <code>run()</code> methods.
738: *
739: * @throws RMICompilerException
740: * If some error occurs.
741: */
742: public void run(String[] classNames) throws RMICompilerException {
743: this .classes = (Object[]) classNames;
744: run();
745: }
746:
747: /**
748: * Produces usage information.
749: *
750: * @throws RMICompilerException
751: * Always. Exception message contains usage information.
752: */
753: private static void usage() throws RMICompilerException {
754: throw new RMICompilerException(EOLN + usageText);
755: }
756:
757: /**
758: * Produces error message.
759: *
760: * @param message
761: * Error message.
762: *
763: * @throws RMICompilerException
764: * Always. Exception message contains the specified message
765: * and usage information.
766: */
767: private static void error(String message)
768: throws RMICompilerException {
769: throw new RMICompilerException(message + EOLN);
770: }
771:
772: /**
773: * Produces error message.
774: *
775: * @param message
776: * Error message, can contain references to a single argument,
777: * specified by <code>arg</code>. References are represented
778: * by <code>%s</code> substrings.
779: *
780: * @param arg
781: * Argument. Each occurrence of <code>%s</code>
782: * in <code>message</code> is replaced with this string.
783: *
784: * @throws RMICompilerException
785: * Always. Exception message contains the specified message
786: * and usage information.
787: */
788: private static void error(String message, String arg)
789: throws RMICompilerException {
790: error(message.replaceAll("%s", arg)); //$NON-NLS-1$
791: }
792:
793: /**
794: * Produces warning message.
795: *
796: * @param tag
797: * Warning tag. Used to track warnings. Also, each occurrence
798: * of <code>%s</code> in <code>message</code> is replaced
799: * with this string.
800: *
801: * @param message
802: * Warning message, can contain references to a single argument,
803: * specified by <code>arg</code>. References are represented
804: * by <code>%s</code> substrings.
805: */
806: private void addWarning(String tag, String message) {
807: warningTags.put(tag, message.replaceAll("%s", tag)); //$NON-NLS-1$
808: }
809:
810: /**
811: * Parses string to a number of tokens, using spaces as separators.
812: * Indeed, {@link StringTokenizer} with no parameters is used.
813: *
814: * @param string
815: * String to process.
816: *
817: * @return String array containing all tokens of the specified string.
818: */
819: private static String[] stringToArray(String string) {
820: StringTokenizer tokenizer = new StringTokenizer(string);
821: int numTokens = tokenizer.countTokens();
822: String[] array = new String[numTokens];
823:
824: for (int i = 0; i < numTokens; i++) {
825: array[i] = tokenizer.nextToken();
826: }
827:
828: return array;
829: }
830: }
|