1 /******************************************************************************* 2 * Copyright (c) 2000, 2015 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.debug.ui; 15 16 17 import java.util.HashMap; 18 19 import org.eclipse.core.runtime.CoreException; 20 import org.eclipse.debug.core.DebugException; 21 import org.eclipse.debug.core.ILaunchConfiguration; 22 import org.eclipse.debug.core.model.IPersistableSourceLocator; 23 import org.eclipse.debug.core.model.IStackFrame; 24 import org.eclipse.jdt.core.IJavaProject; 25 import org.eclipse.jdt.core.JavaCore; 26 import org.eclipse.jdt.debug.core.IJavaReferenceType; 27 import org.eclipse.jdt.debug.core.IJavaStackFrame; 28 import org.eclipse.jdt.debug.core.IJavaThread; 29 import org.eclipse.jdt.internal.debug.ui.DebugUIMessages; 30 import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin; 31 import org.eclipse.jdt.internal.debug.ui.launcher.LauncherMessages; 32 import org.eclipse.jdt.internal.debug.ui.launcher.SourceElementLabelProvider; 33 import org.eclipse.jdt.internal.debug.ui.launcher.SourceElementQualifierProvider; 34 import org.eclipse.jdt.launching.JavaRuntime; 35 import org.eclipse.jdt.launching.sourcelookup.IJavaSourceLocation; 36 import org.eclipse.jdt.launching.sourcelookup.JavaSourceLocator; 37 import org.eclipse.jface.window.Window; 38 import org.eclipse.osgi.util.NLS; 39 import org.eclipse.ui.dialogs.TwoPaneElementSelector; 40 41 /** 42 * A source locator that prompts the user to find source when source cannot 43 * be found on the current source lookup path. 44 * <p> 45 * This class is intended to be instantiated. 46 * </p> 47 * @since 2.0 48 * @deprecated In 3.0, the debug platform provides source lookup facilities that 49 * should be used in place of the Java source lookup support provided in 2.0. 50 * The new facilities provide a source lookup director that coordinates source 51 * lookup among a set of participants, searching a set of source containers. 52 * See the following packages: <code>org.eclipse.debug.core.sourcelookup</code> 53 * and <code>org.eclipse.debug.core.sourcelookup.containers</code>. This class 54 * has been replaced by a Java source lookup director and Java source lookup 55 * participant. To migrate to the new source lookup support clients should 56 * add two new attributes to their launch configuration type extensions:<ul> 57 * <li>sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"</li> 58 * <li>sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer"</li> 59 * </ul> 60 * The source locator id attribute specifies to use the Java source lookup director 61 * for launch configurations of the associated type, and the source path computer id 62 * attribute specifies the class to use when computing a default source lookup 63 * path for a launch configuration. The path computer referenced/provided (by the 64 * above id), computes a default source lookup path based on the support provided in 65 * the 2.0 release - i.e. a configuration's <code>ATTR_SOURCE_PATH_PROVIDER</code> 66 * attribute (if present), or a default source lookup path based on a configuration's 67 * runtime classpath. This class has been replaced by the Java source lookup 68 * director which is an internal class, but can be used via the 69 * <code>sourceLocatorId</code> attribute on a launch configuration type extension. 70 * @noextend This class is not intended to be sub-classed by clients. 71 */ 72 73 @Deprecated 74 public class JavaUISourceLocator implements IPersistableSourceLocator { 75 76 /** 77 * Identifier for the 'Prompting Java Source Locator' extension 78 * (value <code>"org.eclipse.jdt.debug.ui.javaSourceLocator"</code>). 79 */ 80 public static final String ID_PROMPTING_JAVA_SOURCE_LOCATOR = IJavaDebugUIConstants.PLUGIN_ID + ".javaSourceLocator"; //$NON-NLS-1$ 81 82 /** 83 * Launch configuration attribute indicating that this source locator should 84 * locate all source elements that correspond to a stack frame, rather than 85 * the first match. Default value is <code>false</code>. 86 * 87 * @since 2.1 88 */ 89 public static final String ATTR_FIND_ALL_SOURCE_ELEMENTS = IJavaDebugUIConstants.PLUGIN_ID + ".ATTR_FIND_ALL_SOURCE_ELEMENTS"; //$NON-NLS-1$ 90 91 /** 92 * The project being debugged. 93 */ 94 private IJavaProject fJavaProject; 95 96 /** 97 * Underlying source locator. 98 */ 99 private JavaSourceLocator fSourceLocator; 100 101 /** 102 * Whether the user should be prompted for source. 103 * Initially true, until the user checks the 'do not 104 * ask again' box. 105 */ 106 private boolean fAllowedToAsk; 107 108 /** 109 * Whether to find all source elements for a stack frame (in case of 110 * duplicates), or just the first match. 111 */ 112 private boolean fIsFindAllSourceElements = false; 113 114 /** 115 * A cache of types to associated source elements (when duplicates arise and 116 * the users chooses a source element, it is remembered). 117 */ 118 private HashMap<IJavaReferenceType, Object> fTypesToSource = null; 119 120 /** 121 * Constructs an empty source locator. 122 */ JavaUISourceLocator()123 public JavaUISourceLocator() { 124 fSourceLocator = new JavaSourceLocator(); 125 fAllowedToAsk = true; 126 } 127 128 /** 129 * Constructs a new source locator that looks in the 130 * specified project for source, and required projects, if 131 * <code>includeRequired</code> is <code>true</code>. 132 * 133 * @param projects the projects in which to look for source 134 * @param includeRequired whether to look in required projects 135 * as well 136 * @throws CoreException if the underlying {@link JavaSourceLocator} fails to be created 137 */ JavaUISourceLocator(IJavaProject[] projects, boolean includeRequired)138 public JavaUISourceLocator(IJavaProject[] projects, boolean includeRequired) throws CoreException { 139 fSourceLocator = new JavaSourceLocator(projects, includeRequired); 140 fAllowedToAsk = true; 141 } 142 143 /** 144 * Constructs a source locator that searches for source 145 * in the given Java project, and all of its required projects, 146 * as specified by its build path or default source lookup 147 * settings. 148 * 149 * @param project Java project 150 * @exception CoreException if unable to read the project's 151 * build path 152 */ JavaUISourceLocator(IJavaProject project)153 public JavaUISourceLocator(IJavaProject project) throws CoreException { 154 fJavaProject = project; 155 IJavaSourceLocation[] sls = 156 JavaSourceLocator.getDefaultSourceLocations(project); 157 fSourceLocator = new JavaSourceLocator(project); 158 if (sls != null) { 159 fSourceLocator.setSourceLocations(sls); 160 } 161 fAllowedToAsk = true; 162 } 163 164 /** 165 * @see org.eclipse.debug.core.model.ISourceLocator#getSourceElement(IStackFrame) 166 */ 167 @Override getSourceElement(IStackFrame stackFrame)168 public Object getSourceElement(IStackFrame stackFrame) { 169 Object res = findSourceElement(stackFrame); 170 if (res == null && fAllowedToAsk) { 171 IJavaStackFrame frame = 172 stackFrame.getAdapter(IJavaStackFrame.class); 173 if (frame != null) { 174 try { 175 if (!frame.isObsolete()) { 176 showDebugSourcePage(frame); 177 res = fSourceLocator.getSourceElement(stackFrame); 178 } 179 } catch (DebugException e) { 180 } 181 } 182 } 183 return res; 184 } 185 findSourceElement(IStackFrame stackFrame)186 private Object findSourceElement(IStackFrame stackFrame) { 187 if (isFindAllSourceElements()) { 188 Object[] sourceElements = fSourceLocator.getSourceElements(stackFrame); 189 if (sourceElements == null || sourceElements.length == 0) { 190 return null; 191 } 192 if (sourceElements.length == 1) { 193 return sourceElements[0]; 194 } 195 try { 196 IJavaStackFrame frame = (IJavaStackFrame)stackFrame; 197 IJavaReferenceType type = frame.getReferenceType(); 198 Object cachedSource = getSourceElement(type); 199 if (cachedSource != null) { 200 return cachedSource; 201 } 202 // prompt 203 TwoPaneElementSelector dialog = new TwoPaneElementSelector(JDIDebugUIPlugin.getActiveWorkbenchShell(), new SourceElementLabelProvider(),new SourceElementQualifierProvider()); 204 dialog.setTitle(DebugUIMessages.JavaUISourceLocator_Select_Source_1); 205 dialog.setMessage(NLS.bind(DebugUIMessages.JavaUISourceLocator__Select_the_source_that_corresponds_to__0__2, new String[]{type.getName()})); 206 dialog.setElements(sourceElements); 207 dialog.setMultipleSelection(false); 208 dialog.setUpperListLabel(DebugUIMessages.JavaUISourceLocator__Matching_files__3); 209 dialog.setLowerListLabel(DebugUIMessages.JavaUISourceLocator__Location__4); 210 dialog.open(); 211 Object[] result = dialog.getResult(); 212 if (result == null) { 213 return null; 214 } 215 Object sourceElement = result[0]; 216 cacheSourceElement(sourceElement, type); 217 return sourceElement; 218 } catch (CoreException e) { 219 JDIDebugUIPlugin.log(e); 220 return sourceElements[0]; 221 } 222 } 223 return fSourceLocator.getSourceElement(stackFrame); 224 } 225 getSourceElement(IJavaReferenceType type)226 private Object getSourceElement(IJavaReferenceType type) { 227 if (fTypesToSource == null) { 228 return null; 229 } 230 return fTypesToSource.get(type); 231 } 232 cacheSourceElement(Object sourceElement, IJavaReferenceType type)233 private void cacheSourceElement(Object sourceElement, IJavaReferenceType type) { 234 if (fTypesToSource == null) { 235 fTypesToSource = new HashMap<>(); 236 } 237 fTypesToSource.put(type, sourceElement); 238 } 239 240 /** 241 * Prompts to locate the source of the given type. Prompts in the UI 242 * thread, since a source lookup could be the result of a conditional 243 * breakpoint looking up source for an evaluation, from the event 244 * dispatch thread. 245 * @param frame the stack frame to show source for 246 * could not be located 247 */ showDebugSourcePage(final IJavaStackFrame frame)248 private void showDebugSourcePage(final IJavaStackFrame frame) { 249 Runnable prompter = new Runnable() { 250 @Override 251 public void run() { 252 try { 253 String message = NLS.bind(LauncherMessages.JavaUISourceLocator_selectprojects_message, new String[] {frame.getDeclaringTypeName()}); 254 255 ILaunchConfiguration configuration = 256 frame.getLaunch().getLaunchConfiguration(); 257 JavaSourceLookupDialog dialog = 258 new JavaSourceLookupDialog( 259 JDIDebugUIPlugin.getActiveWorkbenchShell(), 260 message, 261 configuration); 262 int result = dialog.open(); 263 if (result == Window.OK) { 264 fAllowedToAsk = !dialog.isNotAskAgain(); 265 JavaUISourceLocator.this.initializeDefaults( 266 configuration); 267 } 268 } catch (CoreException e) { 269 // only report an error if the thread has not resumed 270 if (e.getStatus().getCode() 271 != IJavaThread.ERR_THREAD_NOT_SUSPENDED) { 272 JDIDebugUIPlugin.log(e); 273 } 274 } 275 } 276 }; 277 JDIDebugUIPlugin.getStandardDisplay().syncExec(prompter); 278 } 279 280 /** 281 * @see IPersistableSourceLocator#getMemento() 282 */ 283 @Override getMemento()284 public String getMemento() throws CoreException { 285 String memento = fSourceLocator.getMemento(); 286 String handle = fJavaProject.getHandleIdentifier(); 287 String findAll = Boolean.valueOf(isFindAllSourceElements()).toString(); 288 289 StringBuilder buffer = new StringBuilder(); 290 buffer.append("<project>"); //$NON-NLS-1$ 291 buffer.append(handle); 292 buffer.append("</project>"); //$NON-NLS-1$ 293 buffer.append("<findAll>"); //$NON-NLS-1$ 294 buffer.append(findAll); 295 buffer.append("</findAll>"); //$NON-NLS-1$ 296 buffer.append(memento); 297 return buffer.toString(); 298 } 299 300 /** 301 * @see IPersistableSourceLocator#initializeDefaults(ILaunchConfiguration) 302 */ 303 @Override initializeDefaults(ILaunchConfiguration configuration)304 public void initializeDefaults(ILaunchConfiguration configuration) 305 throws CoreException { 306 fSourceLocator.initializeDefaults(configuration); 307 fJavaProject = JavaRuntime.getJavaProject(configuration); 308 fIsFindAllSourceElements = 309 configuration.getAttribute(ATTR_FIND_ALL_SOURCE_ELEMENTS, false); 310 } 311 312 /** 313 * @see IPersistableSourceLocator#initializeFromMemento(String) 314 */ 315 @Override initializeFromMemento(String memento)316 public void initializeFromMemento(String memento) throws CoreException { 317 if (memento.startsWith("<project>")) { //$NON-NLS-1$ 318 int index = memento.indexOf("</project>"); //$NON-NLS-1$ 319 if (index > 0) { 320 String handle = memento.substring(9, index); 321 int start = index + 19; 322 index = memento.indexOf("</findAll>", start); //$NON-NLS-1$ 323 if (index > 0) { 324 String findAll = memento.substring(start, index); 325 Boolean all = Boolean.valueOf(findAll); 326 String rest = memento.substring(index + 10); 327 fJavaProject = (IJavaProject) JavaCore.create(handle); 328 fIsFindAllSourceElements = all.booleanValue(); 329 fSourceLocator.initializeFromMemento(rest); 330 } 331 } 332 } else { 333 // OLD FORMAT 334 int index = memento.indexOf('\n'); 335 String handle = memento.substring(0, index); 336 String rest = memento.substring(index + 1); 337 fJavaProject = (IJavaProject) JavaCore.create(handle); 338 fIsFindAllSourceElements = false; 339 fSourceLocator.initializeFromMemento(rest); 340 } 341 } 342 343 /** 344 * Returns the locations that this source locator is currently 345 * searching, in the order that they are searched. 346 * 347 * @return the locations that this source locator is currently 348 * searching, in the order that they are searched 349 */ getSourceLocations()350 public IJavaSourceLocation[] getSourceLocations() { 351 return fSourceLocator.getSourceLocations(); 352 } 353 354 /** 355 * /** 356 * Sets the locations that will be searched, in the order 357 * to be searched. 358 * 359 * @param locations the locations that will be searched, in the order 360 * to be searched 361 */ setSourceLocations(IJavaSourceLocation[] locations)362 public void setSourceLocations(IJavaSourceLocation[] locations) { 363 fSourceLocator.setSourceLocations(locations); 364 } 365 366 /** 367 * Returns whether this source locator is configured to search for all 368 * source elements that correspond to a stack frame. When <code>false</code> 369 * is returned, searching stops on the first match. If there is more than 370 * one source element that corresponds to a stack frame, the user is 371 * prompted to choose a source element to open. 372 * 373 * @return whether this source locator is configured to search for all 374 * source elements that correspond to a stack frame 375 * @since 2.1 376 */ isFindAllSourceElements()377 public boolean isFindAllSourceElements() { 378 return fIsFindAllSourceElements; 379 } 380 381 /** 382 * Sets whether this source locator is configured to search for all source 383 * elements that correspond to a stack frame, or the first match. 384 * 385 * @param findAll whether this source locator should search for all source 386 * elements that correspond to a stack frame 387 * @since 2.1 388 */ setFindAllSourceElement(boolean findAll)389 public void setFindAllSourceElement(boolean findAll) { 390 fIsFindAllSourceElements = findAll; 391 } 392 393 } 394