// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.debug.core;
import org.chromium.debug.core.model.VmResourceId;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
import org.eclipse.debug.core.sourcelookup.containers.ContainerSourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.DefaultSourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.WorkspaceSourceContainer;
/**
* Eclipse has a standard facility for looking up source file for a debug artifact.
* LiveEdit feature has an opposite problem: find script in remote VM for a particular js file.
* This class implements some approach to this problem. An instance of this class corresponds
* to a particular debug launch.
*/
public class ReverseSourceLookup {
private final ISourceLookupDirector sourceDirector;
public ReverseSourceLookup(ISourceLookupDirector sourceDirector) {
this.sourceDirector = sourceDirector;
}
/**
* Tries to find a corresponding script for a file from a user workspace. The method uses
* the file name and current source lookup rules to retrieve a resource id, regardless of
* whether the resource has actually been loaded into the VM (you may want to set a breakpoint
* on resource before it is actually loaded).
*/
public VmResourceId findVmResource(IFile sourceFile) throws CoreException {
for (ISourceContainer container : sourceDirector.getSourceContainers()) {
VmResourceId scriptName = tryForContainer(sourceFile, container);
if (scriptName != null) {
return scriptName;
}
}
return null;
}
public static boolean isGoodTargetContainer(ISourceContainer container) {
return wrapNonVirtualContainer(container) != null;
}
private VmResourceId tryForContainer(IFile sourceFile, ISourceContainer container)
throws CoreException {
if (container.isComposite() && isSupportedCompositeContainer(container)) {
ISourceContainer[] subContainers = container.getSourceContainers();
for (ISourceContainer subContainer : subContainers) {
VmResourceId res = tryForContainer(sourceFile, subContainer);
if (res != null) {
return res;
}
}
return null;
} else if (container instanceof VProjectSourceContainer) {
VProjectSourceContainer projectSourceContainer = (VProjectSourceContainer) container;
return projectSourceContainer.findScriptId(sourceFile);
} else {
String name = tryForNonVirtualContainer(sourceFile, container);
if (name == null) {
return null;
}
return new VmResourceId(name, null);
}
}
/**
* We use {@link ISourceContainer#getSourceContainers()} method to unwrap internal containers.
* However it doesn't make sense for all composite containers (some of them may return their
* subdirectories as containers, which is not what we need).
*/
private boolean isSupportedCompositeContainer(ISourceContainer container) {
return container instanceof DefaultSourceContainer;
}
/**
* @param container that may not wrap VProjectSourceContainer
*/
private String tryForNonVirtualContainer(IFile resource, ISourceContainer container) {
ContainerWrapper wrapper = wrapNonVirtualContainer(container);
if (wrapper == null) {
return null;
}
return wrapper.lookup(resource);
}
private static ContainerWrapper wrapNonVirtualContainer(ISourceContainer container) {
if (container instanceof ContainerSourceContainer) {
final ContainerSourceContainer containerSourceContainer =
(ContainerSourceContainer) container;
return new ContainerWrapper() {
@Override
public String lookup(IFile resource) {
return lookupInResourceContainer(resource, containerSourceContainer.getContainer());
}
};
} else if (container instanceof WorkspaceSourceContainer) {
return new ContainerWrapper() {
@Override
public String lookup(IFile resource) {
return lookupInResourceContainer(resource, ResourcesPlugin.getWorkspace().getRoot());
}
};
} else if (container instanceof SourceNameMapperContainer) {
SourceNameMapperContainer mappingContainer = (SourceNameMapperContainer) container;
final ContainerWrapper targetContainerWrapper =
wrapNonVirtualContainer(mappingContainer.getTargetContainer());
final String prefix = mappingContainer.getPrefix();
return new ContainerWrapper() {
@Override
public String lookup(IFile resource) {
String subResult = targetContainerWrapper.lookup(resource);
if (subResult == null) {
return null;
}
return prefix + subResult;
}
};
}
return null;
}
/**
* Wraps a container. This interface guarantees that original container with all inner containers
* are supported by our reversed lookup.
*/
private interface ContainerWrapper {
String lookup(IFile resource);
}
private static String lookupInResourceContainer(IFile resource, IContainer resourceContainer) {
IPath resourceFullPath = resource.getFullPath();
IPath containerFullPath = resourceContainer.getFullPath();
if (!containerFullPath.isPrefixOf(resourceFullPath)) {
return null;
}
int offset = containerFullPath.segmentCount();
IPath newPath = resourceFullPath.removeFirstSegments(offset);
return newPath.toPortableString();
}
}