001: /*******************************************************************************
002: * Copyright (c) 2005, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.debug.core;
011:
012: import java.io.File;
013: import java.util.ArrayList;
014: import java.util.List;
015:
016: import org.eclipse.core.resources.IResource;
017: import org.eclipse.core.runtime.CoreException;
018: import org.eclipse.core.runtime.IAdaptable;
019: import org.eclipse.core.runtime.IPath;
020: import org.eclipse.core.runtime.Path;
021: import org.eclipse.debug.core.DebugException;
022: import org.eclipse.debug.core.ILaunch;
023: import org.eclipse.debug.core.model.ISourceLocator;
024: import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
025: import org.eclipse.jdt.core.IClassFile;
026: import org.eclipse.jdt.core.ICompilationUnit;
027: import org.eclipse.jdt.core.IJavaElement;
028: import org.eclipse.jdt.core.IJavaProject;
029: import org.eclipse.jdt.core.IType;
030: import org.eclipse.jdt.core.JavaCore;
031: import org.eclipse.jdt.debug.core.IJavaReferenceType;
032: import org.eclipse.jdt.debug.core.IJavaStackFrame;
033: import org.eclipse.jdt.debug.core.IJavaThread;
034: import org.eclipse.jdt.debug.core.IJavaType;
035: import org.eclipse.jdt.debug.core.IJavaValue;
036:
037: import com.sun.jdi.VMDisconnectedException;
038:
039: /**
040: * A Utilities class.
041: *
042: * @since 3.2
043: */
044: public class JavaDebugUtils {
045:
046: /**
047: * Resolves and returns a type from the Java model that corresponds to the
048: * declaring type of the given stack frame, or <code>null</code> if none.
049: *
050: * @param frame frame to resolve declaring type for
051: * @return corresponding Java model type or <code>null</code>
052: * @exception CoreException if an exception occurs during the resolution
053: * @since 3.2
054: */
055: public static IType resolveDeclaringType(IJavaStackFrame frame)
056: throws CoreException {
057: IJavaElement javaElement = resolveJavaElement(frame, frame
058: .getLaunch());
059: if (javaElement != null) {
060: return resolveType(frame.getDeclaringTypeName(),
061: javaElement);
062: }
063: return null;
064: }
065:
066: /**
067: * Resolves and returns a type from the Java model that corresponds to the
068: * type of the given value, or <code>null</code> if none.
069: *
070: * @param value value to resolve type for
071: * @return corresponding Java model type or <code>null</code>
072: * @exception CoreException if an exception occurs during the resolution
073: */
074: public static IType resolveType(IJavaValue value)
075: throws CoreException {
076: IJavaElement javaElement = resolveJavaElement(value, value
077: .getLaunch());
078: if (javaElement != null) {
079: return resolveType(value.getJavaType().getName(),
080: javaElement);
081: }
082: return null;
083: }
084:
085: /**
086: * Resolves and returns the Java model type associated with the given
087: * Java debug type, or <code>null</code> if none.
088: *
089: * @param type Java debug model type
090: * @return Java model type or <code>null</code>
091: * @throws CoreException
092: */
093: public static IType resolveType(IJavaType type)
094: throws CoreException {
095: IJavaElement element = resolveJavaElement(type, type
096: .getLaunch());
097: if (element != null) {
098: return resolveType(type.getName(), element);
099: }
100: return null;
101: }
102:
103: /**
104: * Returns the source name associated with the given object, or <code>null</code>
105: * if none.
106: *
107: * @param object an object with an <code>IJavaStackFrame</code> adapter, an IJavaValue
108: * or an IJavaType
109: * @return the source name associated with the given object, or <code>null</code>
110: * if none
111: * @exception CoreException if unable to retrieve the source name
112: */
113: public static String getSourceName(Object object)
114: throws CoreException {
115: if (object instanceof String) {
116: // assume it's a file name
117: return (String) object;
118: }
119: IJavaStackFrame frame = null;
120: if (object instanceof IAdaptable) {
121: frame = (IJavaStackFrame) ((IAdaptable) object)
122: .getAdapter(IJavaStackFrame.class);
123: }
124: String typeName = null;
125: try {
126: if (frame != null) {
127: if (frame.isObsolete()) {
128: return null;
129: }
130: String sourceName = frame.getSourcePath();
131: // TODO: this may break fix to bug 21518
132: if (sourceName == null) {
133: // no debug attributes, guess at source name
134: typeName = frame.getDeclaringTypeName();
135: } else {
136: return sourceName;
137: }
138: } else {
139: if (object instanceof IJavaValue) {
140: // look at its type
141: object = ((IJavaValue) object).getJavaType();
142: }
143: if (object instanceof IJavaReferenceType) {
144: IJavaReferenceType refType = (IJavaReferenceType) object;
145: String[] sourcePaths = refType.getSourcePaths(null);
146: if (sourcePaths != null && sourcePaths.length > 0) {
147: return sourcePaths[0];
148: }
149: }
150: if (object instanceof IJavaType) {
151: typeName = ((IJavaType) object).getName();
152: }
153: }
154: } catch (DebugException e) {
155: int code = e.getStatus().getCode();
156: if (code == IJavaThread.ERR_THREAD_NOT_SUSPENDED
157: || code == IJavaStackFrame.ERR_INVALID_STACK_FRAME
158: || e.getStatus().getException() instanceof VMDisconnectedException) {
159: return null;
160: }
161: throw e;
162: }
163: if (typeName != null) {
164: return generateSourceName(typeName);
165: }
166: return null;
167: }
168:
169: /**
170: * Generates and returns a source file path based on a qualified type name.
171: * For example, when <code>java.lang.String</code> is provided,
172: * the returned source name is <code>java/lang/String.java</code>.
173: *
174: * @param qualifiedTypeName fully qualified type name that may contain inner types
175: * denoted with <code>$</code> character
176: * @return a source file path corresponding to the type name
177: */
178: public static String generateSourceName(String qualifiedTypeName) {
179: int index = qualifiedTypeName.lastIndexOf('.');
180: if (index < 0) {
181: index = 0;
182: }
183: qualifiedTypeName = qualifiedTypeName.replace('.',
184: File.separatorChar);
185: index = qualifiedTypeName.indexOf('$');
186: if (index >= 0) {
187: qualifiedTypeName = qualifiedTypeName.substring(0, index);
188: }
189: if (qualifiedTypeName.length() == 0) {
190: // likely a proxy class (see bug 40815)
191: qualifiedTypeName = null;
192: } else {
193: qualifiedTypeName = qualifiedTypeName + ".java"; //$NON-NLS-1$
194: }
195: return qualifiedTypeName;
196: }
197:
198: /**
199: * Resolves the type corresponding to the given name contained in the given top-level
200: * Java element (class file, compilation unit, or type).
201: *
202: * @param qualifiedName fully qualified type name
203: * @param javaElement java element containing the type
204: * @return type
205: */
206: private static IType resolveType(String qualifiedName,
207: IJavaElement javaElement) {
208: IType type = null;
209: String[] typeNames = getNestedTypeNames(qualifiedName);
210: if (javaElement instanceof IClassFile) {
211: type = ((IClassFile) javaElement).getType();
212: } else if (javaElement instanceof ICompilationUnit) {
213: type = ((ICompilationUnit) javaElement)
214: .getType(typeNames[0]);
215: } else if (javaElement instanceof IType) {
216: type = (IType) javaElement;
217: }
218: if (type != null) {
219: for (int i = 1; i < typeNames.length; i++) {
220: String innerTypeName = typeNames[i];
221: try {
222: Integer.parseInt(innerTypeName);
223: return type;
224: } catch (NumberFormatException e) {
225: }
226: type = type.getType(innerTypeName);
227: }
228: }
229: return type;
230: }
231:
232: /**
233: * Returns the Java element corresponding to the given object or <code>null</code>
234: * if none, in the context of the given launch.
235: *
236: * @param launch provides source locator
237: * @param object object to resolve Java model element for
238: * @return corresponding Java element or <code>null</code>
239: * @throws CoreException
240: */
241: private static IJavaElement resolveJavaElement(Object object,
242: ILaunch launch) throws CoreException {
243: Object sourceElement = resolveSourceElement(object, launch);
244: IJavaElement javaElement = null;
245: if (sourceElement instanceof IJavaElement) {
246: javaElement = (IJavaElement) sourceElement;
247: } else if (sourceElement instanceof IAdaptable) {
248: javaElement = (IJavaElement) ((IAdaptable) sourceElement)
249: .getAdapter(IJavaElement.class);
250: }
251: if (javaElement == null && sourceElement instanceof IResource) {
252: javaElement = JavaCore.create((IResource) sourceElement);
253: }
254: if (javaElement == null) {
255: return null;
256: }
257: if (!javaElement.exists()) {
258: return null;
259: }
260: return javaElement;
261: }
262:
263: /**
264: * Returns the source element corresponding to the given object or <code>null</code>
265: * if none, in the context of the given launch.
266: *
267: * @param launch provides source locator
268: * @param object object to resolve source element for
269: * @return corresponding source element or <code>null</code>
270: * @throws CoreException
271: */
272: public static Object resolveSourceElement(Object object,
273: ILaunch launch) throws CoreException {
274: ISourceLocator sourceLocator = launch.getSourceLocator();
275: if (sourceLocator instanceof ISourceLookupDirector) {
276: ISourceLookupDirector director = (ISourceLookupDirector) sourceLocator;
277: Object[] objects = director.findSourceElements(object);
278: if (objects.length > 0) {
279: return objects[0];
280: }
281: }
282: return null;
283: }
284:
285: /**
286: * Returns an array of simple type names that are
287: * part of the given type's qualified name. For
288: * example, if the given name is <code>x.y.A$B</code>,
289: * an array with <code>["A", "B"]</code> is returned.
290: *
291: * @param typeName fully qualified type name
292: * @return array of nested type names
293: */
294: private static String[] getNestedTypeNames(String typeName) {
295: int index = typeName.lastIndexOf('.');
296: if (index >= 0) {
297: typeName = typeName.substring(index + 1);
298: }
299: index = typeName.indexOf('$');
300: List list = new ArrayList(1);
301: while (index >= 0) {
302: list.add(typeName.substring(0, index));
303: typeName = typeName.substring(index + 1);
304: index = typeName.indexOf('$');
305: }
306: list.add(typeName);
307: return (String[]) list.toArray(new String[list.size()]);
308: }
309:
310: /**
311: * Returns the class file or compilation unit containing the given fully qualified name in the
312: * specified project. All registered java like file extensions are considered.
313: *
314: * @param qualifiedTypeName fully qualified type name
315: * @param project project to search in
316: * @return class file or compilation unit or <code>null</code>
317: */
318: public static IJavaElement findElement(String qualifiedTypeName,
319: IJavaProject project) throws CoreException {
320: String[] javaLikeExtensions = JavaCore.getJavaLikeExtensions();
321: String path = qualifiedTypeName;
322: int pos = path.indexOf('$');
323: if (pos != -1) {
324: path = path.substring(0, pos);
325: }
326: path = path.replace('.', IPath.SEPARATOR);
327: path += "."; //$NON-NLS-1$
328: for (int i = 0; i < javaLikeExtensions.length; i++) {
329: String ext = javaLikeExtensions[i];
330: IJavaElement element = project.findElement(new Path(path
331: + ext));
332: if (element != null) {
333: return element;
334: }
335: }
336: return null;
337: }
338: }
|