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.ruby.debugger.model;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.lang.ref.WeakReference;
047: import org.netbeans.api.debugger.DebuggerManager;
048: import org.netbeans.api.debugger.DebuggerManagerAdapter;
049: import org.netbeans.api.debugger.Watch;
050: import org.netbeans.spi.debugger.ContextProvider;
051: import org.netbeans.spi.viewmodel.ModelEvent;
052: import org.netbeans.spi.viewmodel.ModelListener;
053: import org.netbeans.spi.viewmodel.UnknownTypeException;
054: import org.openide.util.NbBundle;
055: import org.rubyforge.debugcommons.model.RubyVariable;
056: import static org.netbeans.spi.debugger.ui.Constants.LOCALS_TO_STRING_COLUMN_ID;
057: import static org.netbeans.spi.debugger.ui.Constants.LOCALS_TYPE_COLUMN_ID;
058: import static org.netbeans.spi.debugger.ui.Constants.LOCALS_VALUE_COLUMN_ID;
059: import static org.netbeans.spi.debugger.ui.Constants.WATCH_TO_STRING_COLUMN_ID;
060: import static org.netbeans.spi.debugger.ui.Constants.WATCH_TYPE_COLUMN_ID;
061: import static org.netbeans.spi.debugger.ui.Constants.WATCH_VALUE_COLUMN_ID;
062:
063: public final class WatchesModel extends VariablesModel {
064:
065: public static final String WATCH = "org/netbeans/modules/debugger/resources/watchesView/watch_16.png"; // NOI18N
066:
067: private WatchesListener listener;
068:
069: public WatchesModel(final ContextProvider contextProvider) {
070: super (contextProvider);
071: }
072:
073: // TreeModel implementation ................................................
074:
075: @Override
076: public Object[] getChildren(Object parent, int from, int to)
077: throws UnknownTypeException {
078: checkListener(parent);
079: if (parent == ROOT) {
080: return DebuggerManager.getDebuggerManager().getWatches();
081: } else if (parent instanceof Watch) {
082: RubyVariable var = resolveVariable((Watch) parent);
083: return var == null ? new Object[0] : super .getChildren(var,
084: from, to);
085: } else {
086: return super .getChildren(parent, from, to);
087: }
088: }
089:
090: @Override
091: public boolean isLeaf(Object node) throws UnknownTypeException {
092: if (node == ROOT) {
093: return false;
094: } else if (node instanceof Watch) {
095: RubyVariable var = resolveVariable((Watch) node);
096: return var == null ? true : super .isLeaf(var);
097: } else {
098: return super .isLeaf(node);
099: }
100: }
101:
102: @Override
103: public int getChildrenCount(Object node)
104: throws UnknownTypeException {
105: checkListener(node);
106: if (node == ROOT) {
107: return DebuggerManager.getDebuggerManager().getWatches().length;
108: } else if (node instanceof Watch) {
109: RubyVariable var = resolveVariable((Watch) node);
110: return var == null ? 0 : super .getChildrenCount(var);
111: } else {
112: return super .getChildrenCount(node);
113: }
114: }
115:
116: // NodeModel implementation ................................................
117:
118: @Override
119: public String getDisplayName(Object node)
120: throws UnknownTypeException {
121: if (node == ROOT) {
122: return getMessage("CTL_CallstackModel.Column.Name.Name");
123: } else if (node instanceof Watch) {
124: return ((Watch) node).getExpression();
125: } else {
126: return super .getDisplayName(node);
127: }
128: }
129:
130: @Override
131: public String getIconBaseWithExtension(Object node)
132: throws UnknownTypeException {
133: if (node == ROOT || node instanceof Watch) {
134: return WATCH;
135: } else {
136: return super .getIconBaseWithExtension(node);
137: }
138: }
139:
140: @Override
141: public String getShortDescription(Object node)
142: throws UnknownTypeException {
143: if (node == ROOT) {
144: return getMessage("CTL_CallstackModel.Column.Name.Desc");
145: } else if (node instanceof Watch) {
146: RubyVariable var = resolveVariable((Watch) node);
147: return var == null ? getMessage("CTL_WatchesModel.Unknown.Evaluation")
148: : super .getShortDescription(var);
149: } else {
150: return super .getShortDescription(node);
151: }
152: }
153:
154: // TableModel implementation ...............................................
155:
156: @Override
157: public Object getValueAt(Object node, String columnID)
158: throws UnknownTypeException {
159: if (node instanceof Watch) {
160: RubyVariable var = resolveVariable((Watch) node);
161: if (var == null) {
162: return getMessage("CTL_WatchesModel.Unknown.Evaluation");
163: }
164: if (WATCH_VALUE_COLUMN_ID.equals(columnID)) {
165: return super .getValueAt(var, LOCALS_VALUE_COLUMN_ID);
166: } else if (WATCH_TYPE_COLUMN_ID.equals(columnID)) {
167: return super .getValueAt(var, LOCALS_TYPE_COLUMN_ID);
168: }
169: } else {
170: if (WATCH_VALUE_COLUMN_ID.equals(columnID)) {
171: return super .getValueAt(node, LOCALS_VALUE_COLUMN_ID);
172: } else if (WATCH_TYPE_COLUMN_ID.equals(columnID)) {
173: return super .getValueAt(node, LOCALS_TYPE_COLUMN_ID);
174: } else if (WATCH_TO_STRING_COLUMN_ID.equals(columnID)) {
175: return super .getValueAt(node,
176: LOCALS_TO_STRING_COLUMN_ID);
177: }
178: }
179: throw new UnknownTypeException(node);
180: }
181:
182: @Override
183: public boolean isReadOnly(Object node, String columnID)
184: throws UnknownTypeException {
185: return true;
186: }
187:
188: @Override
189: public void setValueAt(Object node, String columnID, Object value)
190: throws UnknownTypeException {
191: throw new UnknownTypeException(node);
192: }
193:
194: private synchronized void checkListener(Object node) {
195: if (listener == null && (node == ROOT || node instanceof Watch)) {
196: listener = new WatchesListener(this );
197: }
198: }
199:
200: private void fireWatchPropertyChanged(Watch watch,
201: String propertyName) {
202: for (ModelListener listener : listeners) {
203: listener.modelChanged(new ModelEvent.NodeChanged(this ,
204: watch));
205: }
206: }
207:
208: private RubyVariable resolveVariable(final Watch watch) {
209: String expr = watch.getExpression();
210: return rubySession.inspectExpression(expr);
211: }
212:
213: private static class WatchesListener extends DebuggerManagerAdapter
214: implements PropertyChangeListener {
215:
216: private WeakReference<WatchesModel> modelRef;
217:
218: public WatchesListener(WatchesModel watchesModel) {
219: modelRef = new WeakReference<WatchesModel>(watchesModel);
220: DebuggerManager.getDebuggerManager().addDebuggerListener(
221: DebuggerManager.PROP_WATCHES, this );
222: Watch[] watches = DebuggerManager.getDebuggerManager()
223: .getWatches();
224: for (Watch watch : watches) {
225: watch.addPropertyChangeListener(this );
226: }
227: }
228:
229: private WatchesModel getModel() {
230: WatchesModel model = modelRef.get();
231: if (model == null) {
232: DebuggerManager.getDebuggerManager()
233: .removeDebuggerListener(
234: DebuggerManager.PROP_WATCHES, this );
235: Watch[] watches = DebuggerManager.getDebuggerManager()
236: .getWatches();
237: for (Watch watch : watches) {
238: watch.addPropertyChangeListener(this );
239: }
240: }
241: return model;
242: }
243:
244: @Override
245: public void watchAdded(Watch watch) {
246: WatchesModel model = getModel();
247: if (model == null) {
248: return;
249: }
250: watch.addPropertyChangeListener(this );
251: model.fireChanges();
252: }
253:
254: @Override
255: public void watchRemoved(Watch watch) {
256: WatchesModel model = getModel();
257: if (model == null) {
258: return;
259: }
260: watch.removePropertyChangeListener(this );
261: model.fireChanges();
262: }
263:
264: @Override
265: public void propertyChange(PropertyChangeEvent evt) {
266: WatchesModel model = getModel();
267: if (model == null) {
268: return;
269: }
270: if (!(evt.getSource() instanceof Watch)) {
271: return;
272: }
273: Watch w = (Watch) evt.getSource();
274: model.fireWatchPropertyChanged(w, evt.getPropertyName());
275: }
276: }
277:
278: private static String getMessage(final String key) {
279: return NbBundle.getMessage(WatchesModel.class, key);
280: }
281: }
|