/******************************************************************************* * Copyright (c) 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.debug.core.xdebug.dbgp.model; import static org.eclipse.php.internal.debug.core.model.IVariableFacet.Facet.VIRTUAL_ARRAY_MEMBER; import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IValue; import org.eclipse.debug.core.model.IVariable; import org.eclipse.php.internal.debug.core.PHPDebugCoreMessages; import org.eclipse.php.internal.debug.core.model.IVariableFacet; import org.eclipse.php.internal.debug.core.model.IVariableFacet.Facet; import org.eclipse.php.internal.debug.core.model.IVirtualPartition.IVariableProvider; import org.eclipse.php.internal.debug.core.model.VirtualPartition; import org.eclipse.php.internal.debug.core.xdebug.dbgp.DBGpLogger; import org.eclipse.php.internal.debug.core.xdebug.dbgp.protocol.DBGpResponse; import org.w3c.dom.Node; /** * DBGp string value. * * @author Bartlomiej Laczkowski */ public class DBGpStringValue extends AbstractDBGpValue { private class InfoVariable extends DBGpElement implements IVariable, IVariableFacet { private String fName; private IValue fValue; private Set<Facet> fFacets = new HashSet<Facet>(); public InfoVariable(String name, IValue value, IDebugTarget debugTarget, Facet... facets) { super(debugTarget); this.fName = name; this.fValue = value; addFacets(facets); } public InfoVariable(String name, IValue value, IDebugTarget debugTarget) { super(debugTarget); this.fName = name; this.fValue = value; } public String getName() throws DebugException { return fName; } public String getReferenceTypeName() throws DebugException { return fValue.getReferenceTypeName(); } public IValue getValue() throws DebugException { return fValue; } public boolean hasValueChanged() throws DebugException { return false; } public void setValue(String expression) throws DebugException { } public void setValue(IValue value) throws DebugException { } public boolean supportsValueModification() { return false; } public boolean verifyValue(String expression) throws DebugException { return true; } public boolean verifyValue(IValue value) throws DebugException { return true; } @Override public boolean hasFacet(Facet facet) { return fFacets.contains(facet); } @Override public void addFacets(Facet... facets) { for (Facet facet : facets) this.fFacets.add(facet); } } private class InfoByteValue extends DBGpElement implements IValue { private byte fValue; public InfoByteValue(byte value, IDebugTarget debugTarget) { super(debugTarget); this.fValue = value; } public String getReferenceTypeName() throws DebugException { return "byte"; //$NON-NLS-1$ } public String getValueString() throws DebugException { String valStr = Integer.toHexString(fValue & 0xFF); if (valStr.length() == 1) { valStr = "0" + valStr; //$NON-NLS-1$ } return valStr; } public IVariable[] getVariables() throws DebugException { return new IVariable[0]; } public boolean hasVariables() throws DebugException { return false; } public boolean isAllocated() throws DebugException { return false; } } private class InfoLengthValue extends DBGpElement implements IValue { private int fCurrentLength; private int fTotalLength; public InfoLengthValue(int currentValue, int wantedValue, IDebugTarget debugTarget) { super(debugTarget); this.fCurrentLength = currentValue; this.fTotalLength = wantedValue; } public String getReferenceTypeName() throws DebugException { return null; } public String getValueString() throws DebugException { if (fCurrentLength == fTotalLength) { return Integer.toString(fCurrentLength); } else { return Integer.toString(fCurrentLength) + " (" //$NON-NLS-1$ + Integer.toString(fTotalLength) + ")"; //$NON-NLS-1$ } } public IVariable[] getVariables() throws DebugException { return new IVariable[0]; } public boolean hasVariables() throws DebugException { return false; } public boolean isAllocated() throws DebugException { return false; } } private boolean fIsComplete = false; private int fRequiredBytes; private IVariable[] fStringInfo = null; private byte[] fValueBytes; /** * Creates new DBGp string value. * * @param owner */ public DBGpStringValue(DBGpVariable owner) { super(owner); } // TODO - where to show complete value? public boolean isComplete() { return fIsComplete; } public int getRequiredBytes() { return fRequiredBytes; } /* * (non-Javadoc) * * @see * org.eclipse.php.internal.debug.core.xdebug.dbgp.model.AbstractDBGpValue# * getVariables() */ @Override public synchronized IVariable[] getVariables() throws DebugException { if (fStringInfo == null) { createVariables(); } return fStringInfo; } /* * (non-Javadoc) * * @see * org.eclipse.php.internal.debug.core.xdebug.dbgp.model.AbstractDBGpValue# * hasVariables() */ @Override public boolean hasVariables() throws DebugException { return true; } /* * (non-Javadoc) * * @see * org.eclipse.php.internal.debug.core.xdebug.dbgp.model.AbstractDBGpValue# * update(org.w3c.dom.Node) */ @Override protected void update(Node descriptor) { super.update(descriptor); // Set up additional string data String size = DBGpResponse.getAttribute(descriptor, "size"); //$NON-NLS-1$ int byteLength = -1; try { byteLength = Integer.parseInt(size); } catch (NumberFormatException e) { } DBGpValueData data = new DBGpValueData(descriptor); fValueBytes = data.getValueBytes(); if (fValueBytes == null) { // We didn't get a binary representation, so we must create one String XMLEncoding = descriptor.getOwnerDocument().getInputEncoding(); if (XMLEncoding == null) { XMLEncoding = ((DBGpTarget) getDebugTarget()).getBinaryEncoding(); } try { fValueBytes = fValueString.getBytes(XMLEncoding); } catch (UnsupportedEncodingException uee) { DBGpLogger.logException("Unexpected encoding problem", this, //$NON-NLS-1$ uee); // Use the platform encoding fValueBytes = fValueString.getBytes(); } } int actualLength = fValueBytes.length; fIsComplete = actualLength >= byteLength; fRequiredBytes = byteLength; } /* * (non-Javadoc) * * @see * org.eclipse.php.internal.debug.core.xdebug.dbgp.model.AbstractDBGpValue# * createValueString(org.eclipse.php.internal.debug.core.xdebug.dbgp.model. * AbstractDBGpValue.DBGpValueData) */ @Override protected String createValueString(DBGpValueData valueData) { fStringInfo = null; String valueString = valueData.getValueString(); if (valueString != null && valueString.trim().length() > 0) { return valueString; } else { fValueBytes = new byte[0]; return ""; //$NON-NLS-1$ } } /* * (non-Javadoc) * * @see * org.eclipse.php.internal.debug.core.xdebug.dbgp.model.AbstractDBGpValue# * supportsValueModification() */ @Override protected boolean supportsValueModification() { return true; } /* * (non-Javadoc) * * @see org.eclipse.php.xdebug.core.dbgp.model.DBGpValue#setValue(java.lang. * String ) */ protected void setValue(String value) { fStringInfo = null; if (value != null) { fValueString = value.trim(); } else { fValueString = IDBGpModelConstants.INVALID_VAR_CONTENT; } byte[] newBytes; try { newBytes = fValueString.getBytes(((DBGpTarget) getDebugTarget()).getBinaryEncoding()); } catch (UnsupportedEncodingException e) { DBGpLogger.logException("unexpected encoding problem", this, e); //$NON-NLS-1$ newBytes = fValueString.getBytes(); } fValueBytes = newBytes; fRequiredBytes = newBytes.length; } /* * (non-Javadoc) * * @see * org.eclipse.php.internal.debug.core.xdebug.dbgp.model.AbstractDBGpValue# * verifyValue(java.lang.String) */ protected boolean verifyValue(String expression) { // any string is ok return true; } /** * Creates string info variables (length and virtual array of bytes). */ protected void createVariables() { int byteCount = fValueBytes.length; fStringInfo = new IVariable[1]; // Add length element fStringInfo[0] = new InfoVariable(PHPDebugCoreMessages.XDebug_DBGpStringValue_0, new InfoLengthValue(byteCount, getRequiredBytes(), getDebugTarget()), getDebugTarget(), Facet.VIRTUAL_LENGTH); // Add pseudo-bytes directly or as a partitions final int childLimit = 100; IVariable[] byteVariables = null; // determine the number of variables to return if (byteCount > childLimit) { // Split to partitions int subCount = (int) Math.ceil((double) byteCount / (double) childLimit); byteVariables = new IVariable[subCount]; for (int i = 0; i < subCount; i++) { int startIndex = i * childLimit; int endIndex = (i + 1) * childLimit - 1; if (endIndex > byteCount) { endIndex = byteCount - 1; } final int partitionSize = (endIndex - startIndex) + 1; final IVariable[] partitionVariables = new IVariable[partitionSize]; for (int j = startIndex; j <= endIndex; j++) { IValue byteValue = new InfoByteValue(fValueBytes[j], getDebugTarget()); partitionVariables[j - startIndex] = new InfoVariable('[' + Integer.toString(j) + ']', byteValue, getDebugTarget(), VIRTUAL_ARRAY_MEMBER); } IVariable partition = new VirtualPartition(this, new IVariableProvider() { @Override public IVariable[] getVariables() throws DebugException { return partitionVariables; } }, startIndex, endIndex); byteVariables[i] = partition; } } else { byteVariables = new InfoVariable[byteCount]; for (int i = 0; i < byteCount; i++) { IValue byteValue = new InfoByteValue(fValueBytes[i], getDebugTarget()); byteVariables[i] = new InfoVariable('[' + Integer.toString(i) + ']', byteValue, getDebugTarget(), VIRTUAL_ARRAY_MEMBER); } } // Add byte variables to info IVariable[] concat = Arrays.copyOf(fStringInfo, fStringInfo.length + byteVariables.length); System.arraycopy(byteVariables, 0, concat, fStringInfo.length, byteVariables.length); fStringInfo = concat; } }