001: /*******************************************************************************
002: * Copyright (c) 2003, 2007 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.breakpoints;
011:
012: import java.util.Iterator;
013: import java.util.List;
014: import java.util.ListIterator;
015: import java.util.Map;
016:
017: import org.eclipse.core.resources.IResource;
018: import org.eclipse.core.resources.IWorkspaceRunnable;
019: import org.eclipse.core.runtime.CoreException;
020: import org.eclipse.core.runtime.IProgressMonitor;
021: import org.eclipse.core.runtime.IStatus;
022: import org.eclipse.core.runtime.Status;
023: import org.eclipse.debug.core.DebugException;
024: import org.eclipse.debug.core.DebugPlugin;
025: import org.eclipse.debug.core.IStatusHandler;
026: import org.eclipse.jdt.debug.core.IJavaStratumLineBreakpoint;
027: import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
028: import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
029:
030: import com.sun.jdi.AbsentInformationException;
031: import com.sun.jdi.ClassNotPreparedException;
032: import com.sun.jdi.Location;
033: import com.sun.jdi.NativeMethodException;
034: import com.sun.jdi.ReferenceType;
035: import com.sun.jdi.VMDisconnectedException;
036: import com.sun.jdi.VirtualMachine;
037:
038: /**
039: * A line breakpoint identified by its source file
040: * name and/or path, and stratum that it is relative to.
041: *
042: * @since 3.0
043: */
044: public class JavaStratumLineBreakpoint extends JavaLineBreakpoint
045: implements IJavaStratumLineBreakpoint {
046: private static final String PATTERN = "org.eclipse.jdt.debug.pattern"; //$NON-NLS-1$
047: private static final String STRATUM = "org.eclipse.jdt.debug.stratum"; //$NON-NLS-1$
048: private static final String SOURCE_PATH = "org.eclipse.jdt.debug.source_path"; //$NON-NLS-1$
049: private static final String STRATUM_BREAKPOINT = "org.eclipse.jdt.debug.javaStratumLineBreakpointMarker"; //$NON-NLS-1$
050: private String[] fTypeNamePatterns;
051: // corresponds to type name patterns with beginning/trailing '*' removed
052: private String[] fSuffix;
053: private String[] fPrefix;
054:
055: public JavaStratumLineBreakpoint() {
056: }
057:
058: /**
059: * Creates and returns a line breakpoint identified by its source file
060: * name and/or path, and stratum that it is relative to.
061: *
062: * @param resource the resource on which to create the associated breakpoint
063: * marker
064: * @param stratum the stratum in which the source name, source path and line number
065: * are relative, or <code>null</code>. If <code>null</code> or if the specified stratum
066: * is not defined for a type, the source name, source path and line number are
067: * relative to the type's default stratum.
068: * @param sourceName the simple name of the source file in which the breakpoint is
069: * set, or <code>null</code>. The breakpoint will install itself in classes that have a source
070: * file name debug attribute that matches this value in the specified stratum,
071: * and satisfies the class name pattern and source path attribute. When <code>null</code>,
072: * the source file name debug attribute is not considered.
073: * @param sourcePath the qualified source file name in which the breakpoint is
074: * set, or <code>null</code>. The breakpoint will install itself in classes that
075: * have a source file path in the specified stratum that matches this value, and
076: * satisfies the class name pattern and source name attribute. When <code>null</code>,
077: * the source path attribute is not considered.
078: * @param classNamePattern the class name pattern to which the breakpoint should
079: * be restricted, or <code>null</code>. The breakpoint will install itself in each type that
080: * matches this class name pattern, with a satisfying source name and source path.
081: * Patterns may begin or end with '*', which matches 0 or more characters. A pattern that
082: * does not contain a '*' is equivalent to a pattern ending in '*'. Specifying <code>null</code>,
083: * or an empty string is the equivalent to "*".
084: * @param lineNumber the lineNumber on which the breakpoint is set - line
085: * numbers are 1 based, associated with the source file (stratum) in which
086: * the breakpoint is set
087: * @param charStart the first character index associated with the breakpoint,
088: * or -1 if unspecified, in the source file in which the breakpoint is set
089: * @param charEnd the last character index associated with the breakpoint,
090: * or -1 if unspecified, in the source file in which the breakpoint is set
091: * @param hitCount the number of times the breakpoint will be hit before
092: * suspending execution - 0 if it should always suspend
093: * @param register whether to add this breakpoint to the breakpoint manager
094: * @param attributes a map of client defined attributes that should be assigned
095: * to the underlying breakpoint marker on creation, or <code>null</code> if none.
096: * @return a stratum breakpoint
097: * @exception CoreException If this method fails. Reasons include:<ul>
098: *<li>Failure creating underlying marker. The exception's status contains
099: * the underlying exception responsible for the failure.</li></ul>
100: * @since 3.0
101: */
102: public JavaStratumLineBreakpoint(IResource resource,
103: String stratum, String sourceName, String sourcePath,
104: String classNamePattern, int lineNumber, int charStart,
105: int charEnd, int hitCount, boolean register, Map attributes)
106: throws DebugException {
107: this (resource, stratum, sourceName, sourcePath,
108: classNamePattern, lineNumber, charStart, charEnd,
109: hitCount, register, attributes, STRATUM_BREAKPOINT);
110: }
111:
112: protected JavaStratumLineBreakpoint(final IResource resource,
113: final String stratum, final String sourceName,
114: final String sourcePath, final String classNamePattern,
115: final int lineNumber, final int charStart,
116: final int charEnd, final int hitCount,
117: final boolean register, final Map attributes,
118: final String markerType) throws DebugException {
119: IWorkspaceRunnable wr = new IWorkspaceRunnable() {
120: public void run(IProgressMonitor monitor)
121: throws CoreException {
122:
123: // create the marker
124: setMarker(resource.createMarker(markerType));
125:
126: // modify pattern
127: String pattern = classNamePattern;
128: if (pattern != null && pattern.length() == 0) {
129: pattern = null;
130: }
131:
132: // add attributes
133: addLineBreakpointAttributes(attributes,
134: getModelIdentifier(), true, lineNumber,
135: charStart, charEnd);
136: addStratumPatternAndHitCount(attributes, stratum,
137: sourceName, sourcePath, pattern, hitCount);
138: // set attributes
139: attributes.put(SUSPEND_POLICY, new Integer(
140: getDefaultSuspendPolicy()));
141: ensureMarker().setAttributes(attributes);
142:
143: register(register);
144: }
145: };
146: run(getMarkerRule(resource), wr);
147: }
148:
149: /**
150: * Adds the class name pattern and hit count attributes to the gvien map.
151: */
152: protected void addStratumPatternAndHitCount(Map attributes,
153: String stratum, String sourceName, String sourcePath,
154: String pattern, int hitCount) {
155: attributes.put(PATTERN, pattern);
156: attributes.put(STRATUM, stratum);
157: if (sourceName != null) {
158: attributes.put(SOURCE_NAME, sourceName);
159: }
160: if (sourcePath != null) {
161: attributes.put(SOURCE_PATH, sourcePath);
162: }
163: if (hitCount > 0) {
164: attributes.put(HIT_COUNT, new Integer(hitCount));
165: attributes.put(EXPIRED, Boolean.FALSE);
166: }
167: }
168:
169: /* (non-Javadoc)
170: * @see org.eclipse.jdt.internal.debug.core.breakpoints.JavaBreakpoint#installableReferenceType(com.sun.jdi.ReferenceType, org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget)
171: */
172: protected boolean installableReferenceType(ReferenceType type,
173: JDIDebugTarget target) throws CoreException {
174:
175: // check the type name.
176: String typeName = type.name();
177: if (!validType(typeName)) {
178: return false;
179: }
180: String stratum = getStratum();
181: // check the source name.
182: String bpSourceName = getSourceName();
183: if (bpSourceName != null) {
184: List sourceNames;
185: try {
186: sourceNames = type.sourceNames(stratum);
187: } catch (AbsentInformationException e1) {
188: return false;
189: } catch (VMDisconnectedException e) {
190: if (!target.isAvailable()) {
191: return false;
192: }
193: throw e;
194: }
195: if (!containsMatch(sourceNames, bpSourceName)) {
196: return false;
197: }
198: }
199:
200: String bpSourcePath = getSourcePath();
201: if (bpSourcePath != null) {
202: // check that source paths match
203: List sourcePaths;
204: try {
205: sourcePaths = type.sourcePaths(stratum);
206: } catch (AbsentInformationException e1) {
207: return false;
208: } catch (VMDisconnectedException e) {
209: if (!target.isAvailable()) {
210: return false;
211: }
212: throw e;
213: }
214: if (!containsMatch(sourcePaths, bpSourcePath)) {
215: return false;
216: }
217: }
218: return queryInstallListeners(target, type);
219: }
220:
221: private boolean containsMatch(List strings, String key) {
222: for (Iterator iter = strings.iterator(); iter.hasNext();) {
223: if (((String) iter.next()).equals(key)) {
224: return true;
225: }
226: }
227: return false;
228: }
229:
230: /**
231: * @param typeName
232: * @return
233: */
234: private boolean validType(String typeName) throws CoreException {
235:
236: String[] patterns = getTypeNamePatterns();
237: for (int i = 0; i < patterns.length; i++) {
238: if (fSuffix[i] != null) {
239: // pattern starting with '*'
240: if (fSuffix[i].length() == 0) {
241: return true;
242: }
243: if (typeName.endsWith(fSuffix[i]))
244: return true;
245: } else if (fPrefix[i] != null) {
246: if (typeName.startsWith(fPrefix[i]))
247: return true;
248: } else {
249: if (typeName.startsWith(patterns[i]))
250: return true;
251: }
252: }
253:
254: // return false if we cannot find a type name to match
255: return false;
256: }
257:
258: /**
259: * Returns a list of locations for the given line number in the given type.
260: * Returns <code>null</code> if a location cannot be determined.
261: */
262: protected List determineLocations(int lineNumber,
263: ReferenceType type, JDIDebugTarget target) {
264: List locations;
265: String sourcePath;
266: try {
267: locations = type.locationsOfLine(getStratum(),
268: getSourceName(), lineNumber);
269: sourcePath = getSourcePath();
270: } catch (AbsentInformationException aie) {
271: IStatus status = new Status(
272: IStatus.ERROR,
273: JDIDebugPlugin.getUniqueIdentifier(),
274: NO_LINE_NUMBERS,
275: JDIDebugBreakpointMessages.JavaLineBreakpoint_Absent_Line_Number_Information_1,
276: null);
277: IStatusHandler handler = DebugPlugin.getDefault()
278: .getStatusHandler(status);
279: if (handler != null) {
280: try {
281: handler.handleStatus(status, type);
282: } catch (CoreException e) {
283: }
284: }
285: return null;
286: } catch (NativeMethodException e) {
287: return null;
288: } catch (VMDisconnectedException e) {
289: return null;
290: } catch (ClassNotPreparedException e) {
291: // could be a nested type that is not yet loaded
292: return null;
293: } catch (RuntimeException e) {
294: // not able to retrieve line info
295: target.internalError(e);
296: return null;
297: } catch (CoreException e) {
298: // not able to retrieve line info
299: JDIDebugPlugin.log(e);
300: return null;
301: }
302:
303: if (sourcePath == null) {
304: if (locations.size() > 0) {
305: return locations;
306: }
307: } else {
308: for (ListIterator iter = locations.listIterator(); iter
309: .hasNext();) {
310: Location location = (Location) iter.next();
311: try {
312: if (!sourcePath.equals(location.sourcePath())) {
313: iter.remove();
314: }
315: } catch (AbsentInformationException e1) {
316: // nothing to do;
317: }
318: }
319: if (locations.size() > 0) {
320: return locations;
321: }
322: }
323:
324: return null;
325: }
326:
327: /* (non-Javadoc)
328: * @see org.eclipse.jdt.debug.core.IJavaStratumLineBreakpoint#getPattern()
329: */
330: public String getPattern() throws CoreException {
331: return ensureMarker().getAttribute(PATTERN, "*"); //$NON-NLS-1$
332: }
333:
334: /* (non-Javadoc)
335: * @see org.eclipse.jdt.debug.core.IJavaStratumLineBreakpoint#getSourceName()
336: */
337: public String getSourceName() throws CoreException {
338: return (String) ensureMarker().getAttribute(SOURCE_NAME);
339: }
340:
341: /* (non-Javadoc)
342: * @see org.eclipse.jdt.debug.core.IJavaStratumLineBreakpoint#getStratum()
343: */
344: public String getStratum() throws CoreException {
345: return (String) ensureMarker().getAttribute(STRATUM);
346: }
347:
348: /* (non-Javadoc)
349: * @see org.eclipse.jdt.debug.core.IJavaStratumLineBreakpoint#getSourcePath()
350: */
351: public String getSourcePath() throws CoreException {
352: return (String) ensureMarker().getAttribute(SOURCE_PATH);
353: }
354:
355: protected void createRequests(JDIDebugTarget target)
356: throws CoreException {
357: if (target.isTerminated() || shouldSkipBreakpoint()) {
358: return;
359: }
360:
361: String[] patterns = null;
362: try {
363: patterns = getTypeNamePatterns();
364: } catch (CoreException e1) {
365: JDIDebugPlugin.log(e1);
366: return;
367: }
368:
369: String sourceName = getSourceName();
370: for (int i = 0; i < patterns.length; i++) {
371: String classPrepareTypeName = patterns[i];
372: // create request to listen to class loads
373: //name may only be partially resolved
374: registerRequest(target.createClassPrepareRequest(
375: classPrepareTypeName, null, true, sourceName),
376: target);
377: }
378:
379: // create breakpoint requests for each class currently loaded
380: VirtualMachine vm = target.getVM();
381: if (vm == null) {
382: target
383: .requestFailed(
384: JDIDebugBreakpointMessages.JavaPatternBreakpoint_Unable_to_add_breakpoint___VM_disconnected__1,
385: null);
386: }
387: List classes = null;
388: try {
389: classes = vm.allClasses();
390: } catch (RuntimeException e) {
391: target.targetRequestFailed(
392: JDIDebugBreakpointMessages.JavaPatternBreakpoint_0,
393: e);
394: }
395: if (classes != null) {
396: Iterator iter = classes.iterator();
397: while (iter.hasNext()) {
398: ReferenceType type = (ReferenceType) iter.next();
399: if (installableReferenceType(type, target)) {
400: createRequest(target, type);
401: }
402: }
403: }
404: }
405:
406: public synchronized String[] getTypeNamePatterns()
407: throws CoreException {
408: if (fTypeNamePatterns != null)
409: return fTypeNamePatterns;
410:
411: String patterns = getPattern();
412:
413: // delimit by ","
414: fTypeNamePatterns = patterns.split(","); //$NON-NLS-1$
415: fSuffix = new String[fTypeNamePatterns.length];
416: fPrefix = new String[fTypeNamePatterns.length];
417: for (int i = 0; i < fTypeNamePatterns.length; i++) {
418: fTypeNamePatterns[i] = fTypeNamePatterns[i].trim();
419: String pattern = fTypeNamePatterns[i];
420: if (pattern.charAt(0) == '*') {
421: if (pattern.length() > 1) {
422: fSuffix[i] = pattern.substring(1);
423: } else {
424: fSuffix[i] = ""; //$NON-NLS-1$
425: }
426: } else if (pattern.charAt(pattern.length() - 1) == '*') {
427: fPrefix[i] = pattern.substring(0, pattern.length() - 1);
428: }
429: }
430:
431: return fTypeNamePatterns;
432: }
433:
434: }
|