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