Copyright (c) 2011-2016 Igor Fedorenko This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: Igor Fedorenko - initial API and implementation
/******************************************************************************* * Copyright (c) 2011-2016 Igor Fedorenko * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * Igor Fedorenko - initial API and implementation *******************************************************************************/
package org.eclipse.jdt.launching.sourcelookup.advanced; import static org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupSupport.getContextMonitor; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.model.DebugElement; import org.eclipse.debug.core.model.IDebugElement; import org.eclipse.debug.core.model.ISourceLocator; import org.eclipse.debug.core.sourcelookup.ISourceContainer; import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector; import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant; import org.eclipse.jdt.internal.launching.sourcelookup.advanced.AdvancedSourceLookupSupport; import org.eclipse.jdt.internal.launching.sourcelookup.advanced.CompositeSourceContainer; import org.eclipse.jdt.internal.launching.sourcelookup.advanced.IJDIHelpers; import org.eclipse.jdt.internal.launching.sourcelookup.advanced.WorkspaceProjectSourceContainers;
Since:3.10
@provisionalThis is part of work in progress and can be changed, moved or removed without notice
/** * @since 3.10 * @provisional This is part of work in progress and can be changed, moved or removed without notice */
public class AdvancedSourceLookupParticipant implements ISourceLookupParticipant { private final IJDIHelpers jdi; private ISourceLookupDirector director; private final Map<File, ISourceContainer> containers = new HashMap<>(); public AdvancedSourceLookupParticipant() { this(IJDIHelpers.INSTANCE); }
@noreferencethis constructor is visible for test purposes only
/** * @noreference this constructor is visible for test purposes only */
public AdvancedSourceLookupParticipant(IJDIHelpers jdi) { this.jdi = jdi; } @Override public void init(ISourceLookupDirector director) { this.director = director; } @Override public Object[] findSourceElements(Object element) throws CoreException { ISourceContainer container = getSourceContainer(element, false /* don't refresh cache */, null /* async */ ); if (container == null) { return null; } String sourcePath = jdi.getSourcePath(element); if (sourcePath == null) { // can't really happen return null; } return container.findSourceElements(sourcePath); } public ISourceContainer getSourceContainer(Object element, boolean refresh, IProgressMonitor monitor) throws CoreException { File location = jdi.getClassesLocation(element); if (location == null) { return null; } synchronized (containers) { if (!refresh && containers.containsKey(location)) { return containers.get(location); } } monitor = getContextMonitor(monitor); WorkspaceProjectSourceContainers projectLocator = AdvancedSourceLookupSupport.getWorkspaceJavaProjects(monitor); if (monitor == null && projectLocator == null) { // reschedule to initialize and resolve sources in background AdvancedSourceLookupSupport.schedule((m) -> getSourceContainer(element, refresh, m)); return null; } if (projectLocator == null) { // we get here when projectLocator initialization has been cancelled by the user return null; } // // lookup strategies that provide java project context necessary for debug expression evaluation // // workspace project identified by their runtime classes location is the preferred sources container ISourceContainer projectContainer = projectLocator.createProjectContainer(location); if (projectContainer != null) { return cacheContainer(element, location, projectContainer); } // dependency of one of workspace projects on the call stack also provides java project context for (File frameLocation : jdi.getStackFramesClassesLocations(element)) { ISourceContainer entryContainer = projectLocator.createClasspathEntryContainer(frameLocation, location); if (entryContainer != null) { return cacheContainer(element, location, entryContainer); } } if (monitor == null) { // reschedule to resolve sources in background AdvancedSourceLookupSupport.schedule((m) -> getSourceContainer(element, refresh, m)); return null; } // // strategies that allow source code lookup but do not provide java project context // // checksum-based lookup in various sources (central, nexus, pde target platform, p2 repositories) for (ISourceContainerResolver repository : getSourceContainerResolvers()) { Collection<ISourceContainer> members = repository.resolveSourceContainers(location, monitor); if (members != null && !members.isEmpty()) { return cacheContainer(element, location, CompositeSourceContainer.compose(members)); } } return null; } private ISourceContainer cacheContainer(Object element, File location, ISourceContainer container) { ISourceContainer oldContainer; synchronized (containers) { oldContainer = containers.put(location, container); if (oldContainer != null) { oldContainer.dispose(); } } if (oldContainer != null || container != null) { updateDebugElement(element); } return container; } protected Collection<ISourceContainerResolver> getSourceContainerResolvers() { List<ISourceContainerResolver> result = new ArrayList<>(); IExtensionRegistry registry = Platform.getExtensionRegistry(); IConfigurationElement[] elements = registry.getConfigurationElementsFor(AdvancedSourceLookupSupport.ID_sourceContainerResolvers); for (IConfigurationElement element : elements) { if ("resolver".equals(element.getName())) { //$NON-NLS-1$ try { result.add((ISourceContainerResolver) element.createExecutableExtension("class")); //$NON-NLS-1$ } catch (CoreException e) { } } } return result; } @Override public String getSourceName(Object object) throws CoreException { return null; // default name->source mapping } @Override public void dispose() { disposeContainers(); } @Override public void sourceContainersChanged(ISourceLookupDirector director) { disposeContainers(); } protected void disposeContainers() { synchronized (containers) { for (ISourceContainer container : containers.values()) { if (container != null) // possible for non-maven jars { container.dispose(); } } containers.clear(); } } private void updateDebugElement(Object element) { director.clearSourceElements(element); if (element instanceof DebugElement) { // this is apparently needed to flush StackFrameSourceDisplayAdapter cache ((DebugElement) element).fireChangeEvent(DebugEvent.CONTENT); } } public static AdvancedSourceLookupParticipant getSourceLookup(Object element) { ISourceLocator sourceLocator = null; if (element instanceof IDebugElement) { sourceLocator = ((IDebugElement) element).getLaunch().getSourceLocator(); } AdvancedSourceLookupParticipant sourceLookup = null; if (sourceLocator instanceof ISourceLookupDirector) { for (ISourceLookupParticipant participant : ((ISourceLookupDirector) sourceLocator).getParticipants()) { if (participant instanceof AdvancedSourceLookupParticipant) { sourceLookup = (AdvancedSourceLookupParticipant) participant; break; } } } return sourceLookup; } }