package joist.sourcegen;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import joist.util.Copy;
import joist.util.Interpolate;
import joist.util.Read;
import joist.util.Write;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GDirectory {
private static final Logger log = LoggerFactory.getLogger(GDirectory.class);
private final File directory;
private final List<GClass> classes = new ArrayList<GClass>();
private final List<File> touched = new ArrayList<File>();
public GDirectory(String directory) {
this.directory = new File(directory);
}
public GClass getClass(String _fullClassName, Object... args) {
String fullClassName = Interpolate.string(_fullClassName, args);
for (GClass gc : this.classes) {
if (gc.isSameClass(fullClassName)) {
return gc;
}
}
GClass gc = new GClass(this, fullClassName);
this.classes.add(gc);
return gc;
}
public boolean exists(String fullClassName) {
return this.getFile(fullClassName).exists();
}
public void output() {
for (GClass gc : this.classes) {
String newCode = gc.toCode();
File file = this.getFile(gc);
if (file.exists()) {
String existingCode = Read.fromFile(file);
if (newCode.equals(existingCode)) {
this.touched.add(file);
continue;
}
}
file.getParentFile().mkdirs();
log.info("Saving {}", file);
Write.toFile(file, newCode);
this.touched.add(file);
}
}
public void pruneIfNotTouched() {
for (File dir : this.getAllDirectories()) {
for (File file : dir.listFiles()) {
if (file.isFile() && !this.touched.contains(file)) {
log.warn("Removing old file {}", file);
file.delete();
}
}
}
}
public void pruneIfNotTouchedWithinUsedPackages() {
for (File dir : this.getUsedDirectories()) {
for (File file : dir.listFiles()) {
if (file.isFile() && !this.touched.contains(file)) {
log.warn("Removing old file {}", file);
file.delete();
}
}
}
}
public List<File> getTouched() {
return this.touched;
}
/** Mark {@code file} as touched even if we're not going to explicitly output it. */
public void markTouched(File file) {
this.touched.add(file);
}
/** Mark {@code className} as touched even if we're not going to explicitly output it. */
public void markTouched(String fullClassName) {
this.markTouched(this.getFile(fullClassName));
}
/** @returns only directories that we've output classes into, so that we conceptually "own" */
public List<File> getUsedDirectories() {
List<File> used = Copy.list();
for (File dir : this.getAllDirectories()) {
boolean foundTouchedFileInThisDir = false;
for (File touched : this.touched) {
if (touched.getParentFile().equals(dir)) {
foundTouchedFileInThisDir = true;
break;
}
}
if (foundTouchedFileInThisDir) {
used.add(dir);
}
}
return used;
}
/** @returns all of the directories starting at {@code this.directory}. */
public List<File> getAllDirectories() {
List<File> directories = Copy.list();
List<File> directoriesToCheck = Copy.list(this.directory);
while (directoriesToCheck.size() > 0) {
File dir = directoriesToCheck.remove(0);
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
directories.add(file);
directoriesToCheck.add(file);
}
}
}
return directories;
}
private File getFile(GClass gc) {
return this.getFile(gc.getFullName());
}
private File getFile(String fullClassName) {
return new File(this.directory, fullClassName.replace('.', File.separatorChar) + ".java");
}
}