package org.jetbrains.ether.dependencyView;
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.BuildListener;
import org.objectweb.asm.ClassReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by IntelliJ IDEA.
* User: db
* Date: 01.04.11
* Time: 21:31
* To change this template use File | Settings | File Templates.
*/
public class AntListener implements BuildListener {
private static final Pattern read = Pattern.compile("\\[parsing started (.*)\\]");
private static final Pattern written = Pattern.compile("\\[wrote (.*)\\]");
private final Map<String, List<String>> readFiles = new HashMap<String, List<String>>();
private final Map<String, List<ClassFileInfo>> writtenFiles = new HashMap<String, List<ClassFileInfo>>();
private final String targetFolder;
private final int stripLength;
private final List<String> sourceRoots;
private final Callbacks.Backend callback;
private static String basename(final String name, final String extension) {
return name.substring(0, name.length() - extension.length());
}
private static ClassReader getClassReader(final String fullName) {
try {
final File f = new File(fullName);
final byte[] buffer = new byte[(int) f.length()];
final FileInputStream i = new FileInputStream(f);
final int n = i.read(buffer);
i.close();
assert ((int) f.length() == n);
return new ClassReader(buffer);
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
return null;
}
}
private class ClassFileInfo {
final String packageName;
final String fullName;
List<String> subClasses = null;
public ClassFileInfo(final String fullName, final String packageName) {
this.packageName = packageName;
this.fullName = fullName;
}
public void attachSubClass(final String name) {
if (subClasses == null) {
subClasses = new LinkedList<String>();
}
subClasses.add(name);
}
public String getFullName() {
return fullName;
}
public String getPackageName() {
return packageName;
}
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append(" Root: ").append(fullName).append(", package ").append(packageName).append("\n");
builder.append(" Subclasses:");
if (subClasses != null)
for (String s : subClasses) {
builder.append(" ").append(s);
}
builder.append(".\n");
return builder.toString();
}
public void associate(final Callbacks.SourceFileNameLookup sourceFileNameLookup) {
if (callback != null) {
callback.associate(fullName, sourceFileNameLookup, getClassReader(fullName));
if (subClasses != null) {
for (String s : subClasses) {
callback.associate(s, sourceFileNameLookup, getClassReader(s));
}
}
}
}
}
public AntListener(final String targetFolder, final List<String> sourceRoots, final Callbacks.Backend callback) {
this.targetFolder = targetFolder;
this.sourceRoots = sourceRoots;
this.callback = callback;
stripLength = targetFolder.length() + 1;
}
public void buildStarted(BuildEvent event) {
//To change body of implemented methods use File | Settings | File Templates.
}
public void buildFinished(BuildEvent event) {
//To change body of implemented methods use File | Settings | File Templates.
}
public void targetStarted(BuildEvent event) {
//To change body of implemented methods use File | Settings | File Templates.
}
public void targetFinished(BuildEvent event) {
//To change body of implemented methods use File | Settings | File Templates.
}
public void taskStarted(BuildEvent event) {
}
public void taskFinished(BuildEvent event) {
if (event.getTask().getTaskName().equals("javac")) {
for (Map.Entry<String, List<ClassFileInfo>> e : writtenFiles.entrySet()) {
final String className = e.getKey();
final List<ClassFileInfo> infos = e.getValue();
final List<String> read = readFiles.get(className);
if (infos.size() == 1 && read != null && read.size() == 1) {
infos.get(0).associate(Callbacks.getDefaultLookup(read.get(0)));
continue;
}
process:
for (ClassFileInfo c : infos) {
final String packageName = c.getPackageName();
final Callbacks.SourceFileNameLookup sourceNameLookup = new Callbacks.SourceFileNameLookup() {
public String get(final String sourceAttribute) {
assert (sourceAttribute != null);
final String className = basename(sourceAttribute, ".java");
final List<String> candidates = readFiles.get(className);
assert (candidates != null);
if (candidates.size() == 1) {
return candidates.get(0);
}
for (String r : candidates) {
if (new PackageNameSelector().get(r).equals(packageName)) {
return r;
}
}
assert (false);
return null;
}
};
c.associate(sourceNameLookup);
}
}
/*
System.out.println("Written files info:");
for (Map.Entry<String, List<ClassFileInfo>> e : writtenFiles.entrySet()) {
final String className = e.getKey();
final List<ClassFileInfo> infos = e.getValue();
System.out.println(" Class name: " + className);
System.out.println(" Class file info(s): ");
for (ClassFileInfo c : infos) {
System.out.println(c.toString());
}
}
System.out.println("End.");
System.out.println("Read files info:");
for (Map.Entry<String, List<String>> e : readFiles.entrySet()) {
final String className = e.getKey();
final List<String> files = e.getValue();
System.out.println(" Class name: " + className);
System.out.print(" Files:");
for (String file : files) {
System.out.print(" " + file);
}
System.out.println(".");
}
System.out.println("End.");
*/
}
}
private String extractPackage(final String s) {
if (s.length() < stripLength)
return "";
return s.substring(stripLength);
}
public void messageLogged(BuildEvent event) {
final String m = event.getMessage();
final Matcher writtenMatcher = written.matcher(m);
final Matcher readMatcher = read.matcher(m);
if (writtenMatcher.matches()) {
final String fullName = writtenMatcher.group(1);
final File file = new File(fullName);
final String classFileName = file.getName();
final String packageName = extractPackage(fullName.substring(0, fullName.length() - classFileName.length() - 1));
String className = basename(classFileName, ".class");
boolean subClass = false;
final int i = className.indexOf('$');
if (i >= 0) {
className = className.substring(0, i);
subClass = true;
}
List<ClassFileInfo> info = writtenFiles.get(className);
if (info == null) {
info = new LinkedList<ClassFileInfo>();
writtenFiles.put(className, info);
}
boolean updated = false;
for (ClassFileInfo ci : info) {
if (subClass) {
if (ci.getPackageName().equals(packageName)) {
ci.attachSubClass(fullName);
updated = true;
}
} else {
if (ci.getFullName().equals(fullName)) {
updated = true;
break;
}
}
}
if (!updated) {
if (subClass) {
final ClassFileInfo c = new ClassFileInfo(targetFolder + File.separator + packageName + File.separator + className + ".class", packageName);
c.attachSubClass(fullName);
info.add(c);
} else {
info.add(new ClassFileInfo(fullName, packageName));
}
}
}
if (readMatcher.matches()) {
final String fullName = readMatcher.group(1);
final File file = new File(fullName);
final String sourceFileName = file.getName();
final String className = basename(sourceFileName, ".java");
List<String> files = readFiles.get(className);
if (files == null) {
files = new LinkedList<String>();
readFiles.put(className, files);
}
files.add(fullName);
}
}
}