| /******************************************************************************* |
| * Copyright (c) 2007, 2014 Wind River Systems, Inc. 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: |
| * Wind River Systems - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.tcf.internal.debug.ui.model; |
| |
| import java.math.BigInteger; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; |
| import org.eclipse.debug.ui.IDebugUIConstants; |
| import org.eclipse.jface.viewers.CellEditor; |
| import org.eclipse.jface.viewers.ICellModifier; |
| import org.eclipse.jface.viewers.TextCellEditor; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.FontData; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.tcf.debug.ui.ITCFRegister; |
| import org.eclipse.tcf.internal.debug.model.TCFContextState; |
| import org.eclipse.tcf.internal.debug.ui.ColorCache; |
| import org.eclipse.tcf.internal.debug.ui.ImageCache; |
| import org.eclipse.tcf.protocol.IToken; |
| import org.eclipse.tcf.protocol.JSON; |
| import org.eclipse.tcf.services.IRegisters; |
| import org.eclipse.tcf.services.IRunControl; |
| import org.eclipse.tcf.util.TCFDataCache; |
| import org.eclipse.tcf.util.TCFTask; |
| |
| |
| public class TCFNodeRegister extends TCFNode implements IElementEditor, IWatchInExpressions, IDetailsProvider, ITCFRegister { |
| |
| public static final String PROPERTY_REG_REPRESENTATION = "PROPERTY_REGISTER_REPRESENTATION"; |
| |
| private final TCFChildrenRegisters children; |
| private final TCFData<IRegisters.RegistersContext> context; |
| private final TCFData<String> expression_text; |
| private final TCFData<byte[]> value; |
| private final boolean is_stack_frame_register; |
| |
| private byte[] prev_value; |
| private byte[] next_value; |
| |
| private int index; |
| |
| TCFNodeRegister(final TCFNode parent, final String id) { |
| super(parent, id); |
| if (parent instanceof TCFNodeStackFrame) is_stack_frame_register = true; |
| else if (parent instanceof TCFNodeRegister) is_stack_frame_register = ((TCFNodeRegister)parent).is_stack_frame_register; |
| else is_stack_frame_register = false; |
| children = new TCFChildrenRegisters(this); |
| context = new TCFData<IRegisters.RegistersContext>(channel) { |
| @Override |
| protected boolean startDataRetrieval() { |
| IRegisters regs = launch.getService(IRegisters.class); |
| command = regs.getContext(id, new IRegisters.DoneGetContext() { |
| public void doneGetContext(IToken token, Exception error, IRegisters.RegistersContext context) { |
| if (context != null) model.getContextMap().put(id, context); |
| set(token, error, context); |
| } |
| }); |
| return false; |
| } |
| }; |
| expression_text = new TCFData<String>(channel) { |
| @Override |
| protected boolean startDataRetrieval() { |
| Throwable err = null; |
| TCFNodeRegister n = TCFNodeRegister.this; |
| ArrayList<String> names = new ArrayList<String>(); |
| for (;;) { |
| if (!n.context.validate(this)) return false; |
| IRegisters.RegistersContext ctx = n.context.getData(); |
| if (ctx == null) { |
| err = n.context.getError(); |
| break; |
| } |
| String s = ctx.getName(); |
| if (s == null) break; |
| names.add(s); |
| if (!(n.parent instanceof TCFNodeRegister)) break; |
| n = (TCFNodeRegister)n.parent; |
| } |
| if (names.size() == 0 || err != null) { |
| set(null, err, null); |
| } |
| else { |
| StringBuffer bf = new StringBuffer(); |
| boolean first = true; |
| int m = names.size(); |
| while (m > 0) { |
| String s = names.get(--m); |
| boolean need_quotes = false; |
| int l = s.length(); |
| for (int i = 0; i < l; i++) { |
| char ch = s.charAt(i); |
| if (ch >= 'A' && ch <= 'Z') continue; |
| if (ch >= 'a' && ch <= 'z') continue; |
| if (i > 0) { |
| if (ch >= '0' && ch <= '9') continue; |
| if (ch == '_') continue; |
| } |
| need_quotes = true; |
| break; |
| } |
| if (!first) bf.append('.'); |
| if (need_quotes) bf.append("$\""); |
| if (first) bf.append('$'); |
| bf.append(s); |
| if (need_quotes) bf.append('"'); |
| first = false; |
| } |
| set(null, null, bf.toString()); |
| } |
| return true; |
| } |
| }; |
| value = new TCFData<byte[]>(channel) { |
| @Override |
| protected boolean startDataRetrieval() { |
| Boolean b = usePrevValue(this); |
| if (b == null) return false; |
| if (b) { |
| set(null, null, prev_value); |
| return true; |
| } |
| if (!context.validate(this)) return false; |
| IRegisters.RegistersContext ctx = context.getData(); |
| int[] bits = ctx.getBitNumbers(); |
| if (bits != null) { |
| // handle bit fields |
| TCFNodeRegister p = (TCFNodeRegister)parent; |
| if (!p.value.validate(this)) return false; |
| byte[] parent_value = p.value.getData(); |
| byte[] bitfield_value = new byte[(bits.length + 7) / 8]; |
| if (parent_value != null) { |
| for (int pos = 0; pos < bits.length; pos++) { |
| int bit = bits[pos]; |
| if (bit / 8 >= parent_value.length) continue; |
| if ((parent_value[bit / 8] & (1 << (bit % 8))) == 0) continue; |
| bitfield_value[pos / 8] |= 1 << (pos % 8); |
| } |
| } |
| set(null, p.value.getError(), bitfield_value); |
| return true; |
| } |
| else { |
| if (ctx == null || ctx.getSize() <= 0) { |
| set(null, null, null); |
| return true; |
| } |
| final TCFDataCache<?> cache = this; |
| command = ctx.get(new IRegisters.DoneGet() { |
| public void doneGet(IToken token, Exception error, byte[] value) { |
| if (command != token) return; |
| command = null; |
| if (error != null) { |
| Boolean b = usePrevValue(cache); |
| if (b == null) return; |
| if (b) { |
| set(null, null, prev_value); |
| return; |
| } |
| } |
| set(null, error, value); |
| } |
| }); |
| } |
| return false; |
| } |
| }; |
| } |
| |
| public TCFDataCache<IRegisters.RegistersContext> getContext() { |
| return context; |
| } |
| |
| public TCFDataCache<byte[]> getValue() { |
| return value; |
| } |
| |
| public TCFChildren getChildren() { |
| return children; |
| } |
| |
| public TCFDataCache<String> getExpressionText() { |
| return expression_text; |
| } |
| |
| void setIndex(int index) { |
| this.index = index; |
| } |
| |
| private Boolean usePrevValue(Runnable done) { |
| // Check if view should show old value. |
| // Old value is shown if context is running or |
| // stack trace does not contain expression parent frame. |
| // Return null if waiting for cache update. |
| if (prev_value == null) return false; |
| TCFNode p = parent; |
| while (p instanceof TCFNodeRegister) p = p.parent; |
| if (p instanceof TCFNodeStackFrame) { |
| TCFNodeExecContext exe = (TCFNodeExecContext)p.parent; |
| TCFDataCache<TCFContextState> state_cache = exe.getState(); |
| if (!state_cache.validate(done)) return null; |
| TCFContextState state = state_cache.getData(); |
| if (state == null || !state.is_suspended) return true; |
| TCFChildrenStackTrace stack_trace_cache = exe.getStackTrace(); |
| if (!stack_trace_cache.validate(done)) return null; |
| if (stack_trace_cache.getData().get(p.id) == null) return true; |
| } |
| else if (p instanceof TCFNodeExecContext) { |
| TCFNodeExecContext exe = (TCFNodeExecContext)p; |
| TCFDataCache<IRunControl.RunControlContext> ctx_cache = exe.getRunContext(); |
| if (!ctx_cache.validate(done)) return null; |
| IRunControl.RunControlContext ctx_data = ctx_cache.getData(); |
| if (ctx_data != null && ctx_data.hasState()) { |
| TCFDataCache<TCFContextState> state_cache = exe.getState(); |
| if (!state_cache.validate(done)) return null; |
| TCFContextState state_data = state_cache.getData(); |
| if (state_data == null || !state_data.is_suspended) return true; |
| } |
| } |
| return false; |
| } |
| |
| public boolean getDetailText(StyledStringBuffer bf, Runnable done) { |
| if (!context.validate(done)) return false; |
| if (!value.validate(done)) return false; |
| if (context.getError() != null) { |
| bf.append(context.getError(), ColorCache.rgb_error); |
| return true; |
| } |
| IRegisters.RegistersContext ctx = context.getData(); |
| if (ctx != null) { |
| if (ctx.getDescription() != null) { |
| bf.append(ctx.getDescription()); |
| bf.append('\n'); |
| } |
| } |
| if (value.getError() != null) { |
| bf.append(value.getError(), ColorCache.rgb_error); |
| } |
| else { |
| byte[] v = value.getData(); |
| if (v != null) { |
| bf.append("Hex: ", SWT.BOLD); |
| bf.append(toNumberString(16), StyledStringBuffer.MONOSPACED); |
| bf.append(", "); |
| bf.append("Dec: ", SWT.BOLD); |
| bf.append(toNumberString(10), StyledStringBuffer.MONOSPACED); |
| bf.append(", "); |
| bf.append("Oct: ", SWT.BOLD); |
| bf.append(toNumberString(8), StyledStringBuffer.MONOSPACED); |
| if ("PC".equals(ctx.getRole())) { |
| TCFNode p = parent; |
| while (p != null) { |
| if (p instanceof TCFNodeExecContext) { |
| TCFNodeExecContext exe = (TCFNodeExecContext)p; |
| BigInteger addr = TCFNumberFormat.toBigInteger(v, 0, v.length, ctx.isBigEndian(), false); |
| if (!exe.appendPointedObject(bf, addr, done)) return false; |
| break; |
| } |
| p = p.parent; |
| } |
| } |
| bf.append('\n'); |
| bf.append("Bin: ", SWT.BOLD); |
| bf.append(toNumberString(2), StyledStringBuffer.MONOSPACED); |
| bf.append('\n'); |
| } |
| } |
| if (ctx != null) { |
| int l = bf.length(); |
| int[] bits = ctx.getBitNumbers(); |
| BigInteger addr = JSON.toBigInteger(ctx.getMemoryAddress()); |
| if (bits != null && addr == null && parent instanceof TCFNodeRegister) { |
| if (!((TCFNodeRegister)parent).context.validate(done)) return false; |
| IRegisters.RegistersContext parent_ctx = ((TCFNodeRegister)parent).context.getData(); |
| if (parent_ctx != null) addr = JSON.toBigInteger(parent_ctx.getMemoryAddress()); |
| } |
| if (addr != null) { |
| bf.append("Address: ", SWT.BOLD); |
| bf.append("0x", StyledStringBuffer.MONOSPACED); |
| bf.append(addr.toString(16), StyledStringBuffer.MONOSPACED); |
| } |
| if (bits != null) { |
| if (bits.length > 0) { |
| if (l < bf.length()) bf.append(", "); |
| bf.append("Bits: ", SWT.BOLD); |
| bf.append("["); |
| for (int i = bits.length; i > 0; i--) { |
| if (i != bits.length) bf.append(","); |
| bf.append(bits[i - 1]); |
| } |
| bf.append("]"); |
| } |
| } |
| else { |
| BigInteger size = JSON.toBigInteger(ctx.getSize()); |
| if (size != null && size.compareTo(BigInteger.ZERO) > 0) { |
| if (l < bf.length()) bf.append(", "); |
| bf.append("Size: ", SWT.BOLD); |
| bf.append(size.toString(10), StyledStringBuffer.MONOSPACED); |
| bf.append(size.compareTo(BigInteger.ONE) == 0 ? " byte" : " bytes"); |
| } |
| } |
| if (ctx.isReadable()) { |
| if (l < bf.length()) bf.append(", "); |
| bf.append("readable"); |
| } |
| if (ctx.isReadOnce()) { |
| if (l < bf.length()) bf.append(", "); |
| bf.append("read once"); |
| } |
| if (ctx.isWriteable()) { |
| if (l < bf.length()) bf.append(", "); |
| bf.append("writable"); |
| } |
| if (ctx.isWriteOnce()) { |
| if (l < bf.length()) bf.append(", "); |
| bf.append("write once"); |
| } |
| if (ctx.hasSideEffects()) { |
| if (l < bf.length()) bf.append(", "); |
| bf.append("side effects"); |
| } |
| if (l < bf.length()) bf.append('\n'); |
| } |
| return true; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private boolean getChildren(IPresentationContext ctx, List<TCFNode> list, Runnable done) { |
| AtomicBoolean b = new AtomicBoolean(); |
| if (!isRepresentationGroup(b, done)) return false; |
| boolean rep_group = b.get(); |
| String rep_id = null; |
| if (rep_group) { |
| Map<String,String> map = (Map<String,String>)ctx.getProperty( |
| TCFNodeRegister.PROPERTY_REG_REPRESENTATION); |
| if (map != null) rep_id = map.get(id); |
| } |
| for (TCFNode child : children.toArray()) { |
| if (!rep_group || child.id.equals(rep_id)) list.add(child); |
| } |
| return true; |
| } |
| |
| @Override |
| protected boolean getData(IHasChildrenUpdate result, Runnable done) { |
| List<TCFNode> list = new ArrayList<TCFNode>(); |
| if (!getChildren(result.getPresentationContext(), list, done)) return false; |
| result.setHasChilren(list.size() > 0); |
| return true; |
| } |
| |
| @Override |
| protected boolean getData(IChildrenCountUpdate result, Runnable done) { |
| List<TCFNode> list = new ArrayList<TCFNode>(); |
| if (!getChildren(result.getPresentationContext(), list, done)) return false; |
| result.setChildCount(list.size()); |
| return true; |
| } |
| |
| @Override |
| protected boolean getData(IChildrenUpdate result, Runnable done) { |
| List<TCFNode> list = new ArrayList<TCFNode>(); |
| if (!getChildren(result.getPresentationContext(), list, done)) return false; |
| int r_offset = result.getOffset(); |
| int r_length = result.getLength(); |
| for (int n = r_offset; n < r_offset + r_length && n < list.size(); n++) { |
| result.setChild(list.get(n), n); |
| } |
| return true; |
| } |
| |
| @Override |
| protected boolean getData(ILabelUpdate result, Runnable done) { |
| TCFDataCache<?> pending = null; |
| if (!context.validate()) pending = context; |
| if (!value.validate()) pending = value; |
| if (pending != null) { |
| pending.wait(done); |
| return false; |
| } |
| String[] cols = result.getColumnIds(); |
| if (cols == null) { |
| setLabel(result, -1, 16); |
| } |
| else { |
| IRegisters.RegistersContext ctx = context.getData(); |
| for (int i = 0; i < cols.length; i++) { |
| String c = cols[i]; |
| if (ctx == null) { |
| result.setForeground(ColorCache.rgb_error, i); |
| result.setLabel("N/A", i); |
| } |
| else if (c.equals(TCFColumnPresentationRegister.COL_NAME)) { |
| result.setLabel(ctx.getName(), i); |
| } |
| else if (c.equals(TCFColumnPresentationRegister.COL_HEX_VALUE)) { |
| setLabel(result, i, 16); |
| } |
| else if (c.equals(TCFColumnPresentationRegister.COL_DEC_VALUE)) { |
| setLabel(result, i, 10); |
| } |
| else if (c.equals(TCFColumnPresentationRegister.COL_DESCRIPTION)) { |
| result.setLabel(ctx.getDescription(), i); |
| } |
| else if (c.equals(TCFColumnPresentationRegister.COL_READBLE)) { |
| result.setLabel(bool(ctx.isReadable()), i); |
| } |
| else if (c.equals(TCFColumnPresentationRegister.COL_READ_ONCE)) { |
| result.setLabel(bool(ctx.isReadOnce()), i); |
| } |
| else if (c.equals(TCFColumnPresentationRegister.COL_WRITEABLE)) { |
| result.setLabel(bool(ctx.isWriteable()), i); |
| } |
| else if (c.equals(TCFColumnPresentationRegister.COL_WRITE_ONCE)) { |
| result.setLabel(bool(ctx.isWriteOnce()), i); |
| } |
| else if (c.equals(TCFColumnPresentationRegister.COL_SIDE_EFFECTS)) { |
| result.setLabel(bool(ctx.hasSideEffects()), i); |
| } |
| else if (c.equals(TCFColumnPresentationRegister.COL_VOLATILE)) { |
| result.setLabel(bool(ctx.isVolatile()), i); |
| } |
| else if (c.equals(TCFColumnPresentationRegister.COL_FLOAT)) { |
| result.setLabel(bool(ctx.isFloat()), i); |
| } |
| else if (c.equals(TCFColumnPresentationRegister.COL_MNEMONIC)) { |
| result.setLabel(getMnemonic(ctx), i); |
| } |
| } |
| } |
| next_value = value.getData(); |
| if (prev_value != null && next_value != null) { |
| boolean changed = false; |
| if (prev_value.length != next_value.length) { |
| changed = true; |
| } |
| else { |
| for (int i = 0; i < prev_value.length; i++) { |
| if (prev_value[i] != next_value[i]) changed = true; |
| } |
| } |
| if (changed) { |
| if (cols != null) { |
| for (int i = 1; i < cols.length; i++) { |
| result.setBackground(ColorCache.rgb_highlight, i); |
| } |
| } |
| else { |
| result.setForeground(ColorCache.rgb_no_columns_color_change, 0); |
| } |
| } |
| } |
| result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_REGISTER), 0); |
| return true; |
| } |
| |
| private void setLabel(ILabelUpdate result, int col, int radix) { |
| String name = null; |
| IRegisters.RegistersContext ctx = context.getData(); |
| if (ctx != null) name = ctx.getName(); |
| Throwable error = context.getError(); |
| if (error == null) error = value.getError(); |
| byte[] data = value.getData(); |
| if (error != null && col >= 0) { |
| result.setForeground(ColorCache.rgb_error, col); |
| result.setLabel("N/A", col); |
| } |
| else if (data != null && error == null) { |
| String s = toNumberString(radix); |
| if (col >= 0) { |
| result.setLabel(s, col); |
| } |
| else if (name != null) { |
| result.setLabel(name + " = " + s, 0); |
| } |
| } |
| else if (col < 0 && name != null) { |
| result.setLabel(name, 0); |
| } |
| } |
| |
| @Override |
| protected void getFontData(ILabelUpdate update, String view_id) { |
| FontData fn = TCFModelFonts.getNormalFontData(view_id); |
| String[] cols = update.getColumnIds(); |
| if (cols == null || cols.length == 0) { |
| update.setFontData(fn, 0); |
| } |
| else { |
| String[] ids = update.getColumnIds(); |
| for (int i = 0; i < cols.length; i++) { |
| if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(ids[i]) || |
| TCFColumnPresentationExpression.COL_DEC_VALUE.equals(ids[i])) { |
| update.setFontData(TCFModelFonts.getMonospacedFontData(view_id), i); |
| } |
| else { |
| update.setFontData(fn, i); |
| } |
| } |
| } |
| } |
| |
| private String toNumberString(int radix) { |
| IRegisters.RegistersContext ctx = context.getData(); |
| byte[] data = value.getData(); |
| int[] bits = ctx.getBitNumbers(); |
| if (ctx == null || data == null) return "N/A"; |
| if (radix == 2) { |
| StringBuffer bf = new StringBuffer(); |
| int i = data.length * 8; |
| if (bits != null) i = bits.length; |
| while (i > 0) { |
| if (i % 4 == 0 && bf.length() > 0) bf.append(','); |
| i--; |
| int j = i / 8; |
| if (ctx.isBigEndian()) j = data.length - j - 1; |
| if ((data[j] & (1 << (i % 8))) != 0) { |
| bf.append('1'); |
| } |
| else { |
| bf.append('0'); |
| } |
| } |
| return bf.toString(); |
| } |
| if (radix == 10 && ctx.isFloat()) { |
| String s = TCFNumberFormat.toFPString(data, 0, data.length, ctx.isBigEndian()); |
| if (s != null) return s; |
| } |
| BigInteger b = TCFNumberFormat.toBigInteger(data, ctx.isBigEndian(), false); |
| String s = b.toString(radix); |
| switch (radix) { |
| case 8: |
| if (!s.startsWith("0")) s = "0" + s; |
| break; |
| case 16: |
| if (s.length() < (bits == null ? data.length * 2 : (bits.length + 3) / 4)) { |
| StringBuffer bf = new StringBuffer(); |
| while (bf.length() + s.length() < data.length * 2) bf.append('0'); |
| bf.append(s); |
| s = bf.toString(); |
| } |
| break; |
| } |
| return s; |
| } |
| |
| private String bool(boolean b) { |
| return b ? "yes" : "no"; |
| } |
| |
| private String getMnemonic(IRegisters.RegistersContext ctx) { |
| if (value.getData() != null) { |
| IRegisters.NamedValue[] arr = ctx.getNamedValues(); |
| if (arr != null) { |
| for (IRegisters.NamedValue n : arr) { |
| if (Arrays.equals(n.getValue(), value.getData())) return n.getName(); |
| } |
| } |
| } |
| return ""; |
| } |
| |
| private void postStateChangedDelta() { |
| for (TCFModelProxy p : model.getModelProxies()) { |
| if (!IDebugUIConstants.ID_REGISTER_VIEW.equals(p.getPresentationContext().getId())) continue; |
| p.addDelta(this, IModelDelta.STATE); |
| } |
| } |
| |
| void onValueChanged() { |
| prev_value = next_value; |
| value.reset(); |
| TCFNode n = parent; |
| while (n != null) { |
| if (n instanceof TCFNodeExecContext) { |
| ((TCFNodeExecContext)n).onRegisterValueChanged(); |
| break; |
| } |
| else if (n instanceof TCFNodeRegister) { |
| TCFNodeRegister r = (TCFNodeRegister)n; |
| if (r.value.isValid() && r.value.getData() != null) { |
| r.value.reset(); |
| r.postStateChangedDelta(); |
| } |
| } |
| n = n.parent; |
| } |
| children.onParentValueChanged(); |
| postStateChangedDelta(); |
| } |
| |
| void onParentValueChanged() { |
| value.reset(); |
| children.onParentValueChanged(); |
| postStateChangedDelta(); |
| } |
| |
| void onSuspended(boolean func_call) { |
| if (!func_call) { |
| prev_value = next_value; |
| value.reset(); |
| // Unlike thread registers, stack frame register list must be retrieved on every suspend |
| if (is_stack_frame_register) children.reset(); |
| // No need to post delta: parent posted CONTENT |
| } |
| else if (value.isValid() && value.getError() != null) { |
| value.reset(); |
| } |
| children.onSuspended(func_call); |
| } |
| |
| void onRegistersChanged() { |
| children.onRegistersChanged(); |
| expression_text.reset(); |
| context.reset(); |
| value.reset(); |
| // No need to post delta: parent posted CONTENT |
| } |
| |
| public CellEditor getCellEditor(IPresentationContext context, String column_id, Object element, Composite parent) { |
| assert element == this; |
| if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(column_id)) { |
| return new TextCellEditor(parent); |
| } |
| if (TCFColumnPresentationRegister.COL_DEC_VALUE.equals(column_id)) { |
| return new TextCellEditor(parent); |
| } |
| return null; |
| } |
| |
| private static final ICellModifier cell_modifier = new ICellModifier() { |
| private Object original_value; |
| |
| public boolean canModify(Object element, final String property) { |
| final TCFNodeRegister node = (TCFNodeRegister)element; |
| try { |
| return new TCFTask<Boolean>() { |
| public void run() { |
| if (!node.context.validate(this)) return; |
| IRegisters.RegistersContext ctx = node.context.getData(); |
| if (ctx != null && ctx.isWriteable()) { |
| if (!ctx.isReadable()) { |
| done(Boolean.TRUE); |
| return; |
| } |
| if (!node.value.validate(this)) return; |
| if (node.value.getError() == null) { |
| if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(property)) { |
| done(TCFNumberFormat.isValidHexNumber(node.toNumberString(16)) == null); |
| return; |
| } |
| if (TCFColumnPresentationRegister.COL_DEC_VALUE.equals(property)) { |
| done(TCFNumberFormat.isValidDecNumber(true, node.toNumberString(10)) == null); |
| return; |
| } |
| } |
| } |
| done(Boolean.FALSE); |
| } |
| }.get(1, TimeUnit.SECONDS); |
| } |
| catch (Exception e) { |
| return false; |
| } |
| } |
| |
| public Object getValue(Object element, final String property) { |
| original_value = null; |
| final TCFNodeRegister node = (TCFNodeRegister)element; |
| try { |
| return original_value = new TCFTask<String>() { |
| public void run() { |
| if (!node.context.validate(this)) return; |
| IRegisters.RegistersContext ctx = node.context.getData(); |
| if (!ctx.isReadable()) { |
| done("0"); |
| return; |
| } |
| if (!node.value.validate(this)) return; |
| if (node.value.getError() == null) { |
| if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(property)) { |
| done(node.toNumberString(16)); |
| return; |
| } |
| if (TCFColumnPresentationRegister.COL_DEC_VALUE.equals(property)) { |
| done(node.toNumberString(10)); |
| return; |
| } |
| } |
| done(null); |
| } |
| }.get(1, TimeUnit.SECONDS); |
| } |
| catch (Exception e) { |
| return null; |
| } |
| } |
| |
| public void modify(Object element, final String property, final Object value) { |
| if (value == null) return; |
| if (original_value != null && original_value.equals(value)) return; |
| final TCFNodeRegister node = (TCFNodeRegister)element; |
| try { |
| new TCFTask<Boolean>() { |
| public void run() { |
| try { |
| if (!node.context.validate(this)) return; |
| IRegisters.RegistersContext ctx = node.context.getData(); |
| if (ctx != null && ctx.isWriteable()) { |
| byte[] bf = null; |
| boolean is_float = ctx.isFloat(); |
| int size = ctx.getSize(); |
| boolean big_endian = ctx.isBigEndian(); |
| String input = (String)value; |
| String error = null; |
| int[] bits = ctx.getBitNumbers(); |
| if (bits != null) size = (bits.length + 7) / 8; |
| if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(property)) { |
| if (input.startsWith("0x")) input = input.substring(2); |
| error = TCFNumberFormat.isValidHexNumber(input); |
| if (error == null) bf = TCFNumberFormat.toByteArray(input, 16, false, size, false, big_endian); |
| } |
| else if (TCFColumnPresentationRegister.COL_DEC_VALUE.equals(property)) { |
| error = TCFNumberFormat.isValidDecNumber(is_float, input); |
| if (error == null) bf = TCFNumberFormat.toByteArray(input, 10, is_float, size, is_float, big_endian); |
| } |
| if (error != null) throw new Exception("Invalid value: " + value, new Exception(error)); |
| if (bf != null) { |
| // handle bit fields |
| if (bits != null) { |
| TCFNodeRegister p = (TCFNodeRegister)node.parent; |
| if (!p.value.validate(this)) return; |
| byte[] parent_value = p.value.getData(); |
| if (!p.context.validate(this)) return; |
| IRegisters.RegistersContext parent_context = p.context.getData(); |
| |
| if (parent_context != null && parent_value != null) { |
| byte[] new_value = new byte[parent_value.length]; |
| System.arraycopy(parent_value, 0, new_value, 0, parent_value.length); |
| for (int pos = 0; pos < bits.length; pos++) { |
| int bit = bits[pos]; |
| if (bit / 8 >= new_value.length) continue; |
| if ((bf[pos / 8] & (1 << (pos % 8))) == 0) { |
| new_value[bit / 8] &= ~(1 << (bit % 8)); |
| } |
| else { |
| new_value[bit / 8] |= 1 << (bit % 8); |
| } |
| } |
| parent_context.set(new_value, new IRegisters.DoneSet() { |
| public void doneSet(IToken token, Exception error) { |
| TCFNodeRegister p = (TCFNodeRegister)node.parent; |
| if (error != null) { |
| p.model.showMessageBox("Cannot modify register value", error); |
| done(Boolean.FALSE); |
| } |
| else { |
| p.value.reset(); |
| p.postStateChangedDelta(); |
| done(Boolean.TRUE); |
| } |
| } |
| }); |
| return; |
| } |
| } |
| else { |
| ctx.set(bf, new IRegisters.DoneSet() { |
| public void doneSet(IToken token, Exception error) { |
| if (error != null) { |
| node.model.showMessageBox("Cannot modify register value", error); |
| done(Boolean.FALSE); |
| } |
| else { |
| node.value.reset(); |
| node.postStateChangedDelta(); |
| done(Boolean.TRUE); |
| } |
| } |
| }); |
| return; |
| } |
| } |
| } |
| done(Boolean.FALSE); |
| } |
| catch (Throwable x) { |
| node.model.showMessageBox("Cannot modify register value", x); |
| done(Boolean.FALSE); |
| } |
| } |
| }.get(10, TimeUnit.SECONDS); |
| } |
| catch (TimeoutException e) { |
| node.model.showMessageBox("Timeout modifying register value", new Exception("No response for 10 seconds.")); |
| } |
| catch (Exception e) { |
| node.model.showMessageBox("Error modifying register value", e); |
| } |
| } |
| }; |
| |
| public ICellModifier getCellModifier(IPresentationContext context, Object element) { |
| assert element == this; |
| return cell_modifier; |
| } |
| |
| @Override |
| public int compareTo(TCFNode n) { |
| if (n instanceof TCFNodeRegister) { |
| TCFNodeRegister r = (TCFNodeRegister)n; |
| if (index < r.index) return -1; |
| if (index > r.index) return +1; |
| } |
| return id.compareTo(n.id); |
| } |
| |
| /** |
| * Check if this register has multiple representations. |
| */ |
| public boolean isRepresentationGroup(AtomicBoolean res, Runnable done) { |
| res.set(false); |
| HashSet<Integer> offsets = new HashSet<Integer>(); |
| if (!context.validate(done)) return false; |
| if (!children.validate(done)) return false; |
| IRegisters.RegistersContext reg_ctx = context.getData(); |
| if (reg_ctx == null) return true; |
| if (reg_ctx.getSize() == 0) return true; |
| for (TCFNode child_node : children.toArray()) { |
| TCFNodeRegister child_reg = (TCFNodeRegister)child_node; |
| if (!child_reg.context.validate(done)) return false; |
| IRegisters.RegistersContext ctx = child_reg.context.getData(); |
| if (ctx == null) continue; |
| int offs = ctx.getOffset(); |
| if (offs >= 0) { |
| if (!offsets.add(Integer.valueOf(offs))) { |
| res.set(true); |
| return true; |
| } |
| continue; |
| } |
| // TODO: checking grand children should not be needed |
| if (!child_reg.children.validate(done)) return false; |
| for (TCFNode grand_child_node : child_reg.children.toArray()) { |
| TCFNodeRegister grand_child_reg = (TCFNodeRegister)grand_child_node; |
| if (!grand_child_reg.context.validate(done)) return false; |
| ctx = grand_child_reg.context.getData(); |
| if (ctx == null) continue; |
| offs = ctx.getOffset(); |
| if (offs >= 0) { |
| if (!offsets.add(Integer.valueOf(offs))) { |
| res.set(true); |
| return true; |
| } |
| } |
| } |
| } |
| return true; |
| } |
| } |