package org.erlide.dialyzer.builder;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.erlide.backend.api.BackendException;
import org.erlide.backend.api.IBackend;
import org.erlide.core.builder.BuilderHelper;
import org.erlide.engine.ErlangEngine;
import org.erlide.engine.model.ErlModelException;
import org.erlide.engine.model.IErlElement;
import org.erlide.engine.model.erlang.SourceKind;
import org.erlide.engine.model.root.IErlElementLocator;
import org.erlide.engine.model.root.IErlFolder;
import org.erlide.engine.model.root.IErlModule;
import org.erlide.engine.model.root.IErlProject;
import org.erlide.runtime.rpc.IOtpRpc;
import org.erlide.runtime.rpc.RpcException;
import org.erlide.runtime.rpc.RpcFuture;
import org.erlide.runtime.rpc.RpcTimeoutException;
import org.erlide.util.ErlLogger;
import org.erlide.util.SystemConfiguration;
import org.erlide.util.Util;
import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangList;
import com.ericsson.otp.erlang.OtpErlangLong;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangRangeException;
import com.ericsson.otp.erlang.OtpErlangTuple;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
public class DialyzerUtils {
private static final int MAX_MSG_LEN = 2000;
private static BuilderHelper helper;
public static void setHelper(final BuilderHelper h) {
helper = h;
}
public static class DialyzerErrorException extends Exception {
public DialyzerErrorException(final String message) {
super(message);
}
private static final long serialVersionUID = -6872359945128662063L;
}
public static void doDialyze(final IProgressMonitor monitor,
final Set<IErlModule> modules, final Set<IErlProject> projects,
final IBackend backend)
throws InvocationTargetException, DialyzerErrorException {
if (backend == null) {
ErlLogger.warn("Trying to dialyze with null backend");
return;
}
try {
for (final IErlModule module : modules) {
DialyzerMarkerUtils.removeDialyzerMarkersFor(module.getResource());
}
// TODO handle preferences from multiple projects
final DialyzerPreferences prefs = DialyzerPreferences.get(null);
final Collection<String> pltPaths = prefs.getPltPaths();
final boolean fromSource = false; // prefs.getFromSource();
final boolean noCheckPLT = true; // prefs.getNoCheckPLT();
final List<String> files = Lists.newArrayList();
final List<IPath> includeDirs = Lists.newArrayList();
final List<String> names = Lists.newArrayList();
collectFilesAndIncludeDirs(modules, projects, files, names, includeDirs,
fromSource);
final String fileNames = names.size() + " modules [" + getFileNames(names)
+ "]";
monitor.subTask(fileNames);
ErlLogger.trace("dialyzer", "run %s", fileNames);
final IOtpRpc b = backend.getOtpRpc();
final RpcFuture future = ErlideDialyze.dialyze(b, files, pltPaths,
includeDirs, fromSource, noCheckPLT);
while (!future.isDone()) {
// check cancellation
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
// check backend down
if (!backend.isRunning()) {
throw new BackendException(
"Dialyzer: backend " + backend.getName() + " is down");
}
OtpErlangObject r = null;
try {
r = future.checkedGet(500, TimeUnit.MILLISECONDS);
} catch (final TimeoutException e) {
} catch (final RpcTimeoutException e) {
}
if (r != null) {
processResult(b, r);
}
}
} catch (final RpcException e) {
throw new InvocationTargetException(e);
} catch (final BackendException e) {
throw new InvocationTargetException(e);
}
}
private static void processResult(final IOtpRpc backend, final OtpErlangObject o)
throws DialyzerErrorException {
if (o instanceof OtpErlangTuple) {
final OtpErlangTuple t = (OtpErlangTuple) o;
final OtpErlangAtom whatA = (OtpErlangAtom) t.elementAt(0);
final String what = whatA.toString();
final OtpErlangObject result = t.elementAt(1);
if ("warnings".equals(what)) {
DialyzerMarkerUtils.addDialyzerWarningMarkersFromResultList(backend,
(OtpErlangList) result);
} else if ("dialyzer_error".equals(what) || "badrpc".equals(what)) {
final String s = Util.ioListToString(result, MAX_MSG_LEN);
throw new DialyzerErrorException(s);
}
} else {
throw new DialyzerErrorException(
"Unknown Dialyzer message: " + Util.ioListToString(o, MAX_MSG_LEN));
}
}
private static String getFileNames(final List<String> names) {
if (names.isEmpty()) {
return "";
}
final StringBuilder sb = new StringBuilder(100);
for (final String name : names) {
if (sb.length() > 100) {
sb.append("..., ");
break;
}
sb.append(name);
sb.append(", ");
}
return sb.substring(0, sb.length() - 2);
}
public static void collectFilesAndIncludeDirs(final Set<IErlModule> modules,
final Set<IErlProject> projects, final Collection<String> files,
final Collection<String> names, final Collection<IPath> includeDirs,
final boolean fromSource) {
for (final IErlModule m : modules) {
final String name = m.getName();
final IErlProject erlProject = ErlangEngine.getInstance()
.getModelUtilService().getProject(m);
final IProject project = erlProject.getWorkspaceProject();
final IFolder ebin = project
.getFolder(erlProject.getProperties().getOutputDir());
if (SourceKind.hasErlExtension(name)) {
if (fromSource) {
final IResource resource = m.getResource();
files.add(resource.getLocation().toPortableString());
} else {
final String moduleName = SystemConfiguration.withoutExtension(name);
final String beamName = moduleName + ".beam";
final IResource beam = ebin.findMember(beamName);
if (beam != null) {
files.add(beam.getLocation().toPortableString());
names.add(moduleName);
}
}
}
}
helper = new BuilderHelper();
for (final IErlProject p : projects) {
helper.getIncludeDirs(p.getWorkspaceProject(), includeDirs);
}
}
public static void checkDialyzeError(final OtpErlangObject result)
throws DialyzerErrorException {
if (result == null) {
throw new DialyzerErrorException(
"Could not execute dialyzer, please check settings.");
}
if (result instanceof OtpErlangTuple) {
final OtpErlangTuple t = (OtpErlangTuple) result;
if (t.arity() > 0) {
final OtpErlangObject element = t.elementAt(0);
if (element instanceof OtpErlangLong) {
final OtpErlangLong l = (OtpErlangLong) element;
try {
final int d = l.intValue();
if (d == 0 || d == 1 || d == 2) {
return;
}
} catch (final OtpErlangRangeException e) {
}
}
}
final String s = Util.ioListToString(t.elementAt(1), MAX_MSG_LEN + 10);
final String r = s.replaceAll("\\\\n", "\n");
if (s.length() > MAX_MSG_LEN) {
ErlLogger.error("%s", s);
}
throw new DialyzerErrorException(r);
}
}
public static Set<IErlModule> collectModulesFromResource(
final IErlElementLocator model, final IResource resource)
throws ErlModelException {
final Set<IErlModule> result = Sets.newHashSet();
final IErlElement element = model.findElement(resource, true);
if (element == null) {
return result;
}
if (element instanceof IErlFolder) {
final IErlFolder folder = (IErlFolder) element;
folder.open(null);
result.addAll(folder.getModules());
} else if (element instanceof IErlModule) {
final IErlModule module = (IErlModule) element;
result.add(module);
} else if (element instanceof IErlProject) {
final IErlProject project = (IErlProject) element;
project.open(null);
result.addAll(project.getModules());
}
return result;
}
}