blob: d33e9ade7e08c2a683ac1e82fc2386ff3e25f1e3 [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.NoDataException;
import com.intellij.debugger.PositionManager;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.requests.ClassPrepareRequestor;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.PsiFile;
import com.sun.jdi.*;
import com.sun.jdi.request.ClassPrepareRequest;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Eugene Zhuravlev
* Date: May 23, 2006
*/
public abstract class JSR45PositionManager<Scope> implements PositionManager {
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.JSR45PositionManager");
protected final DebugProcess myDebugProcess;
protected final Scope myScope;
private final String myStratumId;
protected final SourcesFinder<Scope> mySourcesFinder;
protected final String GENERATED_CLASS_PATTERN;
protected Matcher myGeneratedClassPatternMatcher;
private final Set<LanguageFileType> myFileTypes;
public JSR45PositionManager(DebugProcess debugProcess, Scope scope, final String stratumId, final LanguageFileType[] acceptedFileTypes,
final SourcesFinder<Scope> sourcesFinder) {
myDebugProcess = debugProcess;
myScope = scope;
myStratumId = stratumId;
myFileTypes = Collections.unmodifiableSet(new HashSet<LanguageFileType>(Arrays.asList(acceptedFileTypes)));
mySourcesFinder = sourcesFinder;
String generatedClassPattern = getGeneratedClassesPackage();
if(generatedClassPattern.length() == 0) {
generatedClassPattern = getGeneratedClassesNamePattern();
}
else {
generatedClassPattern = generatedClassPattern + "." + getGeneratedClassesNamePattern();
}
GENERATED_CLASS_PATTERN = generatedClassPattern;
myGeneratedClassPatternMatcher = Pattern.compile(generatedClassPattern.replaceAll("\\*", ".*")).matcher("");
}
@NonNls
protected abstract String getGeneratedClassesPackage();
protected String getGeneratedClassesNamePattern() {
return "*";
}
public final String getStratumId() {
return myStratumId;
}
@Override
public SourcePosition getSourcePosition(final Location location) throws NoDataException {
SourcePosition sourcePosition = null;
try {
String sourcePath = getRelativeSourcePathByLocation(location);
PsiFile file = mySourcesFinder.findSourceFile(sourcePath, myDebugProcess.getProject(), myScope);
if(file != null) {
int lineNumber = getLineNumber(location);
sourcePosition = SourcePosition.createFromLine(file, lineNumber - 1);
}
}
catch (AbsentInformationException ignored) { // ignored
}
catch (Throwable e) {
LOG.info(e);
}
if(sourcePosition == null) {
throw new NoDataException();
}
return sourcePosition;
}
protected String getRelativeSourcePathByLocation(final Location location) throws AbsentInformationException {
return getRelativePath(location.sourcePath(myStratumId));
}
protected int getLineNumber(final Location location) {
return location.lineNumber(myStratumId);
}
@Override
@NotNull
public List<ReferenceType> getAllClasses(@NotNull SourcePosition classPosition) throws NoDataException {
checkSourcePositionFileType(classPosition);
final List<ReferenceType> referenceTypes = myDebugProcess.getVirtualMachineProxy().allClasses();
final List<ReferenceType> result = new ArrayList<ReferenceType>();
for (final ReferenceType referenceType : referenceTypes) {
myGeneratedClassPatternMatcher.reset(referenceType.name());
if (myGeneratedClassPatternMatcher.matches()) {
final List<Location> locations = locationsOfClassAt(referenceType, classPosition);
if (locations != null && locations.size() > 0) {
result.add(referenceType);
}
}
}
return result;
}
private void checkSourcePositionFileType(final SourcePosition classPosition) throws NoDataException {
final FileType fileType = classPosition.getFile().getFileType();
if(!myFileTypes.contains(fileType)) {
throw new NoDataException();
}
}
@Override
@NotNull
public List<Location> locationsOfLine(@NotNull final ReferenceType type, @NotNull final SourcePosition position) throws NoDataException {
List<Location> locations = locationsOfClassAt(type, position);
return locations != null ? locations : Collections.<Location>emptyList();
}
private List<Location> locationsOfClassAt(final ReferenceType type, final SourcePosition position) throws NoDataException {
checkSourcePositionFileType(position);
return ApplicationManager.getApplication().runReadAction(new Computable<List<Location>>() {
@Override
public List<Location> compute() {
try {
final List<String> relativePaths = getRelativeSourePathsByType(type);
for (String relativePath : relativePaths) {
final PsiFile file = mySourcesFinder.findSourceFile(relativePath, myDebugProcess.getProject(), myScope);
if(file != null && file.equals(position.getFile())) {
return getLocationsOfLine(type, getSourceName(file.getName(), type), relativePath, position.getLine() + 1);
}
}
}
catch(ObjectCollectedException ignored) {
}
catch(AbsentInformationException ignored) {
}
catch(ClassNotPreparedException ignored) {
}
catch (InternalError ignored) {
myDebugProcess.printToConsole(
DebuggerBundle.message("internal.error.locations.of.line", type.name()));
}
return null;
}
// Finds exact server file name (from available in type)
// This is needed because some servers (e.g. WebSphere) put not exact file name such as 'A.jsp '
private String getSourceName(final String name, final ReferenceType type) throws AbsentInformationException {
for(String sourceNameFromType: type.sourceNames(myStratumId)) {
if (sourceNameFromType.contains(name)) {
return sourceNameFromType;
}
}
return name;
}
});
}
protected List<String> getRelativeSourePathsByType(final ReferenceType type) throws AbsentInformationException {
final List<String> paths = type.sourcePaths(myStratumId);
final ArrayList<String> relativePaths = new ArrayList<String>();
for (String path : paths) {
relativePaths.add(getRelativePath(path));
}
return relativePaths;
}
protected List<Location> getLocationsOfLine(final ReferenceType type, final String fileName,
final String relativePath, final int lineNumber) throws AbsentInformationException {
return type.locationsOfLine(myStratumId, fileName, lineNumber);
}
@Override
public ClassPrepareRequest createPrepareRequest(@NotNull final ClassPrepareRequestor requestor, @NotNull final SourcePosition position)
throws NoDataException {
checkSourcePositionFileType(position);
return myDebugProcess.getRequestsManager().createClassPrepareRequest(new ClassPrepareRequestor() {
@Override
public void processClassPrepare(DebugProcess debuggerProcess, ReferenceType referenceType) {
onClassPrepare(debuggerProcess, referenceType, position, requestor);
}
}, GENERATED_CLASS_PATTERN);
}
protected void onClassPrepare(final DebugProcess debuggerProcess, final ReferenceType referenceType,
final SourcePosition position, final ClassPrepareRequestor requestor) {
try {
if(locationsOfClassAt(referenceType, position) != null) {
requestor.processClassPrepare(debuggerProcess, referenceType);
}
}
catch (NoDataException ignored) {
}
}
protected String getRelativePath(String sourcePath) {
if (sourcePath != null) {
sourcePath = sourcePath.trim();
String generatedClassesPackage = getGeneratedClassesPackage();
final String prefix = generatedClassesPackage.replace('.', File.separatorChar);
if (sourcePath.startsWith(prefix)) {
sourcePath = sourcePath.substring(prefix.length());
if (sourcePath.startsWith(File.separator)) {
sourcePath = sourcePath.substring(1);
}
}
}
return sourcePath;
}
}