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.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;
public MPArray() {
this.array = new LinkedList<>();
}
public MPArray(List<T> 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
public boolean equals(Object o) {
if (this == o) return true;
@ -52,6 +132,51 @@ public class MPArray<T extends MPType> implements MPType<List<T>> {
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
public String toString() {
StringBuilder result = new StringBuilder();

View File

@ -8,6 +8,9 @@ import java.util.Arrays;
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[]> {
private byte[] value;

View File

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

View File

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

View File

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

View File

@ -8,6 +8,12 @@ import java.util.Objects;
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> {
private long value;

View File

@ -6,14 +6,22 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.*;
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;
public MPMap() {
this.map = new LinkedHashMap<>();
}
public MPMap(Map<K, V> 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
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -4,6 +4,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Representation of msgpack encoded nil / null.
*/
public class MPNil implements MPType<Void> {
private final static int NIL = 0xC0;
@ -27,7 +30,7 @@ public class MPNil implements MPType<Void> {
@Override
public String toString() {
return "nil";
return "null";
}
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.OutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Objects;
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) {
this.value = value;
@ -22,18 +52,18 @@ public class MPString implements MPType<String> {
public void pack(OutputStream out) throws IOException {
int size = value.length();
if (size < 32) {
out.write(0b10100000 + size);
} else if (size < 256) {
out.write(0xD9);
out.write(FIXSTR_PREFIX + size);
} else if (size < STR8_LIMIT) {
out.write(STR8_PREFIX);
out.write(size);
} else if (size < 65536) {
out.write(0xDA);
} else if (size < STR16_LIMIT) {
out.write(STR16_PREFIX);
out.write(ByteBuffer.allocate(2).putShort((short) size).array());
} else {
out.write(0xDB);
out.write(STR32_PREFIX);
out.write(ByteBuffer.allocate(4).putInt(size).array());
}
out.write(value.getBytes("UTF-8"));
out.write(value.getBytes(encoding));
}
@Override
@ -49,30 +79,46 @@ public class MPString implements MPType<String> {
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
public String toString() {
return '"' + value + '"'; // FIXME: escape value
return value;
}
public static class Unpacker implements MPType.Unpacker<MPString> {
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 {
int size;
if ((firstByte & 0b11100000) == 0b10100000) {
size = firstByte & 0b00011111;
} else if (firstByte == 0xD9) {
if ((firstByte & FIXSTR_PREFIX_FILTER) == FIXSTR_PREFIX) {
size = firstByte & FIXSTR_FILTER;
} else if (firstByte == STR8_PREFIX) {
size = in.read();
} else if (firstByte == 0xDA) {
} else if (firstByte == STR16_PREFIX) {
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();
} else {
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;
/**
* Representation for some msgpack encoded data.
* Representation of some msgpack encoded data.
*/
public interface MPType<T> {
interface Unpacker<M extends MPType> {

View File

@ -5,6 +5,9 @@ import java.io.InputStream;
import java.nio.ByteBuffer;
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 {
byte[] result = new byte[count];
int off = 0;

View File

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