001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.api.debugger.jpda;
043:
044: import java.net.MalformedURLException;
045: import java.net.URL;
046: import java.util.Map;
047: import java.util.WeakHashMap;
048: import javax.swing.event.ChangeEvent;
049: import javax.swing.event.ChangeListener;
050: import org.netbeans.api.debugger.Breakpoint;
051: import org.netbeans.api.debugger.DebuggerManager;
052: import org.openide.ErrorManager;
053: import org.openide.filesystems.FileAttributeEvent;
054: import org.openide.filesystems.FileChangeListener;
055: import org.openide.filesystems.FileEvent;
056: import org.openide.filesystems.FileObject;
057: import org.openide.filesystems.FileRenameEvent;
058: import org.openide.filesystems.FileStateInvalidException;
059: import org.openide.filesystems.URLMapper;
060: import org.openide.util.WeakListeners;
061:
062: /**
063: * Notifies about line breakpoint events.
064: *
065: * <br><br>
066: * <b>How to use it:</b>
067: * <pre style="background-color: rgb(255, 255, 153);">
068: * DebuggerManager.addBreakpoint (LineBreakpoint.create (
069: * "examples.texteditor.Ted",
070: * 27
071: * ));</pre>
072: * This breakpoint stops in Ted class on 27 line number.
073: *
074: * @author Jan Jancura
075: */
076: public class LineBreakpoint extends JPDABreakpoint {
077:
078: /** Property name constant */
079: public static final String PROP_LINE_NUMBER = "lineNumber"; // NOI18N
080: /** Property name constant */
081: public static final String PROP_URL = "url"; // NOI18N
082: /** Property name constant. */
083: public static final String PROP_CONDITION = "condition"; // NOI18N
084: /** Property name constant. */
085: public static final String PROP_SOURCE_NAME = "sourceName"; // NOI18N
086: /** Property name constant. */
087: public static final String PROP_SOURCE_PATH = "sourcePath"; // NOI18N
088: /** Property name constant. */
089: public static final String PROP_STRATUM = "stratum"; // NOI18N
090: /** Property name constant. */
091: public static final String PROP_PREFERRED_CLASS_NAME = "classNamePreferred"; // NOI18N
092: /** Property name constant */
093: public static final String PROP_INSTANCE_FILTERS = "instanceFilters"; // NOI18N
094: /** Property name constant */
095: public static final String PROP_THREAD_FILTERS = "threadFilters"; // NOI18N
096:
097: private String url = ""; // NOI18N
098: private int lineNumber;
099: private String condition = ""; // NOI18N
100: private String sourceName = null;
101: private String sourcePath = null;
102: private String stratum = "Java"; // NOI18N
103: private String className = null;
104: private Map<JPDADebugger, ObjectVariable[]> instanceFilters;
105: private Map<JPDADebugger, JPDAThread[]> threadFilters;
106:
107: private LineBreakpoint(String url) {
108: this .url = url;
109: }
110:
111: /**
112: * Creates a new breakpoint for given parameters.
113: *
114: * @param url a string representation of URL of the source file
115: * @param lineNumber a line number
116: * @return a new breakpoint for given parameters
117: */
118: public static LineBreakpoint create(String url, int lineNumber) {
119: LineBreakpoint b = new LineBreakpointImpl(url);
120: b.setLineNumber(lineNumber);
121: return b;
122: }
123:
124: /**
125: * Gets the string representation of URL of the source file,
126: * which contains the class to stop on.
127: *
128: * @return name of class to stop on
129: */
130: public String getURL() {
131: return url;
132: }
133:
134: /**
135: * Sets the string representation of URL of the source file,
136: * which contains the class to stop on.
137: *
138: * @param url the URL of class to stop on
139: */
140: public void setURL(String url) {
141: String old;
142: synchronized (this ) {
143: if (url == null)
144: url = "";
145: if ((url == this .url)
146: || ((url != null) && (this .url != null) && url
147: .equals(this .url)))
148: return;
149: old = this .url;
150: this .url = url;
151: }
152: firePropertyChange(PROP_URL, old, url);
153: }
154:
155: /**
156: * Gets number of line to stop on.
157: *
158: * @return line number to stop on
159: */
160: public int getLineNumber() {
161: return lineNumber;
162: }
163:
164: /**
165: * Sets number of line to stop on.
166: *
167: * @param ln a line number to stop on
168: */
169: public void setLineNumber(int ln) {
170: int old;
171: synchronized (this ) {
172: if (ln == lineNumber)
173: return;
174: old = lineNumber;
175: lineNumber = ln;
176: }
177: firePropertyChange(PROP_LINE_NUMBER, new Integer(old),
178: new Integer(ln));
179: }
180:
181: /**
182: * Get the instance filter for a specific debugger session.
183: * @return The instances or <code>null</code> when there is no instance restriction.
184: */
185: public ObjectVariable[] getInstanceFilters(JPDADebugger session) {
186: if (instanceFilters != null) {
187: return instanceFilters.get(session);
188: } else {
189: return null;
190: }
191: }
192:
193: /**
194: * Set the instance filter for a specific debugger session. This restricts
195: * the breakpoint to specific instances in that session.
196: * @param session the debugger session
197: * @param instances the object instances or <code>null</code> to unset the filter.
198: */
199: public void setInstanceFilters(JPDADebugger session,
200: ObjectVariable[] instances) {
201: if (instanceFilters == null) {
202: instanceFilters = new WeakHashMap<JPDADebugger, ObjectVariable[]>();
203: }
204: if (instances != null) {
205: instanceFilters.put(session, instances);
206: } else {
207: instanceFilters.remove(session);
208: }
209: firePropertyChange(PROP_INSTANCE_FILTERS, null,
210: instances != null ? new Object[] { session, instances }
211: : null);
212: }
213:
214: /**
215: * Get the thread filter for a specific debugger session.
216: * @return The thread or <code>null</code> when there is no thread restriction.
217: */
218: public JPDAThread[] getThreadFilters(JPDADebugger session) {
219: if (threadFilters != null) {
220: return threadFilters.get(session);
221: } else {
222: return null;
223: }
224: }
225:
226: /**
227: * Set the thread filter for a specific debugger session. This restricts
228: * the breakpoint to specific threads in that session.
229: * @param session the debugger session
230: * @param threads the threads or <code>null</code> to unset the filter.
231: */
232: public void setThreadFilters(JPDADebugger session,
233: JPDAThread[] threads) {
234: if (threadFilters == null) {
235: threadFilters = new WeakHashMap<JPDADebugger, JPDAThread[]>();
236: }
237: if (threads != null) {
238: threadFilters.put(session, threads);
239: } else {
240: threadFilters.remove(session);
241: }
242: firePropertyChange(PROP_THREAD_FILTERS, null,
243: threads != null ? new Object[] { session, threads }
244: : null);
245: }
246:
247: /**
248: * Returns condition.
249: *
250: * @return cond a condition
251: */
252: public String getCondition() {
253: return condition;
254: }
255:
256: /**
257: * Sets condition.
258: *
259: * @param c a new condition
260: */
261: public void setCondition(String c) {
262: String old;
263: synchronized (this ) {
264: if (c == null)
265: c = "";
266: c = c.trim();
267: if ((c == condition)
268: || ((c != null) && (condition != null) && condition
269: .equals(c)))
270: return;
271: old = condition;
272: condition = c;
273: }
274: firePropertyChange(PROP_CONDITION, old, c);
275: }
276:
277: /**
278: * Returns stratum.
279: *
280: * @return a stratum
281: */
282: public String getStratum() {
283: return stratum;
284: }
285:
286: /**
287: * Sets stratum.
288: *
289: * @param s a new stratum
290: */
291: public void setStratum(String s) {
292: String old;
293: synchronized (this ) {
294: if (s == null)
295: s = "";
296: s = s.trim();
297: if ((s == stratum)
298: || ((s != null) && (stratum != null) && stratum
299: .equals(s)))
300: return;
301: old = stratum;
302: stratum = s;
303: }
304: firePropertyChange(PROP_CONDITION, old, s);
305: }
306:
307: /**
308: * Returns the name of the source file.
309: *
310: * @return a source name or <code>null</code> when no source name is defined.
311: */
312: public String getSourceName() {
313: return sourceName;
314: }
315:
316: /**
317: * Sets the name of the source file.
318: *
319: * @param sn a new source name or <code>null</code>.
320: */
321: public void setSourceName(String sn) {
322: String old;
323: synchronized (this ) {
324: if (sn != null)
325: sn = sn.trim();
326: if ((sn == sourceName)
327: || ((sn != null) && (sourceName != null) && sourceName
328: .equals(sn)))
329: return;
330: old = sourceName;
331: sourceName = sn;
332: }
333: firePropertyChange(PROP_SOURCE_NAME, old, sn);
334: }
335:
336: /**
337: * Returns source path, relative to the source root.
338: *
339: * @return a source path or <code>null</code> when no source path is defined.
340: *
341: * @since 1.3
342: */
343: public String getSourcePath() {
344: return sourcePath;
345: }
346:
347: /**
348: * Sets source path, relative to the source root.
349: *
350: * @param sp a new source path or <code>null</code>
351: *
352: * @since 1.3
353: */
354: public void setSourcePath(String sp) {
355: String old;
356: synchronized (this ) {
357: if (sp != null)
358: sp = sp.trim();
359: if (sp == sourcePath
360: || (sp != null && sp.equals(sourcePath))) {
361: return;
362: }
363: old = sourcePath;
364: sourcePath = sp;
365: }
366: firePropertyChange(PROP_SOURCE_PATH, old, sp);
367: }
368:
369: /**
370: * Sets the binary class name that is used to submit the breakpoint.
371: * @param className The binary class name, or <code>null</code> if the class
372: * name should be retrieved automatically from the URL and line number.
373: * @since 2.8
374: */
375: public void setPreferredClassName(String className) {
376: String old;
377: synchronized (this ) {
378: if (this .className == className
379: || (className != null && className
380: .equals(this .className))) {
381: return;
382: }
383: old = className;
384: this .className = className;
385: }
386: firePropertyChange(PROP_PREFERRED_CLASS_NAME, old, className);
387: }
388:
389: /**
390: * Gets the binary class name that is used to submit the breakpoint.
391: * @return The binary class name, if previously set by {@link setPreferedClassName}
392: * method, or <code>null</code> if the class name should be retrieved
393: * automatically from the URL and line number.
394: * @since 2.8
395: */
396: public String getPreferredClassName() {
397: return className;
398: }
399:
400: /**
401: * Returns a string representation of this object.
402: *
403: * @return a string representation of the object
404: */
405: public String toString() {
406: String fileName = null;
407: try {
408: FileObject fo = URLMapper.findFileObject(new URL(url));
409: if (fo != null) {
410: fileName = fo.getNameExt();
411: }
412: } catch (MalformedURLException ex) {
413: ErrorManager.getDefault().notify(ex);
414: }
415: if (fileName == null)
416: fileName = url;
417: return "LineBreakpoint " + fileName + " : " + lineNumber;
418: }
419:
420: private static class LineBreakpointImpl extends LineBreakpoint
421: implements Comparable, FileChangeListener, ChangeListener {
422:
423: // We need to hold our FileObject so that it's not GC'ed, because we'd loose our listener.
424: private FileObject fo;
425:
426: public LineBreakpointImpl(String url) {
427: super (url);
428: try {
429: fo = URLMapper.findFileObject(new URL(url));
430: if (fo != null) {
431: fo.addFileChangeListener(WeakListeners.create(
432: FileChangeListener.class, this , fo));
433: }
434: } catch (MalformedURLException ex) {
435: ErrorManager.getDefault().notify(ex);
436: }
437: }
438:
439: public int compareTo(Object o) {
440: if (o instanceof LineBreakpointImpl) {
441: LineBreakpoint lbthis = this ;
442: LineBreakpoint lb = (LineBreakpoint) o;
443: int uc = lbthis .url.compareTo(lb.url);
444: if (uc != 0) {
445: return uc;
446: } else {
447: return lbthis .lineNumber - lb.lineNumber;
448: }
449: } else {
450: return -1;
451: }
452: }
453:
454: public void fileFolderCreated(FileEvent fe) {
455: }
456:
457: public void fileDataCreated(FileEvent fe) {
458: }
459:
460: public void fileChanged(FileEvent fe) {
461: }
462:
463: public void fileDeleted(FileEvent fe) {
464: DebuggerManager.getDebuggerManager().removeBreakpoint(this );
465: fo = null;
466: }
467:
468: public void fileRenamed(FileRenameEvent fe) {
469: try {
470: this .setURL(((FileObject) fe.getSource()).getURL()
471: .toString());
472: } catch (FileStateInvalidException ex) {
473: ErrorManager.getDefault().notify(ex);
474: }
475: }
476:
477: public void fileAttributeChanged(FileAttributeEvent fe) {
478: }
479:
480: public void stateChanged(ChangeEvent chev) {
481: Object source = chev.getSource();
482: if (source instanceof Breakpoint.VALIDITY) {
483: setValidity((Breakpoint.VALIDITY) source, chev
484: .toString());
485: } else {
486: throw new UnsupportedOperationException(chev.toString());
487: }
488: }
489:
490: }
491: }
|