import com.sun.source.util.JavacTask;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;

import java.net.URI;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.FileObject;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class Class3
{
  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 class EmptyJavaFileObject implements JavaFileObject
  {
    private JavaFileObject javaFileObject;
    EmptyJavaFileObject(JavaFileObject javaFileObject)
    {
      this.javaFileObject = javaFileObject;
    }
    public JavaFileObject.Kind getKind()
    {
      return javaFileObject.getKind();
    }
    public boolean isNameCompatible(String simpleName, JavaFileObject.Kind kind)
    {
      return javaFileObject.isNameCompatible(simpleName, kind);
    }
    public NestingKind getNestingKind()
    {
      return javaFileObject.getNestingKind();
    }
    public Modifier getAccessLevel()
    {
      return javaFileObject.getAccessLevel();
    }
    public URI toUri()
    {
      return javaFileObject.toUri();
    }
    public String getName()
    {
      return javaFileObject.getName();
    }
    public InputStream openInputStream()
      throws IOException
    {
      return new ByteArrayInputStream(new byte[0]);
    }
    public OutputStream openOutputStream()
      throws IOException
    {
      return javaFileObject.openOutputStream();
    }
    public Reader openReader(boolean ignoreEncodingErrors)
      throws IOException
    {
      return javaFileObject.openReader(ignoreEncodingErrors);
    }
    public CharSequence getCharContent(boolean ignoreEncodingErrors)
      throws IOException
    {
      return javaFileObject.getCharContent(ignoreEncodingErrors);
    }
    public Writer openWriter()
      throws IOException
    {
      return javaFileObject.openWriter();
    }
    public long getLastModified()
    {
      return javaFileObject.getLastModified();
    }
    public boolean delete()
    {
      return javaFileObject.delete();
    }
  }
  private static JavaFileManager getJavaFileManager(JavaCompiler compiler, DiagnosticListener dc)
  {
    final StandardJavaFileManager fileManager = compiler.getStandardFileManager(dc, null, null);
    return new JavaFileManager()
    {
      public ClassLoader getClassLoader(JavaFileManager.Location location)
      {
        return fileManager.getClassLoader(location);
      }
      public Iterable<JavaFileObject> list(JavaFileManager.Location location, String packageName,
                                           Set<JavaFileObject.Kind> kinds, boolean recurse)
        throws IOException
      {
        List<JavaFileObject> files = new ArrayList<>();
        for ( JavaFileObject file : fileManager.list(location, packageName, kinds, recurse))
        {
          if (file.getName().indexOf("List") >= 0)
          {
            files.add(new EmptyJavaFileObject(file));
          }
          else
          {
            files.add(file);
          }
        }
        return files;
      }
      public String inferBinaryName(JavaFileManager.Location location, JavaFileObject file)
      {
        if ( file instanceof EmptyJavaFileObject )
          return fileManager.inferBinaryName(location, ((EmptyJavaFileObject)file).javaFileObject);
        return fileManager.inferBinaryName(location, file);
      }
      public boolean isSameFile(FileObject a, FileObject b)
      {
        return fileManager.isSameFile(a, b);
      }
      public boolean handleOption(String current, Iterator<String> remaining)
      {
        return fileManager.handleOption(current, remaining);
      }
      public boolean hasLocation(JavaFileManager.Location location)
      {
        return fileManager.hasLocation(location);
      }
      public JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className,
                                                JavaFileObject.Kind kind)
        throws IOException
      {
        return fileManager.getJavaFileForInput(location, className, kind);
      }
      public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className,
                                                 JavaFileObject.Kind kind, FileObject sibling)
        throws IOException
      {
        return fileManager.getJavaFileForOutput(location, className, kind, sibling);
      }
      public FileObject getFileForInput(JavaFileManager.Location location, String packageName, String relativeName)
        throws IOException
      {
        return fileManager.getFileForInput(location, packageName, relativeName);
      }
      public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName,
                                         FileObject sibling)
        throws IOException
      {
        return fileManager.getFileForOutput(location, packageName, relativeName, sibling);
      }
      public void flush()
        throws IOException
      {
        fileManager.flush();
      }
      public void close()
        throws IOException
      {
        fileManager.flush();
      }
      public int isSupportedOption(String option)
      {
        return fileManager.isSupportedOption(option);
      }
    };
  }

  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("import java.util.Map;\n");
      code.append("import java.util.List;\n");
      code.append("public class ");
      code.append(name);
      code.append(" {\n");
      code.append("Map map;\n");
      code.append("List list;\n");
      code.append("}\n");
      File tempDir = source.getParentFile();
      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;
      javacTask.analyze();
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }
}

