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.models;
043:
044: import com.sun.jdi.ObjectReference;
045: import com.sun.jdi.PrimitiveValue;
046: import com.sun.jdi.Value;
047:
048: import java.beans.PropertyChangeEvent;
049: import java.beans.PropertyChangeListener;
050: import java.beans.PropertyChangeSupport;
051: import java.lang.ref.WeakReference;
052: import java.util.*;
053: import javax.security.auth.RefreshFailedException;
054: import javax.security.auth.Refreshable;
055:
056: import org.netbeans.api.debugger.Watch;
057: import org.netbeans.api.debugger.DebuggerManager;
058: import org.netbeans.api.debugger.DebuggerManagerAdapter;
059: import org.netbeans.api.debugger.jpda.InvalidExpressionException;
060: import org.netbeans.api.debugger.jpda.Variable;
061: import org.netbeans.spi.debugger.ContextProvider;
062: import org.netbeans.api.debugger.jpda.JPDADebugger;
063: import org.netbeans.api.debugger.jpda.JPDAWatch;
064: import org.netbeans.spi.viewmodel.ModelEvent;
065: import org.netbeans.spi.viewmodel.TreeModel;
066: import org.netbeans.spi.viewmodel.ModelListener;
067: import org.netbeans.spi.viewmodel.UnknownTypeException;
068:
069: import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
070: import org.netbeans.modules.debugger.jpda.expr.Expression;
071: import org.netbeans.modules.debugger.jpda.expr.ParseException;
072:
073: import org.openide.util.RequestProcessor;
074: import org.openide.util.WeakListeners;
075:
076: /**
077: * @author Jan Jancura
078: */
079: public class WatchesModel implements TreeModel {
080:
081: private static boolean verbose = (System
082: .getProperty("netbeans.debugger.viewrefresh") != null)
083: && (System.getProperty("netbeans.debugger.viewrefresh")
084: .indexOf('w') >= 0);
085:
086: private JPDADebuggerImpl debugger;
087: private Listener listener;
088: private Vector<ModelListener> listeners = new Vector<ModelListener>();
089: private ContextProvider lookupProvider;
090: // Watch to Expression or Exception
091: private Map<Watch, JPDAWatchEvaluating> watchToValue = new WeakHashMap<Watch, JPDAWatchEvaluating>(); // <node (expression), JPDAWatch>
092:
093: //private final JPDAWatch EMPTY_WATCH;
094:
095: public WatchesModel(ContextProvider lookupProvider) {
096: debugger = (JPDADebuggerImpl) lookupProvider.lookupFirst(null,
097: JPDADebugger.class);
098: this .lookupProvider = lookupProvider;
099: //EMPTY_WATCH = new EmptyWatch();
100: }
101:
102: /**
103: *
104: * @return threads contained in this group of threads
105: */
106: public Object getRoot() {
107: return ROOT;
108: }
109:
110: /**
111: *
112: * @return watches contained in this group of watches
113: */
114: public Object[] getChildren(Object parent, int from, int to)
115: throws UnknownTypeException {
116: if (parent == ROOT) {
117:
118: // 1) ger Watches
119: Watch[] ws = DebuggerManager.getDebuggerManager()
120: .getWatches();
121: to = Math.min(ws.length, to);
122: from = Math.min(ws.length, from);
123: Watch[] fws = new Watch[to - from];
124: System.arraycopy(ws, from, fws, 0, to - from);
125:
126: // 2) create JPDAWatches for Watches
127: int i, k = fws.length;
128: JPDAWatch[] jws = new JPDAWatch[k];// + 1];
129: for (i = 0; i < k; i++) {
130:
131: JPDAWatchEvaluating jw = watchToValue.get(fws[i]);
132: if (jw == null) {
133: jw = new JPDAWatchEvaluating(this , fws[i], debugger);
134: watchToValue.put(fws[i], jw);
135: }
136: jws[i] = jw;
137:
138: // The actual expressions are computed on demand in JPDAWatchEvaluating
139: }
140: //jws[k] = EMPTY_WATCH;
141:
142: if (listener == null)
143: listener = new Listener(this , debugger);
144: return jws;
145: }
146: if (parent instanceof JPDAWatchImpl) {
147: return getLocalsTreeModel().getChildren(parent, from, to);
148: }
149: return getLocalsTreeModel().getChildren(parent, from, to);
150: }
151:
152: /**
153: * Returns number of children for given node.
154: *
155: * @param node the parent node
156: * @throws UnknownTypeException if this TreeModel implementation is not
157: * able to resolve children for given node type
158: *
159: * @return true if node is leaf
160: */
161: public int getChildrenCount(Object node)
162: throws UnknownTypeException {
163: if (node == ROOT) {
164: if (listener == null)
165: listener = new Listener(this , debugger);
166: // Performance, see issue #59058.
167: return Integer.MAX_VALUE;
168: //return DebuggerManager.getDebuggerManager ().getWatches ().length;
169: }
170: if (node instanceof JPDAWatchImpl) {
171: return getLocalsTreeModel().getChildrenCount(node);
172: }
173: return getLocalsTreeModel().getChildrenCount(node);
174: }
175:
176: public boolean isLeaf(Object node) throws UnknownTypeException {
177: if (node == ROOT)
178: return false;
179: if (node instanceof JPDAWatchEvaluating) {
180: JPDAWatchEvaluating jwe = (JPDAWatchEvaluating) node;
181: JPDAWatch jw = jwe.getEvaluatedWatch();
182: if (jw instanceof JPDAWatchImpl) {
183: return ((JPDAWatchImpl) jw).isPrimitive();
184: }
185: }
186: //if (node == EMPTY_WATCH) return true;
187: return getLocalsTreeModel().isLeaf(node);
188: }
189:
190: public void addModelListener(ModelListener l) {
191: listeners.add(l);
192: }
193:
194: public void removeModelListener(ModelListener l) {
195: listeners.remove(l);
196: }
197:
198: private void fireTreeChanged() {
199: synchronized (watchToValue) {
200: for (Iterator<JPDAWatchEvaluating> it = watchToValue
201: .values().iterator(); it.hasNext();) {
202: it.next().setEvaluated(null);
203: }
204: }
205: Vector v = (Vector) listeners.clone();
206: int i, k = v.size();
207: ModelEvent event = new ModelEvent.TreeChanged(this );
208: for (i = 0; i < k; i++)
209: ((ModelListener) v.get(i)).modelChanged(event);
210: }
211:
212: private void fireWatchesChanged() {
213: Vector v = (Vector) listeners.clone();
214: int i, k = v.size();
215: ModelEvent event = new ModelEvent.NodeChanged(this , ROOT,
216: ModelEvent.NodeChanged.CHILDREN_MASK);
217: for (i = 0; i < k; i++)
218: ((ModelListener) v.get(i)).modelChanged(event);
219: }
220:
221: void fireTableValueChangedChanged(Object node, String propertyName) {
222: ((JPDAWatchEvaluating) node).setEvaluated(null);
223: fireTableValueChangedComputed(node, propertyName);
224: }
225:
226: void fireTableValueChangedComputed(Object node, String propertyName) {
227: Vector v = (Vector) listeners.clone();
228: int i, k = v.size();
229: for (i = 0; i < k; i++)
230: ((ModelListener) v.get(i))
231: .modelChanged(new ModelEvent.TableValueChanged(
232: this , node, propertyName));
233: }
234:
235: // other methods ...........................................................
236:
237: JPDADebuggerImpl getDebugger() {
238: return debugger;
239: }
240:
241: private LocalsTreeModel localsTreeModel;
242:
243: LocalsTreeModel getLocalsTreeModel() {
244: if (localsTreeModel == null)
245: localsTreeModel = (LocalsTreeModel) lookupProvider
246: .lookupFirst("LocalsView", TreeModel.class);
247: return localsTreeModel;
248: }
249:
250: // innerclasses ............................................................
251:
252: private static class JPDAWatchEvaluating extends
253: AbstractObjectVariable implements JPDAWatch, Variable,
254: Refreshable, //.Lazy {
255: PropertyChangeListener {
256:
257: private WatchesModel model;
258: private Watch w;
259: private JPDADebuggerImpl debugger;
260: private JPDAWatch evaluatedWatch;
261: private Expression expression;
262: private ParseException parseException;
263: private boolean[] evaluating = new boolean[] { false };
264:
265: public JPDAWatchEvaluating(WatchesModel model, Watch w,
266: JPDADebuggerImpl debugger) {
267: this (model, w, debugger, 0);
268: }
269:
270: private JPDAWatchEvaluating(WatchesModel model, Watch w,
271: JPDADebuggerImpl debugger, int cloneNumber) {
272: super (debugger, null, (cloneNumber > 0) ? w + "_clone"
273: + cloneNumber : "" + w);
274: this .model = model;
275: this .w = w;
276: this .debugger = debugger;
277: parseExpression(w.getExpression());
278: debugger.varChangeSupport
279: .addPropertyChangeListener(WeakListeners
280: .propertyChange(this ,
281: debugger.varChangeSupport));
282: }
283:
284: private void parseExpression(String exprStr) {
285: try {
286: expression = Expression.parse(exprStr,
287: Expression.LANGUAGE_JAVA_1_5);
288: parseException = null;
289: } catch (ParseException e) {
290: setEvaluated(new JPDAWatchImpl(debugger, w, e, this ));
291: parseException = e;
292: }
293: }
294:
295: Expression getParsedExpression() throws ParseException {
296: if (parseException != null) {
297: throw parseException;
298: }
299: return expression;
300: }
301:
302: public void setEvaluated(JPDAWatch evaluatedWatch) {
303: synchronized (this ) {
304: this .evaluatedWatch = evaluatedWatch;
305: }
306: if (evaluatedWatch != null) {
307: if (evaluatedWatch instanceof JPDAWatchImpl) {
308: setInnerValue(((JPDAWatchImpl) evaluatedWatch)
309: .getInnerValue());
310: } else if (evaluatedWatch instanceof JPDAObjectWatchImpl) {
311: setInnerValue(((JPDAObjectWatchImpl) evaluatedWatch)
312: .getInnerValue());
313: }
314: //propSupp.firePropertyChange(PROP_INITIALIZED, null, Boolean.TRUE);
315: } else {
316: setInnerValue(null);
317: }
318: //model.fireTableValueChangedComputed(this, null);
319: }
320:
321: synchronized JPDAWatch getEvaluatedWatch() {
322: return evaluatedWatch;
323: }
324:
325: public void expressionChanged() {
326: setEvaluated(null);
327: parseExpression(w.getExpression());
328: }
329:
330: public synchronized String getExceptionDescription() {
331: if (evaluatedWatch != null) {
332: return evaluatedWatch.getExceptionDescription();
333: } else {
334: return null;
335: }
336: }
337:
338: public synchronized String getExpression() {
339: if (evaluatedWatch != null) {
340: return evaluatedWatch.getExpression();
341: } else {
342: return w.getExpression();
343: }
344: }
345:
346: public String getToStringValue()
347: throws InvalidExpressionException {
348: JPDAWatch evaluatedWatch;
349: synchronized (this ) {
350: evaluatedWatch = this .evaluatedWatch;
351: }
352: if (evaluatedWatch == null) {
353: JPDAWatch[] watchRef = new JPDAWatch[] { null };
354: getValue(watchRef); // To init the evaluatedWatch
355: evaluatedWatch = watchRef[0];
356: }
357: return evaluatedWatch.getToStringValue();
358: }
359:
360: public String getType() {
361: JPDAWatch evaluatedWatch;
362: synchronized (this ) {
363: evaluatedWatch = this .evaluatedWatch;
364: }
365: if (evaluatedWatch == null) {
366: JPDAWatch[] watchRef = new JPDAWatch[] { null };
367: getValue(watchRef); // To init the evaluatedWatch
368: evaluatedWatch = watchRef[0];
369: }
370: return evaluatedWatch.getType();
371: }
372:
373: public String getValue() {
374: return getValue((JPDAWatch[]) null);
375: }
376:
377: private String getValue(JPDAWatch[] watchRef) {
378: synchronized (evaluating) {
379: if (evaluating[0]) {
380: try {
381: evaluating.wait();
382: } catch (InterruptedException iex) {
383: return null;
384: }
385: }
386: synchronized (this ) {
387: if (evaluatedWatch != null) {
388: if (watchRef != null)
389: watchRef[0] = evaluatedWatch;
390: return evaluatedWatch.getValue();
391: }
392: }
393: evaluating[0] = true;
394: }
395:
396: JPDAWatch jw = null;
397: try {
398: Expression expr = getParsedExpression();
399: Value v = debugger.evaluateIn(expr);
400: //if (v instanceof ObjectReference)
401: // jw = new JPDAObjectWatchImpl (debugger, w, (ObjectReference) v);
402: if (v instanceof PrimitiveValue) {
403: JPDAWatchImpl jwi = new JPDAWatchImpl(debugger, w,
404: (PrimitiveValue) v, this );
405: jwi.addPropertyChangeListener(this );
406: jw = jwi;
407: } else { // ObjectReference or VoidValue
408: JPDAObjectWatchImpl jwi = new JPDAObjectWatchImpl(
409: debugger, w, v);
410: jwi.addPropertyChangeListener(this );
411: jw = jwi;
412: }
413: } catch (InvalidExpressionException e) {
414: JPDAWatchImpl jwi = new JPDAWatchImpl(debugger, w, e,
415: this );
416: jwi.addPropertyChangeListener(this );
417: jw = jwi;
418: } catch (ParseException e) {
419: JPDAWatchImpl jwi = new JPDAWatchImpl(debugger, w, e,
420: this );
421: jwi.addPropertyChangeListener(this );
422: jw = jwi;
423: } finally {
424: setEvaluated(jw);
425: if (watchRef != null)
426: watchRef[0] = jw;
427: synchronized (evaluating) {
428: evaluating[0] = false;
429: evaluating.notifyAll();
430: }
431: }
432: //System.out.println(" value = "+jw.getValue());
433: return jw.getValue();
434: }
435:
436: public synchronized void remove() {
437: if (evaluatedWatch != null) {
438: evaluatedWatch.remove();
439: } else {
440: w.remove();
441: }
442: }
443:
444: public void setExpression(String expression) {
445: w.setExpression(expression);
446: expressionChanged();
447: }
448:
449: public synchronized void setValue(String value)
450: throws InvalidExpressionException {
451: if (evaluatedWatch != null) {
452: evaluatedWatch.setValue(value);
453: } else {
454: throw new InvalidExpressionException(
455: "Can not set value while evaluating.");
456: }
457: }
458:
459: public void propertyChange(PropertyChangeEvent evt) {
460: if (evt.getSource() instanceof JPDAWatchEvaluating) {
461: // Do not re-fire my own changes
462: return;
463: }
464: model.fireTableValueChangedChanged(this , null);
465: }
466:
467: /** Does wait for the value to be evaluated. */
468: public void refresh() throws RefreshFailedException {
469: synchronized (evaluating) {
470: if (evaluating[0]) {
471: try {
472: evaluating.wait();
473: } catch (InterruptedException iex) {
474: throw new RefreshFailedException(iex
475: .getLocalizedMessage());
476: }
477: }
478: }
479: }
480:
481: /** Tells whether the variable is fully initialized and getValue()
482: * returns the value immediately. */
483: public synchronized boolean isCurrent() {
484: return evaluatedWatch != null;
485: }
486:
487: private int cloneNumber = 1;
488:
489: public JPDAWatchEvaluating clone() {
490: return new JPDAWatchEvaluating(model, w, debugger,
491: cloneNumber++);
492: }
493:
494: }
495:
496: private static class Listener extends DebuggerManagerAdapter
497: implements PropertyChangeListener {
498:
499: private WeakReference<WatchesModel> model;
500: private WeakReference<JPDADebuggerImpl> debugger;
501:
502: private Listener(WatchesModel tm, JPDADebuggerImpl debugger) {
503: model = new WeakReference<WatchesModel>(tm);
504: this .debugger = new WeakReference<JPDADebuggerImpl>(
505: debugger);
506: DebuggerManager.getDebuggerManager().addDebuggerListener(
507: DebuggerManager.PROP_WATCHES, this );
508: debugger.addPropertyChangeListener(this );
509: Watch[] ws = DebuggerManager.getDebuggerManager()
510: .getWatches();
511: int i, k = ws.length;
512: for (i = 0; i < k; i++)
513: ws[i].addPropertyChangeListener(this );
514: }
515:
516: private WatchesModel getModel() {
517: WatchesModel m = model.get();
518: if (m == null)
519: destroy();
520: return m;
521: }
522:
523: public void watchAdded(Watch watch) {
524: WatchesModel m = getModel();
525: if (m == null)
526: return;
527: watch.addPropertyChangeListener(this );
528: m.fireWatchesChanged();
529: }
530:
531: public void watchRemoved(Watch watch) {
532: WatchesModel m = getModel();
533: if (m == null)
534: return;
535: watch.removePropertyChangeListener(this );
536: m.fireWatchesChanged();
537: }
538:
539: // currently waiting / running refresh task
540: // there is at most one
541: private RequestProcessor.Task task;
542:
543: public void propertyChange(PropertyChangeEvent evt) {
544: String propName = evt.getPropertyName();
545: // We already have watchAdded & watchRemoved. Ignore PROP_WATCHES:
546: if (DebuggerManager.PROP_WATCHES.equals(propName))
547: return;
548: final WatchesModel m = getModel();
549: if (m == null)
550: return;
551: if (m.debugger.getState() == JPDADebugger.STATE_DISCONNECTED) {
552: destroy();
553: return;
554: }
555: if (m.debugger.getState() == JPDADebugger.STATE_RUNNING) {
556: return;
557: }
558:
559: if (evt.getSource() instanceof Watch) {
560: Object node;
561: synchronized (m.watchToValue) {
562: node = m.watchToValue.get(evt.getSource());
563: }
564: if (node != null) {
565: m.fireTableValueChangedChanged(node, null);
566: return;
567: }
568: }
569:
570: if (task == null) {
571: task = RequestProcessor.getDefault().create(
572: new Runnable() {
573: public void run() {
574: if (verbose)
575: System.out.println("WM do task "
576: + task);
577: m.fireTreeChanged();
578: }
579: });
580: if (verbose)
581: System.out.println("WM create task " + task);
582: }
583: task.schedule(100);
584: }
585:
586: private void destroy() {
587: DebuggerManager.getDebuggerManager()
588: .removeDebuggerListener(
589: DebuggerManager.PROP_WATCHES, this );
590: JPDADebugger d = debugger.get();
591: if (d != null)
592: d.removePropertyChangeListener(this );
593:
594: Watch[] ws = DebuggerManager.getDebuggerManager()
595: .getWatches();
596: int i, k = ws.length;
597: for (i = 0; i < k; i++)
598: ws[i].removePropertyChangeListener(this );
599:
600: if (task != null) {
601: // cancel old task
602: task.cancel();
603: if (verbose)
604: System.out.println("WM cancel old task " + task);
605: task = null;
606: }
607: }
608: }
609:
610: /*
611: * The last empty watch, that can be used to enter new watch expressions.
612: *
613: private final class EmptyWatch implements JPDAWatch {
614:
615:
616: public String getExpression() {
617: return "";
618: }
619:
620: public void setExpression(String expression) {
621: DebuggerManager.getDebuggerManager().createWatch(expression);
622:
623: Vector v = (Vector) listeners.clone ();
624: int i, k = v.size ();
625: for (i = 0; i < k; i++)
626: ((ModelListener) v.get (i)).modelChanged (
627: new ModelEvent.NodeChanged (WatchesModel.this, EmptyWatch.this)
628: );
629: }
630:
631: public void remove() {
632: // Can not be removed
633: }
634:
635: public String getType() {
636: return "";
637: }
638:
639: public String getValue() {
640: return "";
641: }
642:
643: public String getExceptionDescription() {
644: return null;
645: }
646:
647: public void setValue(String value) throws InvalidExpressionException {
648: // Can not be set
649: }
650:
651: public String getToStringValue() throws InvalidExpressionException {
652: return "";
653: }
654: }
655: */
656: }
|