package de.hub.srcrepo.internal;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.gmt.modisco.java.ASTNode;
import org.eclipse.gmt.modisco.java.CompilationUnit;
import org.eclipse.gmt.modisco.java.Model;
import org.eclipse.gmt.modisco.java.NamedElement;
import org.eclipse.gmt.modisco.java.UnresolvedItem;
import org.eclipse.gmt.modisco.java.emf.JavaFactory;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.internal.ui.text.java.ImportCompletionProposal;
import org.eclipse.modisco.java.discoverer.internal.io.java.JavaReader;
import org.eclipse.modisco.java.discoverer.internal.io.java.binding.PendingElement;
import com.google.common.base.Preconditions;
import de.hub.srcrepo.SrcRepoActivator;
import de.hub.srcrepo.repositorymodel.CompilationUnitModel;
import de.hub.srcrepo.repositorymodel.RepositoryModelFactory;
import de.hub.srcrepo.repositorymodel.Target;
import de.hub.srcrepo.repositorymodel.UnresolvedLink;
@SuppressWarnings("restriction")
public abstract class ImportJavaCompilationUnitsJob extends WorkspaceJob {
private final Collection<ICompilationUnit> compilationUnits;
private final JavaFactory javaFactory;
private final RepositoryModelFactory repositoryFactory;
private final Map<ICompilationUnit, ImportJavaCompilationUnitResult> results = new HashMap<>();
public class ImportJavaCompilationUnitResult {
private final String projectID;
private final CompilationUnitModel model;
private ImportJavaCompilationUnitResult(String projectID, CompilationUnitModel model) {
super();
this.projectID = projectID;
this.model = model;
}
public String getProjectID() {
return projectID;
}
public CompilationUnitModel getModel() {
return model;
}
}
public ImportJavaCompilationUnitsJob(Collection<ICompilationUnit> compilationUnits,
JavaFactory javaFactory,
RepositoryModelFactory repositoryFactory) {
super(ImportJavaCompilationUnitsJob.class.getName() + " import compilation units for current ref.");
this.compilationUnits = compilationUnits;
this.javaFactory = javaFactory;
this.repositoryFactory = repositoryFactory;
}
protected abstract void skipWarning(String message);
protected abstract void skipError(String message, Exception e);
protected boolean importCompilationUnit(ICompilationUnit compilationUnit) {
try {
long fileSize = EFS.getStore(compilationUnit.getResource().getLocationURI()).fetchInfo().getLength();
if (fileSize > 300000) { // TODO makes this configurable, add functionality to detect generated files
skipWarning("Skipped compilation unit " + compilationUnit.getResource().getProjectRelativePath() +
" because it is awefully large (" + (fileSize/1024) + "kb) and probably generated.");
return false;
}
} catch (Exception e) {
skipError("Could not estimate size of " + compilationUnit.getResource().getProjectRelativePath(), e);
return false;
}
CompilationUnitModel compilationUnitModel = repositoryFactory.createCompilationUnitModel();
String projectID = compilationUnit.getJavaProject().getPath().toPortableString();
ImportJavaCompilationUnitResult result = new ImportJavaCompilationUnitResult(projectID, compilationUnitModel);
SrcRepoBindingManager bindings = new SrcRepoBindingManager(javaFactory) {
@Override
protected void manageUnresolvedBindings(final Model model1,
final List<PendingElement> unresolvedBindings) {
if (model1 != null) {
for (PendingElement pe : unresolvedBindings) {
ASTNode source = pe.getClientNode();
UnresolvedLink unresolvedLink = repositoryFactory.createUnresolvedLink();
compilationUnitModel.getUnresolvedLinks().add(unresolvedLink);
unresolvedLink.setId(pe.getBinding().toString());
unresolvedLink.setSource(source);
EClass sourceClass = source.eClass();
EStructuralFeature feature = sourceClass.getEStructuralFeature(pe.getLinkName());
unresolvedLink.setFeatureID(sourceClass.getFeatureID(feature));
NamedElement target = null;
target = getProxyElement(pe, model1);
if (target != null) {
// There is a bug in modisco. MethodDeclaration and AnnotationTypeMemberDeclarations
// are sometimes confused with each other (they probably have colliding ids). This
// causes a ClassCastException in internaly; affectTarget however will return normaly.
// Will later cause problems during traversal when resolution is repeated.
pe.affectTarget(target);
Preconditions.checkState(target instanceof UnresolvedItem || target.isProxy());
unresolvedLink.setTarget(target);
if (feature.isMany()) {
unresolvedLink.setFeatureIndex(((List<?>)source.eGet(feature)).indexOf(target));
} else {
unresolvedLink.setFeatureIndex(-1);
}
} else {
// TODO maybe there is a regular case where this happen?
SrcRepoActivator.INSTANCE.warning("Found an element that could not be resolved, " +
"even with proxies or unresolved items: " + pe.getBinding().getName());
}
}
}
}
};
// TODO reuse javaReader and Discover...
JavaReader javaReader = new JavaReader(javaFactory, new HashMap<String, Object>(), null);
javaReader.setDeepAnalysis(true);
javaReader.setIncremental(false);
Model javaModel = javaFactory.createModel();
SrcRepoActivator.INSTANCE.debug("import compilation unit " + compilationUnit.getPath());
try {
javaReader.readModel(compilationUnit, javaModel, bindings, new NullProgressMonitor());
// We need to try to resolve the bindings. This will save performance, when snapshots are merged,
// and more importantly, it will create bindings that result in proxy elements, which will
// be added to the targets of the binding manager. Those targets are also placed in the java model.
// bindings.resolveBindings(javaModel);
javaReader.terminate(new NullProgressMonitor());
} catch (Exception e) {
if (e.getClass().getName().endsWith("AbortCompilation")) {
skipError("Could not compile " + compilationUnit.getResource().getProjectRelativePath() +
" (is ignored): " + e.getMessage(), e);
EcoreUtil.delete(javaModel);
} else {
EcoreUtil.delete(javaModel);
skipError("Could not compile " + compilationUnit.getResource().getProjectRelativePath() +
" (is ignored) for unknown reasons: " + e.getMessage(), e);
}
return false;
}
if (javaModel.getCompilationUnits().size() == 1) {
// check if a new top level class was imported (and not only the compilation unit)
if (javaModel.getCompilationUnits().get(0).getTypes().isEmpty()) {
SrcRepoActivator.INSTANCE.warning("A compilation was imported, but no new type created, probably due to parser errors: " + compilationUnit.getPath());
}
CompilationUnit importedCompilationUnit = javaModel.getCompilationUnits().get(0);
compilationUnitModel.setCompilationUnit(importedCompilationUnit);
compilationUnitModel.setJavaModel(javaModel);
// save targets
for(Map.Entry<String, NamedElement> target: bindings.getTargets().entrySet()) {
Target targetModel = repositoryFactory.createTarget();
targetModel.setId(target.getKey());
targetModel.setTarget(target.getValue());
compilationUnitModel.getTargets().add(targetModel);
}
results.put(compilationUnit, result);
return true;
} else {
EcoreUtil.delete(compilationUnitModel);
EcoreUtil.delete(javaModel);
skipError("Sucessfully imported a compilation unit, but no model was created: " + compilationUnit, null);
return false;
}
}
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
// import diffs
SrcRepoActivator.INSTANCE.info("about to import " + compilationUnits.size() + " compilation units");
int count = 0;
for(ICompilationUnit compilationUnit: compilationUnits) {
if (importCompilationUnit(compilationUnit)) {
count++;
}
}
SrcRepoActivator.INSTANCE.info("imported " + count + " compilation units");
return Status.OK_STATUS;
}
public Map<ICompilationUnit, ImportJavaCompilationUnitResult> getResults() {
return results;
}
}