From 7e0a91cef9960901145459f8b4f6e1c4fcea8eba Mon Sep 17 00:00:00 2001
From: Sven Gestegard Robertz <sven.robertz@cs.lth.se>
Date: Fri, 7 Feb 2014 10:16:46 +0100
Subject: [PATCH] started creating labcomm2006 for legacy interfaces

---
 .../se/lth/control/labcomm2006/LabComm.java   |  32 ++++
 .../control/labcomm2006/LabCommDecoder.java   |  19 +++
 .../labcomm2006/LabCommDecoderChannel.java    | 152 ++++++++++++++++++
 .../labcomm2006/LabCommDecoderRegistry.java   | 144 +++++++++++++++++
 .../labcomm2006/LabCommDispatcher.java        |  15 ++
 .../control/labcomm2006/LabCommEncoder.java   |  20 +++
 .../labcomm2006/LabCommEncoderChannel.java    | 117 ++++++++++++++
 .../labcomm2006/LabCommEncoderRegistry.java   |  55 +++++++
 .../control/labcomm2006/LabCommHandler.java   |   4 +
 .../control/labcomm2006/LabCommReader.java    |   7 +
 .../control/labcomm2006/LabCommSample.java    |   5 +
 .../lth/control/labcomm2006/LabCommType.java  |   5 +
 .../control/labcomm2006/LabCommWriter.java    |   9 ++
 .../control/labcomm2006/WriterWrapper.java    |  17 ++
 14 files changed, 601 insertions(+)
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabComm.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommDecoder.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommDecoderChannel.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommDecoderRegistry.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommDispatcher.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommEncoder.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommEncoderChannel.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommEncoderRegistry.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommHandler.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommReader.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommSample.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommType.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommWriter.java
 create mode 100644 lib/java/se/lth/control/labcomm2006/WriterWrapper.java

