{"id":1478,"date":"2012-07-29T12:14:28","date_gmt":"2012-07-29T10:14:28","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=1478"},"modified":"2012-07-29T12:21:55","modified_gmt":"2012-07-29T10:21:55","slug":"mit-der-internen-compiler-api-auf-den-ast-einer-klasse-zugreifen","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2012\/07\/mit-der-internen-compiler-api-auf-den-ast-einer-klasse-zugreifen\/","title":{"rendered":"Mit der internen Compiler-API auf den AST einer Klasse zugreifen"},"content":{"rendered":"<p>Der Zugriff zum Java-Compiler ist \u00fcber die Java-Compiler-API standardisiert, jedoch sind alle Interna, wie die tats\u00e4chliche Repr\u00e4sentation des Programmcodes verborgen. Die Compiler-API abstrahiert alles \u00fcber Schnittstellen, und so kommen Entwickler nur mit JavaCompiler, StandardJavaFileManager und CompilationTask in Kontakt \u2013 alles Schnittstellen aus dem Paket javax.tools. Um etwas tiefer einzusteigen, l\u00e4sst sich zum einem Trick greifen: Klassen implementieren Schnittstellen und wenn ein Programm den Schnittstellentyp auf den konkreten Klassentyp anpasst, dann stehen in der Regel mehr Methoden zur Verf\u00fcgung. So l\u00e4sst sich der CompilationTask auf eine com.sun.tools.javac.api.JavacTaskImpl casten und dann steht eine parse()-Methode f\u00fcr Verf\u00fcgung. Die parse()-Methode liefert als R\u00fcckgabe eine Aufz\u00e4hlung von CompilationUnitTree. Um diesen Baum nun abzulaufen, l\u00e4sst sich das Besuchermuster einsetzen. CompilationUnitTree bietet eine accept(\u2026)-Methode; der \u00fcbergeben wir einen TreeScanner. Die accept(\u2026)-Methode ruft dann beim Ablaufen jedes Knotens unseren Besucher auf.<\/p>\n<pre class=\"csharpcode\">package com.tutego.tools.javac;\r\n\r\nimport java.io.*;\r\nimport java.net.*;\r\nimport javax.tools.*;\r\nimport javax.tools.JavaCompiler.CompilationTask;\r\nimport com.sun.source.tree.*;\r\nimport com.sun.source.util.TreeScanner;\r\nimport com.sun.tools.javac.api.JavacTaskImpl;\r\n\r\n<span class=\"kwrd\">public<\/span> <span class=\"kwrd\">class<\/span> PrintAllMethodNames {\r\n\r\n  final <span class=\"kwrd\">static<\/span> TreeScanner&lt;?, ?&gt; methodPrintingTreeVisitor = <span class=\"kwrd\">new<\/span> TreeScanner&lt;Void, Void&gt;() {\r\n    @Override <span class=\"kwrd\">public<\/span> Void visitCompilationUnit( CompilationUnitTree unit, Void arg ) {\r\n      System.<span class=\"kwrd\">out<\/span>.println( <span class=\"str\">\"Paket: \"<\/span> + unit.getPackageName() );\r\n      <span class=\"kwrd\">return<\/span> super.visitCompilationUnit( unit, arg );\r\n    };\r\n    @Override <span class=\"kwrd\">public<\/span> Void visitClass( ClassTree classTree, Void arg ) {\r\n      System.<span class=\"kwrd\">out<\/span>.println( <span class=\"str\">\"Klasse: \"<\/span> + classTree.getSimpleName() );\r\n      <span class=\"kwrd\">return<\/span> super.visitClass( classTree, arg );\r\n    }\r\n    @Override <span class=\"kwrd\">public<\/span> Void visitMethod( MethodTree methodTree, Void arg ) {\r\n      System.<span class=\"kwrd\">out<\/span>.println( <span class=\"str\">\"Methode: \"<\/span> + methodTree.getName() );\r\n      <span class=\"kwrd\">return<\/span> super.visitMethod( methodTree, arg );\r\n    }\r\n  };\r\n\r\n  <span class=\"kwrd\">public<\/span> <span class=\"kwrd\">static<\/span> <span class=\"kwrd\">void<\/span> main( String[] args ) throws IOException, URISyntaxException {\r\n    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();\r\n    StandardJavaFileManager fileManager = compiler.getStandardFileManager( <span class=\"kwrd\">null<\/span>, <span class=\"kwrd\">null<\/span>, <span class=\"kwrd\">null<\/span> );\r\n    URI filename = PrintAllMethodNames.<span class=\"kwrd\">class<\/span>.getResource( <span class=\"str\">\"PrintAllMethodNames.java\"<\/span> ).toURI();\r\n    Iterable&lt;? extends JavaFileObject&gt; fileObjects = fileManager.getJavaFileObjects( <span class=\"kwrd\">new<\/span> File( filename ) );\r\n    CompilationTask task = compiler.getTask( <span class=\"kwrd\">null<\/span>, <span class=\"kwrd\">null<\/span>, <span class=\"kwrd\">null<\/span>, <span class=\"kwrd\">null<\/span>, <span class=\"kwrd\">null<\/span>, fileObjects );\r\n\r\n    JavacTaskImpl javacTask = (JavacTaskImpl) task;\r\n\r\n    <span class=\"kwrd\">for<\/span> ( CompilationUnitTree tree : javacTask.parse() )\r\n      tree.accept( methodPrintingTreeVisitor, <span class=\"kwrd\">null<\/span> );\r\n  }\r\n}<\/pre>\n<p>Ein TreeScanner hat viele Methoden, wir interessieren uns nur f\u00fcr den Start einer Compilationseinheit f\u00fcr den Paketnamen, f\u00fcr alle Klassen und Methoden. Wir k\u00f6nnten uns aber auch \u00fcber alle Annotationen oder do-while-Schleifen informieren lassen. Die Ausgabe ist:<\/p>\n<p>Paket: com.tutego.tools.javac<\/p>\n<p>Klasse: PrintAllMethodNames<\/p>\n<p>Klasse:<\/p>\n<p>Methode: visitCompilationUnit<\/p>\n<p>Methode: visitClass<\/p>\n<p>Methode: visitMethod<\/p>\n<p>Methode: main<\/p>\n<p>Die zweite Angabe f\u00fcr den Klassennamen ist leer, da die anonyme Klasse eben keinen Namen hat.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Der Zugriff zum Java-Compiler ist \u00fcber die Java-Compiler-API standardisiert, jedoch sind alle Interna, wie die tats\u00e4chliche Repr\u00e4sentation des Programmcodes verborgen. Die Compiler-API abstrahiert alles \u00fcber Schnittstellen, und so kommen Entwickler nur mit JavaCompiler, StandardJavaFileManager und CompilationTask in Kontakt \u2013 alles Schnittstellen aus dem Paket javax.tools. Um etwas tiefer einzusteigen, l\u00e4sst sich zum einem Trick greifen: [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[11],"tags":[],"class_list":["post-1478","post","type-post","status-publish","format-standard","hentry","category-insel"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1478","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=1478"}],"version-history":[{"count":3,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1478\/revisions"}],"predecessor-version":[{"id":1482,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1478\/revisions\/1482"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=1478"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=1478"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=1478"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}