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