
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TreeScanner;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Writer;

import java.net.URI;

import java.util.ArrayList;
import java.util.List;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;

public class Class1
{
  private static boolean writeOneFile(String content, File testFile)
  {
    try (FileOutputStream stream = new FileOutputStream(testFile))
    {
      stream.write(content.getBytes());
    }
    catch (IOException e)
    {
      e.printStackTrace();
      return false;
    }
    return true;
  }

  private static JavaFileManager getJavaFileManager(JavaCompiler compiler, DiagnosticListener dc)
  {
    return compiler.getStandardFileManager(dc, null, null);
  }

  static public JavaFileObject getJavaFileObject(URI uri, final String code)
  {
    return new SimpleJavaFileObject(uri, JavaFileObject.Kind.SOURCE)
    {
      public boolean isNameCompatible(String simpleName, Kind kind)
      {
        return true;
      }

      public CharSequence getCharContent(boolean ignoreEncodingErrors)
        throws IOException
      {
        return code;
      }
    };
  }

  public static void main(String[] args)
  {
    try
    {
      File source = File.createTempFile("Bug", ".java");
      source.deleteOnExit();
      String name = source.getName();
      int index = name.lastIndexOf('.');
      name = name.substring(0, index);
      final StringBuilder code = new StringBuilder();
      code.append("public class ");
      code.append(name);
      code.append("{\n");
      code.append("  class Inner{}\n");
      code.append("  void method(");
      code.append(name);
      code.append(" clazz) {\n");
      code.append("    Inner inner1 = clazz.new Inner();\n");
      code.append("    Inner inner2 = clazz.new Inner(){};\n");
      code.append("  }\n}\n");
      System.out.println(code.toString());
      File tempDir = source.getParentFile();
      Class1.writeOneFile(code.toString(), source);
      JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
      Writer writer = new Writer()
      {
        public void write(char[] cbuf, int off, int len)
        {
          System.out.println(new String(cbuf, off, len));
        }
        public void flush(){}
        public void close() {}
      };
      DiagnosticListener dc = new DiagnosticListener() {
        public void report(Diagnostic diagnostic) {
          System.out.println( diagnostic.getMessage( null ));
        }
      };
      JavaFileManager javaFileManager = getJavaFileManager(compiler, dc);
      List<String> options = new ArrayList<String>();
      options.add("-cp");
      options.add(tempDir.getPath());
      options.add("-d");
      options.add(tempDir.getPath());
      List<JavaFileObject> sources = new ArrayList<JavaFileObject>();
      sources.add(getJavaFileObject(source.toURI(), code.toString()));
      JavaCompiler.CompilationTask task =
        compiler.getTask(writer, javaFileManager, dc, options, null, sources);
      JavacTask javacTask = (JavacTask) task;
      Iterable<? extends CompilationUnitTree> units = javacTask.parse();
      javacTask.analyze();
      final CompilationUnitTree unit = units.iterator().next();
      ClassTree decl = (ClassTree) unit.getTypeDecls().get(0);
      TreeScanner visitor = new TreeScanner()
      {
        public Object visitNewClass(NewClassTree node, Object p)
        {
          System.out.println(node.toString());
          if (node.getClassBody() == null)
          {
            if (node.getEnclosingExpression() == null)
              System.out.println("No enclosing expression for new class w/o body");
            else
              System.out.println("Enclosing expression for new class w/o body");
          }
          else
          {
            if (node.getEnclosingExpression() == null)
              System.out.println("No enclosing expression for new class with body");
            else
              System.out.println("Enclosing expression for new class with body");
          }
          System.out.println();
          return super.visitNewClass(node, p);
        }
      };
      decl.accept(visitor, null);
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }
}

