0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.cnd.debugger.gdb.models;
0043:
0044: import java.beans.Customizer;
0045: import java.beans.PropertyChangeListener;
0046: import java.util.HashSet;
0047: import java.util.Map;
0048: import java.util.Set;
0049: import java.util.logging.Level;
0050: import java.util.logging.Logger;
0051: import javax.swing.SwingUtilities;
0052: import org.netbeans.api.debugger.DebuggerEngine;
0053: import org.netbeans.api.debugger.DebuggerManager;
0054:
0055: import org.netbeans.modules.cnd.debugger.gdb.InvalidExpressionException;
0056: import org.netbeans.modules.cnd.debugger.gdb.Field;
0057: import org.netbeans.modules.cnd.debugger.gdb.GdbDebugger;
0058: import org.netbeans.modules.cnd.debugger.gdb.GdbVariable;
0059: import org.netbeans.modules.cnd.debugger.gdb.LocalVariable;
0060: import org.netbeans.modules.cnd.debugger.gdb.TypeInfo;
0061: import org.netbeans.modules.cnd.debugger.gdb.utils.GdbUtils;
0062: import org.openide.DialogDisplayer;
0063: import org.openide.NotifyDescriptor;
0064: import org.openide.util.NbBundle;
0065: import org.openide.util.Utilities;
0066:
0067: /*
0068: * An AbstractVariable is an array, pointer, struct, or union.
0069: *
0070: * @author Nik Molchanov (copied from Jan Jancura's JPDA implementation)
0071: */
0072: public class AbstractVariable implements LocalVariable, Customizer {
0073:
0074: private GdbDebugger debugger;
0075: protected String name;
0076: protected String type;
0077: protected String value;
0078: protected String ovalue;
0079: protected String derefValue;
0080: protected Field[] fields;
0081: protected TypeInfo tinfo;
0082: private static Logger log = Logger.getLogger("gdb.logger"); // NOI18N
0083:
0084: private Set<PropertyChangeListener> listeners = new HashSet<PropertyChangeListener>();
0085:
0086: /** Create the AV from a GV. If the GV has children then create similar children for the AV */
0087: public AbstractVariable(GdbVariable var) {
0088: this (var.getName(), var.getValue());
0089: }
0090:
0091: public AbstractVariable(String name) {
0092: this (name, null);
0093: }
0094:
0095: public AbstractVariable(String name, String value) {
0096: assert name.indexOf('{') == -1; // this means a mis-parsed gdb response...
0097: assert !Thread.currentThread().getName().equals("GdbReaderRP"); // NOI18N
0098: assert !SwingUtilities.isEventDispatchThread();
0099: this .name = name;
0100: type = getDebugger().requestWhatis(name);
0101: ovalue = null;
0102: fields = new Field[0];
0103: tinfo = TypeInfo.getTypeInfo(getDebugger(), this );
0104:
0105: if (Utilities.getOperatingSystem() != Utilities.OS_MAC) {
0106: this .value = value;
0107: } else {
0108: // Convert the Mac-specific value to standard gdb/mi format
0109: this .value = GdbUtils.mackHack(value);
0110: }
0111:
0112: if (GdbUtils.isSinglePointer(type)) {
0113: derefValue = getDebugger().requestValue('*' + name);
0114: } else {
0115: derefValue = null;
0116: }
0117: }
0118:
0119: protected AbstractVariable() {
0120: } // used by AbstractField instantiation...
0121:
0122: protected TypeInfo getTypeInfo() {
0123: if (tinfo == null) {
0124: tinfo = TypeInfo.getTypeInfo(getDebugger(), this );
0125: }
0126: return tinfo;
0127: }
0128:
0129: protected void resetVariable() {
0130: tinfo = null;
0131: type = "";
0132: value = "";
0133: fields = new Field[0];
0134: }
0135:
0136: /**
0137: * Declared type of this local.
0138: *
0139: * @return declared type of this local
0140: */
0141: public String getType() {
0142: return type;
0143: }
0144:
0145: /**
0146: * Similar to getType() except this method will wait for the type to be supplied in
0147: * an alternate thread.
0148: *
0149: * @return declared type of this local
0150: */
0151: public String waitForType() {
0152: if (!SwingUtilities.isEventDispatchThread()) {
0153: int count = 20;
0154:
0155: // this can get called while var is waiting for its type to be returned
0156: while (type == null
0157: && count-- > 0
0158: && debugger.getState().equals(
0159: GdbDebugger.STATE_STOPPED)) {
0160: if (log.isLoggable(Level.FINE) && count == 19) {
0161: log.fine("AV.waitForType: Waiting on type for "
0162: + name);
0163: }
0164: try {
0165: Thread.sleep(100);
0166: } catch (InterruptedException ex) {
0167: return null;
0168: }
0169: }
0170: }
0171: return type;
0172: }
0173:
0174: public void setType(String type) {
0175: this .type = type;
0176: }
0177:
0178: /**
0179: * Returns string representation of type of this variable.
0180: *
0181: * @return string representation of type of this variable.
0182: */
0183: public String getValue() {
0184: if (value.startsWith(">") && value.endsWith(".\"<")) { // NOI18N
0185: return '>' + value.substring(2, value.length() - 3)
0186: .replace("\\\"", "\"") + '<'; // NOI18N
0187: } else {
0188: return value.replace("\\\"", "\""); // NOI18N
0189: }
0190: }
0191:
0192: /**
0193: * Sets string representation of value of this variable. In this case we ignore the
0194: * request because we only allow setting values on leaves.
0195: *
0196: * @param value string representation of value of this variable.
0197: */
0198: public void setValue(String value) {
0199: String msg = null;
0200: String rt = getTypeInfo().getResolvedType(this );
0201: int pos;
0202:
0203: if (getDebugger() != null) {
0204: value = value.trim();
0205: if (value.length() > 0
0206: && value.charAt(0) == '('
0207: && (pos = GdbUtils.findMatchingParen(value, 0)) != -1) {
0208: // Strip a cast
0209: value = value.substring(pos + 1).trim();
0210: }
0211: if (rt.equals("char") || rt.equals("unsigned char")) { // NOI18N
0212: value = setValueChar(value);
0213: if (value == null) { // Invalid input
0214: msg = NbBundle.getMessage(AbstractVariable.class,
0215: "ERR_SetValue_Invalid_Char"); // NOI18N
0216: }
0217: } else if (rt.equals("char *")
0218: || rt.equals("unsigned char *")) { // NOI18N
0219: value = setValueCharStar(value);
0220: if (value == null) { // Invalid input
0221: msg = NbBundle.getMessage(AbstractVariable.class,
0222: "ERR_SetValue_Invalid_Char*"); // NOI18N
0223: }
0224: } else if ((rt.equals("int") || rt.equals("long"))) { // NOI18N
0225: value = setValueNumber(value);
0226: if (value == null) { // Invalid input
0227: msg = NbBundle.getMessage(AbstractVariable.class,
0228: "ERR_SetValue_Invalid_Number"); // NOI18N
0229: }
0230: } else if (getDebugger().isCplusPlus() && rt.equals("bool")) { // NOI18N
0231: if (!value.equals("true") && !value.equals("false")
0232: && !isNumber(value)) { // NOI18N
0233: msg = NbBundle.getMessage(AbstractVariable.class,
0234: "ERR_SetValue_Invalid_CplusPlus_Bool"); // NOI18N
0235: }
0236: } else if (rt.startsWith("enum ")) { // NOI18N
0237: value = setValueEnum(value);
0238: if (value == null) { // Invalid input
0239: msg = NbBundle.getMessage(AbstractVariable.class,
0240: "ERR_SetValue_Invalid_Enum"); // NOI18N
0241: }
0242: } else if (value.charAt(0) == '"'
0243: || (value.startsWith("0x") && value.endsWith("\""))) { // NOI18N
0244: value = setValueCharStar(value);
0245: if (value == null) { // Invalid input
0246: msg = NbBundle.getMessage(AbstractVariable.class,
0247: "ERR_SetValue_Invalid_Char*"); // NOI18N
0248: }
0249: } else if (GdbUtils.isPointer(rt)) {
0250: // no current validation
0251: }
0252: if (value != null) {
0253: if (value.endsWith("\\\"")) { // NOI18N
0254: pos = value.indexOf('"');
0255: if (pos != -1) {
0256: value = value
0257: .substring(pos, value.length() - 1) + '"';
0258: }
0259: }
0260: if (value.charAt(0) == '(') {
0261: pos = GdbUtils.findMatchingParen(value, 0);
0262: if (pos != -1) {
0263: value = value.substring(pos + 1).trim();
0264: }
0265: }
0266: }
0267: if (msg == null) {
0268: String fullname;
0269: if (this instanceof GdbWatchVariable) {
0270: fullname = ((GdbWatchVariable) this ).getWatch()
0271: .getExpression();
0272: } else {
0273: if (this instanceof AbstractField) {
0274: fullname = ((AbstractField) this )
0275: .getFullName(false);
0276: } else {
0277: fullname = name;
0278: }
0279: }
0280: ovalue = this .value;
0281: value = getDebugger().updateVariable(fullname, value);
0282: }
0283: }
0284: if (msg != null) {
0285: NotifyDescriptor nd = new NotifyDescriptor.Message(msg);
0286: nd.setTitle("TITLE_SetValue_Warning"); // NOI18N
0287: DialogDisplayer.getDefault().notify(nd);
0288: }
0289: }
0290:
0291: public void restoreOldValue() {
0292: value = ovalue;
0293: }
0294:
0295: public synchronized void setModifiedValue(String value) {
0296: this .value = value;
0297: if (fields.length > 0) {
0298: fields = new Field[0];
0299: derefValue = null;
0300: if (value.length() > 0) {
0301: expandChildren();
0302: }
0303: }
0304: }
0305:
0306: /**
0307: * Validate the string passed to setValue. Verify its a correct char format and remove a leading
0308: * address if needed.
0309: *
0310: * @param value The value typed by the user into the value editor
0311: * @returns A valid value (valid in the sense gdb should accept it) or null
0312: */
0313: private String setValueChar(String value) {
0314: int pos;
0315:
0316: if (value.startsWith("0x") && (pos = value.indexOf(" '")) != -1
0317: && value.endsWith("'")) { // NOI18N
0318: value = value.substring(pos + 1);
0319: } else if (value.charAt(0) == '\''
0320: && value.charAt(value.length() - 1) == '\'') {
0321: // OK
0322: } else {
0323: value = null;
0324: }
0325: return value;
0326: }
0327:
0328: /**
0329: * Validate the string passed to setValue. Verify its a correct char format and remove a leading
0330: * address if needed.
0331: *
0332: * @param value The value typed by the user into the value editor
0333: * @returns A valid value (valid in the sense gdb should accept it) or null
0334: */
0335: private String setValueCharStar(String value) {
0336: int pos;
0337:
0338: if (value.startsWith("0x")
0339: && (pos = value.indexOf(" \\\"")) != -1
0340: && value.endsWith("\\\"")) { // NOI18N
0341: value = '"' + value.substring(pos + 3, value.length() - 2) + '"'; // NOI18N
0342: } else if (value.startsWith("0x")
0343: && (pos = value.indexOf(" \"")) != -1
0344: && value.endsWith("\"")) { // NOI18N
0345: value = value.substring(pos + 1);
0346: } else if (value.charAt(0) == '"'
0347: && value.charAt(value.length() - 1) == '"') { // NOI18N
0348: // OK
0349: } else {
0350: value = null;
0351: }
0352: return value;
0353: }
0354:
0355: /**
0356: * Validate the string passed to setValue.
0357: *
0358: * @param value The value typed by the user into the value editor
0359: * @returns A valid value (valid in the sense gdb should accept it) or null
0360: */
0361: private String setValueEnum(String value) {
0362: String rt = getTypeInfo().getResolvedType(this );
0363: int pos1 = rt.indexOf('{');
0364: int pos2 = rt.indexOf('}');
0365: if (pos1 > 0 && pos2 > 0) {
0366: String enum_values = rt.substring(pos1 + 1, pos2);
0367: for (String frag : enum_values.split(", ")) { // NOI18N
0368: if (value.equals(frag)) {
0369: return value;
0370: }
0371: }
0372: }
0373: return null;
0374: }
0375:
0376: /**
0377: * Validate the string passed to setValue. Verify its a correct numerical format .
0378: *
0379: * @param value The value typed by the user into the value editor
0380: * @returns A valid value (valid in the sense gdb should accept it) or null
0381: */
0382: private String setValueNumber(String value) {
0383: if (isNumber(value)) {
0384: // OK
0385: } else {
0386: value = null;
0387: }
0388: return value;
0389: }
0390:
0391: public void setObject(Object bean) {
0392: }
0393:
0394: /**
0395: * See if this variable <i>will</i> have fields and should show a turner.
0396: * We're not actually creating or counting fields here.
0397: *
0398: * @return 0 if the variable shouldn't have a turner and fields.length if it should
0399: */
0400: public int getFieldsCount() {
0401: if (getDebugger() == null
0402: || !getDebugger().getState().equals(
0403: GdbDebugger.STATE_STOPPED)) {
0404: return 0;
0405: } else if (fields.length > 0) {
0406: return fields.length;
0407: } else if (mightHaveFields()) {
0408: return estimateFieldCount();
0409: } else {
0410: return 0;
0411: }
0412: }
0413:
0414: /**
0415: * The else-if in getFieldsCount() was getting too complex, so I've factored it out and
0416: * made it into multiple if/else-if statements. I think its easier to track this way.
0417: *
0418: * @return true if the field should have a turner and false if it shouldn't
0419: */
0420: private boolean mightHaveFields() {
0421: String rt = getTypeInfo().getResolvedType(this );
0422: if (GdbUtils.isArray(rt) && !isCharString(rt)) {
0423: return true;
0424: } else if (isValidPointerAddress()) {
0425: if (GdbUtils.isFunctionPointer(rt) || rt.equals("void *") || // NOI18N
0426: (isCharString(rt) && !GdbUtils.isMultiPointer(rt))) {
0427: return false;
0428: } else {
0429: return true;
0430: }
0431: } else if (value != null && value.length() > 0
0432: && value.charAt(0) == '{') {
0433: return true;
0434: }
0435: return false;
0436: }
0437:
0438: /**
0439: * I'd like to estimate field count based on the value string. However, this might
0440: * actually be better. If I set the children count high then it gets reset once the
0441: * children get created. If I set it too low, only that number of fields are shown
0442: * (even though the var has more fields).
0443: */
0444: private int estimateFieldCount() {
0445: int count = 100;
0446: return count;
0447: }
0448:
0449: private boolean isValidPointerAddress() {
0450: String frag = "";
0451: int pos1;
0452: long i;
0453:
0454: if (value == null && this instanceof GdbWatchVariable) {
0455: getValue(); // A watch might not have a value yet. This will initialize "value"
0456: }
0457: if (value != null) { // value can be null for watches during initialization...
0458: if (value.length() > 0 && value.charAt(0) == '(') {
0459: pos1 = value.indexOf("*) 0x"); // NOI18N
0460: if (pos1 == -1) {
0461: pos1 = value.indexOf("* const) 0x"); // NOI18N
0462: if (pos1 != -1) {
0463: frag = value.substring(pos1 + 11);
0464: }
0465: } else {
0466: frag = value.substring(pos1 + 5);
0467: }
0468: if (pos1 != -1) {
0469: try {
0470: i = Long.parseLong(frag, 16);
0471: } catch (NumberFormatException ex) {
0472: return false;
0473: }
0474: return i > 0;
0475: }
0476: } else if (value.startsWith("0x")) { // NOI18N
0477: try {
0478: i = Long.parseLong(value.substring(2), 16);
0479: } catch (NumberFormatException ex) {
0480: return false;
0481: }
0482: return i > 0;
0483: }
0484: }
0485: return false;
0486: }
0487:
0488: /**
0489: * Returns field defined in this object.
0490: *
0491: * @param name a name of field to be returned
0492: * @return field defined in this object
0493: */
0494: public Field getField(String name) {
0495: int i, k = fields.length;
0496: for (i = 0; i < k; i++) {
0497: Field f = fields[i];
0498: if (name.equals(f.getName())) {
0499: return f;
0500: }
0501: }
0502: return null; // Not found
0503: }
0504:
0505: /**
0506: * Returns all fields declared in this type that are in interval
0507: * <<code>from</code>, <code>to</code>).
0508: */
0509: public Field[] getFields(int from, int to) {
0510: if (to != 0) {
0511: if (fields.length == 0) {
0512: expandChildren();
0513: }
0514: to = Math.min(fields.length, to);
0515: from = Math.min(fields.length, from);
0516: Field[] fv = new Field[to - from];
0517: System.arraycopy(fields, from, fv, 0, to - from);
0518: return fv;
0519: }
0520: return fields;
0521: }
0522:
0523: /**
0524: * In the JPDA implementation a value isn't always a String. We're (currently)
0525: * storing the value as a String so no conversion is done. However, keeping
0526: * this method makes it possible for us to change at a later date...
0527: *
0528: * @return The value of this instance
0529: */
0530: public String getToStringValue() throws InvalidExpressionException {
0531: return getValue();
0532: }
0533:
0534: @Override
0535: public boolean equals(Object o) {
0536: return o instanceof AbstractVariable
0537: && getFullName(true).equals(
0538: ((AbstractVariable) o).getFullName(true));
0539: }
0540:
0541: @Override
0542: public int hashCode() {
0543: return name.hashCode();
0544: }
0545:
0546: public String getName() {
0547: return name;
0548: }
0549:
0550: protected final GdbDebugger getDebugger() {
0551: if (debugger == null) {
0552: DebuggerEngine currentEngine = DebuggerManager
0553: .getDebuggerManager().getCurrentEngine();
0554: if (currentEngine == null) {
0555: return null;
0556: }
0557: debugger = currentEngine.lookupFirst(null,
0558: GdbDebugger.class);
0559: }
0560: return debugger;
0561: }
0562:
0563: public synchronized boolean expandChildren() {
0564: if (fields.length == 0) {
0565: createChildren();
0566: }
0567: return fields.length > 0;
0568: }
0569:
0570: private void createChildren() {
0571: String resolvedType = getTypeInfo().getResolvedType(this );
0572: Map<String, Object> map;
0573: String t;
0574: String v;
0575:
0576: if (GdbUtils.isPointer(resolvedType)
0577: && !isCharString(resolvedType)
0578: && !GdbUtils.isMultiPointer(resolvedType)) {
0579: if (value.endsWith(" 0") || value.endsWith(" 0x0")) { // NOI18N
0580: t = null;
0581: v = null;
0582: } else {
0583: t = GdbUtils.getBaseType(resolvedType);
0584: v = getDebugger()
0585: .requestValue('*' + getFullName(false));
0586: }
0587: } else {
0588: t = resolvedType;
0589: v = value;
0590: }
0591: if (v != null) { // v can be null if we're no longer in a stopped state
0592: if (GdbUtils.isArray(t)) {
0593: createChildrenForArray(t, v);
0594: } else if (GdbUtils.isMultiPointer(t)) {
0595: createChildrenForMultiPointer(t);
0596: } else {
0597: map = getTypeInfo().getMap();
0598: if (map != null) { // a null map means we never got type information
0599: if (map.isEmpty()) {
0600: // an empty map means its a pointer to a non-struct/class/union
0601: createChildrenForPointer(t, v);
0602: } else if (v.length() > 0) {
0603: String val = v.substring(1, v.length() - 1);
0604: int start = 0;
0605: int end = GdbUtils.findNextComma(val, 0);
0606: while (end != -1) {
0607: String vfrag = val.substring(start, end)
0608: .trim();
0609: addField(completeFieldDefinition(this , map,
0610: vfrag));
0611: start = end + 1;
0612: end = GdbUtils.findNextComma(val, end + 1);
0613: }
0614: addField(completeFieldDefinition(this , map, val
0615: .substring(start).trim()));
0616: } else {
0617: log
0618: .fine("AV.createChildren: 0 length value for "
0619: + getFullName(false));
0620: }
0621: }
0622: }
0623: }
0624: }
0625:
0626: private void createChildrenForPointer(String t, String v) {
0627: addField(new AbstractField(this , '*' + getName(), t, v));
0628: }
0629:
0630: private void createChildrenForMultiPointer(String t) {
0631: int i = 0;
0632: String fullname = getFullName(false);
0633: String t2 = t.substring(0, t.length() - 1);
0634: int max_fields = t2.startsWith("char *") ? 20 : 10; // NOI18N
0635:
0636: while (max_fields-- > 0) {
0637: String v = getDebugger().requestValue(
0638: fullname + '[' + i + ']');
0639: if (v == null || v.length() < 1 || v.endsWith("0x0")) { // NOI18N
0640: return;
0641: }
0642: addField(new AbstractField(this , getName() + '[' + i++
0643: + ']', t2, v));
0644: }
0645: }
0646:
0647: /**
0648: * Check the type. Does it resolve to a char *? If so then we don't want to
0649: * expand it further. But if its not a char * then we (probably) do.
0650: *
0651: * @param info The string to verify
0652: * @return true if t is some kind of a character pointer
0653: */
0654: private boolean isCharString(String t) {
0655: if (t != null && t.endsWith("*") && !t.endsWith("**")) { // NOI18N
0656: t = GdbUtils.getBaseType(t);
0657: if (t.equals("char") || t.equals("unsigned char")) { // NOI18N
0658: return true;
0659: }
0660: }
0661: return false;
0662: }
0663:
0664: /**
0665: * Complete and create the field information. Its OK to return null because addField
0666: * ignores it.
0667: */
0668: private AbstractField completeFieldDefinition(
0669: AbstractVariable parent, Map<String, Object> map,
0670: String info) {
0671: String n, t, v;
0672: int count;
0673:
0674: if (info.charAt(0) == '{') { // we've got an anonymous class/struct/union...
0675: try {
0676: count = Integer.parseInt((String) map
0677: .get("<anon-count>")); // NOI18N
0678: } catch (NumberFormatException nfe) {
0679: count = 0;
0680: }
0681: info = info.substring(1, info.length() - 1);
0682: for (int i = 1; i <= count; i++) {
0683: @SuppressWarnings("unchecked")
0684: Map<String, Object> m = (Map<String, Object>) map
0685: .get("<anonymous" + i + ">"); // NOI18N
0686: int start = 0;
0687: int end = GdbUtils.findNextComma(info, 0);
0688: while (end != -1) {
0689: String vfrag = info.substring(start, end).trim();
0690: parent.addField(completeFieldDefinition(parent, m,
0691: vfrag));
0692: start = end + 1;
0693: end = GdbUtils.findNextComma(info, end + 1);
0694: }
0695: parent.addField(completeFieldDefinition(parent, m, info
0696: .substring(start).trim()));
0697: }
0698: } else {
0699: int pos = info.indexOf('=');
0700: if (pos != -1) {
0701: if (info.charAt(0) == '<') {
0702: n = NbBundle.getMessage(AbstractVariable.class,
0703: "LBL_BaseClass"); // NOI18N
0704: t = info.substring(1, pos - 2).trim();
0705: v = info.substring(pos + 1).trim();
0706: if (n.startsWith("_vptr")) { // NOI18N
0707: return null;
0708: }
0709: } else {
0710: n = info.substring(0, pos - 1).trim();
0711: v = info.substring(pos + 1).trim();
0712: if (n.startsWith("_vptr")) { // NOI18N
0713: return null;
0714: }
0715: Object o = map.get(n);
0716: if (o instanceof String) {
0717: t = o.toString();
0718: } else if (o instanceof Map) {
0719: t = (String) ((Map) o).get("<typename>"); // NOI18N
0720: } else if (isNumber(v)) {
0721: t = "int"; // NOI18N - best guess (std::string drops an "int")
0722: } else {
0723: log.warning("Cannot determine field type for "
0724: + n); // NOI18N
0725: return null;
0726: }
0727: }
0728: return new AbstractField(parent, n, t, v);
0729: } else if (info.trim().equals("<No data fields>")) { // NOI18N
0730: return new AbstractField(parent, "", "", info.trim()); // NOI18N
0731: }
0732: }
0733: return null;
0734: }
0735:
0736: private void parseCharArray(AbstractVariable var, String basename,
0737: String type, String value) {
0738: String frag;
0739: int idx = 0;
0740: int pos;
0741: boolean truncated = false;
0742:
0743: while (idx < value.length()) {
0744: if (value.substring(idx).startsWith("\\\"")) { // NOI18N
0745: pos = value.indexOf("\\\",", idx); // NOI18N
0746: if (pos >= 0) {
0747: frag = value.substring(idx + 2, pos);
0748: idx += frag.length() + 4;
0749: } else {
0750: // Reached the end of the string...
0751: if (value.endsWith("\\\"...")) { // NOI18N
0752: frag = value.substring(idx + 2,
0753: value.length() - 5);
0754: truncated = true;
0755: } else {
0756: frag = value.substring(idx + 2,
0757: value.length() - 2);
0758: }
0759: idx = value.length(); // stop iterating...
0760: }
0761: parseCharArrayFragment(var, basename, type, frag);
0762: if (truncated) {
0763: String high;
0764: try {
0765: high = type.substring(type.indexOf("[") + 1,
0766: type.indexOf("]")); // NOI18N
0767: Integer.parseInt(high);
0768: } catch (Exception ex) {
0769: high = "..."; // NOI18N
0770: }
0771:
0772: var.addField(new AbstractField(var, basename + "["
0773: + var.fields.length + "-" + high + "]", // NOI18N
0774: "", "...")); // NOI18N
0775: }
0776: } else if (value.charAt(idx) == ' '
0777: || value.charAt(idx) == ',') {
0778: idx++;
0779: } else {
0780: pos = GdbUtils.findNextComma(value, idx);
0781: if (pos > 0) {
0782: frag = value.substring(idx, pos);
0783: } else {
0784: frag = value.substring(idx);
0785: }
0786: parseRepeatArrayFragment(var, basename, type, frag);
0787: idx += frag.length();
0788: }
0789: }
0790: }
0791:
0792: private void parseRepeatArrayFragment(AbstractVariable var,
0793: String basename, String type, String value) {
0794: String t = type.substring(0, type.indexOf('[')).trim();
0795: int count;
0796: int idx = var.fields.length;
0797: int pos = value.indexOf(' ');
0798: String val = value.substring(0, pos).replace("\\\\", "\\"); // NOI18N
0799: int pos1 = value.indexOf("<repeats "); // NOI18N
0800: int pos2 = value.indexOf(" times>"); // NOI18N
0801:
0802: try {
0803: count = Integer.parseInt(value.substring(pos1 + 9, pos2));
0804: } catch (Exception ex) {
0805: return;
0806: }
0807:
0808: while (--count >= 0) {
0809: var.addField(new AbstractField(var, basename + "[" + idx++
0810: + "]", t, val)); // NOI18N
0811: }
0812: }
0813:
0814: private void parseCharArrayFragment(AbstractVariable var,
0815: String basename, String type, String value) {
0816: String t = type.substring(0, type.indexOf('[')).trim();
0817: int idx = 0;
0818: value = value.replace("\\\\", "\\"); // NOI18N - gdb doubles all backslashes...
0819: int count = value.length();
0820: int fcount = var.fields.length;
0821:
0822: while (idx < count) {
0823: int vstart = idx;
0824: char ch = value.charAt(idx++);
0825: String val;
0826:
0827: if (ch == '\\' && idx < count) {
0828: ch = value.charAt(idx++);
0829: if (ch >= '0' && ch <= '7') { // Octal constant
0830: StringBuilder sb = new StringBuilder();
0831: sb.append('\\');
0832: sb.append(ch);
0833: int i;
0834: for (i = 0; i < 2 && idx < count; i++) {
0835: ch = value.charAt(idx);
0836: if (ch < '0' || ch > '7') {
0837: break;
0838: } else {
0839: sb.append(ch);
0840: idx++;
0841: }
0842: }
0843: val = sb.toString();
0844: } else if (ch == 'x' || ch == 'X') { // Hex constant
0845: StringBuilder sb = new StringBuilder();
0846: sb.append('\\');
0847: sb.append(ch);
0848: int i;
0849: for (i = 0; i < 2 && idx < count; i++) {
0850: ch = value.charAt(idx);
0851: if ((ch >= '0' && ch <= '9')
0852: || (ch >= 'a' && ch <= 'f')
0853: || (ch >= 'A' && ch <= 'F')) {
0854: break;
0855: } else {
0856: sb.append(ch);
0857: idx++;
0858: }
0859: }
0860: val = sb.toString();
0861: } else if (value.substring(idx - 1, idx).matches(
0862: "['\"?abfnrt]")
0863: || ch == '\\') { // NOI18N
0864: val = '\\' + value.substring(idx - 1, idx);
0865: } else {
0866: log
0867: .warning("AV.parseCharArrayFragment: Ignoring invalid character array fragment"); // NOI18N
0868: continue;
0869: }
0870: } else {
0871: val = value.substring(vstart, idx);
0872: }
0873: var.addField(new AbstractField(var, basename + "["
0874: + fcount++ + "]", // NOI18N
0875: t, '\'' + val + '\''));
0876: }
0877: }
0878:
0879: private void createChildrenForArray(String type, String value) {
0880: String t;
0881: int lbpos;
0882: int cbrace = type.lastIndexOf('}');
0883: if (cbrace == -1) {
0884: lbpos = type.indexOf('[');
0885: } else {
0886: lbpos = type.indexOf('[', cbrace);
0887: cbrace = type.indexOf('{');
0888: }
0889: int rbpos = GdbUtils.findMatchingBrace(type, lbpos);
0890: assert rbpos != -1;
0891: int vstart = 0;
0892: int vend;
0893: int size;
0894: int nextbrace = type.indexOf('[', rbpos);
0895: String extra;
0896:
0897: if (nextbrace == -1) {
0898: extra = "";
0899: } else {
0900: extra = type.substring(nextbrace);
0901: }
0902: if (cbrace == -1) {
0903: t = type.substring(0, lbpos).trim() + extra;
0904: } else {
0905: t = type.substring(0, cbrace).trim() + extra;
0906: }
0907:
0908: try {
0909: size = Integer.valueOf(type.substring(lbpos + 1, rbpos));
0910: } catch (Exception ex) {
0911: size = 0;
0912: }
0913: if (t.equals("char")) { // NOI18N
0914: parseCharArray(this , getName(), type, value);
0915: } else {
0916: value = value.substring(1, value.length() - 1);
0917: for (int i = 0; i < size && vstart != -1; i++) {
0918: if (value.charAt(vstart) == '{') {
0919: vend = GdbUtils.findNextComma(value, GdbUtils
0920: .findMatchingCurly(value, vstart));
0921: } else {
0922: vend = GdbUtils.findNextComma(value, vstart);
0923: }
0924: addField(new AbstractField(this , getName() + "[" + i
0925: + "]", t, // NOI18N
0926: vend == -1 ? value.substring(vstart) : value
0927: .substring(vstart, vend)));
0928: vstart = GdbUtils.firstNonWhite(value, vend + 1);
0929: }
0930: }
0931: }
0932:
0933: /**
0934: * Adds a field.
0935: *
0936: * Note: completeFieldDefinition returns null for _vptr data. Its easier to let it return null and
0937: * ignore it here than to check the return value in expandChildrenFromValue and not call addField
0938: * if its null.
0939: *
0940: * @parameter field A field to add.
0941: */
0942: public void addField(Field field) {
0943: if (field != null) {
0944: int n = fields.length;
0945: Field[] fv = new Field[n + 1];
0946: System.arraycopy(fields, 0, fv, 0, n);
0947: fields = fv;
0948: fields[n] = field;
0949: }
0950: }
0951:
0952: public void addPropertyChangeListener(PropertyChangeListener l) {
0953: listeners.add(l);
0954: }
0955:
0956: public void removePropertyChangeListener(PropertyChangeListener l) {
0957: listeners.remove(l);
0958: }
0959:
0960: @Override
0961: public String toString() {
0962: return getFullName(false);
0963: }
0964:
0965: private boolean isNumber(String value) {
0966: try {
0967: Long.parseLong(value);
0968: return true;
0969: } catch (NumberFormatException ex) {
0970: return false;
0971: }
0972: }
0973:
0974: public String getFullName() {
0975: return getFullName(false);
0976: }
0977:
0978: public String getFullName(boolean showBase) {
0979: if (this instanceof AbstractField) {
0980: return ((AbstractField) this ).getFullName(showBase);
0981: } else {
0982: return getName();
0983: }
0984: }
0985:
0986: public class AbstractField extends AbstractVariable implements
0987: Field {
0988:
0989: private AbstractVariable parent;
0990:
0991: public AbstractField(AbstractVariable parent, String name,
0992: String type, String value) {
0993: if (name.startsWith("static ")) { // NOI18N
0994: this .name = name.substring(7);
0995: } else {
0996: this .name = name;
0997: }
0998: int lcurly = type.indexOf('{');
0999: if (lcurly == -1) {
1000: this .type = type;
1001: } else {
1002: int rcurly = type.indexOf('}', lcurly);
1003: this .type = type.substring(0, lcurly).trim()
1004: + type.substring(rcurly + 1);
1005: }
1006: this .parent = parent;
1007: fields = new Field[0];
1008: derefValue = null;
1009: tinfo = null;
1010:
1011: if (Utilities.getOperatingSystem() == Utilities.OS_MAC) {
1012: this .value = GdbUtils.mackHack(value);
1013: } else {
1014: this .value = value;
1015: }
1016: }
1017:
1018: public boolean isStatic() {
1019: return false;
1020: }
1021:
1022: @Override
1023: public String getFullName(boolean showBaseClass) {
1024: String pname; // parent part of name
1025: String fullname;
1026: int pos;
1027:
1028: if (parent instanceof AbstractField) {
1029: pname = ((AbstractField) parent)
1030: .getFullName(showBaseClass);
1031: } else {
1032: pname = parent.getName();
1033: }
1034:
1035: if (name.equals(NbBundle.getMessage(AbstractVariable.class,
1036: "LBL_BaseClass"))) { // NOI18N
1037: if (showBaseClass) {
1038: fullname = pname + ".<" + type + ">"; // NOI18N
1039: } else {
1040: fullname = pname;
1041: }
1042: } else if (name.indexOf('[') != -1) {
1043: if ((pos = pname.lastIndexOf('.')) != -1) {
1044: fullname = pname.substring(0, pos) + '.' + name;
1045: } else {
1046: fullname = name;
1047: }
1048: } else if (GdbUtils.isSimplePointer(parent.getType())
1049: && name.startsWith("*")) { // NOI18N
1050: fullname = '*' + pname;
1051: } else if (GdbUtils.isPointer(parent.getType())) {
1052: fullname = pname + "->" + name; // NOI18N
1053: } else if (name.length() > 0) {
1054: fullname = pname + '.' + name;
1055: } else {
1056: fullname = pname;
1057: }
1058: return fullname;
1059: }
1060: }
1061: }
|