Added type-safe accessors and iterator accessors for lists and maps.

This commit is contained in:
akwizgran
2011-07-10 18:31:18 +01:00
parent 1f5e52c31b
commit 0f4ffe9fbc
3 changed files with 432 additions and 67 deletions

View File

@@ -1,8 +1,10 @@
package net.sf.briar.api.serial;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public interface Reader {
@@ -37,17 +39,17 @@ public interface Reader {
byte[] readRaw() throws IOException;
byte[] readRaw(int maxLength) throws IOException;
// FIXME: Add type-safe readers and iterator readers
boolean hasList(boolean definite) throws IOException;
List<?> readList(boolean definite) throws IOException;
boolean hasList() throws IOException;
List<?> readList() throws IOException;
List<Object> readList() throws IOException;
Iterator<Object> readListElements() throws IOException;
<E> List<E> readList(Class<E> e) throws IOException;
<E> Iterator<E> readListElements(Class<E> e) throws IOException;
boolean hasMap(boolean definite) throws IOException;
Map<?, ?> readMap(boolean definite) throws IOException;
boolean hasMap() throws IOException;
Map<?, ?> readMap() throws IOException;
Map<Object, Object> readMap() throws IOException;
Iterator<Entry<Object, Object>> readMapEntries() throws IOException;
<K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException;
<K, V> Iterator<Entry<K, V>> readMapEntries(Class<K> k, Class<V> v) throws IOException;
boolean hasNull() throws IOException;
void readNull() throws IOException;

View File

@@ -4,8 +4,11 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import net.sf.briar.api.serial.FormatException;
import net.sf.briar.api.serial.Reader;
@@ -243,30 +246,45 @@ public class ReaderImpl implements Reader {
return b;
}
public boolean hasList(boolean definite) throws IOException {
public boolean hasList() throws IOException {
if(!started) readNext(true);
if(eof) return false;
if(definite) return next == Tag.LIST_DEF;
else return next == Tag.LIST_INDEF;
return next == Tag.LIST_DEF || next == Tag.LIST_INDEF;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public List<?> readList(boolean definite) throws IOException {
if(!hasList(definite)) throw new FormatException();
public List<Object> readList() throws IOException {
return readList(Object.class);
}
public Iterator<Object> readListElements() throws IOException {
return readListElements(Object.class);
}
public <E> List<E> readList(Class<E> e) throws IOException {
if(!hasList()) throw new FormatException();
boolean definite = next == Tag.LIST_DEF;
readNext(false);
List list = new ArrayList();
List<E> list = new ArrayList<E>();
if(definite) {
long l = readIntAny();
if(l < 0 || l > Integer.MAX_VALUE) throw new FormatException();
int length = (int) l;
for(int i = 0; i < length; i++) list.add(readObject());
for(int i = 0; i < length; i++) list.add(readObject(e));
} else {
while(!hasEnd()) list.add(readObject());
while(!hasEnd()) list.add(readObject(e));
readEnd();
}
return list;
}
public <E> Iterator<E> readListElements(Class<E> e) throws IOException {
if(!hasList()) throw new FormatException();
boolean definite = next == Tag.LIST_DEF;
readNext(false);
if(definite) return new DefiniteListIterator<E>(e);
else return new IndefiniteListIterator<E>(e);
}
private boolean hasEnd() throws IOException {
if(!started) readNext(true);
if(eof) return false;
@@ -299,40 +317,13 @@ public class ReaderImpl implements Reader {
throw new IllegalStateException();
}
public boolean hasList() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.LIST_DEF || next == Tag.LIST_INDEF;
}
public List<?> readList() throws IOException {
if(hasList(true)) return readList(true);
if(hasList(false)) return readList(false);
@SuppressWarnings("unchecked")
private <T> T readObject(Class<T> t) throws IOException {
try {
return (T) readObject();
} catch(ClassCastException e) {
throw new FormatException();
}
public boolean hasMap(boolean definite) throws IOException {
if(!started) readNext(true);
if(eof) return false;
if(definite) return next == Tag.MAP_DEF;
else return next == Tag.MAP_INDEF;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Map<?, ?> readMap(boolean definite) throws IOException {
if(!hasMap(definite)) throw new FormatException();
readNext(false);
Map m = new HashMap();
if(definite) {
long l = readIntAny();
if(l < 0 || l > Integer.MAX_VALUE) throw new FormatException();
int length = (int) l;
for(int i = 0; i < length; i++) m.put(readObject(), readObject());
} else {
while(!hasEnd()) m.put(readObject(), readObject());
readEnd();
}
return m;
}
public boolean hasMap() throws IOException {
@@ -341,10 +332,38 @@ public class ReaderImpl implements Reader {
return next == Tag.MAP_DEF || next == Tag.MAP_INDEF;
}
public Map<?, ?> readMap() throws IOException {
if(hasMap(true)) return readMap(true);
if(hasMap(false)) return readMap(false);
throw new FormatException();
public Map<Object, Object> readMap() throws IOException {
return readMap(Object.class, Object.class);
}
public Iterator<Entry<Object, Object>> readMapEntries() throws IOException {
return readMapEntries(Object.class, Object.class);
}
public <K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException {
if(!hasMap()) throw new FormatException();
boolean definite = next == Tag.MAP_DEF;
readNext(false);
Map<K, V> m = new HashMap<K, V>();
if(definite) {
long l = readIntAny();
if(l < 0 || l > Integer.MAX_VALUE) throw new FormatException();
int length = (int) l;
for(int i = 0; i < length; i++) m.put(readObject(k), readObject(v));
} else {
while(!hasEnd()) m.put(readObject(k), readObject(v));
readEnd();
}
return m;
}
public <K, V> Iterator<Entry<K, V>> readMapEntries(Class<K> k, Class<V> v)
throws IOException {
if(!hasMap()) throw new FormatException();
boolean definite = next == Tag.MAP_DEF;
readNext(false);
if(definite) return new DefiniteMapIterator<K, V>(k, v);
else return new IndefiniteMapIterator<K, V>(k, v);
}
public boolean hasNull() throws IOException {
@@ -357,4 +376,167 @@ public class ReaderImpl implements Reader {
if(!hasNull()) throw new FormatException();
readNext(true);
}
private class DefiniteListIterator<E> implements Iterator<E> {
private final Class<E> e;
private int remaining;
private DefiniteListIterator(Class<E> e) throws IOException {
this.e = e;
long l = readIntAny();
if(l < 0 || l > Integer.MAX_VALUE) throw new FormatException();
remaining = (int) l;
}
public boolean hasNext() {
return remaining > 0;
}
public E next() {
if(remaining == 0) throw new NoSuchElementException();
remaining--;
try {
return readObject(e);
} catch(IOException ex) {
throw new RuntimeException(ex);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
private class IndefiniteListIterator<E> implements Iterator<E> {
private final Class<E> e;
private boolean hasNext = true;
private IndefiniteListIterator(Class<E> e) throws IOException {
this.e = e;
if(hasEnd()) {
readEnd();
hasNext = false;
}
}
public boolean hasNext() {
return hasNext;
}
public E next() {
if(!hasNext) throw new NoSuchElementException();
try {
E next = readObject(e);
if(hasEnd()) {
readEnd();
hasNext = false;
}
return next;
} catch(IOException ex) {
throw new RuntimeException(ex);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
private class DefiniteMapIterator<K, V> implements Iterator<Entry<K, V>> {
private final Class<K> k;
private final Class<V> v;
private int remaining;
private DefiniteMapIterator(Class<K> k, Class<V> v) throws IOException {
this.k = k;
this.v = v;
long l = readIntAny();
if(l < 0 || l > Integer.MAX_VALUE) throw new FormatException();
remaining = (int) l;
}
public boolean hasNext() {
return remaining > 0;
}
public Entry<K, V> next() {
if(remaining == 0) throw new NoSuchElementException();
remaining--;
try {
return new MapEntry<K, V>(readObject(k), readObject(v));
} catch(IOException ex) {
throw new RuntimeException(ex);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
private class IndefiniteMapIterator<K, V> implements Iterator<Entry<K, V>> {
private final Class<K> k;
private final Class<V> v;
private boolean hasNext = true;
private IndefiniteMapIterator(Class<K> k, Class<V> v)
throws IOException {
this.k = k;
this.v = v;
if(hasEnd()) {
readEnd();
hasNext = false;
}
}
public boolean hasNext() {
return hasNext;
}
public Entry<K, V> next() {
if(!hasNext) throw new NoSuchElementException();
try {
Entry<K, V> next =
new MapEntry<K, V>(readObject(k), readObject(v));
if(hasEnd()) {
readEnd();
hasNext = false;
}
return next;
} catch(IOException ex) {
throw new RuntimeException(ex);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
private static class MapEntry<K, V> implements Entry<K, V> {
private final K k;
private final V v;
MapEntry(K k, V v) {
this.k = k;
this.v = v;
}
public K getKey() {
return k;
}
public V getValue() {
return v;
}
public V setValue(V value) {
throw new UnsupportedOperationException();
}
}
}

View File

@@ -3,6 +3,7 @@ package net.sf.briar.serial;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -169,10 +170,9 @@ public class ReaderImplTest extends TestCase {
}
@Test
@SuppressWarnings("rawtypes")
public void testReadDefiniteList() throws IOException {
setContents("F5" + "03" + "01" + "F703666F6F" + "FC0080");
List l = r.readList(true);
List<Object> l = r.readList();
assertNotNull(l);
assertEquals(3, l.size());
assertEquals((byte) 1, l.get(0));
@@ -182,10 +182,51 @@ public class ReaderImplTest extends TestCase {
}
@Test
@SuppressWarnings("rawtypes")
public void testReadDefiniteListElements() throws IOException {
setContents("F5" + "03" + "01" + "F703666F6F" + "FC0080");
Iterator<Object> i = r.readListElements();
assertNotNull(i);
assertTrue(i.hasNext());
assertEquals((byte) 1, i.next());
assertTrue(i.hasNext());
assertEquals("foo", i.next());
assertTrue(i.hasNext());
assertEquals((short) 128, i.next());
assertFalse(i.hasNext());
assertTrue(r.eof());
}
@Test
public void testReadDefiniteListTypeSafe() throws IOException {
setContents("F5" + "03" + "01" + "02" + "03");
List<Byte> l = r.readList(Byte.class);
assertNotNull(l);
assertEquals(3, l.size());
assertEquals(Byte.valueOf((byte) 1), l.get(0));
assertEquals(Byte.valueOf((byte) 2), l.get(1));
assertEquals(Byte.valueOf((byte) 3), l.get(2));
assertTrue(r.eof());
}
@Test
public void testReadDefiniteListElementsTypeSafe() throws IOException {
setContents("F5" + "03" + "01" + "02" + "03");
Iterator<Byte> i = r.readListElements(Byte.class);
assertNotNull(i);
assertTrue(i.hasNext());
assertEquals(Byte.valueOf((byte) 1), i.next());
assertTrue(i.hasNext());
assertEquals(Byte.valueOf((byte) 2), i.next());
assertTrue(i.hasNext());
assertEquals(Byte.valueOf((byte) 3), i.next());
assertFalse(i.hasNext());
assertTrue(r.eof());
}
@Test
public void testReadDefiniteMap() throws IOException {
setContents("F4" + "02" + "F703666F6F" + "7B" + "F600" + "F0");
Map m = r.readMap(true);
Map<Object, Object> m = r.readMap();
assertNotNull(m);
assertEquals(2, m.size());
assertEquals((byte) 123, m.get("foo"));
@@ -196,10 +237,58 @@ public class ReaderImplTest extends TestCase {
}
@Test
@SuppressWarnings("rawtypes")
public void testReadDefiniteMapEntries() throws IOException {
setContents("F4" + "02" + "F703666F6F" + "7B" + "F600" + "F0");
Iterator<Entry<Object, Object>> i = r.readMapEntries();
assertNotNull(i);
assertTrue(i.hasNext());
Entry<Object, Object> e = i.next();
assertNotNull(e);
assertEquals("foo", e.getKey());
assertEquals((byte) 123, e.getValue());
assertTrue(i.hasNext());
e = i.next();
assertNotNull(e);
assertEquals(new RawImpl(new byte[] {}), e.getKey());
assertNull(e.getValue());
assertTrue(r.eof());
}
@Test
public void testReadDefiniteMapTypeSafe() throws IOException {
setContents("F4" + "02" + "F703666F6F" + "7B" + "F700" + "F0");
Map<String, Byte> m = r.readMap(String.class, Byte.class);
assertNotNull(m);
assertEquals(2, m.size());
assertEquals(Byte.valueOf((byte) 123), m.get("foo"));
assertTrue(m.containsKey(""));
assertNull(m.get(""));
assertTrue(r.eof());
}
@Test
public void testReadDefiniteMapEntriesTypeSafe() throws IOException {
setContents("F4" + "02" + "F703666F6F" + "7B" + "F700" + "F0");
Iterator<Entry<String, Byte>> i =
r.readMapEntries(String.class, Byte.class);
assertNotNull(i);
assertTrue(i.hasNext());
Entry<String, Byte> e = i.next();
assertNotNull(e);
assertEquals("foo", e.getKey());
assertEquals(Byte.valueOf((byte) 123), e.getValue());
assertTrue(i.hasNext());
e = i.next();
assertNotNull(e);
assertEquals("", e.getKey());
assertNull(e.getValue());
assertTrue(r.eof());
}
@Test
public void testReadIndefiniteList() throws IOException {
setContents("F3" + "01" + "F703666F6F" + "FC0080" + "F1");
List l = r.readList(false);
List<Object> l = r.readList();
assertNotNull(l);
assertEquals(3, l.size());
assertEquals((byte) 1, l.get(0));
@@ -209,10 +298,51 @@ public class ReaderImplTest extends TestCase {
}
@Test
@SuppressWarnings("rawtypes")
public void testReadIndfiniteListElements() throws IOException {
setContents("F3" + "01" + "F703666F6F" + "FC0080" + "F1");
Iterator<Object> i = r.readListElements();
assertNotNull(i);
assertTrue(i.hasNext());
assertEquals((byte) 1, i.next());
assertTrue(i.hasNext());
assertEquals("foo", i.next());
assertTrue(i.hasNext());
assertEquals((short) 128, i.next());
assertFalse(i.hasNext());
assertTrue(r.eof());
}
@Test
public void testReadIndefiniteListTypeSafe() throws IOException {
setContents("F3" + "01" + "02" + "03" + "F1");
List<Byte> l = r.readList(Byte.class);
assertNotNull(l);
assertEquals(3, l.size());
assertEquals(Byte.valueOf((byte) 1), l.get(0));
assertEquals(Byte.valueOf((byte) 2), l.get(1));
assertEquals(Byte.valueOf((byte) 3), l.get(2));
assertTrue(r.eof());
}
@Test
public void testReadIndefiniteListElementsTypeSafe() throws IOException {
setContents("F3" + "01" + "02" + "03" + "F1");
Iterator<Byte> i = r.readListElements(Byte.class);
assertNotNull(i);
assertTrue(i.hasNext());
assertEquals(Byte.valueOf((byte) 1), i.next());
assertTrue(i.hasNext());
assertEquals(Byte.valueOf((byte) 2), i.next());
assertTrue(i.hasNext());
assertEquals(Byte.valueOf((byte) 3), i.next());
assertFalse(i.hasNext());
assertTrue(r.eof());
}
@Test
public void testReadIndefiniteMap() throws IOException {
setContents("F2" + "F703666F6F" + "7B" + "F600" + "F0" + "F1");
Map m = r.readMap(false);
Map<Object, Object> m = r.readMap();
assertNotNull(m);
assertEquals(2, m.size());
assertEquals((byte) 123, m.get("foo"));
@@ -223,19 +353,70 @@ public class ReaderImplTest extends TestCase {
}
@Test
@SuppressWarnings("rawtypes")
public void testReadIndefiniteMapEntries() throws IOException {
setContents("F2" + "F703666F6F" + "7B" + "F600" + "F0" + "F1");
Iterator<Entry<Object, Object>> i = r.readMapEntries();
assertNotNull(i);
assertTrue(i.hasNext());
Entry<Object, Object> e = i.next();
assertNotNull(e);
assertEquals("foo", e.getKey());
assertEquals((byte) 123, e.getValue());
assertTrue(i.hasNext());
e = i.next();
assertNotNull(e);
assertEquals(new RawImpl(new byte[] {}), e.getKey());
assertNull(e.getValue());
assertFalse(i.hasNext());
assertTrue(r.eof());
}
@Test
public void testReadIndefiniteMapTypeSafe() throws IOException {
setContents("F2" + "F703666F6F" + "7B" + "F700" + "F0" + "F1");
Map<String, Byte> m = r.readMap(String.class, Byte.class);
assertNotNull(m);
assertEquals(2, m.size());
assertEquals(Byte.valueOf((byte) 123), m.get("foo"));
assertTrue(m.containsKey(""));
assertNull(m.get(""));
assertTrue(r.eof());
}
@Test
public void testReadIndefiniteMapEntriesTypeSafe() throws IOException {
setContents("F2" + "F703666F6F" + "7B" + "F700" + "F0" + "F1");
Iterator<Entry<String, Byte>> i =
r.readMapEntries(String.class, Byte.class);
assertNotNull(i);
assertTrue(i.hasNext());
Entry<String, Byte> e = i.next();
assertNotNull(e);
assertEquals("foo", e.getKey());
assertEquals(Byte.valueOf((byte) 123), e.getValue());
assertTrue(i.hasNext());
e = i.next();
assertNotNull(e);
assertEquals("", e.getKey());
assertNull(e.getValue());
assertFalse(i.hasNext());
assertTrue(r.eof());
}
@Test
@SuppressWarnings("unchecked")
public void testReadNestedMapsAndLists() throws IOException {
setContents("F4" + "01" + "F4" + "01" + "F703666F6F" + "7B" +
"F5" + "01" + "01");
Map m = r.readMap();
Map<Object, Object> m = r.readMap();
assertNotNull(m);
assertEquals(1, m.size());
Entry e = (Entry) m.entrySet().iterator().next();
Map m1 = (Map) e.getKey();
Entry<Object, Object> e = m.entrySet().iterator().next();
Map<Object, Object> m1 = (Map<Object, Object>) e.getKey();
assertNotNull(m1);
assertEquals(1, m1.size());
assertEquals((byte) 123, m1.get("foo"));
List l = (List) e.getValue();
List<Object> l = (List<Object>) e.getValue();
assertNotNull(l);
assertEquals(1, l.size());
assertEquals((byte) 1, l.get(0));