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.modules.debugger.jpda.ui;
043:
044: import java.beans.PropertyChangeListener;
045: import java.util.List;
046: import org.netbeans.api.debugger.*;
047: import org.netbeans.api.debugger.DebuggerManager;
048: import org.netbeans.api.debugger.Session;
049: import org.netbeans.api.debugger.jpda.*;
050: import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent;
051: import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener;
052: import org.netbeans.modules.debugger.jpda.ui.models.BreakpointsNodeModel;
053: import org.netbeans.spi.debugger.ContextProvider;
054: import java.beans.PropertyChangeEvent;
055: import java.util.*;
056: import java.util.regex.Pattern;
057: import java.util.regex.Matcher;
058: import org.netbeans.spi.viewmodel.NodeModel;
059: import org.openide.util.NbBundle;
060:
061: /**
062: * Listener on all breakpoints and prints text specified in the breakpoint when a it hits.
063: *
064: * @see JPDABreakpoint#setPrintText(java.lang.String)
065: * @author Maros Sandor
066: */
067: public class BreakpointOutput extends LazyActionsManagerListener
068: implements DebuggerManagerListener, JPDABreakpointListener,
069: PropertyChangeListener {
070:
071: private static final Pattern dollarEscapePattern = Pattern
072: .compile("\\$");
073: private static final Pattern backslashEscapePattern = Pattern
074: .compile("\\\\");
075: private static final Pattern threadNamePattern = Pattern
076: .compile("\\{threadName\\}");
077: private static final Pattern classNamePattern = Pattern
078: .compile("\\{className\\}");
079: private static final Pattern methodNamePattern = Pattern
080: .compile("\\{methodName\\}");
081: private static final Pattern lineNumberPattern = Pattern
082: .compile("\\{lineNumber\\}");
083: private static final Pattern expressionPattern = Pattern
084: .compile("\\{=(.*?)\\}");
085:
086: private IOManager ioManager;
087: private JPDADebugger debugger;
088: private ContextProvider contextProvider;
089: private Object lock = new Object();
090:
091: public BreakpointOutput(ContextProvider contextProvider) {
092: this .contextProvider = contextProvider;
093: this .debugger = contextProvider.lookupFirst(null,
094: JPDADebugger.class);
095: debugger.addPropertyChangeListener(JPDADebugger.PROP_STATE,
096: this );
097: hookBreakpoints();
098: DebuggerManager.getDebuggerManager().addDebuggerListener(
099: DebuggerManager.PROP_BREAKPOINTS, this );
100: }
101:
102: // LazyActionsManagerListener ..............................................
103:
104: protected void destroy() {
105: DebuggerManager.getDebuggerManager().removeDebuggerListener(
106: DebuggerManager.PROP_BREAKPOINTS, this );
107: unhookBreakpoints();
108: synchronized (lock) {
109: ioManager = null;
110: debugger = null;
111: }
112: }
113:
114: public String[] getProperties() {
115: return new String[] { ActionsManagerListener.PROP_ACTION_PERFORMED };
116: }
117:
118: // JPDABreakpointListener ..................................................
119:
120: public void breakpointReached(JPDABreakpointEvent event) {
121: synchronized (lock) {
122: if (event.getDebugger() != debugger)
123: return;
124: }
125: if (event.getConditionResult() == event.CONDITION_FALSE)
126: return;
127: JPDABreakpoint breakpoint = (JPDABreakpoint) event.getSource();
128: getBreakpointsNodeModel().setCurrentBreakpoint(breakpoint);
129: synchronized (lock) {
130: if (ioManager == null) {
131: lookupIOManager();
132: if (ioManager == null)
133: return;
134: }
135: }
136: String printText = breakpoint.getPrintText();
137: if (printText == null || printText.length() == 0)
138: return;
139: printText = substitute(printText, event);
140: synchronized (lock) {
141: if (ioManager != null) {
142: ioManager.println(printText, null);
143: }
144: }
145: }
146:
147: // DebuggerManagerListener .................................................
148:
149: public void breakpointAdded(Breakpoint breakpoint) {
150: hookBreakpoint(breakpoint);
151: }
152:
153: public void breakpointRemoved(Breakpoint breakpoint) {
154: unhookBreakpoint(breakpoint);
155: }
156:
157: public Breakpoint[] initBreakpoints() {
158: return new Breakpoint[0];
159: }
160:
161: public void initWatches() {
162: }
163:
164: public void watchAdded(Watch watch) {
165: }
166:
167: public void watchRemoved(Watch watch) {
168: }
169:
170: public void sessionAdded(Session session) {
171: }
172:
173: public void sessionRemoved(Session session) {
174: }
175:
176: public void engineAdded(DebuggerEngine engine) {
177: }
178:
179: public void engineRemoved(DebuggerEngine engine) {
180: }
181:
182: // PropertyChangeListener ..................................................
183:
184: public void propertyChange(PropertyChangeEvent evt) {
185: if (JPDABreakpoint.PROP_VALIDITY.equals(evt.getPropertyName())) {
186: JPDABreakpoint bp = (JPDABreakpoint) evt.getSource();
187: if (bp.isHidden()) {
188: // Ignore hidden breakpoints
189: return;
190: }
191: if (JPDABreakpoint.VALIDITY.INVALID.equals(evt
192: .getNewValue())) {
193: String msg = bp.getValidityMessage();
194: synchronized (lock) {
195: if (ioManager == null) {
196: lookupIOManager();
197: if (ioManager == null)
198: return;
199: }
200: String printText = (msg != null) ? NbBundle
201: .getMessage(BreakpointOutput.class,
202: "MSG_InvalidBreakpointWithReason",
203: bp.toString(), msg) : NbBundle
204: .getMessage(BreakpointOutput.class,
205: "MSG_InvalidBreakpoint", bp
206: .toString());
207: IOManager.Line line = null;
208: if (bp instanceof LineBreakpoint) {
209: line = new IOManager.Line(((LineBreakpoint) bp)
210: .getURL(), ((LineBreakpoint) bp)
211: .getLineNumber(), debugger);
212: }
213: ioManager.println(printText, null, true);
214: if (line != null) {
215: ioManager.println(NbBundle
216: .getMessage(BreakpointOutput.class,
217: "Link_InvalidBreakpoint", bp
218: .toString()), line,
219: true);
220: }
221: }
222: } else if (JPDABreakpoint.VALIDITY.VALID.equals(evt
223: .getNewValue())) {
224: synchronized (lock) {
225: if (ioManager == null) {
226: lookupIOManager();
227: if (ioManager == null)
228: return;
229: }
230: String printText = NbBundle.getMessage(
231: BreakpointOutput.class,
232: "MSG_ValidBreakpoint", bp.toString());
233: IOManager.Line line = null;
234: if (bp instanceof LineBreakpoint) {
235: line = new IOManager.Line(((LineBreakpoint) bp)
236: .getURL(), ((LineBreakpoint) bp)
237: .getLineNumber(), debugger);
238: }
239: ioManager.println(printText, line, false);
240: }
241: }
242: return;
243: }
244: synchronized (lock) {
245: if (debugger == null
246: || evt.getPropertyName() != debugger.PROP_STATE
247: || debugger.getState() == debugger.STATE_STOPPED) {
248:
249: return;
250: }
251: }
252: getBreakpointsNodeModel().setCurrentBreakpoint(null);
253:
254: }
255:
256: // private methods .........................................................
257:
258: /**
259: * threadName name of thread where breakpoint ocurres
260: * className name of class where breakpoint ocurres
261: * methodName name of method where breakpoint ocurres
262: * lineNumber number of line where breakpoint ocurres
263: *
264: * @param printText
265: * @return
266: */
267: private String substitute(String printText,
268: JPDABreakpointEvent event) {
269:
270: // 1) replace {threadName} by the name of current thread
271: JPDAThread t = event.getThread();
272: if (t != null) {
273: // replace \ by \\
274: String name = backslashEscapePattern.matcher(t.getName())
275: .replaceAll("\\\\\\\\");
276: // replace $ by \$
277: name = dollarEscapePattern.matcher(name).replaceAll(
278: "\\\\\\$");
279: printText = threadNamePattern.matcher(printText)
280: .replaceAll(name);
281: } else
282: printText = threadNamePattern.matcher(printText)
283: .replaceAll("?");
284:
285: // 2) replace {className} by the name of current class
286: if (event.getReferenceType() != null) {
287: // replace $ by \$
288: String name = dollarEscapePattern.matcher(
289: event.getReferenceType().name()).replaceAll(
290: "\\\\\\$");
291: printText = classNamePattern.matcher(printText).replaceAll(
292: name);
293: } else
294: printText = classNamePattern.matcher(printText).replaceAll(
295: "?");
296:
297: // 3) replace {methodName} by the name of current method
298: Session session = null;
299: Session[] sessions = DebuggerManager.getDebuggerManager()
300: .getSessions();
301: for (int i = 0; i < sessions.length; i++) {
302: if (sessions[i].lookupFirst(null, JPDADebugger.class) == debugger) {
303: session = sessions[i];
304: break;
305: }
306: }
307: String language = (session != null) ? session
308: .getCurrentLanguage() : null;
309: String methodName = t.getMethodName();
310: if ("".equals(methodName))
311: methodName = "?";
312: // replace $ by \$
313: methodName = dollarEscapePattern.matcher(methodName)
314: .replaceAll("\\\\\\$");
315: printText = methodNamePattern.matcher(printText).replaceAll(
316: methodName);
317:
318: // 4) replace {lineNumber} by the current line number
319: int lineNumber = t.getLineNumber(language);
320: if (lineNumber < 0)
321: printText = lineNumberPattern.matcher(printText)
322: .replaceAll("?");
323: else
324: printText = lineNumberPattern.matcher(printText)
325: .replaceAll(String.valueOf(lineNumber));
326:
327: // 5) resolve all expressions {=expression}
328: for (;;) {
329: Matcher m = expressionPattern.matcher(printText);
330: if (!m.find())
331: break;
332: String expression = m.group(1);
333: String value = "";
334: try {
335: JPDADebugger theDebugger;
336: synchronized (lock) {
337: if (debugger == null) {
338: return value; // The debugger is gone
339: }
340: theDebugger = debugger;
341: }
342: value = theDebugger.evaluate(expression).getValue();
343: value = backslashEscapePattern.matcher(value)
344: .replaceAll("\\\\\\\\");
345: value = dollarEscapePattern.matcher(value).replaceAll(
346: "\\\\\\$");
347: } catch (InvalidExpressionException e) {
348: // expression is invalid or cannot be evaluated
349: String msg = e.getCause() != null ? e.getCause()
350: .getMessage() : e.getMessage();
351: synchronized (lock) {
352: if (ioManager != null) {
353: ioManager.println(
354: "Cannot evaluate expression '"
355: + expression + "' : " + msg,
356: null);
357: }
358: }
359: }
360: printText = m.replaceFirst(value);
361: }
362: Throwable thr = event.getConditionException();
363: if (thr != null) {
364: printText = printText + "\n***\n"
365: + thr.getLocalizedMessage() + "\n***\n";
366: }
367: return printText;
368: }
369:
370: private void lookupIOManager() {
371: List lamls = contextProvider.lookup(null,
372: LazyActionsManagerListener.class);
373: for (Iterator i = lamls.iterator(); i.hasNext();) {
374: Object o = i.next();
375: if (o instanceof DebuggerOutput) {
376: ioManager = ((DebuggerOutput) o).getIOManager();
377: break;
378: }
379: }
380: }
381:
382: private void hookBreakpoints() {
383: Breakpoint[] bpts = DebuggerManager.getDebuggerManager()
384: .getBreakpoints();
385: for (int i = 0; i < bpts.length; i++) {
386: Breakpoint bpt = bpts[i];
387: hookBreakpoint(bpt);
388: }
389: }
390:
391: private void unhookBreakpoints() {
392: Breakpoint[] bpts = DebuggerManager.getDebuggerManager()
393: .getBreakpoints();
394: for (int i = 0; i < bpts.length; i++) {
395: Breakpoint bpt = bpts[i];
396: unhookBreakpoint(bpt);
397: }
398: }
399:
400: private void hookBreakpoint(Breakpoint breakpoint) {
401: if (breakpoint instanceof JPDABreakpoint) {
402: JPDABreakpoint jpdaBreakpoint = (JPDABreakpoint) breakpoint;
403: jpdaBreakpoint.addJPDABreakpointListener(this );
404: jpdaBreakpoint.addPropertyChangeListener(
405: JPDABreakpoint.PROP_VALIDITY, this );
406: }
407: }
408:
409: private void unhookBreakpoint(Breakpoint breakpoint) {
410: if (breakpoint instanceof JPDABreakpoint) {
411: JPDABreakpoint jpdaBreakpoint = (JPDABreakpoint) breakpoint;
412: jpdaBreakpoint.removeJPDABreakpointListener(this );
413: jpdaBreakpoint.removePropertyChangeListener(
414: JPDABreakpoint.PROP_VALIDITY, this );
415: }
416: }
417:
418: private BreakpointsNodeModel breakpointsNodeModel;
419:
420: private BreakpointsNodeModel getBreakpointsNodeModel() {
421: if (breakpointsNodeModel == null) {
422: List l = DebuggerManager.getDebuggerManager().lookup(
423: "BreakpointsView", NodeModel.class);
424: Iterator it = l.iterator();
425: while (it.hasNext()) {
426: NodeModel nm = (NodeModel) it.next();
427: if (nm instanceof BreakpointsNodeModel) {
428: breakpointsNodeModel = (BreakpointsNodeModel) nm;
429: break;
430: }
431: }
432: }
433: return breakpointsNodeModel;
434: }
435: }
|