From 0f4ffe9fbcf1ddd1c74e8f8c5aebee633d6cf094 Mon Sep 17 00:00:00 2001 From: akwizgran <akwizgran@users.sourceforge.net> 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 | 254 +++++++++++++++--- test/net/sf/briar/serial/ReaderImplTest.java | 207 +++++++++++++- 3 files changed, 422 insertions(+), 57 deletions(-) diff --git a/api/net/sf/briar/api/serial/Reader.java b/api/net/sf/briar/api/serial/Reader.java index ec90dd65f1..652cd94d3d 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<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; diff --git a/components/net/sf/briar/serial/ReaderImpl.java b/components/net/sf/briar/serial/ReaderImpl.java index a57390f33a..cffaf557a4 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; + } + + public List<Object> readList() throws IOException { + return readList(Object.class); + } + + public Iterator<Object> readListElements() throws IOException { + return readListElements(Object.class); } - @SuppressWarnings({ "unchecked", "rawtypes" }) - public List<?> readList(boolean definite) throws IOException { - if(!hasList(definite)) throw new FormatException(); + 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,52 +317,53 @@ public class ReaderImpl implements Reader { throw new IllegalStateException(); } - public boolean hasList() throws IOException { + @SuppressWarnings("unchecked") + private <T> T readObject(Class<T> t) throws IOException { + try { + return (T) readObject(); + } catch(ClassCastException e) { + throw new FormatException(); + } + } + + public boolean hasMap() throws IOException { if(!started) readNext(true); if(eof) return false; - return next == Tag.LIST_DEF || next == Tag.LIST_INDEF; + return next == Tag.MAP_DEF || next == Tag.MAP_INDEF; } - public List<?> readList() throws IOException { - if(hasList(true)) return readList(true); - if(hasList(false)) return readList(false); - throw new FormatException(); + public Map<Object, Object> readMap() throws IOException { + return readMap(Object.class, Object.class); } - 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; + public Iterator<Entry<Object, Object>> readMapEntries() throws IOException { + return readMapEntries(Object.class, Object.class); } - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Map<?, ?> readMap(boolean definite) throws IOException { - if(!hasMap(definite)) throw new FormatException(); + 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 m = new HashMap(); + 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(), readObject()); + for(int i = 0; i < length; i++) m.put(readObject(k), readObject(v)); } else { - while(!hasEnd()) m.put(readObject(), readObject()); + while(!hasEnd()) m.put(readObject(k), readObject(v)); readEnd(); } return m; } - public boolean hasMap() throws IOException { - if(!started) readNext(true); - if(eof) return false; - 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 <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(); + } + } } diff --git a/test/net/sf/briar/serial/ReaderImplTest.java b/test/net/sf/briar/serial/ReaderImplTest.java index ea9fb88b52..f1826f5fd5 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<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)); -- GitLab