001: /*
002: * Janino - An embedded Java[TM] compiler
003: *
004: * Copyright (c) 2006, Arno Unkrig
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * 2. Redistributions in binary form must reproduce the above
014: * copyright notice, this list of conditions and the following
015: * disclaimer in the documentation and/or other materials
016: * provided with the distribution.
017: * 3. The name of the author may not be used to endorse or promote
018: * products derived from this software without specific prior
019: * written permission.
020: *
021: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
022: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
023: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
024: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
025: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
026: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
027: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
028: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
029: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
030: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
031: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
032: */
033:
034: package org.codehaus.janino.tools;
035:
036: import java.util.List;
037: import java.util.ArrayList;
038: import java.util.Iterator;
039:
040: import java.io.*;
041:
042: import org.codehaus.janino.*;
043: import org.codehaus.janino.util.*;
044: import org.codehaus.janino.util.enumerator.*;
045: import org.codehaus.janino.util.iterator.*;
046: import org.codehaus.janino.util.resource.*;
047:
048: /**
049: * Reads a set of compilation units from the file system and searches it for specific
050: * Java<sup>TM</sup> constructs, e.g. invocations of a particular method.
051: *
052: * Usage:
053: * <pre>
054: * java org.codehaus.janino.JGrep \
055: * [ -dirs <i>directory-name-patterns</i> ] \
056: * [ -files <i>file-name-patterns</i> ] \
057: * { <i>directory-path</i> } \
058: * -method-invocation <i>class.method(arg-types)</i>
059: * java org.codehaus.janino.JGrep -help
060: * </pre>
061: *
062: * If "-dirs" is not given, then all <i>directory-path</i>es are scanned for files.
063: * The <i>directory-name-patterns</i> work as described in
064: * {@link org.codehaus.janino.util.StringPattern#parseCombinedPattern(String)}.
065: * <p>
066: * If "-files" is not given, then all files ending in ".java" are read. The
067: * <i>file-name-patterns</i> work as described in
068: * {@link org.codehaus.janino.util.StringPattern#parseCombinedPattern(String)}.
069: */
070: public class JGrep {
071: private static final boolean DEBUG = false;
072: private List parsedCompilationUnits = new ArrayList(); // UnitCompiler
073:
074: /**
075: * Command line interface.
076: */
077: public static void main(String[] args) {
078: int idx = 0;
079:
080: StringPattern[] directoryNamePatterns = StringPattern.PATTERNS_ALL;
081: StringPattern[] fileNamePatterns = new StringPattern[] { new StringPattern(
082: "*.java") };
083: File[] classPath = new File[] { new File(".") };
084: File[] optionalExtDirs = null;
085: File[] optionalBootClassPath = null;
086: String optionalCharacterEncoding = null;
087: boolean verbose = false;
088:
089: for (; idx < args.length; ++idx) {
090: String arg = args[idx];
091: if (arg.charAt(0) != '-')
092: break;
093: if (arg.equals("-dirs")) {
094: directoryNamePatterns = StringPattern
095: .parseCombinedPattern(args[++idx]);
096: } else if (arg.equals("-files")) {
097: fileNamePatterns = StringPattern
098: .parseCombinedPattern(args[++idx]);
099: } else if (arg.equals("-classpath")) {
100: classPath = PathResourceFinder.parsePath(args[++idx]);
101: } else if (arg.equals("-extdirs")) {
102: optionalExtDirs = PathResourceFinder
103: .parsePath(args[++idx]);
104: } else if (arg.equals("-bootclasspath")) {
105: optionalBootClassPath = PathResourceFinder
106: .parsePath(args[++idx]);
107: } else if (arg.equals("-encoding")) {
108: optionalCharacterEncoding = args[++idx];
109: } else if (arg.equals("-verbose")) {
110: verbose = true;
111: } else if (arg.equals("-help")) {
112: for (int j = 0; j < JGrep.USAGE.length; ++j)
113: System.out.println(JGrep.USAGE[j]);
114: System.exit(1);
115: } else {
116: System.err
117: .println("Unexpected command-line argument \""
118: + arg + "\", try \"-help\".");
119: System.exit(1);
120: /* NEVER REACHED */return;
121: }
122: }
123:
124: // { directory-path }
125: File[] rootDirectories;
126: {
127: int first = idx;
128: for (; idx < args.length && args[idx].charAt(0) != '-'; ++idx)
129: ;
130: if (idx == first) {
131: System.err
132: .println("No <directory-path>es given, try \"-help\".");
133: System.exit(1);
134: /* NEVER REACHED */return;
135: }
136: rootDirectories = new File[idx - first];
137: for (int i = first; i < idx; ++i)
138: rootDirectories[i - first] = new File(args[i]);
139: }
140:
141: // Create the JGrep object.
142: final JGrep jGrep = new JGrep(classPath, optionalExtDirs,
143: optionalBootClassPath, optionalCharacterEncoding,
144: verbose);
145:
146: List mits = new ArrayList();
147: for (; idx < args.length; ++idx) {
148: String arg = args[idx];
149: if (arg.equals("-method-invocation")) {
150: MethodInvocationTarget mit;
151: try {
152: mit = JGrep
153: .parseMethodInvocationPattern(args[++idx]);
154: } catch (Exception ex) {
155: System.err
156: .println("Parsing method invocation pattern \""
157: + args[idx]
158: + "\": "
159: + ex.getMessage());
160: System.exit(1);
161: /* NEVER REACHED */return;
162: }
163: while (idx < args.length - 1) {
164: arg = args[idx + 1];
165: if (arg.startsWith("predicate:")) {
166: String predicateExpression = arg.substring(10);
167: try {
168: mit.predicates
169: .add(ExpressionEvaluator
170: .createFastExpressionEvaluator(
171: new Scanner(
172: null,
173: new StringReader(
174: predicateExpression)), // expression
175: JGrep.class
176: .getName()
177: + "PE", // className
178: null, // optionalExtendedType
179: MethodInvocationPredicate.class, // interfaceToImplement
180: new String[] {
181: "uc",
182: "invocation",
183: "method" }, // parameterNames
184: null // optionalClassLoader
185: ));
186: } catch (Exception ex) {
187: System.err
188: .println("Compiling predicate expression \""
189: + predicateExpression
190: + "\": " + ex.getMessage());
191: System.exit(1);
192: /* NEVER REACHED */return;
193: }
194: } else if (arg.startsWith("action:")) {
195: String action = arg.substring(7);
196: try {
197: mit.actions.add(Action
198: .getMethodInvocationAction(action));
199: } catch (Exception ex) {
200: System.err
201: .println("Compiling method invocation action \""
202: + action
203: + "\": "
204: + ex.getMessage());
205: System.exit(1);
206: /* NEVER REACHED */return;
207: }
208: } else {
209: break;
210: }
211: ++idx;
212: }
213: mits.add(mit);
214: } else {
215: System.err
216: .println("Unexpected command-line argument \""
217: + arg + "\", try \"-help\".");
218: System.exit(1);
219: /* NEVER REACHED */return;
220: }
221: }
222:
223: // JGrep the root directories.
224: try {
225: jGrep.jGrep(rootDirectories, directoryNamePatterns,
226: fileNamePatterns, mits // methodInvocationTargets
227: );
228: } catch (Exception e) {
229: System.err.println(e.toString());
230: System.exit(1);
231: }
232: }
233:
234: private static final class Action extends Enumerator {
235: public static final Action PRINT_LOCATION_AND_MATCH = new Action(
236: "print-location-and-match");
237: public static final Action PRINT_LOCATION = new Action(
238: "print-location");
239:
240: private Action(String name) {
241: super (name);
242: }
243:
244: public static Action fromString(String name)
245: throws EnumeratorFormatException {
246: return (Action) Enumerator.fromString(name, Action.class);
247: }
248:
249: static MethodInvocationAction getMethodInvocationAction(
250: String action) throws CompileException,
251: Parser.ParseException, Scanner.ScanException {
252: if ("print-location-and-match".equals(action)) {
253: return new MethodInvocationAction() {
254: public void execute(UnitCompiler uc,
255: Java.Invocation invocation,
256: IClass.IMethod method) {
257: System.out.println(invocation.getLocation()
258: + ": " + method);
259: }
260: };
261: } else if ("print-location".equals(action)) {
262: return new MethodInvocationAction() {
263: public void execute(UnitCompiler uc,
264: Java.Invocation invocation,
265: IClass.IMethod method) {
266: System.out.println(invocation.getLocation());
267: }
268: };
269: } else {
270: return (MethodInvocationAction) ScriptEvaluator
271: .createFastScriptEvaluator(action, // script
272: MethodInvocationAction.class, // interfaceToImplement
273: new String[] { "uc", "invocation",
274: "method" } // parameterNames
275: );
276: }
277: }
278: }
279:
280: private static MethodInvocationTarget parseMethodInvocationPattern(
281: String mip) throws Scanner.ScanException, IOException,
282: Parser.ParseException {
283: MethodInvocationTarget mit = new MethodInvocationTarget();
284: Scanner scanner = new Scanner(null, new StringReader(mip));
285: Parser parser = new Parser(scanner);
286:
287: for (;;) {
288: String s = JGrep.readIdentifierPattern(parser);
289: if (parser.peekOperator("(")) {
290: mit.methodNamePattern = s;
291: parser.eatToken();
292: List l = new ArrayList();
293: if (!parser.peekOperator(")")) {
294: for (;;) {
295: l.add(JGrep.readIdentifierPattern(parser));
296: if (parser.peekOperator(")"))
297: break;
298: parser.readOperator(",");
299: }
300: }
301: mit.optionalArgumentTypeNamePatterns = (String[]) l
302: .toArray(new String[l.size()]);
303: return mit;
304: } else if (parser.peekOperator(".")) {
305: if (mit.optionalClassNamePattern == null) {
306: mit.optionalClassNamePattern = s;
307: } else {
308: mit.optionalClassNamePattern += '.' + s;
309: }
310: parser.eatToken();
311: } else if (scanner.peek().isEOF()) {
312: mit.methodNamePattern = s;
313: return mit;
314: }
315: }
316: }
317:
318: private static String readIdentifierPattern(Parser p)
319: throws Parser.ParseException, Scanner.ScanException,
320: IOException {
321: StringBuffer sb = new StringBuffer();
322: if (p.peekOperator("*")) {
323: sb.append('*');
324: p.eatToken();
325: } else {
326: sb.append(p.readIdentifier());
327: }
328: for (;;) {
329: if (p.peekOperator("*")) {
330: sb.append('*');
331: p.eatToken();
332: } else if (p.peekIdentifier()) {
333: sb.append(p.readIdentifier());
334: } else {
335: return sb.toString();
336: }
337: }
338: }
339:
340: private static class MethodInvocationTarget {
341: String optionalClassNamePattern = null;
342: String methodNamePattern = null;
343: String[] optionalArgumentTypeNamePatterns = null;
344: List predicates = new ArrayList(); // MethodInvocationPredicate
345: List actions = new ArrayList(); // MethodInvocationAction
346:
347: void apply(UnitCompiler uc, Java.Invocation invocation,
348: IClass.IMethod method) throws CompileException {
349: if (this .optionalClassNamePattern != null) {
350: if (!typeMatches(this .optionalClassNamePattern,
351: Descriptor.toClassName(method
352: .getDeclaringIClass().getDescriptor())))
353: return;
354: }
355: if (!new StringPattern(this .methodNamePattern)
356: .matches(method.getName()))
357: return;
358: IClass[] fpts = method.getParameterTypes();
359: if (this .optionalArgumentTypeNamePatterns != null) {
360: String[] atnps = this .optionalArgumentTypeNamePatterns;
361: if (atnps.length != fpts.length)
362: return;
363: for (int i = 0; i < atnps.length; ++i) {
364: if (!new StringPattern(atnps[i]).matches(Descriptor
365: .toClassName(fpts[i].getDescriptor())))
366: ;
367: }
368: }
369: for (Iterator it = this .predicates.iterator(); it.hasNext();) {
370: MethodInvocationPredicate mip = (MethodInvocationPredicate) it
371: .next();
372: try {
373: if (!mip.evaluate(uc, invocation, method))
374: return;
375: } catch (Exception ex) {
376: return; // Treat exception as a "false" predicate.
377: }
378: }
379: for (Iterator it = this .actions.iterator(); it.hasNext();) {
380: MethodInvocationAction mia = (MethodInvocationAction) it
381: .next();
382: try {
383: mia.execute(uc, invocation, method);
384: } catch (Exception ex) {
385: ; // Ignore action throwing an exception.
386: }
387: }
388: }
389: }
390:
391: public interface MethodInvocationPredicate {
392: boolean evaluate(UnitCompiler uc, Java.Invocation invocation,
393: IClass.IMethod method) throws Exception;
394: }
395:
396: public interface MethodInvocationAction {
397: void execute(UnitCompiler uc, Java.Invocation invocation,
398: IClass.IMethod method) throws Exception;
399: }
400:
401: static boolean typeMatches(String pattern, String typeName) {
402: return new StringPattern(pattern)
403: .matches(pattern.indexOf('.') == -1 ? typeName
404: .substring(typeName.lastIndexOf('.') + 1)
405: : typeName);
406: }
407:
408: private static final String[] USAGE = { "To be written.", // TODO
409: /*
410: "Usage:",
411: "",
412: " java org.codehaus.janino.Compiler [ <option> ] ... <source-file> ...",
413: "",
414: "Supported <option>s are:",
415: " -d <output-dir> Where to save class files",
416: " -sourcepath <dirlist> Where to look for other source files",
417: " -classpath <dirlist> Where to look for other class files",
418: " -extdirs <dirlist> Where to look for other class files",
419: " -bootclasspath <dirlist> Where to look for other class files",
420: " -encoding <encoding> Encoding of source files, e.g. \"UTF-8\" or \"ISO-8859-1\"",
421: " -verbose",
422: " -g Generate all debugging info",
423: " -g:none Generate no debugging info",
424: " -g:{lines,vars,source} Generate only some debugging info",
425: " -warn:<pattern-list> Issue certain warnings; examples:",
426: " -warn:* Enables all warnings",
427: " -warn:IASF Only warn against implicit access to static fields",
428: " -warn:*-IASF Enables all warnings, except those against implicit",
429: " access to static fields",
430: " -warn:*-IA*+IASF Enables all warnings, except those against implicit",
431: " accesses, but do warn against implicit access to",
432: " static fields",
433: " -rebuild Compile all source files, even if the class files",
434: " seems up-to-date",
435: " -help",
436: "",
437: "The default encoding in this environment is \"" + new InputStreamReader(new ByteArrayInputStream(new byte[0])).getEncoding() + "\".",
438: */
439:
440: };
441:
442: private/*final*/IClassLoader iClassLoader;
443: private/*final*/String optionalCharacterEncoding;
444: private/*final*/Benchmark benchmark;
445:
446: public JGrep(File[] classPath, File[] optionalExtDirs,
447: File[] optionalBootClassPath,
448: String optionalCharacterEncoding, boolean verbose) {
449: this (JGrep.createJavacLikePathIClassLoader( // iClassLoader
450: optionalBootClassPath, optionalExtDirs, classPath),
451: optionalCharacterEncoding, // optionalCharacterEncoding
452: verbose // verbose
453: );
454:
455: this .benchmark
456: .report("*** JGrep - search Java(TM) source files for specific language constructs");
457: this .benchmark
458: .report("*** For more information visit http://janino.codehaus.org");
459: this .benchmark.report("Class path", classPath);
460: this .benchmark.report("Ext dirs", optionalExtDirs);
461: this .benchmark.report("Boot class path", optionalBootClassPath);
462: this .benchmark.report("Character encoding",
463: optionalCharacterEncoding);
464: }
465:
466: public JGrep(IClassLoader iClassLoader,
467: final String optionalCharacterEncoding, boolean verbose) {
468: this .iClassLoader = new JGrepIClassLoader(iClassLoader);
469: this .optionalCharacterEncoding = optionalCharacterEncoding;
470: this .benchmark = new Benchmark(verbose);
471: }
472:
473: /**
474: * Create an {@link IClassLoader} that looks for classes in the given "boot class
475: * path", then in the given "extension directories", and then in the given
476: * "class path".
477: * <p>
478: * The default for the <code>optionalBootClassPath</code> is the path defined in
479: * the system property "sun.boot.class.path", and the default for the
480: * <code>optionalExtensionDirs</code> is the path defined in the "java.ext.dirs"
481: * system property.
482: */
483: private static IClassLoader createJavacLikePathIClassLoader(
484: final File[] optionalBootClassPath,
485: final File[] optionalExtDirs, final File[] classPath) {
486: ResourceFinder bootClassPathResourceFinder = new PathResourceFinder(
487: optionalBootClassPath == null ? PathResourceFinder
488: .parsePath(System
489: .getProperty("sun.boot.class.path"))
490: : optionalBootClassPath);
491: ResourceFinder extensionDirectoriesResourceFinder = new JarDirectoriesResourceFinder(
492: optionalExtDirs == null ? PathResourceFinder
493: .parsePath(System.getProperty("java.ext.dirs"))
494: : optionalExtDirs);
495: ResourceFinder classPathResourceFinder = new PathResourceFinder(
496: classPath);
497:
498: // We can load classes through "ResourceFinderIClassLoader"s, which means
499: // they are read into "ClassFile" objects, or we can load classes through
500: // "ClassLoaderIClassLoader"s, which means they are loaded into the JVM.
501: //
502: // In my environment, the latter is slightly faster. No figures about
503: // resource usage yet.
504: //
505: // In applications where the generated classes are not loaded into the
506: // same JVM instance, we should avoid to use the
507: // ClassLoaderIClassLoader, because that assumes that final fields have
508: // a constant value, even if not compile-time-constant but only
509: // initialization-time constant. The classical example is
510: // "File.separator", which is non-blank final, but not compile-time-
511: // constant.
512: if (true) {
513: IClassLoader icl;
514: icl = new ResourceFinderIClassLoader(
515: bootClassPathResourceFinder, null);
516: icl = new ResourceFinderIClassLoader(
517: extensionDirectoriesResourceFinder, icl);
518: icl = new ResourceFinderIClassLoader(
519: classPathResourceFinder, icl);
520: return icl;
521: } else {
522: ClassLoader cl;
523:
524: // This is the only way to instantiate a "bootstrap" class loader, i.e. a class loader
525: // that finds the bootstrap classes like "Object", "String" and "Throwable", but not
526: // the classes on the JVM's class path.
527: cl = new ClassLoader(null) {
528: };
529: cl = new ResourceFinderClassLoader(
530: bootClassPathResourceFinder, cl);
531: cl = new ResourceFinderClassLoader(
532: extensionDirectoriesResourceFinder, cl);
533: cl = new ResourceFinderClassLoader(classPathResourceFinder,
534: cl);
535:
536: return new ClassLoaderIClassLoader(cl);
537: }
538: }
539:
540: public void jGrep(File[] rootDirectories,
541: final StringPattern[] directoryNamePatterns,
542: final StringPattern[] fileNamePatterns,
543: List methodInvocationTargets // MethodInvocationTarget
544: ) throws Scanner.ScanException, Parser.ParseException,
545: CompileException, IOException {
546: this .benchmark.report("Root dirs", rootDirectories);
547: this .benchmark.report("Directory name patterns",
548: directoryNamePatterns);
549: this .benchmark.report("File name patterns", fileNamePatterns);
550:
551: this .jGrep(DirectoryIterator.traverseDirectories(
552: rootDirectories, // rootDirectories
553: new FilenameFilter() { // directoryNameFilter
554: public boolean accept(File dir, String name) {
555: return StringPattern.matches(
556: directoryNamePatterns, name);
557: }
558: }, new FilenameFilter() { // fileNameFilter
559: public boolean accept(File dir, String name) {
560: return StringPattern.matches(fileNamePatterns,
561: name);
562: }
563: }), methodInvocationTargets);
564: }
565:
566: public void jGrep(Iterator sourceFilesIterator,
567: final List methodInvocationTargets)
568: throws Scanner.ScanException, Parser.ParseException,
569: CompileException, IOException {
570:
571: // Parse the given source files.
572: this .benchmark.beginReporting();
573: int sourceFileCount = 0;
574: try {
575:
576: // Parse all source files.
577: while (sourceFilesIterator.hasNext()) {
578: File sourceFile = (File) sourceFilesIterator.next();
579: UnitCompiler uc = new UnitCompiler(this
580: .parseCompilationUnit(sourceFile, // sourceFile
581: this .optionalCharacterEncoding // optionalCharacterEncoding
582: ), this .iClassLoader);
583: this .parsedCompilationUnits.add(uc);
584: ++sourceFileCount;
585: }
586: } finally {
587: this .benchmark.endReporting("Parsed " + sourceFileCount
588: + " source file(s)");
589: }
590:
591: // Traverse the parsed compilation units.
592: this .benchmark.beginReporting();
593: try {
594: for (Iterator it = this .parsedCompilationUnits.iterator(); it
595: .hasNext();) {
596: final UnitCompiler uc = (UnitCompiler) it.next();
597: this .benchmark.beginReporting("Grepping \""
598: + uc.compilationUnit.optionalFileName + "\"");
599: class UCE extends RuntimeException {
600: final CompileException ce;
601:
602: UCE(CompileException ce) {
603: this .ce = ce;
604: }
605: }
606: try {
607: new Traverser() {
608:
609: // "method(...)", "x.method(...)"
610: public void traverseMethodInvocation(
611: Java.MethodInvocation mi) {
612: try {
613: this .match(mi, uc.findIMethod(mi));
614: } catch (CompileException ex) {
615: throw new UCE(ex);
616: }
617: super .traverseMethodInvocation(mi);
618: }
619:
620: // "super.method(...)"
621: public void traverseSuperclassMethodInvocation(
622: Java.SuperclassMethodInvocation scmi) {
623: try {
624: this .match(scmi, uc.findIMethod(scmi));
625: } catch (CompileException ex) {
626: throw new UCE(ex);
627: }
628: super
629: .traverseSuperclassMethodInvocation(scmi);
630: }
631:
632: // new Xyz(...)
633: public void traverseNewClassInstance(
634: Java.NewClassInstance nci) {
635: // System.out.println(nci.getLocation() + ": " + nci);
636: super .traverseNewClassInstance(nci);
637: }
638:
639: // new Xyz(...) {}
640: public void traverseNewAnonymousClassInstance(
641: Java.NewAnonymousClassInstance naci) {
642: // System.out.println(naci.getLocation() + ": " + naci);
643: super
644: .traverseNewAnonymousClassInstance(naci);
645: }
646:
647: // Explicit constructor invocation ("this(...)", "super(...)").
648: public void traverseConstructorInvocation(
649: Java.ConstructorInvocation ci) {
650: // System.out.println(ci.getLocation() + ": " + ci);
651: super .traverseConstructorInvocation(ci);
652: }
653:
654: private void match(Java.Invocation invocation,
655: IClass.IMethod method)
656: throws CompileException {
657: for (Iterator it2 = methodInvocationTargets
658: .iterator(); it2.hasNext();) {
659: MethodInvocationTarget mit = (MethodInvocationTarget) it2
660: .next();
661: mit.apply(uc, invocation, method);
662: }
663: }
664: }.traverseCompilationUnit(uc.compilationUnit);
665: } catch (UCE uce) {
666: throw uce.ce;
667: } finally {
668: this .benchmark.endReporting();
669: }
670: }
671: } finally {
672: this .benchmark.endReporting("Traversed " + sourceFileCount
673: + " compilation units");
674: }
675: }
676:
677: /**
678: * Read one compilation unit from a file and parse it.
679: * <p>
680: * The <code>inputStream</code> is closed before the method returns.
681: * @return the parsed compilation unit
682: */
683: private Java.CompilationUnit parseCompilationUnit(File sourceFile,
684: String optionalCharacterEncoding)
685: throws Scanner.ScanException, Parser.ParseException,
686: IOException {
687: InputStream is = new BufferedInputStream(new FileInputStream(
688: sourceFile));
689: try {
690: Parser parser = new Parser(
691: new Scanner(sourceFile.getPath(), is,
692: optionalCharacterEncoding));
693:
694: this .benchmark.beginReporting("Parsing \"" + sourceFile
695: + "\"");
696: try {
697: return parser.parseCompilationUnit();
698: } finally {
699: this .benchmark.endReporting();
700: }
701: } finally {
702: try {
703: is.close();
704: } catch (IOException ex) {
705: }
706: }
707: }
708:
709: /**
710: * Construct the name of a file that could store the byte code of the class with the given
711: * name.
712: * <p>
713: * If <code>optionalDestinationDirectory</code> is non-null, the returned path is the
714: * <code>optionalDestinationDirectory</code> plus the package of the class (with dots replaced
715: * with file separators) plus the class name plus ".class". Example:
716: * "destdir/pkg1/pkg2/Outer$Inner.class"
717: * <p>
718: * If <code>optionalDestinationDirectory</code> is null, the returned path is the
719: * directory of the <code>sourceFile</code> plus the class name plus ".class". Example:
720: * "srcdir/Outer$Inner.class"
721: * @param className E.g. "pkg1.pkg2.Outer$Inner"
722: * @param sourceFile E.g. "srcdir/Outer.java"
723: * @param optionalDestinationDirectory E.g. "destdir"
724: */
725: public static File getClassFile(String className, File sourceFile,
726: File optionalDestinationDirectory) {
727: if (optionalDestinationDirectory != null) {
728: return new File(optionalDestinationDirectory, ClassFile
729: .getClassFileResourceName(className));
730: } else {
731: int idx = className.lastIndexOf('.');
732: return new File(sourceFile.getParentFile(), ClassFile
733: .getClassFileResourceName(className
734: .substring(idx + 1)));
735: }
736: }
737:
738: /**
739: * A specialized {@link IClassLoader} that loads {@link IClass}es from the following
740: * sources:
741: * <ol>
742: * <li>An already-parsed compilation unit
743: * <li>A class file in the output directory (if existant and younger than source file)
744: * <li>A source file in any of the source path directories
745: * <li>The parent class loader
746: * </ol>
747: * Notice that the {@link JGrepIClassLoader} is an inner class of {@link JGrep} and
748: * heavily uses {@link JGrep}'s members.
749: */
750: private class JGrepIClassLoader extends IClassLoader {
751:
752: /**
753: * @param optionalParentIClassLoader {@link IClassLoader} through which {@link IClass}es are to be loaded
754: */
755: public JGrepIClassLoader(IClassLoader optionalParentIClassLoader) {
756: super (optionalParentIClassLoader);
757: super .postConstruct();
758: }
759:
760: /**
761: * @param type field descriptor of the {@IClass} to load, e.g. "Lpkg1/pkg2/Outer$Inner;"
762: */
763: protected IClass findIClass(final String type) {
764: if (JGrep.DEBUG)
765: System.out.println("type = " + type);
766:
767: // Class type.
768: String className = Descriptor.toClassName(type); // E.g. "pkg1.pkg2.Outer$Inner"
769: if (JGrep.DEBUG)
770: System.out.println("2 className = \"" + className
771: + "\"");
772:
773: // Do not attempt to load classes from package "java".
774: if (className.startsWith("java."))
775: return null;
776:
777: // Check the already-parsed compilation units.
778: for (int i = 0; i < JGrep.this .parsedCompilationUnits
779: .size(); ++i) {
780: UnitCompiler uc = (UnitCompiler) JGrep.this .parsedCompilationUnits
781: .get(i);
782: IClass res = uc.findClass(className);
783: if (res != null) {
784: this.defineIClass(res);
785: return res;
786: }
787: }
788: return null;
789: }
790: }
791: }
|