/*
* Copyright 2008-2010 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package compiler.java;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardLocation;
import files.RelativeSourcePath;
import files.Package;
import util.Multimap;
/**
* A JavaFileManager that collects the name and bytecode of compiled classes
* into {@link CompiledClass} objects. Also allow the compiler to find classes
* that were previously compiled via the {@link #list()} method.
*
* Based on the original work by A. Sundararajan.
*/
public final class CollectingJavaFileManager
extends ForwardingJavaFileManager<JavaFileManager>
{
/*****************************************************************************
* A file object that stores the bytecode into a new {@link CompiledClass}
* object it adds to $classes.
*/
private class ByteClass extends SimpleJavaFileObject
{
private final String name;
ByteClass(String name)
{
super(URI.create("string:///" + name), Kind.CLASS);
this.name = name;
byteclasses.add(RelativeSourcePath.make(name).pkg(), this);
}
@Override public InputStream openInputStream()
{
return new ByteArrayInputStream(
MemoryClassLoader.get().getBytecode(name));
}
@Override public OutputStream openOutputStream()
{
return new FilterOutputStream(new ByteArrayOutputStream())
{
@Override public void close() throws IOException
{
out.close();
ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
classes.add(new CompiledClass(name, bos.toByteArray()));
}
};
}
}
//////////////////////////////////////////////////////////////////////////////
/****************************************************************************/
public final List<CompiledClass> classes = new ArrayList<>();
/****************************************************************************/
public final Multimap<Package, JavaFileObject> byteclasses = new Multimap<>();
/****************************************************************************/
public CollectingJavaFileManager(JavaFileManager fileManager)
{
super(fileManager);
}
/*****************************************************************************
* javax.tools.JavaCompiler seems to pick up the class loader even without
* this (which is a bit surprising to me), but you're better safe than sorry.
*/
@Override public ClassLoader getClassLoader(JavaFileManager.Location location)
{
return MemoryClassLoader.get();
}
/****************************************************************************/
@Override public Iterable<JavaFileObject> list(Location location,
String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse)
throws IOException
{
Iterable<JavaFileObject> stdResults = fileManager.list(location, packageName, kinds, recurse);
if (location != StandardLocation.CLASS_PATH
|| !kinds.contains(JavaFileObject.Kind.CLASS))
{
return stdResults;
}
Set<JavaFileObject> notOnDisk = byteclasses.get(new Package(packageName));
if (notOnDisk == null || notOnDisk.isEmpty()) {
return stdResults;
}
List<JavaFileObject> out = new ArrayList<>();
for (JavaFileObject obj : notOnDisk) {
out.add(obj);
}
for (JavaFileObject obj : stdResults) {
out.add(obj);
}
return out;
}
/****************************************************************************/
@Override public void close() throws IOException
{
classes.clear();
}
/****************************************************************************/
@Override public JavaFileObject getJavaFileForOutput(
Location location, String className, Kind kind, FileObject sibling)
throws IOException
{
if (kind == Kind.CLASS) {
return new ByteClass(className);
} else {
return super.getJavaFileForOutput(location, className, kind, sibling);
}
}
/****************************************************************************/
@Override public String inferBinaryName(
Location location, JavaFileObject file)
{
if (file instanceof ByteClass) {
return ((ByteClass) file).name;
} else {
return fileManager.inferBinaryName(location, file);
}
}
}