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.ArrayReference;
045: import com.sun.jdi.BooleanType;
046: import com.sun.jdi.ByteType;
047: import com.sun.jdi.ByteValue;
048: import com.sun.jdi.CharType;
049: import com.sun.jdi.CharValue;
050: import com.sun.jdi.ClassNotLoadedException;
051: import com.sun.jdi.ClassObjectReference;
052: import com.sun.jdi.ClassType;
053: import com.sun.jdi.DoubleType;
054: import com.sun.jdi.DoubleValue;
055: import com.sun.jdi.Field;
056: import com.sun.jdi.FloatType;
057: import com.sun.jdi.FloatValue;
058: import com.sun.jdi.IncompatibleThreadStateException;
059: import com.sun.jdi.IntegerType;
060: import com.sun.jdi.IntegerValue;
061: import com.sun.jdi.InvalidTypeException;
062: import com.sun.jdi.InvocationException;
063: import com.sun.jdi.LongType;
064: import com.sun.jdi.LongValue;
065: import com.sun.jdi.ObjectCollectedException;
066: import com.sun.jdi.ObjectReference;
067: import com.sun.jdi.PrimitiveType;
068: import com.sun.jdi.PrimitiveValue;
069: import com.sun.jdi.ReferenceType;
070: import com.sun.jdi.ShortType;
071: import com.sun.jdi.ShortValue;
072: import com.sun.jdi.StringReference;
073: import com.sun.jdi.Type;
074: import com.sun.jdi.Value;
075: import com.sun.jdi.VoidValue;
076: import com.sun.jdi.VMDisconnectedException;
077: import java.beans.Customizer;
078: import java.beans.PropertyChangeEvent;
079:
080: import java.beans.PropertyChangeListener;
081: import java.util.HashSet;
082: import java.util.Set;
083:
084: import org.netbeans.api.debugger.jpda.InvalidExpressionException;
085: import org.netbeans.api.debugger.jpda.JPDAClassType;
086: import org.netbeans.api.debugger.jpda.JPDAThread;
087: import org.netbeans.api.debugger.jpda.Variable;
088: import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
089: import org.netbeans.modules.debugger.jpda.expr.EvaluatorVisitor;
090: import org.netbeans.modules.debugger.jpda.expr.JDIVariable;
091: import org.openide.util.NbBundle;
092:
093: /**
094: * @author Jan Jancura
095: */
096: class AbstractVariable implements JDIVariable, Customizer, Cloneable {
097: // Customized for add/removePropertyChangeListener
098: // Cloneable for fixed watches
099:
100: private Value value;
101: private JPDADebuggerImpl debugger;
102: private String id;
103:
104: private Set<PropertyChangeListener> listeners = new HashSet<PropertyChangeListener>();
105:
106: AbstractVariable(JPDADebuggerImpl debugger, Value value, String id) {
107: this .debugger = debugger;
108: this .value = value;
109: this .id = id;
110: if (this .id == null)
111: this .id = Integer.toString(super .hashCode());
112: }
113:
114: // public interface ........................................................
115:
116: /**
117: * Returns string representation of type of this variable.
118: *
119: * @return string representation of type of this variable.
120: */
121: public String getValue() {
122: Value v = getInnerValue();
123: return getValue(v);
124: }
125:
126: static String getValue(Value v) {
127: if (v == null)
128: return "null";
129: if (v instanceof VoidValue)
130: return "void";
131: if (v instanceof CharValue)
132: return "\'" + v.toString() + "\'";
133: if (v instanceof PrimitiveValue)
134: return v.toString();
135: if (v instanceof StringReference)
136: return "\"" + ((StringReference) v).value() + "\"";
137: if (v instanceof ClassObjectReference)
138: return "class "
139: + ((ClassObjectReference) v).reflectedType().name();
140: if (v instanceof ArrayReference)
141: return "#" + ((ArrayReference) v).uniqueID() + "(length="
142: + ((ArrayReference) v).length() + ")";
143: return "#" + ((ObjectReference) v).uniqueID();
144: }
145:
146: /**
147: * Sets string representation of value of this variable.
148: *
149: * @param value string representation of value of this variable.
150: */
151: public void setValue(String expression)
152: throws InvalidExpressionException {
153: String oldValue = getValue();
154: if (expression.equals(oldValue)) {
155: return; // Do nothing, since the values are identical
156: }
157: Value value;
158: Value oldV = getInnerValue();
159: if (oldV instanceof CharValue && expression.startsWith("'")
160: && expression.endsWith("'") && expression.length() > 1) {
161: value = oldV.virtualMachine()
162: .mirrorOf(expression.charAt(1));
163: } else if ((oldV instanceof StringReference || oldV == null)
164: && expression.startsWith("\"")
165: && expression.endsWith("\"") && expression.length() > 1) {
166: value = debugger.getVirtualMachine().mirrorOf(
167: expression.substring(1, expression.length() - 1));
168: } else if (oldV instanceof ObjectReference
169: && ((ObjectReference) oldV).referenceType() instanceof ClassType
170: && ((ClassType) ((ObjectReference) oldV)
171: .referenceType()).isEnum()) {
172: ClassType enumType = (ClassType) ((ObjectReference) oldV)
173: .referenceType();
174: Field enumValue = enumType.fieldByName(expression);
175: if (enumValue != null) {
176: value = enumType.getValue(enumValue);
177: } else {
178: throw new InvalidExpressionException(expression);
179: }
180: } else if ("null".equals(expression)) {
181: value = null;
182: } else {
183: // evaluate expression to Value
184: Value evaluatedValue = debugger.evaluateIn(expression);
185: if (oldV != null && evaluatedValue != null) {
186: Type type = oldV.type();
187: if (!type.equals(evaluatedValue.type())) {
188: evaluatedValue = convertValue(evaluatedValue, type);
189: }
190: }
191: value = evaluatedValue;
192: }
193: // set new value to remote veriable
194: setValue(value);
195: // set new value to this model
196: setInnerValue(value);
197: }
198:
199: private Value convertValue(Value value, Type type) {
200: if (type instanceof PrimitiveType) {
201: if (value instanceof ObjectReference) {
202: JPDAThread ct = getDebugger().getCurrentThread();
203: if (ct != null) {
204: try {
205: value = EvaluatorVisitor.unbox(
206: (ObjectReference) value,
207: (PrimitiveType) type,
208: ((JPDAThreadImpl) ct)
209: .getThreadReference());
210: } catch (InvalidTypeException ex) {
211: } catch (ClassNotLoadedException ex) {
212: } catch (IncompatibleThreadStateException ex) {
213: } catch (InvocationException ex) {
214: }
215: }
216: if (value.type().equals(type)) {
217: return value;
218: }
219: }
220: if (value instanceof PrimitiveValue) {
221: PrimitiveValue pv = (PrimitiveValue) value;
222: if (type instanceof BooleanType)
223: return pv.virtualMachine().mirrorOf(
224: pv.booleanValue());
225: if (type instanceof ByteType)
226: return pv.virtualMachine().mirrorOf(pv.byteValue());
227: if (type instanceof CharType)
228: return pv.virtualMachine().mirrorOf(pv.charValue());
229: if (type instanceof ShortType)
230: return pv.virtualMachine()
231: .mirrorOf(pv.shortValue());
232: if (type instanceof IntegerType)
233: return pv.virtualMachine().mirrorOf(pv.intValue());
234: if (type instanceof LongType)
235: return pv.virtualMachine().mirrorOf(pv.longValue());
236: if (type instanceof FloatType)
237: return pv.virtualMachine()
238: .mirrorOf(pv.floatValue());
239: if (type instanceof DoubleType)
240: return pv.virtualMachine().mirrorOf(
241: pv.doubleValue());
242: }
243: }
244: if (type instanceof ClassType
245: && value instanceof PrimitiveValue) {
246: JPDAThread ct = getDebugger().getCurrentThread();
247: if (ct != null) {
248: PrimitiveValue pv = (PrimitiveValue) value;
249: String classType = type.name();
250: if (classType.equals("java.lang.Byte")
251: && !(pv instanceof ByteValue)) {
252: pv = pv.virtualMachine().mirrorOf(pv.byteValue());
253: }
254: if (classType.equals("java.lang.Character")
255: && !(pv instanceof CharValue)) {
256: pv = pv.virtualMachine().mirrorOf(pv.charValue());
257: }
258: if (classType.equals("java.lang.Short")
259: && !(pv instanceof ShortValue)) {
260: pv = pv.virtualMachine().mirrorOf(pv.shortValue());
261: }
262: if (classType.equals("java.lang.Integer")
263: && !(pv instanceof IntegerValue)) {
264: pv = pv.virtualMachine().mirrorOf(pv.intValue());
265: }
266: if (classType.equals("java.lang.Long")
267: && !(pv instanceof LongValue)) {
268: pv = pv.virtualMachine().mirrorOf(pv.longValue());
269: }
270: if (classType.equals("java.lang.Float")
271: && !(pv instanceof FloatValue)) {
272: pv = pv.virtualMachine().mirrorOf(pv.floatValue());
273: }
274: if (classType.equals("java.lang.Double")
275: && !(pv instanceof DoubleValue)) {
276: pv = pv.virtualMachine().mirrorOf(pv.doubleValue());
277: }
278: try {
279: value = EvaluatorVisitor.box(pv,
280: (ReferenceType) type, ((JPDAThreadImpl) ct)
281: .getThreadReference());
282: } catch (InvalidTypeException ex) {
283: } catch (ClassNotLoadedException ex) {
284: } catch (IncompatibleThreadStateException ex) {
285: } catch (InvocationException ex) {
286: }
287: }
288: }
289: return value;
290: }
291:
292: /**
293: * Override, but do not call directly!
294: */
295: protected void setValue(Value value)
296: throws InvalidExpressionException {
297: throw new InternalError();
298: }
299:
300: public void setObject(Object bean) {
301: try {
302: if (bean instanceof String) {
303: setValue((String) bean);
304: //} else if (bean instanceof Value) {
305: // setValue((Value) bean); -- do not call directly
306: } else {
307: throw new IllegalArgumentException("" + bean);
308: }
309: } catch (InvalidExpressionException ieex) {
310: IllegalArgumentException iaex = new IllegalArgumentException(
311: ieex.getLocalizedMessage());
312: iaex.initCause(ieex);
313: throw iaex;
314: }
315: }
316:
317: /**
318: * Declared type of this local.
319: *
320: * @return declared type of this local
321: */
322: public String getType() {
323: if (getInnerValue() == null)
324: return "";
325: try {
326: return this .getInnerValue().type().name();
327: } catch (VMDisconnectedException vmdex) {
328: // The session is gone.
329: return NbBundle.getMessage(AbstractVariable.class,
330: "MSG_Disconnected");
331: } catch (ObjectCollectedException ocex) {
332: // The object is gone.
333: return NbBundle.getMessage(AbstractVariable.class,
334: "MSG_ObjCollected");
335: }
336: }
337:
338: public JPDAClassType getClassType() {
339: Value value = getInnerValue();
340: if (value == null)
341: return null;
342: com.sun.jdi.Type type = value.type();
343: if (type instanceof ReferenceType) {
344: return new JPDAClassTypeImpl(debugger, (ReferenceType) type);
345: } else {
346: return null;
347: }
348: }
349:
350: public boolean equals(Object o) {
351: return (o instanceof AbstractVariable)
352: && (id.equals(((AbstractVariable) o).id));
353: }
354:
355: public int hashCode() {
356: return id.hashCode();
357: }
358:
359: // other methods............................................................
360:
361: protected Value getInnerValue() {
362: return value;
363: }
364:
365: protected void setInnerValue(Value v) {
366: value = v;
367: // refresh tree
368: PropertyChangeEvent evt = new PropertyChangeEvent(this ,
369: "value", null, value);
370: Object[] ls;
371: synchronized (listeners) {
372: ls = listeners.toArray();
373: }
374: for (int i = 0; i < ls.length; i++) {
375: ((PropertyChangeListener) ls[i]).propertyChange(evt);
376: }
377: debugger.varChangeSupport.firePropertyChange(evt);
378: //pchs.firePropertyChange("value", null, value);
379: //getModel ().fireTableValueChangedChanged (this, null);
380: }
381:
382: public Value getJDIValue() {
383: return value;
384: }
385:
386: protected final JPDADebuggerImpl getDebugger() {
387: return debugger;
388: }
389:
390: protected final String getID() {
391: return id;
392: }
393:
394: private int cloneNumber = 1;
395:
396: public Variable clone() {
397: AbstractVariable clon = new AbstractVariable(debugger, value,
398: id + "_clone" + (cloneNumber++));
399: return clon;
400: }
401:
402: public final void addPropertyChangeListener(PropertyChangeListener l) {
403: listeners.add(l);
404: }
405:
406: public final void removePropertyChangeListener(
407: PropertyChangeListener l) {
408: listeners.remove(l);
409: }
410:
411: public String toString() {
412: return "Variable ";
413: }
414:
415: /* Uncomment when needed. Was used to create "readable" String and Char values.
416: private static String convertToStringInitializer (String s) {
417: StringBuffer sb = new StringBuffer ();
418: int i, k = s.length ();
419: for (i = 0; i < k; i++)
420: switch (s.charAt (i)) {
421: case '\b':
422: sb.append ("\\b");
423: break;
424: case '\f':
425: sb.append ("\\f");
426: break;
427: case '\\':
428: sb.append ("\\\\");
429: break;
430: case '\t':
431: sb.append ("\\t");
432: break;
433: case '\r':
434: sb.append ("\\r");
435: break;
436: case '\n':
437: sb.append ("\\n");
438: break;
439: case '\"':
440: sb.append ("\\\"");
441: break;
442: default:
443: sb.append (s.charAt (i));
444: }
445: return sb.toString();
446: }
447:
448: private static String convertToCharInitializer (String s) {
449: StringBuffer sb = new StringBuffer ();
450: int i, k = s.length ();
451: for (i = 0; i < k; i++)
452: switch (s.charAt (i)) {
453: case '\b':
454: sb.append ("\\b");
455: break;
456: case '\f':
457: sb.append ("\\f");
458: break;
459: case '\\':
460: sb.append ("\\\\");
461: break;
462: case '\t':
463: sb.append ("\\t");
464: break;
465: case '\r':
466: sb.append ("\\r");
467: break;
468: case '\n':
469: sb.append ("\\n");
470: break;
471: case '\'':
472: sb.append ("\\\'");
473: break;
474: default:
475: sb.append (s.charAt (i));
476: }
477: return sb.toString();
478: }
479: */
480:
481: }
|