From 0f4ffe9fbcf1ddd1c74e8f8c5aebee633d6cf094 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Sun, 10 Jul 2011 18:31:18 +0100 Subject: [PATCH] Added type-safe accessors and iterator accessors for lists and maps. --- api/net/sf/briar/api/serial/Reader.java | 18 +- .../net/sf/briar/serial/ReaderImpl.java | 274 +++++++++++++++--- test/net/sf/briar/serial/ReaderImplTest.java | 207 ++++++++++++- 3 files changed, 432 insertions(+), 67 deletions(-) diff --git a/api/net/sf/briar/api/serial/Reader.java b/api/net/sf/briar/api/serial/Reader.java index ec90dd65f..652cd94d3 100644 --- a/api/net/sf/briar/api/serial/Reader.java +++ b/api/net/sf/briar/api/serial/Reader.java @@ -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 readList() throws IOException; + Iterator readListElements() throws IOException; + List readList(Class e) throws IOException; + Iterator readListElements(Class e) throws IOException; - boolean hasMap(boolean definite) throws IOException; - Map readMap(boolean definite) throws IOException; boolean hasMap() throws IOException; - Map readMap() throws IOException; + Map readMap() throws IOException; + Iterator> readMapEntries() throws IOException; + Map readMap(Class k, Class v) throws IOException; + Iterator> readMapEntries(Class k, Class v) throws IOException; boolean hasNull() throws IOException; void readNull() throws IOException; diff --git a/components/net/sf/briar/serial/ReaderImpl.java b/components/net/sf/briar/serial/ReaderImpl.java index a57390f33..cffaf557a 100644 --- a/components/net/sf/briar/serial/ReaderImpl.java +++ b/components/net/sf/briar/serial/ReaderImpl.java @@ -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 readList() throws IOException { + return readList(Object.class); + } + + public Iterator readListElements() throws IOException { + return readListElements(Object.class); + } + + public List readList(Class e) throws IOException { + if(!hasList()) throw new FormatException(); + boolean definite = next == Tag.LIST_DEF; readNext(false); - List list = new ArrayList(); + List list = new ArrayList(); 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 Iterator readListElements(Class e) throws IOException { + if(!hasList()) throw new FormatException(); + boolean definite = next == Tag.LIST_DEF; + readNext(false); + if(definite) return new DefiniteListIterator(e); + else return new IndefiniteListIterator(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); - 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(); + @SuppressWarnings("unchecked") + private T readObject(Class t) throws IOException { + try { + return (T) readObject(); + } catch(ClassCastException e) { + throw new FormatException(); } - 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 readMap() throws IOException { + return readMap(Object.class, Object.class); + } + + public Iterator> readMapEntries() throws IOException { + return readMapEntries(Object.class, Object.class); + } + + public Map readMap(Class k, Class v) throws IOException { + if(!hasMap()) throw new FormatException(); + boolean definite = next == Tag.MAP_DEF; + 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(k), readObject(v)); + } else { + while(!hasEnd()) m.put(readObject(k), readObject(v)); + readEnd(); + } + return m; + } + + public Iterator> readMapEntries(Class k, Class v) + throws IOException { + if(!hasMap()) throw new FormatException(); + boolean definite = next == Tag.MAP_DEF; + readNext(false); + if(definite) return new DefiniteMapIterator(k, v); + else return new IndefiniteMapIterator(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 implements Iterator { + + private final Class e; + private int remaining; + + private DefiniteListIterator(Class 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 implements Iterator { + + private final Class e; + private boolean hasNext = true; + + private IndefiniteListIterator(Class 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 implements Iterator> { + + private final Class k; + private final Class v; + private int remaining; + + private DefiniteMapIterator(Class k, Class 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 next() { + if(remaining == 0) throw new NoSuchElementException(); + remaining--; + try { + return new MapEntry(readObject(k), readObject(v)); + } catch(IOException ex) { + throw new RuntimeException(ex); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } + + private class IndefiniteMapIterator implements Iterator> { + + private final Class k; + private final Class v; + private boolean hasNext = true; + + private IndefiniteMapIterator(Class k, Class v) + throws IOException { + this.k = k; + this.v = v; + if(hasEnd()) { + readEnd(); + hasNext = false; + } + } + + public boolean hasNext() { + return hasNext; + } + + public Entry next() { + if(!hasNext) throw new NoSuchElementException(); + try { + Entry next = + new MapEntry(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 implements Entry { + + 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(); + } + } } diff --git a/test/net/sf/briar/serial/ReaderImplTest.java b/test/net/sf/briar/serial/ReaderImplTest.java index ea9fb88b5..f1826f5fd 100644 --- a/test/net/sf/briar/serial/ReaderImplTest.java +++ b/test/net/sf/briar/serial/ReaderImplTest.java @@ -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 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 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 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 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 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> i = r.readMapEntries(); + assertNotNull(i); + assertTrue(i.hasNext()); + Entry 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 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> i = + r.readMapEntries(String.class, Byte.class); + assertNotNull(i); + assertTrue(i.hasNext()); + Entry 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 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 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 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 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 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> i = r.readMapEntries(); + assertNotNull(i); + assertTrue(i.hasNext()); + Entry 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 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> i = + r.readMapEntries(String.class, Byte.class); + assertNotNull(i); + assertTrue(i.hasNext()); + Entry 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 m = r.readMap(); assertNotNull(m); assertEquals(1, m.size()); - Entry e = (Entry) m.entrySet().iterator().next(); - Map m1 = (Map) e.getKey(); + Entry e = m.entrySet().iterator().next(); + Map m1 = (Map) e.getKey(); assertNotNull(m1); assertEquals(1, m1.size()); assertEquals((byte) 123, m1.get("foo")); - List l = (List) e.getValue(); + List l = (List) e.getValue(); assertNotNull(l); assertEquals(1, l.size()); assertEquals((byte) 1, l.get(0));