package co.nstant.in.cbor.encoder;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Comparator;
import java.util.TreeMap;
import co.nstant.in.cbor.CborEncoder;
import co.nstant.in.cbor.CborException;
import co.nstant.in.cbor.model.DataItem;
import co.nstant.in.cbor.model.MajorType;
import co.nstant.in.cbor.model.Map;
import co.nstant.in.cbor.model.SimpleValue;
public class MapEncoder extends AbstractEncoder<Map> {
public MapEncoder(CborEncoder encoder, OutputStream outputStream) {
super(encoder, outputStream);
}
@Override
public void encode(Map map) throws CborException {
Collection<DataItem> keys = map.getKeys();
if (map.isChunked()) {
encodeTypeChunked(MajorType.MAP);
} else {
encodeTypeAndLength(MajorType.MAP, keys.size());
}
if (keys.isEmpty()) {
return;
}
if (map.isChunked()) {
for (DataItem key : keys) {
encoder.encode(key);
encoder.encode(map.get(key));
}
encoder.encode(SimpleValue.BREAK);
return;
}
/**
* The keys in every map must be sorted lowest value to highest. Sorting
* is performed on the bytes of the representation of the key data items
* without paying attention to the 3/5 bit splitting for major types.
* (Note that this rule allows maps that have keys of different types,
* even though that is probably a bad practice that could lead to errors
* in some canonicalization implementations.) The sorting rules are:
*
* If two keys have different lengths, the shorter one sorts earlier;
*
* If two keys have the same length, the one with the lower value in
* (byte-wise) lexical order sorts earlier.
*/
TreeMap<byte[], byte[]> sortedMap = new TreeMap<>(
new Comparator<byte[]>() {
@Override
public int compare(byte[] o1, byte[] o2) {
if (o1.length < o2.length) {
return -1;
}
if (o1.length > o2.length) {
return 1;
}
for (int i = 0; i < o1.length; i++) {
if (o1[i] < o2[i]) {
return -1;
}
if (o1[i] > o2[i]) {
return 1;
}
}
return 0;
}
});
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
CborEncoder e = new CborEncoder(byteArrayOutputStream);
for (DataItem key : keys) {
// Key
e.encode(key);
byte[] keyBytes = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.reset();
// Value
e.encode(map.get(key));
byte[] valueBytes = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.reset();
sortedMap.put(keyBytes, valueBytes);
}
for (java.util.Map.Entry<byte[], byte[]> entry : sortedMap.entrySet()) {
write(entry.getKey());
write(entry.getValue());
}
}
}