Added comments, implemented interfaces for some added convenience.

This commit is contained in:
Christian Basler 2017-01-20 22:43:44 +01:00
parent 4b79e232d0
commit 30da667fdb
12 changed files with 291 additions and 28 deletions

View File

@ -8,9 +8,19 @@ import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
public class MPArray<T extends MPType> implements MPType<List<T>> { /**
* Representation of a msgpack encoded array. Uses a list to represent data internally, and implements the {@link List}
* interface for your convenience.
*
* @param <T>
*/
public class MPArray<T extends MPType> implements MPType<List<T>>, List<T> {
private List<T> array; private List<T> array;
public MPArray() {
this.array = new LinkedList<>();
}
public MPArray(List<T> array) { public MPArray(List<T> array) {
this.array = array; this.array = array;
} }
@ -39,6 +49,76 @@ public class MPArray<T extends MPType> implements MPType<List<T>> {
} }
} }
@Override
public int size() {
return array.size();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
@Override
public boolean contains(Object o) {
return array.contains(o);
}
@Override
public Iterator<T> iterator() {
return array.iterator();
}
@Override
public Object[] toArray() {
return array.toArray();
}
@Override
public <T1> T1[] toArray(T1[] t1s) {
return array.toArray(t1s);
}
@Override
public boolean add(T t) {
return array.add(t);
}
@Override
public boolean remove(Object o) {
return array.remove(o);
}
@Override
public boolean containsAll(Collection<?> collection) {
return array.containsAll(collection);
}
@Override
public boolean addAll(Collection<? extends T> collection) {
return array.addAll(collection);
}
@Override
public boolean addAll(int i, Collection<? extends T> collection) {
return array.addAll(i, collection);
}
@Override
public boolean removeAll(Collection<?> collection) {
return array.removeAll(collection);
}
@Override
public boolean retainAll(Collection<?> collection) {
return array.retainAll(collection);
}
@Override
public void clear() {
array.clear();
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
@ -52,6 +132,51 @@ public class MPArray<T extends MPType> implements MPType<List<T>> {
return Objects.hash(array); return Objects.hash(array);
} }
@Override
public T get(int i) {
return array.get(i);
}
@Override
public T set(int i, T t) {
return array.set(i, t);
}
@Override
public void add(int i, T t) {
array.add(i, t);
}
@Override
public T remove(int i) {
return array.remove(i);
}
@Override
public int indexOf(Object o) {
return array.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return array.lastIndexOf(o);
}
@Override
public ListIterator<T> listIterator() {
return array.listIterator();
}
@Override
public ListIterator<T> listIterator(int i) {
return array.listIterator(i);
}
@Override
public List<T> subList(int fromIndex, int toIndex) {
return array.subList(fromIndex, toIndex);
}
@Override @Override
public String toString() { public String toString() {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();

View File

@ -8,6 +8,9 @@ import java.util.Arrays;
import static ch.dissem.msgpack.types.Utils.bytes; import static ch.dissem.msgpack.types.Utils.bytes;
/**
* Representation of msgpack encoded binary data a.k.a. byte array.
*/
public class MPBinary implements MPType<byte[]> { public class MPBinary implements MPType<byte[]> {
private byte[] value; private byte[] value;

View File

@ -5,6 +5,9 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Objects; import java.util.Objects;
/**
* Representation of a msgpack encoded boolean.
*/
public class MPBoolean implements MPType<Boolean> { public class MPBoolean implements MPType<Boolean> {
private final static int FALSE = 0xC2; private final static int FALSE = 0xC2;
private final static int TRUE = 0xC3; private final static int TRUE = 0xC3;

View File

@ -8,6 +8,9 @@ import java.util.Objects;
import static ch.dissem.msgpack.types.Utils.bytes; import static ch.dissem.msgpack.types.Utils.bytes;
/**
* Representation of a msgpack encoded float64 number.
*/
public class MPDouble implements MPType<Double> { public class MPDouble implements MPType<Double> {
private double value; private double value;

View File

@ -8,6 +8,9 @@ import java.util.Objects;
import static ch.dissem.msgpack.types.Utils.bytes; import static ch.dissem.msgpack.types.Utils.bytes;
/**
* Representation of a msgpack encoded float32 number.
*/
public class MPFloat implements MPType<Float> { public class MPFloat implements MPType<Float> {
private float value; private float value;

View File

@ -8,6 +8,12 @@ import java.util.Objects;
import static ch.dissem.msgpack.types.Utils.bytes; import static ch.dissem.msgpack.types.Utils.bytes;
/**
* Representation of a msgpack encoded integer. The encoding is automatically selected according to the value's size.
* Uses long due to the fact that the msgpack integer implementation may contain up to 64 bit numbers, corresponding
* to Java long values. Also note that uint64 values may be too large for signed long (thanks Java for not supporting
* unsigned values) and end in a negative value.
*/
public class MPInteger implements MPType<Long> { public class MPInteger implements MPType<Long> {
private long value; private long value;

View File

@ -6,14 +6,22 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Iterator; import java.util.*;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
public class MPMap<K extends MPType, V extends MPType> implements MPType<Map<K, V>> { /**
* Representation of a msgpack encoded map. It is recommended to use a {@link LinkedHashMap} to ensure the order
* of entries. For convenience, it also implements the {@link Map} interface.
*
* @param <K>
* @param <V>
*/
public class MPMap<K extends MPType, V extends MPType> implements MPType<Map<K, V>>, Map<K, V> {
private Map<K, V> map; private Map<K, V> map;
public MPMap() {
this.map = new LinkedHashMap<>();
}
public MPMap(Map<K, V> map) { public MPMap(Map<K, V> map) {
this.map = map; this.map = map;
} }
@ -40,6 +48,66 @@ public class MPMap<K extends MPType, V extends MPType> implements MPType<Map<K,
} }
} }
@Override
public int size() {
return map.size();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return map.containsKey(o);
}
@Override
public boolean containsValue(Object o) {
return map.containsValue(o);
}
@Override
public V get(Object o) {
return map.get(o);
}
@Override
public V put(K k, V v) {
return map.put(k, v);
}
@Override
public V remove(Object o) {
return map.remove(o);
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
this.map.putAll(map);
}
@Override
public void clear() {
map.clear();
}
@Override
public Set<K> keySet() {
return map.keySet();
}
@Override
public Collection<V> values() {
return map.values();
}
@Override
public Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

View File

@ -4,6 +4,9 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
/**
* Representation of msgpack encoded nil / null.
*/
public class MPNil implements MPType<Void> { public class MPNil implements MPType<Void> {
private final static int NIL = 0xC0; private final static int NIL = 0xC0;
@ -27,7 +30,7 @@ public class MPNil implements MPType<Void> {
@Override @Override
public String toString() { public String toString() {
return "nil"; return "null";
} }
public static class Unpacker implements MPType.Unpacker<MPNil> { public static class Unpacker implements MPType.Unpacker<MPNil> {

View File

@ -4,12 +4,42 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Objects; import java.util.Objects;
import static ch.dissem.msgpack.types.Utils.bytes; import static ch.dissem.msgpack.types.Utils.bytes;
public class MPString implements MPType<String> { /**
private String value; * Representation of a msgpack encoded string. The encoding is automatically selected according to the string's length.
* <p>
* The default encoding is UTF-8.
* </p>
*/
public class MPString implements MPType<String>, CharSequence {
private static final int FIXSTR_PREFIX = 0b10100000;
private static final int FIXSTR_PREFIX_FILTER = 0b11100000;
private static final int STR8_PREFIX = 0xD9;
private static final int STR8_LIMIT = 256;
private static final int STR16_PREFIX = 0xDA;
private static final int STR16_LIMIT = 65536;
private static final int STR32_PREFIX = 0xDB;
private static final int FIXSTR_FILTER = 0b00011111;
private static Charset encoding = Charset.forName("UTF-8");
/**
* Use this method if for some messed up reason you really need to use something else than UTF-8.
* Ask yourself: why should I? Is this really necessary?
* <p>
* It will set the encoding for all {@link MPString}s, but if you have inconsistent encoding in your
* format you're lost anyway.
* </p>
*/
public static void setEncoding(Charset encoding) {
MPString.encoding = encoding;
}
private final String value;
public MPString(String value) { public MPString(String value) {
this.value = value; this.value = value;
@ -22,18 +52,18 @@ public class MPString implements MPType<String> {
public void pack(OutputStream out) throws IOException { public void pack(OutputStream out) throws IOException {
int size = value.length(); int size = value.length();
if (size < 32) { if (size < 32) {
out.write(0b10100000 + size); out.write(FIXSTR_PREFIX + size);
} else if (size < 256) { } else if (size < STR8_LIMIT) {
out.write(0xD9); out.write(STR8_PREFIX);
out.write(size); out.write(size);
} else if (size < 65536) { } else if (size < STR16_LIMIT) {
out.write(0xDA); out.write(STR16_PREFIX);
out.write(ByteBuffer.allocate(2).putShort((short) size).array()); out.write(ByteBuffer.allocate(2).putShort((short) size).array());
} else { } else {
out.write(0xDB); out.write(STR32_PREFIX);
out.write(ByteBuffer.allocate(4).putInt(size).array()); out.write(ByteBuffer.allocate(4).putInt(size).array());
} }
out.write(value.getBytes("UTF-8")); out.write(value.getBytes(encoding));
} }
@Override @Override
@ -49,30 +79,46 @@ public class MPString implements MPType<String> {
return Objects.hash(value); return Objects.hash(value);
} }
@Override
public int length() {
return value.length();
}
@Override
public char charAt(int i) {
return value.charAt(i);
}
@Override
public CharSequence subSequence(int beginIndex, int endIndex) {
return value.subSequence(beginIndex, endIndex);
}
@Override @Override
public String toString() { public String toString() {
return '"' + value + '"'; // FIXME: escape value return value;
} }
public static class Unpacker implements MPType.Unpacker<MPString> { public static class Unpacker implements MPType.Unpacker<MPString> {
public boolean is(int firstByte) { public boolean is(int firstByte) {
return firstByte == 0xD9 || firstByte == 0xDA || firstByte == 0xDB || (firstByte & 0b11100000) == 0b10100000; return firstByte == STR8_PREFIX || firstByte == STR16_PREFIX || firstByte == STR32_PREFIX
|| (firstByte & FIXSTR_PREFIX_FILTER) == FIXSTR_PREFIX;
} }
public MPString unpack(int firstByte, InputStream in) throws IOException { public MPString unpack(int firstByte, InputStream in) throws IOException {
int size; int size;
if ((firstByte & 0b11100000) == 0b10100000) { if ((firstByte & FIXSTR_PREFIX_FILTER) == FIXSTR_PREFIX) {
size = firstByte & 0b00011111; size = firstByte & FIXSTR_FILTER;
} else if (firstByte == 0xD9) { } else if (firstByte == STR8_PREFIX) {
size = in.read(); size = in.read();
} else if (firstByte == 0xDA) { } else if (firstByte == STR16_PREFIX) {
size = in.read() << 8 | in.read(); size = in.read() << 8 | in.read();
} else if (firstByte == 0xDB) { } else if (firstByte == STR32_PREFIX) {
size = in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read(); size = in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read();
} else { } else {
throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte)); throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte));
} }
return new MPString(new String(bytes(in, size).array(), "UTF-8")); return new MPString(new String(bytes(in, size).array(), encoding));
} }
} }
} }

View File

@ -5,7 +5,7 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
/** /**
* Representation for some msgpack encoded data. * Representation of some msgpack encoded data.
*/ */
public interface MPType<T> { public interface MPType<T> {
interface Unpacker<M extends MPType> { interface Unpacker<M extends MPType> {

View File

@ -5,6 +5,9 @@ import java.io.InputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
class Utils { class Utils {
/**
* Returns a {@link ByteBuffer} containing the next <code>count</code> bytes from the {@link InputStream}.
*/
static ByteBuffer bytes(InputStream in, int count) throws IOException { static ByteBuffer bytes(InputStream in, int count) throws IOException {
byte[] result = new byte[count]; byte[] result = new byte[count];
int off = 0; int off = 0;

View File

@ -29,9 +29,9 @@ public class ReaderTest {
@Test @Test
public void ensureDemoJsonIsEncodedCorrectly() throws Exception { public void ensureDemoJsonIsEncodedCorrectly() throws Exception {
MPMap<MPString, MPType<?>> object = new MPMap<>(new LinkedHashMap<MPString, MPType<?>>()); MPMap<MPString, MPType<?>> object = new MPMap<>();
object.getValue().put(new MPString("compact"), new MPBoolean(true)); object.put(new MPString("compact"), new MPBoolean(true));
object.getValue().put(new MPString("schema"), new MPInteger(0)); object.put(new MPString("schema"), new MPInteger(0));
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
object.pack(out); object.pack(out);
assertThat(out.toByteArray(), is(bytes("demo.mp"))); assertThat(out.toByteArray(), is(bytes("demo.mp")));