package com.redhat.ceylon.eclipse.core.builder;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.compiler.CompilationParticipant;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.core.builder.ClasspathLocation;
import org.eclipse.jdt.internal.core.builder.ClasspathMultiDirectory;
import org.eclipse.jdt.internal.core.builder.State;
public class JavaProjectStateMirror extends CompilationParticipant {
private final static Map<IProject, State> javaProjectCurrentStates = new HashMap<IProject, State>();
private final static Map<IProject, State> javaProjectLastStates = new HashMap<IProject, State>();
@Override
public boolean isActive(IJavaProject javaProject) {
if (! javaProject.getProject().isAccessible()) {
return false;
}
return CeylonNature.isEnabled(javaProject.getProject());
}
@Override
public int aboutToBuild(IJavaProject javaProject) {
javaProjectLastStates.put(javaProject.getProject(), (State)JavaModelManager.getJavaModelManager().getLastBuiltState(javaProject.getProject(), null));
return READY_FOR_BUILD;
}
@Override
public void buildFinished(IJavaProject javaProject) {
javaProjectCurrentStates.put(javaProject.getProject(), (State)JavaModelManager.getJavaModelManager().getLastBuiltState(javaProject.getProject(), null));
}
public static State getProjectLastState(IProject project) {
return javaProjectLastStates.get(project);
}
public static State getProjectCurrentState(IProject project) {
return javaProjectCurrentStates.get(project);
}
public static void cleanup(IProject project) {
javaProjectLastStates.remove(project);
javaProjectCurrentStates.remove(project);
}
static boolean isSourceFolderEmpty(State state, IContainer sourceFolder) {
String sourceFolderName = sourceFolder.getProjectRelativePath().addTrailingSeparator().toString();
Object[] table = state.typeLocators.valueTable;
for (int i = 0, l = table.length; i < l; i++)
if (table[i] != null && ((String) table[i]).startsWith(sourceFolderName))
return false;
return true;
}
private static IContainer getSourceFolder(ClasspathMultiDirectory classpathMultiDirectory) {
try {
Field field = classpathMultiDirectory.getClass().getDeclaredField("sourceFolder");
field.setAccessible(true);
return (IContainer) field.get(classpathMultiDirectory);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
return null;
}
}
private static ClasspathLocation[] getBinaryLocations(State state) {
try {
Field field = state.getClass().getDeclaredField("binaryLocations");
field.setAccessible(true);
return (ClasspathLocation[]) field.get(state);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
return null;
}
}
public static boolean hasClasspathChanged(IProject project) {
State currentState = getProjectCurrentState(project);
State lastState = getProjectLastState(project);
if (lastState == null || currentState == null) {
return true;
}
ClasspathMultiDirectory[] newSourceLocations = currentState.sourceLocations;
ClasspathMultiDirectory[] oldSourceLocations = lastState.sourceLocations;
int newLength = newSourceLocations.length;
int oldLength = oldSourceLocations.length;
int n, o;
for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
if (newSourceLocations[n].equals(oldSourceLocations[o])) continue; // checks source & output folders
try {
if (getSourceFolder(newSourceLocations[n]).members().length == 0) { // added new empty source folder
o--;
continue;
} else if (isSourceFolderEmpty(lastState, getSourceFolder(oldSourceLocations[o]))) {
n--;
continue;
}
} catch (CoreException ignore) { // skip it
}
return true;
}
while (n < newLength) {
try {
if (getSourceFolder(newSourceLocations[n]).members().length == 0) { // added new empty source folder
n++;
continue;
}
} catch (CoreException ignore) {
// skip it
}
return true;
}
while (o < oldLength) {
if (isSourceFolderEmpty(lastState, getSourceFolder(oldSourceLocations[o]))) {
o++;
continue;
}
return true;
}
ClasspathLocation[] newBinaryLocations = getBinaryLocations(currentState);
ClasspathLocation[] oldBinaryLocations = getBinaryLocations(lastState);
newLength = newBinaryLocations.length;
oldLength = oldBinaryLocations.length;
for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
if (newBinaryLocations[n].toString().equals(oldBinaryLocations[o].toString())) continue;
return true;
}
if (n < newLength || o < oldLength) {
return true;
}
return false;
}
}