From 88b970982d76474224bbf1c2c29a67c344bb1475 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Thu, 10 Nov 2011 19:36:42 -0500 Subject: [PATCH] make ConfigList implement java.util.List --- .../config/impl/AbstractConfigObject.java | 2 +- .../com/typesafe/config/impl/ConfigList.java | 201 +++++++++++++++++- src/test/resources/test01.conf | 3 +- .../typesafe/config/impl/ConfParserTest.scala | 2 +- .../com/typesafe/config/impl/ConfigTest.scala | 2 + .../config/impl/ConfigValueTest.scala | 83 ++++++++ .../com/typesafe/config/impl/JsonTest.scala | 2 +- 7 files changed, 286 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java b/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java index 4c3a1ade..bc19433a 100644 --- a/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java +++ b/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java @@ -295,7 +295,7 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements @Override public List getList(String path) { AbstractConfigValue v = find(path, ConfigValueType.LIST, path); - return ((ConfigList) v).asJavaList(); + return (ConfigList) v; } @Override diff --git a/src/main/java/com/typesafe/config/impl/ConfigList.java b/src/main/java/com/typesafe/config/impl/ConfigList.java index 89aa6e14..649d0ad7 100644 --- a/src/main/java/com/typesafe/config/impl/ConfigList.java +++ b/src/main/java/com/typesafe/config/impl/ConfigList.java @@ -1,14 +1,17 @@ package com.typesafe.config.impl; import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import com.typesafe.config.ConfigException; import com.typesafe.config.ConfigOrigin; import com.typesafe.config.ConfigValue; import com.typesafe.config.ConfigValueType; -final class ConfigList extends AbstractConfigValue { +final class ConfigList extends AbstractConfigValue implements List { private List value; @@ -17,10 +20,6 @@ final class ConfigList extends AbstractConfigValue { this.value = value; } - List asJavaList() { - return value; - } - @Override public ConfigValueType valueType() { return ConfigValueType.LIST; @@ -108,4 +107,196 @@ final class ConfigList extends AbstractConfigValue { sb.append(")"); return sb.toString(); } + + @Override + public boolean contains(Object o) { + return value.contains(o); + } + + @Override + public boolean containsAll(Collection c) { + return value.containsAll(c); + } + + @Override + public ConfigValue get(int index) { + return value.get(index); + } + + @Override + public int indexOf(Object o) { + return value.indexOf(o); + } + + @Override + public boolean isEmpty() { + return value.isEmpty(); + } + + @Override + public Iterator iterator() { + final Iterator i = value.iterator(); + + return new Iterator() { + @Override + public boolean hasNext() { + return i.hasNext(); + } + + @Override + public ConfigValue next() { + return i.next(); + } + + @Override + public void remove() { + throw weAreImmutable("iterator().remove"); + } + }; + } + + @Override + public int lastIndexOf(Object o) { + return value.lastIndexOf(o); + } + + private static ListIterator wrapListIterator( + final ListIterator i) { + return new ListIterator() { + @Override + public boolean hasNext() { + return i.hasNext(); + } + + @Override + public ConfigValue next() { + return i.next(); + } + + @Override + public void remove() { + throw weAreImmutable("listIterator().remove"); + } + + @Override + public void add(ConfigValue arg0) { + throw weAreImmutable("listIterator().add"); + } + + @Override + public boolean hasPrevious() { + return i.hasPrevious(); + } + + @Override + public int nextIndex() { + return i.nextIndex(); + } + + @Override + public ConfigValue previous() { + return i.previous(); + } + + @Override + public int previousIndex() { + return i.previousIndex(); + } + + @Override + public void set(ConfigValue arg0) { + throw weAreImmutable("listIterator().set"); + } + }; + } + + @Override + public ListIterator listIterator() { + return wrapListIterator(value.listIterator()); + } + + @Override + public ListIterator listIterator(int index) { + return wrapListIterator(value.listIterator(index)); + } + + @Override + public int size() { + return value.size(); + } + + @Override + public List subList(int fromIndex, int toIndex) { + List list = new ArrayList(); + // yay bloat caused by lack of type variance + for (AbstractConfigValue v : value.subList(fromIndex, toIndex)) { + list.add(v); + } + return list; + } + + @Override + public Object[] toArray() { + return value.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return value.toArray(a); + } + + private static UnsupportedOperationException weAreImmutable(String method) { + return new UnsupportedOperationException( + "ConfigList is immutable, you can't call List.'" + method + "'"); + } + + @Override + public boolean add(ConfigValue e) { + throw weAreImmutable("add"); + } + + @Override + public void add(int index, ConfigValue element) { + throw weAreImmutable("add"); + } + + @Override + public boolean addAll(Collection c) { + throw weAreImmutable("addAll"); + } + + @Override + public boolean addAll(int index, Collection c) { + throw weAreImmutable("addAll"); + } + + @Override + public void clear() { + throw weAreImmutable("clear"); + } + + @Override + public boolean remove(Object o) { + throw weAreImmutable("remove"); + } + + @Override + public ConfigValue remove(int index) { + throw weAreImmutable("remove"); + } + + @Override + public boolean removeAll(Collection c) { + throw weAreImmutable("removeAll"); + } + + @Override + public boolean retainAll(Collection c) { + throw weAreImmutable("retainAll"); + } + + @Override + public ConfigValue set(int index, ConfigValue element) { + throw weAreImmutable("set"); + } } diff --git a/src/test/resources/test01.conf b/src/test/resources/test01.conf index 61876b13..54d94dda 100644 --- a/src/test/resources/test01.conf +++ b/src/test/resources/test01.conf @@ -34,7 +34,8 @@ "ofNull" : [null, null, null], "ofBoolean" : [true, false], "ofArray" : [${arrays.ofString}, ${arrays.ofString}, ${arrays.ofString}], - "ofObject" : [${ints}, ${booleans}, ${strings}] + "ofObject" : [${ints}, ${booleans}, ${strings}], + "firstElementNotASubst" : [ "a", ${strings.b} ] }, "booleans" : { diff --git a/src/test/scala/com/typesafe/config/impl/ConfParserTest.scala b/src/test/scala/com/typesafe/config/impl/ConfParserTest.scala index 4245cc1d..9a76ab60 100644 --- a/src/test/scala/com/typesafe/config/impl/ConfParserTest.scala +++ b/src/test/scala/com/typesafe/config/impl/ConfParserTest.scala @@ -56,7 +56,7 @@ class ConfParserTest extends TestUtils { val tree = parseWithoutResolving("[${" + s + "}]") val result = tree match { case list: ConfigList => - list.asJavaList().get(0) match { + list.get(0) match { case subst: ConfigSubstitution => subst.pieces().get(0) match { case p: Path => p diff --git a/src/test/scala/com/typesafe/config/impl/ConfigTest.scala b/src/test/scala/com/typesafe/config/impl/ConfigTest.scala index 28d0770b..62523bf0 100644 --- a/src/test/scala/com/typesafe/config/impl/ConfigTest.scala +++ b/src/test/scala/com/typesafe/config/impl/ConfigTest.scala @@ -194,6 +194,8 @@ class ConfigTest extends TestUtils { assertEquals(Seq(Seq("a", "b", "c"), Seq("a", "b", "c"), Seq("a", "b", "c")), listOfLists) assertEquals(3, conf.getObjectList("arrays.ofObject").asScala.length) + assertEquals(Seq("a", "b"), conf.getStringList("arrays.firstElementNotASubst").asScala) + // plain getList should work assertEquals(Seq(intValue(1), intValue(2), intValue(3)), conf.getList("arrays.ofInt").asScala) assertEquals(Seq(stringValue("a"), stringValue("b"), stringValue("c")), conf.getList("arrays.ofString").asScala) diff --git a/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala b/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala index 7f780beb..58d32875 100644 --- a/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala +++ b/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala @@ -66,6 +66,19 @@ class ConfigValueTest extends TestUtils { checkNotEqualObjects(a, b) } + @Test + def configListEquality() { + val aScalaSeq = Seq(1, 2, 3) map { intValue(_): AbstractConfigValue } + val aList = new ConfigList(fakeOrigin(), aScalaSeq.asJava) + val sameAsAList = new ConfigList(fakeOrigin(), aScalaSeq.asJava) + val bScalaSeq = Seq(4, 5, 6) map { intValue(_): AbstractConfigValue } + val bList = new ConfigList(fakeOrigin(), bScalaSeq.asJava) + + checkEqualObjects(aList, aList) + checkEqualObjects(aList, sameAsAList) + checkNotEqualObjects(aList, bList) + } + @Test def configSubstitutionEquality() { val a = subst("foo") @@ -137,4 +150,74 @@ class ConfigValueTest extends TestUtils { unsupported { m.putAll(Collections.emptyMap[String, AbstractConfigValue]()) } unsupported { m.remove("a") } } + + @Test + def configListImplementsList() { + val l: ConfigList = new ConfigList(fakeOrigin(), List[AbstractConfigValue](stringValue("a"), stringValue("b"), stringValue("c")).asJava) + val scalaSeq = Seq(stringValue("a"), stringValue("b"), stringValue("c")) + + assertEquals(scalaSeq(0), l.get(0)) + assertEquals(scalaSeq(1), l.get(1)) + assertEquals(scalaSeq(2), l.get(2)) + + assertTrue(l.contains(stringValue("a"))) + + assertTrue(l.containsAll(List[AbstractConfigValue](stringValue("b")).asJava)) + assertFalse(l.containsAll(List[AbstractConfigValue](stringValue("d")).asJava)) + + assertEquals(1, l.indexOf(scalaSeq(1))) + + assertFalse(l.isEmpty()); + + assertEquals(scalaSeq, l.iterator().asScala.toSeq) + + unsupported { l.iterator().remove() } + + assertEquals(1, l.lastIndexOf(scalaSeq(1))) + + val li = l.listIterator() + var i = 0 + while (li.hasNext()) { + assertEquals(i > 0, li.hasPrevious()) + assertEquals(i, li.nextIndex()) + assertEquals(i - 1, li.previousIndex()) + + unsupported { li.remove() } + unsupported { li.add(intValue(3)) } + unsupported { li.set(stringValue("foo")) } + + val v = li.next() + assertEquals(l.get(i), v) + + if (li.hasPrevious()) { + // go backward + assertEquals(scalaSeq(i), li.previous()) + // go back forward + li.next() + } + + i += 1 + } + + l.listIterator(1) // doesn't throw! + + assertEquals(3, l.size()) + + assertEquals(scalaSeq.tail, l.subList(1, l.size()).asScala) + + assertEquals(scalaSeq, l.toArray.toList) + + assertEquals(scalaSeq, l.toArray(new Array[ConfigValue](l.size())).toList) + + unsupported { l.add(intValue(3)) } + unsupported { l.add(1, intValue(4)) } + unsupported { l.addAll(List[ConfigValue]().asJava) } + unsupported { l.addAll(1, List[ConfigValue]().asJava) } + unsupported { l.clear() } + unsupported { l.remove(intValue(2)) } + unsupported { l.remove(1) } + unsupported { l.removeAll(List[ConfigValue](intValue(1)).asJava) } + unsupported { l.retainAll(List[ConfigValue](intValue(1)).asJava) } + unsupported { l.set(0, intValue(42)) } + } } diff --git a/src/test/scala/com/typesafe/config/impl/JsonTest.scala b/src/test/scala/com/typesafe/config/impl/JsonTest.scala index 716377c3..f50d36ff 100644 --- a/src/test/scala/com/typesafe/config/impl/JsonTest.scala +++ b/src/test/scala/com/typesafe/config/impl/JsonTest.scala @@ -26,7 +26,7 @@ class JsonTest extends TestUtils { case v: ConfigObject => lift.JObject(v.keySet().asScala.map({ k => lift.JField(k, toLift(v.get(k))) }).toList) case v: ConfigList => - lift.JArray(v.asJavaList().asScala.toList.map({ elem => toLift(elem) })) + lift.JArray(v.asScala.toList.map({ elem => toLift(elem) })) case v: ConfigBoolean => lift.JBool(v.unwrapped()) case v: ConfigInt =>