/*
* Copyright 2012-2014 Sergey Ignatov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.intellij.erlang.debugger.xdebug;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.xdebugger.XDebuggerUtil;
import com.intellij.xdebugger.XSourcePosition;
import org.intellij.erlang.debugger.node.ErlangProcessSnapshot;
import org.intellij.erlang.debugger.node.ErlangTraceElement;
import org.intellij.erlang.psi.ErlangCompositeElement;
import org.intellij.erlang.psi.ErlangFile;
import org.intellij.erlang.psi.ErlangFunction;
import org.intellij.erlang.psi.impl.ErlangPsiImplUtil;
import org.intellij.erlang.utils.ErlangModulesUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class ErlangSourcePosition {
private static final Pattern FUN_PATTERN = Pattern.compile("^-(.*)/(\\d+)-(\\w+)-(\\d+)-$");
private final XSourcePosition mySourcePosition;
private final String myFunctionName;
private final int myFunctionArity;
private final String myFunExpressionName;
private final int myFunExpressionArity;
private ErlangSourcePosition(@NotNull XSourcePosition sourcePosition) {
this(sourcePosition, null, -1);
}
private ErlangSourcePosition(@NotNull XSourcePosition sourcePosition,
@Nullable String functionName, int functionArity) {
this(sourcePosition, functionName, functionArity, null, -1);
}
private ErlangSourcePosition(@NotNull XSourcePosition sourcePosition,
@Nullable String functionName, int functionArity,
@Nullable String funExpressionName, int funExpressionArity) {
mySourcePosition = sourcePosition;
myFunctionName = functionName;
myFunctionArity = functionArity;
myFunExpressionName = funExpressionName;
myFunExpressionArity = funExpressionArity;
}
@NotNull
public XSourcePosition getSourcePosition() {
return mySourcePosition;
}
@Nullable
public String getFunctionName() {
return myFunctionName;
}
public int getFunctionArity() {
return myFunctionArity;
}
@Nullable
public String getFunExpressionName() {
return myFunExpressionName;
}
public int getFunExpressionArity() {
return myFunExpressionArity;
}
@NotNull
public String getErlangModuleName() {
return mySourcePosition.getFile().getNameWithoutExtension();
}
public int getLine() {
return mySourcePosition.getLine();
}
@NotNull
public VirtualFile getFile() {
return mySourcePosition.getFile();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ErlangSourcePosition that = (ErlangSourcePosition) o;
return myFunExpressionArity == that.myFunExpressionArity &&
myFunctionArity == that.myFunctionArity &&
Comparing.equal(myFunctionName, that.myFunctionName) &&
Comparing.equal(myFunExpressionName, that.myFunExpressionName) &&
Comparing.equal(getFile(), that.getFile()) &&
Comparing.equal(getLine(), that.getLine());
}
@Override
public int hashCode() {
int result = myFunctionName != null ? myFunctionName.hashCode() : 0;
result = 31 * result + (myFunExpressionName != null ? myFunExpressionName.hashCode() : 0);
result = 31 * result + myFunctionArity;
result = 31 * result + myFunExpressionArity;
result = 31 * result + getFile().hashCode();
result = 31 * result + getLine();
return result;
}
@NotNull
public static ErlangSourcePosition create(@NotNull XSourcePosition position) {
return new ErlangSourcePosition(position);
}
@Nullable
public static ErlangSourcePosition create(@NotNull Project project, @NotNull ErlangProcessSnapshot snapshot) {
String module = snapshot.getBreakModule();
int line = snapshot.getBreakLine();
return module != null ? create(project, module, line) : null;
}
@Nullable
public static ErlangSourcePosition create(@NotNull Project project, @NotNull ErlangTraceElement traceElement) {
return create(project, traceElement.getModule(), traceElement.getFunction(), traceElement.getFunctionArgs().arity());
}
@Nullable
public static ErlangSourcePosition create(@NotNull Project project, @NotNull String module, int line) {
ErlangFile file = ErlangModulesUtil.getErlangModuleFile(project, module);
VirtualFile moduleFile = file != null ? file.getVirtualFile() : null;
XSourcePosition sourcePosition = XDebuggerUtil.getInstance().createPosition(moduleFile, line);
return sourcePosition != null ? new ErlangSourcePosition(sourcePosition) : null;
}
@Nullable
public static ErlangSourcePosition create(@NotNull final Project project, @NotNull final String module,
@NotNull String functionOrFunExpression, int arity) {
final String functionName;
final String funExpressionName;
final int functionArity;
final int funExpressionArity;
Matcher matcher = FUN_PATTERN.matcher(functionOrFunExpression);
final boolean inFunExpression = matcher.matches();
if (inFunExpression) {
functionName = matcher.group(1);
functionArity = Integer.parseInt(matcher.group(2));
funExpressionName = matcher.group(3);
funExpressionArity = Integer.parseInt(matcher.group(4));
} else {
functionName = functionOrFunExpression;
functionArity = arity;
funExpressionName = null;
funExpressionArity = -1;
}
XSourcePosition position = ApplicationManager.getApplication().runReadAction(new Computable<XSourcePosition>() {
@Nullable
@Override
public XSourcePosition compute() {
ErlangFile erlangModule = ErlangModulesUtil.getErlangModuleFile(project, module);
if (erlangModule == null) return null;
//TODO use fun expression name to improve resolution (?)
ErlangFunction function = erlangModule.getFunction(functionName, functionArity);
ErlangCompositeElement clarifyingElement = inFunExpression && function != null ?
ErlangPsiImplUtil.findFunExpression(function, funExpressionArity) : function;
VirtualFile virtualFile = erlangModule.getVirtualFile();
int offset = clarifyingElement != null ? clarifyingElement.getTextOffset() : 0;
return XDebuggerUtil.getInstance().createPositionByOffset(virtualFile, offset);
}
});
return position != null ?
new ErlangSourcePosition(position, functionName, functionArity, funExpressionName, funExpressionArity) : null;
}
}