blob: eeaa1c21015e5637343ed0b7b50b397bf241928a [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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 com.intellij.debugger.engine;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.DebuggerManager;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.jsp.JspFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.sun.jdi.ReferenceType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* User: lex
* Date: Sep 2, 2003
* Time: 11:25:59 AM
*/
public class JVMNameUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.JVMNameUtil");
@SuppressWarnings({"HardCodedStringLiteral"})
public static String getPrimitiveSignature(String typeName) {
if(PsiType.BOOLEAN.getCanonicalText().equals(typeName)) {
return "Z";
}
else if (PsiType.BYTE.getCanonicalText().equals(typeName)) {
return "B";
}
else if (PsiType.CHAR.getCanonicalText().equals(typeName)) {
return "C";
}
else if (PsiType.SHORT.getCanonicalText().equals(typeName)) {
return "S";
}
else if (PsiType.INT.getCanonicalText().equals(typeName)) {
return "I";
}
else if (PsiType.LONG.getCanonicalText().equals(typeName)) {
return "J";
}
else if (PsiType.FLOAT.getCanonicalText().equals(typeName)) {
return "F";
}
else if (PsiType.DOUBLE.getCanonicalText().equals(typeName)) {
return "D";
}
else if (PsiType.VOID.getCanonicalText().equals(typeName)) {
return "V";
}
return null;
}
@SuppressWarnings({"HardCodedStringLiteral"})
private static void appendJVMSignature(JVMNameBuffer buffer , PsiType type){
if (type == null) {
return;
}
final PsiType psiType = TypeConversionUtil.erasure(type);
if (psiType instanceof PsiArrayType) {
buffer.append(new JVMRawText("["));
appendJVMSignature(buffer, ((PsiArrayType) psiType).getComponentType());
}
else if (psiType instanceof PsiClassType) {
final JVMName jvmName = getJVMQualifiedName(psiType);
appendJvmClassQualifiedName(buffer, jvmName);
}
else if (psiType instanceof PsiPrimitiveType) {
buffer.append(getPrimitiveSignature(psiType.getCanonicalText()));
}
else {
LOG.error("unknown type " + type.getCanonicalText());
}
}
private static void appendJvmClassQualifiedName(JVMNameBuffer buffer, final JVMName jvmName) {
buffer.append("L");
if(jvmName instanceof JVMRawText) {
buffer.append(((JVMRawText)jvmName).getName().replace('.','/'));
}
else {
buffer.append(new JVMName() {
public String getName(DebugProcessImpl process) throws EvaluateException {
return jvmName.getName(process).replace('.','/');
}
public String getDisplayName(DebugProcessImpl debugProcess) {
return jvmName.getDisplayName(debugProcess);
}
});
}
buffer.append(";");
}
private static class JVMNameBuffer {
List<JVMName> myList = new ArrayList<JVMName>();
public void append(@NotNull JVMName evaluator){
myList.add(evaluator);
}
public void append(char name){
append(Character.toString(name));
}
public void append(String text){
myList.add(getJVMRawText(text));
}
public JVMName toName() {
final List<JVMName> optimised = new ArrayList<JVMName>();
for (JVMName evaluator : myList) {
if (evaluator instanceof JVMRawText && !optimised.isEmpty() && optimised.get(optimised.size() - 1) instanceof JVMRawText) {
JVMRawText nameEvaluator = (JVMRawText)optimised.get(optimised.size() - 1);
nameEvaluator.setName(nameEvaluator.getName() + ((JVMRawText)evaluator).getName());
}
else {
optimised.add(evaluator);
}
}
if(optimised.size() == 1) return optimised.get(0);
if(optimised.isEmpty()) return new JVMRawText("");
return new JVMName() {
String myName = null;
public String getName(DebugProcessImpl process) throws EvaluateException {
if(myName == null){
String name = "";
for (JVMName nameEvaluator : optimised) {
name += nameEvaluator.getName(process);
}
myName = name;
}
return myName;
}
public String getDisplayName(DebugProcessImpl debugProcess) {
if(myName == null) {
String displayName = "";
for (JVMName nameEvaluator : optimised) {
displayName += nameEvaluator.getDisplayName(debugProcess);
}
return displayName;
}
return myName;
}
};
}
}
private static class JVMRawText implements JVMName {
private String myText;
public JVMRawText(String text) {
myText = text;
}
public String getName(DebugProcessImpl process) throws EvaluateException {
return myText;
}
public String getDisplayName(DebugProcessImpl debugProcess) {
return myText;
}
public String getName() {
return myText;
}
public void setName(String name) {
myText = name;
}
}
private static class JVMClassAt implements JVMName {
private final SourcePosition mySourcePosition;
public JVMClassAt(SourcePosition sourcePosition) {
mySourcePosition = sourcePosition;
}
public String getName(DebugProcessImpl process) throws EvaluateException {
List<ReferenceType> allClasses = process.getPositionManager().getAllClasses(mySourcePosition);
if(!allClasses.isEmpty()) {
return allClasses.get(0).name();
}
throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("error.class.not.loaded", getDisplayName(process)));
}
public String getDisplayName(final DebugProcessImpl debugProcess) {
return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
public String compute() {
return getSourcePositionClassDisplayName(debugProcess, mySourcePosition);
}
});
}
}
public static JVMName getJVMRawText(String qualifiedName) {
return new JVMRawText(qualifiedName);
}
public static JVMName getJVMQualifiedName(PsiType psiType) {
if(psiType instanceof PsiArrayType) {
final PsiArrayType arrayType = (PsiArrayType)psiType;
JVMName jvmName = getJVMQualifiedName(arrayType.getComponentType());
JVMNameBuffer buffer = new JVMNameBuffer();
buffer.append(jvmName);
buffer.append("[]");
return buffer.toName();
}
PsiClass psiClass = PsiUtil.resolveClassInType(psiType);
if (psiClass == null) {
return getJVMRawText(psiType.getCanonicalText());
}
else {
return getJVMQualifiedName(psiClass);
}
}
public static JVMName getJVMQualifiedName(PsiClass psiClass) {
if (!PsiUtil.isLocalOrAnonymousClass(psiClass)) {
final String name = getNonAnonymousClassName(psiClass);
if (name != null) {
return getJVMRawText(name);
}
}
return new JVMClassAt(SourcePosition.createFromElement(psiClass));
}
@Nullable
public static JVMName getContextClassJVMQualifiedName(@Nullable SourcePosition pos) {
final PsiClass psiClass = getClassAt(pos);
if (psiClass == null) {
return null;
}
if (!PsiUtil.isLocalOrAnonymousClass(psiClass)) {
final String name = getNonAnonymousClassName(psiClass);
if (name != null) {
return getJVMRawText(name);
}
}
return new JVMClassAt(pos);
}
@Nullable
public static String getNonAnonymousClassName(PsiClass aClass) {
PsiClass parentClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class, true);
if(parentClass != null) {
final String parentName = getNonAnonymousClassName(parentClass);
if (parentName == null) {
return null;
}
return parentName + "$" + aClass.getName();
}
return DebuggerManager.getInstance(aClass.getProject()).getVMClassQualifiedName(aClass);
}
public static JVMName getJVMConstructorSignature(@Nullable PsiMethod method, @Nullable PsiClass declaringClass) {
return getJVMSignature(method, true, declaringClass);
}
public static JVMName getJVMSignature(@NotNull PsiMethod method) {
return getJVMSignature(method, method.isConstructor(), method.getContainingClass());
}
@SuppressWarnings({"HardCodedStringLiteral"})
private static JVMName getJVMSignature(@Nullable PsiMethod method, boolean constructor, @Nullable PsiClass declaringClass) {
JVMNameBuffer signature = new JVMNameBuffer();
signature.append("(");
if (constructor) {
if (declaringClass != null) {
final PsiClass outerClass = declaringClass.getContainingClass();
if (outerClass != null) {
// declaring class is an inner class
if (!declaringClass.hasModifierProperty(PsiModifier.STATIC)) {
appendJvmClassQualifiedName(signature, getJVMQualifiedName(outerClass));
}
}
}
}
if (method != null) {
for (PsiParameter psiParameter : method.getParameterList().getParameters()) {
appendJVMSignature(signature, psiParameter.getType());
}
}
signature.append(")");
if (!constructor && method != null) {
appendJVMSignature(signature, method.getReturnType());
}
else {
signature.append(new JVMRawText("V"));
}
return signature.toName();
}
@Nullable
public static PsiClass getClassAt(@Nullable SourcePosition position) {
if (position == null) {
return null;
}
final PsiElement element = position.getElementAt();
return (element != null) ? PsiTreeUtil.getParentOfType(element, PsiClass.class, false) : null;
}
@Nullable
public static String getSourcePositionClassDisplayName(DebugProcessImpl debugProcess, @Nullable SourcePosition position) {
if (position == null) {
return null;
}
final PsiFile positionFile = position.getFile();
if (positionFile instanceof JspFile) {
return positionFile.getName();
}
final PsiClass psiClass = getClassAt(position);
if(psiClass != null) {
final String qName = psiClass.getQualifiedName();
if(qName != null) {
return qName;
}
}
if(debugProcess != null && debugProcess.isAttached()) {
List<ReferenceType> allClasses = debugProcess.getPositionManager().getAllClasses(position);
if(!allClasses.isEmpty()) {
return allClasses.get(0).name();
}
}
if (psiClass == null) {
if (positionFile instanceof PsiClassOwner) {
return positionFile.getName();
}
return DebuggerBundle.message("string.file.line.position", positionFile.getName(), position.getLine());
}
return calcClassDisplayName(psiClass);
}
static String calcClassDisplayName(final PsiClass aClass) {
final String qName = aClass.getQualifiedName();
if (qName != null) {
return qName;
}
final PsiClass parent = PsiTreeUtil.getParentOfType(aClass, PsiClass.class, true);
if (parent == null) {
return null;
}
final String name = aClass.getName();
if (name != null) {
return calcClassDisplayName(parent) + "$" + name;
}
final Ref<Integer> classIndex = new Ref<Integer>(0);
try {
parent.accept(new JavaRecursiveElementVisitor() {
public void visitAnonymousClass(PsiAnonymousClass cls) {
classIndex.set(classIndex.get() + 1);
if (aClass.equals(cls)) {
throw new ProcessCanceledException();
}
}
});
}
catch (ProcessCanceledException ignored) {
}
return calcClassDisplayName(parent) + "$" + classIndex.get();
}
@Nullable
public static String getSourcePositionPackageDisplayName(DebugProcessImpl debugProcess, @Nullable SourcePosition position) {
if (position == null) {
return null;
}
final PsiFile positionFile = position.getFile();
if (positionFile instanceof JspFile) {
final PsiDirectory dir = positionFile.getContainingDirectory();
return dir != null? dir.getVirtualFile().getPresentableUrl() : null;
}
final PsiClass psiClass = getClassAt(position);
if(psiClass != null) {
PsiClass toplevel = PsiUtil.getTopLevelClass(psiClass);
if(toplevel != null) {
String qName = toplevel.getQualifiedName();
if (qName != null) {
int i = qName.lastIndexOf('.');
return i > 0 ? qName.substring(0, i) : "";
}
}
}
if(debugProcess != null && debugProcess.isAttached()) {
List<ReferenceType> allClasses = debugProcess.getPositionManager().getAllClasses(position);
if(!allClasses.isEmpty()) {
final String className = allClasses.get(0).name();
int dotIndex = className.lastIndexOf('.');
if (dotIndex >= 0) {
return className.substring(0, dotIndex);
}
}
}
return "";
}
public static PsiClass getTopLevelParentClass(PsiClass psiClass) {
PsiClass enclosing = PsiTreeUtil.getParentOfType(psiClass, PsiClass.class, true);
while (enclosing != null) {
psiClass = enclosing;
enclosing = PsiTreeUtil.getParentOfType(enclosing, PsiClass.class, true);
}
return psiClass;
}
}