diff --git a/lib/java/se/lth/control/labcomm2006/LabComm.java b/lib/java/se/lth/control/labcomm2006/LabComm.java
new file mode 100644
index 0000000..16b5abd
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabComm.java
@@ -0,0 +1,32 @@
+package se.lth.control.labcomm2006;
+
+public class LabComm {
+
+  public static final String VERSION = "LabComm2006";
+
+  /*
+   * Predeclared aggregate type indices
+   */
+  public static final int TYPEDEF          = 0x01;
+  public static final int SAMPLE           = 0x02;
+  public static final int ARRAY            = 0x10;
+  public static final int STRUCT           = 0x11;
+  
+  /*
+   * Predeclared primitive type indices
+   */
+  public static final int BOOLEAN          = 0x20;
+  public static final int BYTE             = 0x21;
+  public static final int SHORT            = 0x22;
+  public static final int INT              = 0x23;
+  public static final int LONG             = 0x24;
+  public static final int FLOAT            = 0x25;
+  public static final int DOUBLE           = 0x26;
+  public static final int STRING           = 0x27;
+
+  /*
+   * Start of user declared types
+   */
+  public static final int FIRST_USER_INDEX = 0x40;
+
+}
diff --git a/lib/java/se/lth/control/labcomm2006/LabCommDecoder.java b/lib/java/se/lth/control/labcomm2006/LabCommDecoder.java
new file mode 100644
index 0000000..864e43e
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabCommDecoder.java
@@ -0,0 +1,19 @@
+package se.lth.control.labcomm2006;
+
+import java.io.IOException;
+
+public interface LabCommDecoder {
+  
+  public void register(LabCommDispatcher dispatcher, 
+		       LabCommHandler handler) throws IOException;
+  public boolean decodeBoolean() throws IOException;
+  public byte decodeByte() throws IOException;
+  public short decodeShort() throws IOException;
+  public int decodeInt() throws IOException;
+  public long decodeLong() throws IOException;
+  public float decodeFloat() throws IOException;
+  public double decodeDouble() throws IOException;
+  public String decodeString() throws IOException;
+  public int decodePacked32() throws IOException;
+
+}
diff --git a/lib/java/se/lth/control/labcomm2006/LabCommDecoderChannel.java b/lib/java/se/lth/control/labcomm2006/LabCommDecoderChannel.java
new file mode 100644
index 0000000..f12fffc
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabCommDecoderChannel.java
@@ -0,0 +1,152 @@
+package se.lth.control.labcomm2006;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+public class LabCommDecoderChannel implements LabCommDecoder {
+
+  private DataInputStream in;
+  private LabCommDecoderRegistry registry;
+
+  public LabCommDecoderChannel(InputStream in) throws IOException {
+    this.in = new DataInputStream(in);
+    registry = new LabCommDecoderRegistry();
+  }
+
+  public void runOne() throws Exception {
+    boolean done = false;
+    while (!done) {
+      int tag = decodePacked32();
+      switch (tag) {
+	case LabComm.TYPEDEF:
+	case LabComm.SAMPLE: {
+	  int index = decodePacked32();
+	  String name = decodeString();
+	  ByteArrayOutputStream signature = new ByteArrayOutputStream();
+	  collectFlatSignature(new LabCommEncoderChannel(signature, false));
+	  registry.add(index, name, signature.toByteArray());
+	} break;
+	default: {
+	  LabCommDecoderRegistry.Entry e = registry.get(tag);
+	  if (e == null) {
+	    throw new IOException("Unhandled tag " + tag);
+	  }
+	  LabCommDispatcher d = e.getDispatcher();
+	  if (d == null) {
+	    throw new IOException("No dispatcher for '" + e.getName() + "'");
+	  }
+	  LabCommHandler h = e.getHandler();
+	  if (h == null) {
+	    throw new IOException("No handler for '" + e.getName() +"'");
+	  }
+	  d.decodeAndHandle(this, h);
+	  done = true;
+	}
+      }
+    }
+  }
+
+  public void run() throws Exception {
+    while (true) {
+      runOne();
+    }
+  }
+
+  private void collectFlatSignature(LabCommEncoder out) throws IOException {
+    int type = decodePacked32();
+    out.encodePacked32(type);
+    switch (type) {
+      case LabComm.ARRAY: {
+	int dimensions = decodePacked32();
+	out.encodePacked32(dimensions);
+	for (int i = 0 ; i < dimensions ; i++) {
+	  out.encodePacked32(decodePacked32());
+	}
+	collectFlatSignature(out);
+      } break;
+      case LabComm.STRUCT: {
+	int fields = decodePacked32();
+	out.encodePacked32(fields);
+	for (int i = 0 ; i < fields ; i++) {
+	  out.encodeString(decodeString());
+	  collectFlatSignature(out);
+	}
+      } break;
+      case LabComm.BOOLEAN:
+      case LabComm.BYTE:
+      case LabComm.SHORT:
+      case LabComm.INT:
+      case LabComm.LONG:
+      case LabComm.FLOAT:
+      case LabComm.DOUBLE:
+      case LabComm.STRING: {
+      } break;
+      default: {
+	throw new IOException("Unimplemented type=" + type);
+      }
+    }
+    out.end(null);
+  }
+
+  public void register(LabCommDispatcher dispatcher, 
+                       LabCommHandler handler) throws IOException {
+    registry.add(dispatcher, handler);
+  }
+
+  public boolean decodeBoolean() throws IOException {
+    return in.readBoolean();
+  }
+
+  public byte decodeByte() throws IOException {
+    return in.readByte();
+  }
+  
+  public short decodeShort() throws IOException {
+    return in.readShort();
+  }
+
+  public int decodeInt() throws IOException {
+    return in.readInt();
+  }
+
+  public long decodeLong() throws IOException {
+    return in.readLong();
+  }
+
+  public float decodeFloat() throws IOException {
+    return in.readFloat();
+  }
+
+  public double decodeDouble() throws IOException {
+    return in.readDouble();
+  }
+
+  public String decodeString() throws IOException {
+    //in.readShort(); // HACK
+    //return in.readUTF();
+    int len = decodePacked32() & 0xffffffff;
+    byte[] chars = new byte[len];
+    for(int i=0; i<len; i++) {
+      chars[i] = in.readByte();
+    }
+    return new String(chars);
+  }
+
+  public int decodePacked32() throws IOException {
+    long res=0;
+    byte i=0;
+    boolean cont=true;
+
+    do {
+      byte c = in.readByte();
+      res = (res << 7) | (c & 0x7f);
+      cont = (c & 0x80) != 0;
+      i++;
+    } while(cont);
+
+    return (int) (res & 0xffffffff);
+  }
+}
+
diff --git a/lib/java/se/lth/control/labcomm2006/LabCommDecoderRegistry.java b/lib/java/se/lth/control/labcomm2006/LabCommDecoderRegistry.java
new file mode 100644
index 0000000..a94bc6e
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabCommDecoderRegistry.java
@@ -0,0 +1,144 @@
+package se.lth.control.labcomm2006;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+public class LabCommDecoderRegistry {
+
+  public static class Entry {
+    
+    private LabCommDispatcher dispatcher;
+    private LabCommHandler handler;
+    private int index;
+    private String name;
+    private byte[] signature;
+
+    public Entry(LabCommDispatcher dispatcher,
+		 LabCommHandler handler) {
+      this.dispatcher = dispatcher;
+      this.name = dispatcher.getName();
+      this.signature = dispatcher.getSignature();
+      this.handler = handler;
+    }
+
+    public Entry(int index, String name, byte[] signature) {
+      this.index = index;
+      this.name = name;
+      this.signature = signature;
+    }
+
+    public LabCommDispatcher getDispatcher() {
+      return dispatcher;
+    }
+
+    public void setDispatcher(LabCommDispatcher dispatcher) {
+      this.dispatcher = dispatcher;
+    }
+
+    public LabCommHandler getHandler() {
+      return handler;
+    }
+
+    public void setHandler(LabCommHandler handler) {
+      this.handler = handler;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public int getIndex() {
+      return index;
+    }
+
+    public void setIndex(int index) throws IOException {
+      if (this.index != 0 && this.index != index) {
+	throw new IOException("Index mismatch " + 
+			      this.index + " != " + index);
+      }
+      this.index = index;
+    }
+
+    public boolean matchName(String name) {
+      return this.name.equals(name);
+    }
+
+    public boolean matchSignature(byte[] signature) {
+      boolean result = this.signature.length == signature.length;
+      for (int i = 0 ; result && i < signature.length ; i++) {
+	result = this.signature[i] == signature[i];
+      }
+      return result;
+    }
+
+    public boolean match(String name, byte[] signature) {
+      return matchName(name) && matchSignature(signature);
+    }
+
+    public void check(String name, byte[] signature) throws IOException {
+      if (!matchName(name)) {
+	throw new IOException("Name mismatch '" + 
+			      this.name + "' != '" + name + "'");
+      } 
+      if (!matchSignature(signature)) {
+	throw new IOException("Signature mismatch");
+      } 
+    }
+  }
+
+  private HashMap<Class, Entry> byClass;
+  private HashMap<Integer, Entry> byIndex;
+
+  public LabCommDecoderRegistry() {
+    byClass = new HashMap<Class, Entry>();
+    byIndex = new HashMap<Integer, Entry>();
+  }
+
+  public synchronized void add(LabCommDispatcher dispatcher,
+			       LabCommHandler handler) throws IOException{
+    Entry e = byClass.get(dispatcher.getSampleClass());
+    if (e != null) {
+      e.check(dispatcher.getName(), dispatcher.getSignature());
+      e.setHandler(handler);
+    } else {
+      for (Entry e2 : byIndex.values()) {
+	if (e2.match(dispatcher.getName(), dispatcher.getSignature())) {
+	  e2.setDispatcher(dispatcher);
+	  e2.setHandler(handler);
+	  e = e2;
+	  break;
+	}
+      }
+      if (e == null) {
+	e = new Entry(dispatcher, handler);
+	byClass.put(dispatcher.getSampleClass(), e);
+      }
+    }
+  }
+
+  public synchronized void add(int index, 
+			       String name,
+			       byte[] signature) throws IOException {
+    Entry e = byIndex.get(Integer.valueOf(index));
+    if (e != null) {
+      e.check(name, signature);
+    } else {
+      for (Entry e2 : byClass.values()) {
+	if (e2.match(name, signature)) {
+	  e2.setIndex(index);
+	  e = e2;
+	  break;
+	}
+      }
+      if (e == null) {
+	e = new Entry(index, name, signature);
+      }
+      byIndex.put(Integer.valueOf(index), e);
+    }
+  }
+
+  public synchronized Entry get(int index) {
+    return byIndex.get(Integer.valueOf(index));
+  }
+
+}
\ No newline at end of file
diff --git a/lib/java/se/lth/control/labcomm2006/LabCommDispatcher.java b/lib/java/se/lth/control/labcomm2006/LabCommDispatcher.java
new file mode 100644
index 0000000..469627e
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabCommDispatcher.java
@@ -0,0 +1,15 @@
+package se.lth.control.labcomm2006;
+
+public interface LabCommDispatcher {
+    
+  public Class getSampleClass();
+    
+  public String getName();
+
+  public byte[] getSignature();
+
+  public void decodeAndHandle(LabCommDecoder decoder,
+			      LabCommHandler handler) throws Exception;
+
+}
+
diff --git a/lib/java/se/lth/control/labcomm2006/LabCommEncoder.java b/lib/java/se/lth/control/labcomm2006/LabCommEncoder.java
new file mode 100644
index 0000000..51c2223
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabCommEncoder.java
@@ -0,0 +1,20 @@
+package se.lth.control.labcomm2006;
+
+import java.io.IOException;
+
+public interface LabCommEncoder {
+
+  public void register(LabCommDispatcher dispatcher) throws IOException;
+  public void begin(Class<? extends LabCommSample> c) throws IOException;
+  public void end(Class<? extends LabCommSample> c) throws IOException;
+  public void encodeBoolean(boolean value) throws IOException;
+  public void encodeByte(byte value) throws IOException;
+  public void encodeShort(short value) throws IOException;
+  public void encodeInt(int value) throws IOException;
+  public void encodeLong(long value) throws IOException;
+  public void encodeFloat(float value) throws IOException;
+  public void encodeDouble(double value) throws IOException;
+  public void encodeString(String value) throws IOException;
+  public void encodePacked32(long value) throws IOException;
+
+}
diff --git a/lib/java/se/lth/control/labcomm2006/LabCommEncoderChannel.java b/lib/java/se/lth/control/labcomm2006/LabCommEncoderChannel.java
new file mode 100644
index 0000000..c38c852
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabCommEncoderChannel.java
@@ -0,0 +1,117 @@
+package se.lth.control.labcomm2006;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class LabCommEncoderChannel implements LabCommEncoder {
+
+  private LabCommWriter writer;
+  private ByteArrayOutputStream bytes;
+  private DataOutputStream data;
+  private LabCommEncoderRegistry registry;
+
+  public LabCommEncoderChannel(LabCommWriter writer, 
+			       boolean emitVersion) throws IOException {
+    this.writer = writer;
+    bytes = new ByteArrayOutputStream();
+    data = new DataOutputStream(bytes);
+    registry = new LabCommEncoderRegistry();
+    if (emitVersion) {
+        throw new RuntimeError("Labcomm 2006 does not support emitVersion");
+    }
+  }
+
+  public LabCommEncoderChannel(LabCommWriter writer) throws IOException {
+    this(writer, true);
+  }
+
+  public LabCommEncoderChannel(OutputStream writer, 
+			       boolean emitVersion) throws IOException {
+    this(new WriterWrapper(writer), emitVersion);
+  }
+
+  public LabCommEncoderChannel(OutputStream writer) throws IOException {
+    this(new WriterWrapper(writer), true);
+  }
+
+  public void register(LabCommDispatcher dispatcher) throws IOException {
+    int index = registry.add(dispatcher);
+    encodePacked32(LabComm.SAMPLE);
+    encodePacked32(index);
+    encodeString(dispatcher.getName());
+    byte[] signature = dispatcher.getSignature();
+    for (int i = 0 ; i < signature.length ; i++) {
+      encodeByte(signature[i]);
+    }
+    end(null);
+  }
+
+  public void begin(Class<? extends LabCommSample> c) throws IOException {
+    encodePacked32(registry.getTag(c));
+  }
+
+  public void end(Class<? extends LabCommSample> c) throws IOException {
+    data.flush();
+    //XXX when writer was a stream, it was probably a bit more GC efficient:
+    //bytes.writeTo(writer);
+    writer.write(bytes.toByteArray());
+    bytes.reset();
+  }
+
+  public void encodeBoolean(boolean value) throws IOException{
+    data.writeBoolean(value);
+  }
+
+  public void encodeByte(byte value) throws IOException {
+    data.writeByte(value);
+  }
+
+  public void encodeShort(short value) throws IOException {
+    data.writeShort(value);
+  }
+
+  public void encodeInt(int value) throws IOException {
+    data.writeInt(value);
+  }
+
+  public void encodeLong(long value) throws IOException {
+    data.writeLong(value);
+  }
+
+  public void encodeFloat(float value) throws IOException {
+    data.writeFloat(value);
+  }
+
+  public void encodeDouble(double value) throws IOException {
+    data.writeDouble(value);
+  }
+
+  public void encodeString(String value) throws IOException {
+    data.writeShort(0); // HACK...
+    data.writeUTF(value);
+
+    //kludge, to replace above hack with packed length
+    ByteArrayOutputStream tmpb = new ByteArrayOutputStream();
+    DataOutputStream tmps = new DataOutputStream(tmpb);
+
+    tmps.writeUTF(value);
+    tmps.flush();
+    byte[] tmp = tmpb.toByteArray();
+  
+    encodePacked32(tmp.length-2);
+    for (int i = 2 ; i < tmp.length ; i++) {
+      encodeByte(tmp[i]);
+    }
+  }
+
+  /**
+     method for API harmonization with labcomm2013.
+     Labcomm2006 encodes lengths etc as 32 bit ints.
+  */
+  public inline void encodePacked32(long value) throws IOException {
+    encodeInt(value);
+  }
+}
+
diff --git a/lib/java/se/lth/control/labcomm2006/LabCommEncoderRegistry.java b/lib/java/se/lth/control/labcomm2006/LabCommEncoderRegistry.java
new file mode 100644
index 0000000..2a5564e
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabCommEncoderRegistry.java
@@ -0,0 +1,55 @@
+package se.lth.control.labcomm2006;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+public class LabCommEncoderRegistry {
+
+  public static class Entry {
+    
+    private LabCommDispatcher dispatcher;
+    private int index;
+
+    public Entry(LabCommDispatcher dispatcher, int index) {
+      this.dispatcher = dispatcher;
+      this.index = index;
+    }
+
+    public LabCommDispatcher getDispatcher() {
+      return dispatcher;
+    }
+
+    public int getIndex() {
+      return index;
+    }
+
+  }
+
+  private int userIndex = LabComm.FIRST_USER_INDEX;
+  private HashMap<Class, Entry> byClass;
+
+  public LabCommEncoderRegistry() {
+    byClass = new HashMap<Class, Entry>();
+  }
+
+  public synchronized int add(LabCommDispatcher dispatcher) {
+    Entry e = byClass.get(dispatcher.getSampleClass());
+    if (e == null) {
+      e = new Entry(dispatcher, userIndex);
+      byClass.put(dispatcher.getSampleClass(), e);
+      userIndex++;
+    }
+    return e.getIndex();
+  }
+  
+  public int getTag(Class<? extends LabCommSample> sample) throws IOException {
+    Entry e = byClass.get(sample);
+    if (e == null) {
+      throw new IOException("'" + 
+			    sample.getSimpleName() + 
+			    "' is not registered");
+    }
+    return e.index;
+  }
+
+}
\ No newline at end of file
diff --git a/lib/java/se/lth/control/labcomm2006/LabCommHandler.java b/lib/java/se/lth/control/labcomm2006/LabCommHandler.java
new file mode 100644
index 0000000..2880c19
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabCommHandler.java
@@ -0,0 +1,4 @@
+package se.lth.control.labcomm2006;
+
+public interface LabCommHandler {
+}
\ No newline at end of file
diff --git a/lib/java/se/lth/control/labcomm2006/LabCommReader.java b/lib/java/se/lth/control/labcomm2006/LabCommReader.java
new file mode 100644
index 0000000..1c2561d
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabCommReader.java
@@ -0,0 +1,7 @@
+package se.lth.control.labcomm2006;
+
+public interface LabCommReader {
+
+  public void handle(byte[] data, int begin, int end);
+
+}
diff --git a/lib/java/se/lth/control/labcomm2006/LabCommSample.java b/lib/java/se/lth/control/labcomm2006/LabCommSample.java
new file mode 100644
index 0000000..d1dc1ff
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabCommSample.java
@@ -0,0 +1,5 @@
+package se.lth.control.labcomm2006;
+
+public interface LabCommSample {
+
+}
\ No newline at end of file
diff --git a/lib/java/se/lth/control/labcomm2006/LabCommType.java b/lib/java/se/lth/control/labcomm2006/LabCommType.java
new file mode 100644
index 0000000..0722e6d
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabCommType.java
@@ -0,0 +1,5 @@
+package se.lth.control.labcomm2006;
+
+public interface LabCommType {
+
+}
\ No newline at end of file
diff --git a/lib/java/se/lth/control/labcomm2006/LabCommWriter.java b/lib/java/se/lth/control/labcomm2006/LabCommWriter.java
new file mode 100644
index 0000000..e70e142
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/LabCommWriter.java
@@ -0,0 +1,9 @@
+package se.lth.control.labcomm2006;
+
+import java.io.IOException;
+
+public interface LabCommWriter {
+
+  public void write(byte[] data) throws IOException;
+
+}
diff --git a/lib/java/se/lth/control/labcomm2006/WriterWrapper.java b/lib/java/se/lth/control/labcomm2006/WriterWrapper.java
new file mode 100644
index 0000000..4c96d88
--- /dev/null
+++ b/lib/java/se/lth/control/labcomm2006/WriterWrapper.java
@@ -0,0 +1,17 @@
+package se.lth.control.labcomm2006;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+class WriterWrapper implements LabCommWriter{
+
+  private OutputStream os;
+
+  public WriterWrapper(OutputStream os) {
+    this.os = os;
+  }
+
+  public void write(byte[] data) throws IOException {
+    os.write(data);
+  }
+}
-- 
GitLab