/******************************************************************************* * Copyright (c) 2007, 2015 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.services.remote; import java.io.IOException; import java.math.BigInteger; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.tcf.core.Command; import org.eclipse.tcf.protocol.IChannel; import org.eclipse.tcf.protocol.IErrorReport; import org.eclipse.tcf.protocol.IToken; import org.eclipse.tcf.protocol.JSON; import org.eclipse.tcf.services.IMemory; public class MemoryProxy implements IMemory { private final IChannel channel; private final Map<MemoryListener,IChannel.IEventListener> listeners = new HashMap<MemoryListener,IChannel.IEventListener>(); private static class Range implements Comparable<Range> { final int offs; final int size; final int stat; final String msg; Range(int offs, int size, int stat, String msg) { this.offs = offs; this.size = size; this.stat = stat; this.msg = msg; } @Override public int compareTo(Range o) { if (offs < o.offs) return -1; if (offs > o.offs) return +1; return 0; } @Override public boolean equals(Object o) { if (o instanceof Range) return compareTo((Range)o) == 0; return false; } @Override public int hashCode() { return offs; } } private class MemoryErrorReport extends MemoryError implements ErrorOffset, IErrorReport { private static final long serialVersionUID = 796525409870265390L; private final Map<String,Object> attrs; private final Range[] ranges; @SuppressWarnings("unchecked") MemoryErrorReport(String msg, Map<String,Object> attrs, Number addr, Object ranges) { super(msg); this.attrs = attrs; Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)ranges; this.ranges = c == null ? null : new Range[c.size()]; if (c != null) { int n = 0; BigInteger addr_bi = JSON.toBigInteger(addr); for (Map<String,Object> m : c) { Number x = (Number)m.get(RANGE_KEY_ADDR); BigInteger y = JSON.toBigInteger(x); Range r = new Range( y.subtract(addr_bi).intValue(), ((Number)m.get(RANGE_KEY_SIZE)).intValue(), ((Number)m.get(RANGE_KEY_STAT)).intValue(), Command.toErrorString(m.get(RANGE_KEY_MSG))); assert r.offs >= 0; assert r.size >= 0; this.ranges[n++] = r; } Arrays.sort(this.ranges); } } public int getErrorCode() { Number n = (Number)attrs.get(ERROR_CODE); if (n == null) return 0; return n.intValue(); } public int getAltCode() { Number n = (Number)attrs.get(ERROR_ALT_CODE); if (n == null) return 0; return n.intValue(); } public String getAltOrg() { return (String)attrs.get(ERROR_ALT_ORG); } public Map<String, Object> getAttributes() { return attrs; } public String getMessage(int offset) { if (ranges == null) return null; int l = 0; int h = ranges.length - 1; while (l <= h) { int n = (l + h) >>> 1; Range r = ranges[n]; if (r.offs > offset) { h = n - 1; } else if (offset >= r.offs + r.size) { l = n + 1; } else { return r.msg; } } return null; } public int getStatus(int offset) { if (ranges == null) return BYTE_UNKNOWN; int l = 0; int h = ranges.length - 1; while (l <= h) { int n = (l + h) >>> 1; Range r = ranges[n]; if (r.offs > offset) { h = n - 1; } else if (offset >= r.offs + r.size) { l = n + 1; } else { return r.stat; } } return BYTE_UNKNOWN; } } private class MemContext implements MemoryContext { private final Map<String,Object> props; MemContext(Map<String,Object> props) { this.props = props; } public String getID() { return (String)props.get(PROP_ID); } public String getParentID() { return (String)props.get(PROP_PARENT_ID); } public int getAddressSize() { Number n = (Number)props.get(PROP_ADDRESS_SIZE); if (n == null) return 0; return n.intValue(); } public String getProcessID() { return (String)props.get(PROP_PROCESS_ID); } public boolean isBigEndian() { Boolean n = (Boolean)props.get(PROP_BIG_ENDIAN); if (n == null) return false; return n.booleanValue(); } @SuppressWarnings("unchecked") public Collection<String> getAccessTypes() { return (Collection<String>)props.get(PROP_ACCESS_TYPES); } public Number getEndBound() { return (Number)props.get(PROP_END_BOUND); } public String getName() { return (String)props.get(PROP_NAME); } public Number getStartBound() { return (Number)props.get(PROP_START_BOUND); } public int getAddressableUnitSize() { Number n = (Number)props.get(PROP_ADDRESSABLE_UNIT_SIZE); if (n == null) return 1; return n.intValue(); } public int getDefaultWordSize() { Number n = (Number)props.get(PROP_DEFAULT_WORD_SIZE); if (n == null) return 0; return n.intValue(); } public Map<String, Object> getProperties() { return props; } public IToken fill(final Number addr, int word_size, byte[] value, int size, int mode, final DoneMemory done) { return new MemoryCommand("fill", new Object[] { getID(), addr, word_size, size, mode, value } ) { public void done(Exception error, Object[] args) { MemoryError e = null; if (error != null) { e = new MemoryError(error.getMessage()); } else { assert args.length == 2; e = toMemoryError(addr, args[0], args[1]); } done.doneMemory(token, e); } }.token; } public IToken get(final Number addr, int word_size, final byte[] buf, final int offs, final int size, int mode, final DoneMemory done) { return new MemoryCommand("get", new Object[] { getID(), addr, word_size, size, mode } ) { public void done(Exception error, Object[] args) { MemoryError e = null; if (error != null) { e = new MemoryError(error.getMessage()); } else { assert args.length == 3; JSON.toByteArray(buf, offs, size, args[0]); e = toMemoryError(addr, args[1], args[2]); } done.doneMemory(token, e); } }.token; } public IToken set(final Number addr, int word_size, byte[] buf, int offs, int size, int mode, final DoneMemory done) { return new MemoryCommand("set", new Object[] { getID(), addr, word_size, size, mode, new JSON.Binary(buf, offs, size) } ) { public void done(Exception error, Object[] args) { MemoryError e = null; if (error != null) { e = new MemoryError(error.getMessage()); } else { assert args.length == 2; e = toMemoryError(addr, args[0], args[1]); } done.doneMemory(token, e); } }.token; } public String toString() { return "[Memory Context " + props.toString() + "]"; } } public MemoryProxy(IChannel channel) { this.channel = channel; } public void addListener(final MemoryListener listener) { IChannel.IEventListener l = new IChannel.IEventListener() { public void event(String name, byte[] data) { try { Object[] args = JSON.parseSequence(data); if (name.equals("contextAdded")) { assert args.length == 1; listener.contextAdded(toContextArray(args[0])); } else if (name.equals("contextChanged")) { assert args.length == 1; listener.contextChanged(toContextArray(args[0])); } else if (name.equals("contextRemoved")) { assert args.length == 1; listener.contextRemoved(toStringArray(args[0])); } else if (name.equals("memoryChanged")) { assert args.length == 2; listener.memoryChanged((String)args[0], toAddrArray(args[1]), toSizeArray(args[1])); } else { throw new IOException("Memory service: unknown event: " + name); } } catch (Throwable x) { channel.terminate(x); } } }; channel.addEventListener(this, l); listeners.put(listener, l); } public void removeListener(MemoryListener listener) { IChannel.IEventListener l = listeners.remove(listener); if (l != null) channel.removeEventListener(this, l); } public IToken getContext(String context_id, final DoneGetContext done) { return new Command(channel, this, "getContext", new Object[]{ context_id }) { @SuppressWarnings("unchecked") @Override public void done(Exception error, Object[] args) { MemContext ctx = null; if (error == null) { assert args.length == 2; error = toError(args[0]); if (args[1] != null) ctx = new MemContext((Map<String,Object>)args[1]); } done.doneGetContext(token, error, ctx); } }.token; } public IToken getChildren(String parent_context_id, final DoneGetChildren done) { return new Command(channel, this, "getChildren", new Object[]{ parent_context_id }) { @Override public void done(Exception error, Object[] args) { String[] arr = null; if (error == null) { assert args.length == 2; error = toError(args[0]); arr = toStringArray(args[1]); } done.doneGetChildren(token, error, arr); } }.token; } public String getName() { return NAME; } private abstract class MemoryCommand extends Command { MemoryCommand(String cmd, Object[] args) { super(channel, MemoryProxy.this, cmd, args); } @SuppressWarnings("unchecked") MemoryError toMemoryError(Number addr, Object data, Object ranges) { if (data == null) return null; Map<String,Object> map = (Map<String,Object>)data; Integer code = (Integer)map.get(IErrorReport.ERROR_CODE); MemoryError e = new MemoryErrorReport( "TCF command exception:" + "\nCommand: " + getCommandString(0) + "\nException: " + toErrorString(data) + "\nError code: " + code, map, addr, ranges); Object caused_by = map.get(IErrorReport.ERROR_CAUSED_BY); if (caused_by != null) e.initCause(toError(caused_by, false)); return e; } } @SuppressWarnings("unchecked") private MemoryContext[] toContextArray(Object o) { Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; if (c == null) return new MemoryContext[0]; int n = 0; MemoryContext[] ctx = new MemoryContext[c.size()]; for (Iterator<Map<String,Object>> i = c.iterator(); i.hasNext();) { ctx[n++] = new MemContext(i.next()); } return ctx; } @SuppressWarnings("unchecked") private long[] toSizeArray(Object o) { Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; if (c == null) return null; long[] a = new long[c.size()]; int n = 0; for (Map<String,Object> m : c) { Number sz = (Number)m.get("size"); a[n++] = sz == null ? 0 : sz.longValue(); } return a; } @SuppressWarnings("unchecked") private Number[] toAddrArray(Object o) { Collection<Map<String,Object>> c = (Collection<Map<String,Object>>)o; if (c == null) return null; Number[] a = new Number[c.size()]; int n = 0; for (Map<String,Object> m : c) { a[n++] = (Number)m.get("addr"); } return a; } @SuppressWarnings("unchecked") private String[] toStringArray(Object o) { if (o == null) return null; Collection<String> c = (Collection<String>)o; return (String[])c.toArray(new String[c.size()]); } }