Commit 5b47d6d3 authored by akwizgran's avatar akwizgran
Browse files

Merge branch 'bdf-improvements' into 'master'

BDF improvements

* Change the semantics of the BdfList/Dictionary methods with default values so they return the default value if the value is missing, or throw an exception if it the value's present but has the wrong type - this is more useful for validators, which have to deal with optional values without accepting values of the wrong type
* Reuse BdfReader/Writer for MetadataParser/Encoder so we don't have two implementations of BDF


See merge request !117
parents 08099714 db188657
......@@ -40,6 +40,13 @@ public class BdfDictionary extends Hashtable<String, Object> {
throw new FormatException();
}
public Boolean getOptionalBoolean(String key) throws FormatException {
Object o = get(key);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof Boolean) return (Boolean) o;
throw new FormatException();
}
public Boolean getBoolean(String key, Boolean defaultValue) {
Object o = get(key);
if (o instanceof Boolean) return (Boolean) o;
......@@ -55,6 +62,16 @@ public class BdfDictionary extends Hashtable<String, Object> {
throw new FormatException();
}
public Long getOptionalLong(String key) throws FormatException {
Object o = get(key);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof Long) return (Long) o;
if (o instanceof Integer) return ((Integer) o).longValue();
if (o instanceof Short) return ((Short) o).longValue();
if (o instanceof Byte) return ((Byte) o).longValue();
throw new FormatException();
}
public Long getLong(String key, Long defaultValue) {
Object o = get(key);
if (o instanceof Long) return (Long) o;
......@@ -71,6 +88,14 @@ public class BdfDictionary extends Hashtable<String, Object> {
throw new FormatException();
}
public Double getOptionalDouble(String key) throws FormatException {
Object o = get(key);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof Double) return (Double) o;
if (o instanceof Float) return ((Float) o).doubleValue();
throw new FormatException();
}
public Double getDouble(String key, Double defaultValue) {
Object o = get(key);
if (o instanceof Double) return (Double) o;
......@@ -84,6 +109,13 @@ public class BdfDictionary extends Hashtable<String, Object> {
throw new FormatException();
}
public String getOptionalString(String key) throws FormatException {
Object o = get(key);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof String) return (String) o;
throw new FormatException();
}
public String getString(String key, String defaultValue) {
Object o = get(key);
if (o instanceof String) return (String) o;
......@@ -97,6 +129,14 @@ public class BdfDictionary extends Hashtable<String, Object> {
throw new FormatException();
}
public byte[] getOptionalRaw(String key) throws FormatException {
Object o = get(key);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof byte[]) return (byte[]) o;
if (o instanceof Bytes) return ((Bytes) o).getBytes();
throw new FormatException();
}
public byte[] getRaw(String key, byte[] defaultValue) {
Object o = get(key);
if (o instanceof byte[]) return (byte[]) o;
......@@ -110,6 +150,13 @@ public class BdfDictionary extends Hashtable<String, Object> {
throw new FormatException();
}
public BdfList getOptionalList(String key) throws FormatException {
Object o = get(key);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof BdfList) return (BdfList) o;
throw new FormatException();
}
public BdfList getList(String key, BdfList defaultValue) {
Object o = get(key);
if (o instanceof BdfList) return (BdfList) o;
......@@ -122,6 +169,14 @@ public class BdfDictionary extends Hashtable<String, Object> {
throw new FormatException();
}
public BdfDictionary getOptionalDictionary(String key)
throws FormatException {
Object o = get(key);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof BdfDictionary) return (BdfDictionary) o;
throw new FormatException();
}
public BdfDictionary getDictionary(String key, BdfDictionary defaultValue) {
Object o = get(key);
if (o instanceof BdfDictionary) return (BdfDictionary) o;
......
......@@ -7,6 +7,8 @@ import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import static org.briarproject.api.data.BdfDictionary.NULL_VALUE;
public class BdfList extends Vector<Object> {
/**
......@@ -33,6 +35,13 @@ public class BdfList extends Vector<Object> {
throw new FormatException();
}
public Boolean getOptionalBoolean(int index) throws FormatException {
Object o = get(index);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof Boolean) return (Boolean) o;
throw new FormatException();
}
public Boolean getBoolean(int index, Boolean defaultValue) {
Object o = get(index);
if (o instanceof Boolean) return (Boolean) o;
......@@ -48,6 +57,16 @@ public class BdfList extends Vector<Object> {
throw new FormatException();
}
public Long getOptionalLong(int index) throws FormatException {
Object o = get(index);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof Long) return (Long) o;
if (o instanceof Integer) return ((Integer) o).longValue();
if (o instanceof Short) return ((Short) o).longValue();
if (o instanceof Byte) return ((Byte) o).longValue();
throw new FormatException();
}
public Long getLong(int index, Long defaultValue) {
Object o = get(index);
if (o instanceof Long) return (Long) o;
......@@ -64,6 +83,14 @@ public class BdfList extends Vector<Object> {
throw new FormatException();
}
public Double getOptionalDouble(int index) throws FormatException {
Object o = get(index);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof Double) return (Double) o;
if (o instanceof Float) return ((Float) o).doubleValue();
throw new FormatException();
}
public Double getDouble(int index, Double defaultValue) {
Object o = get(index);
if (o instanceof Double) return (Double) o;
......@@ -77,6 +104,13 @@ public class BdfList extends Vector<Object> {
throw new FormatException();
}
public String getOptionalString(int index) throws FormatException {
Object o = get(index);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof String) return (String) o;
throw new FormatException();
}
public String getString(int index, String defaultValue) {
Object o = get(index);
if (o instanceof String) return (String) o;
......@@ -90,6 +124,14 @@ public class BdfList extends Vector<Object> {
throw new FormatException();
}
public byte[] getOptionalRaw(int index) throws FormatException {
Object o = get(index);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof byte[]) return (byte[]) o;
if (o instanceof Bytes) return ((Bytes) o).getBytes();
throw new FormatException();
}
public byte[] getRaw(int index, byte[] defaultValue) {
Object o = get(index);
if (o instanceof byte[]) return (byte[]) o;
......@@ -103,6 +145,13 @@ public class BdfList extends Vector<Object> {
throw new FormatException();
}
public BdfList getOptionalList(int index) throws FormatException {
Object o = get(index);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof BdfList) return (BdfList) o;
throw new FormatException();
}
public BdfList getList(int index, BdfList defaultValue) {
Object o = get(index);
if (o instanceof BdfList) return (BdfList) o;
......@@ -115,6 +164,14 @@ public class BdfList extends Vector<Object> {
throw new FormatException();
}
public BdfDictionary getOptionalDictionary(int index)
throws FormatException {
Object o = get(index);
if (o == null || o == NULL_VALUE) return null;
if (o instanceof BdfDictionary) return (BdfDictionary) o;
throw new FormatException();
}
public BdfDictionary getDictionary(int index, BdfDictionary defaultValue) {
Object o = get(index);
if (o instanceof BdfDictionary) return (BdfDictionary) o;
......
package org.briarproject.data;
import com.google.inject.Inject;
import org.briarproject.api.Bytes;
import org.briarproject.api.FormatException;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfWriter;
import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.db.Metadata;
import org.briarproject.util.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import static org.briarproject.api.data.BdfDictionary.NULL_VALUE;
import static org.briarproject.api.db.Metadata.REMOVE;
import static org.briarproject.data.Types.DICTIONARY;
import static org.briarproject.data.Types.END;
import static org.briarproject.data.Types.FALSE;
import static org.briarproject.data.Types.FLOAT_64;
import static org.briarproject.data.Types.INT_16;
import static org.briarproject.data.Types.INT_32;
import static org.briarproject.data.Types.INT_64;
import static org.briarproject.data.Types.INT_8;
import static org.briarproject.data.Types.LIST;
import static org.briarproject.data.Types.NULL;
import static org.briarproject.data.Types.RAW_16;
import static org.briarproject.data.Types.RAW_32;
import static org.briarproject.data.Types.RAW_8;
import static org.briarproject.data.Types.STRING_16;
import static org.briarproject.data.Types.STRING_32;
import static org.briarproject.data.Types.STRING_8;
import static org.briarproject.data.Types.TRUE;
class MetadataEncoderImpl implements MetadataEncoder {
private final BdfWriterFactory bdfWriterFactory;
@Inject
MetadataEncoderImpl(BdfWriterFactory bdfWriterFactory) {
this.bdfWriterFactory = bdfWriterFactory;
}
@Override
public Metadata encode(BdfDictionary d) throws FormatException {
Metadata m = new Metadata();
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (Entry<String, Object> e : d.entrySet()) {
if (e.getValue() == NULL_VALUE) {
// Special case: if the value is null, the key is being removed
m.put(e.getKey(), REMOVE);
} else {
encodeObject(out, e.getValue());
m.put(e.getKey(), out.toByteArray());
out.reset();
BdfWriter writer = bdfWriterFactory.createWriter(out);
try {
for (Entry<String, Object> e : d.entrySet()) {
if (e.getValue() == NULL_VALUE) {
// Special case: if value is null, key is being removed
m.put(e.getKey(), REMOVE);
} else {
encodeObject(writer, e.getValue());
m.put(e.getKey(), out.toByteArray());
out.reset();
}
}
} catch (FormatException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(e);
}
return m;
}
private void encodeObject(ByteArrayOutputStream out, Object o)
throws FormatException {
if (o == NULL_VALUE) out.write(NULL);
else if (o instanceof Boolean) out.write((Boolean) o ? TRUE : FALSE);
else if (o instanceof Byte) encodeInteger(out, (Byte) o);
else if (o instanceof Short) encodeInteger(out, (Short) o);
else if (o instanceof Integer) encodeInteger(out, (Integer) o);
else if (o instanceof Long) encodeInteger(out, (Long) o);
else if (o instanceof Float) encodeFloat(out, (Float) o);
else if (o instanceof Double) encodeFloat(out, (Double) o);
else if (o instanceof String) encodeString(out, (String) o);
else if (o instanceof byte[]) encodeRaw(out, (byte[]) o);
else if (o instanceof Bytes) encodeRaw(out, ((Bytes) o).getBytes());
else if (o instanceof List) encodeList(out, (List) o);
else if (o instanceof Map) encodeDictionary(out, (Map) o);
private void encodeObject(BdfWriter writer, Object o)
throws IOException {
if (o instanceof Boolean) writer.writeBoolean((Boolean) o);
else if (o instanceof Byte) writer.writeLong((Byte) o);
else if (o instanceof Short) writer.writeLong((Short) o);
else if (o instanceof Integer) writer.writeLong((Integer) o);
else if (o instanceof Long) writer.writeLong((Long) o);
else if (o instanceof Float) writer.writeDouble((Float) o);
else if (o instanceof Double) writer.writeDouble((Double) o);
else if (o instanceof String) writer.writeString((String) o);
else if (o instanceof byte[]) writer.writeRaw((byte[]) o);
else if (o instanceof Bytes) writer.writeRaw(((Bytes) o).getBytes());
else if (o instanceof List) writer.writeList((List) o);
else if (o instanceof Map) writer.writeDictionary((Map) o);
else throw new FormatException();
}
private void encodeInteger(ByteArrayOutputStream out, byte i) {
out.write(INT_8);
encodeInt8(out, i);
}
private void encodeInteger(ByteArrayOutputStream out, short i) {
if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
encodeInteger(out, (byte) i);
} else {
out.write(INT_16);
encodeInt16(out, i);
}
}
private void encodeInteger(ByteArrayOutputStream out, int i) {
if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
encodeInteger(out, (short) i);
} else {
out.write(INT_32);
encodeInt32(out, i);
}
}
private void encodeInteger(ByteArrayOutputStream out, long i) {
if (i >= Integer.MIN_VALUE && i <= Integer.MAX_VALUE) {
encodeInteger(out, (int) i);
} else {
out.write(INT_64);
encodeInt64(out, i);
}
}
private void encodeInt8(ByteArrayOutputStream out, byte i) {
out.write(i);
}
private void encodeInt16(ByteArrayOutputStream out, short i) {
out.write((byte) (i >> 8));
out.write((byte) ((i << 8) >> 8));
}
private void encodeInt32(ByteArrayOutputStream out, int i) {
out.write((byte) (i >> 24));
out.write((byte) ((i << 8) >> 24));
out.write((byte) ((i << 16) >> 24));
out.write((byte) ((i << 24) >> 24));
}
private void encodeInt64(ByteArrayOutputStream out, long i) {
out.write((byte) (i >> 56));
out.write((byte) ((i << 8) >> 56));
out.write((byte) ((i << 16) >> 56));
out.write((byte) ((i << 24) >> 56));
out.write((byte) ((i << 32) >> 56));
out.write((byte) ((i << 40) >> 56));
out.write((byte) ((i << 48) >> 56));
out.write((byte) ((i << 56) >> 56));
}
private void encodeFloat(ByteArrayOutputStream out, float f) {
encodeFloat(out, (double) f);
}
private void encodeFloat(ByteArrayOutputStream out, double d) {
out.write(FLOAT_64);
encodeInt64(out, Double.doubleToLongBits(d));
}
private void encodeString(ByteArrayOutputStream out, String s) {
byte[] b = StringUtils.toUtf8(s);
if (b.length <= Byte.MAX_VALUE) {
out.write(STRING_8);
encodeInt8(out, (byte) b.length);
} else if (b.length <= Short.MAX_VALUE) {
out.write(STRING_16);
encodeInt16(out, (short) b.length);
} else {
out.write(STRING_32);
encodeInt32(out, b.length);
}
out.write(b, 0, b.length);
}
private void encodeRaw(ByteArrayOutputStream out, byte[] b) {
if (b.length <= Byte.MAX_VALUE) {
out.write(RAW_8);
encodeInt8(out, (byte) b.length);
} else if (b.length <= Short.MAX_VALUE) {
out.write(RAW_16);
encodeInt16(out, (short) b.length);
} else {
out.write(RAW_32);
encodeInt32(out, b.length);
}
out.write(b, 0, b.length);
}
private void encodeList(ByteArrayOutputStream out, List list)
throws FormatException {
out.write(LIST);
for (Object o : list) encodeObject(out, o);
out.write(END);
}
private void encodeDictionary(ByteArrayOutputStream out, Map<?, ?> map)
throws FormatException {
out.write(DICTIONARY);
for (Entry<?, ?> e : map.entrySet()) {
if (!(e.getKey() instanceof String)) throw new FormatException();
encodeString(out, (String) e.getKey());
encodeObject(out, e.getValue());
}
out.write(END);
}
}
package org.briarproject.data;
import com.google.inject.Inject;
import org.briarproject.api.FormatException;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.BdfReader;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.MetadataParser;
import org.briarproject.api.db.Metadata;
import org.briarproject.util.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Map.Entry;
import static org.briarproject.api.data.BdfDictionary.NULL_VALUE;
import static org.briarproject.api.db.Metadata.REMOVE;
import static org.briarproject.data.Types.DICTIONARY;
import static org.briarproject.data.Types.END;
import static org.briarproject.data.Types.FALSE;
import static org.briarproject.data.Types.FLOAT_64;
import static org.briarproject.data.Types.INT_16;
import static org.briarproject.data.Types.INT_32;
import static org.briarproject.data.Types.INT_64;
import static org.briarproject.data.Types.INT_8;
import static org.briarproject.data.Types.LIST;
import static org.briarproject.data.Types.NULL;
import static org.briarproject.data.Types.RAW_16;
import static org.briarproject.data.Types.RAW_32;
import static org.briarproject.data.Types.RAW_8;
import static org.briarproject.data.Types.STRING_16;
import static org.briarproject.data.Types.STRING_32;
import static org.briarproject.data.Types.STRING_8;
import static org.briarproject.data.Types.TRUE;
class MetadataParserImpl implements MetadataParser {
private final BdfReaderFactory bdfReaderFactory;
@Inject
MetadataParserImpl(BdfReaderFactory bdfReaderFactory) {
this.bdfReaderFactory = bdfReaderFactory;
}
@Override
public BdfDictionary parse(Metadata m) throws FormatException {
BdfDictionary d = new BdfDictionary();
for (Entry<String, byte[]> e : m.entrySet())
d.put(e.getKey(), parseValue(e.getValue()));
try {
for (Entry<String, byte[]> e : m.entrySet()) {
// Special case: if key is being removed, value is null
if (e.getValue() == REMOVE) d.put(e.getKey(), NULL_VALUE);
else d.put(e.getKey(), parseValue(e.getValue()));
}
} catch (FormatException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(e);
}
return d;
}
private Object parseValue(byte[] b) throws FormatException {
if (b == REMOVE) return NULL_VALUE;
private Object parseValue(byte[] b) throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(b);
Object o = parseObject(in);
if (in.available() > 0) throw new FormatException();
BdfReader reader = bdfReaderFactory.createReader(in);
Object o = parseObject(reader);
if (!reader.eof()) throw new FormatException();
return o;
}
private Object parseObject(ByteArrayInputStream in) throws FormatException {
switch(in.read()) {
case NULL:
return NULL_VALUE;
case TRUE:
return Boolean.TRUE;
case FALSE:
return Boolean.FALSE;
case INT_8:
return (long) parseInt8(in);
case INT_16:
return (long) parseInt16(in);
case INT_32:
return (long) parseInt32(in);
case INT_64:
return parseInt64(in);
case FLOAT_64:
return Double.longBitsToDouble(parseInt64(in));
case STRING_8:
return parseString(in, parseInt8(in));
case STRING_16:
return parseString(in, parseInt16(in));
case STRING_32:
return parseString(in, parseInt32(in));
case RAW_8:
return parseRaw(in, parseInt8(in));
case RAW_16:
return parseRaw(in, parseInt16(in));
case RAW_32:
return parseRaw(in, parseInt32(in));
case LIST:
return parseList(in);
case DICTIONARY:
return parseDictionary(in);
default:
throw new FormatException();
}
}
private String parseString(ByteArrayInputStream in) throws FormatException {
switch(in.read()) {
case STRING_8:
return parseString(in, parseInt8(in));
case STRING_16:
return parseString(in, parseInt16(in));
case STRING_32:
return parseString(in, parseInt32(in));
default:
throw new FormatException();
}
}
private byte parseInt8(ByteArrayInputStream in) throws FormatException {
if (in.available() < 1) throw new FormatException();
return (byte) in.read();
}
private short parseInt16(ByteArrayInputStream in) throws FormatException {