maps n lists

This commit is contained in:
Robert Muir 2016-06-21 11:25:43 -04:00
parent 80734c75b5
commit 42d60f9f28
11 changed files with 491 additions and 28 deletions

View File

@ -20,9 +20,14 @@
package org.elasticsearch.painless;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.ObjIntConsumer;
@ -30,15 +35,26 @@ import java.util.function.Predicate;
import java.util.function.ToDoubleFunction;
import java.util.regex.Matcher;
/** Additional methods added to classes. These must be static methods with receiver as first argument */
public class Augmentation {
// static methods only!
private Augmentation() {}
/** Exposes List.size() as getLength(), so that .length shortcut works on lists */
public static <T> int getLength(List<T> receiver) {
return receiver.size();
}
/** Exposes Matcher.group(String) as namedGroup(String), so it doesn't conflict with group(int) */
public static String namedGroup(Matcher receiver, String name) {
return receiver.group(name);
}
// some groovy methods on iterable
// see http://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/Iterable.html
/** Iterates over the contents of an iterable, and checks whether a predicate is valid for at least one element. */
public static <T> boolean any(Iterable<T> receiver, Predicate<T> predicate) {
for (T t : receiver) {
if (predicate.test(t)) {
@ -48,6 +64,7 @@ public class Augmentation {
return false;
}
/** Counts the number of occurrences which satisfy the given predicate from inside this Iterable. */
public static <T> int count(Iterable<T> receiver, Predicate<T> predicate) {
int count = 0;
for (T t : receiver) {
@ -58,12 +75,20 @@ public class Augmentation {
return count;
}
public static <T> Iterable<T> each(Iterable<T> receiver, Consumer<T> consumer) {
// instead of covariant overrides for every possibility, we just return receiver as 'def' for now
// that way if someone chains the calls, everything works.
/** Iterates through an Iterable, passing each item to the given consumer. */
public static <T> Object each(Iterable<T> receiver, Consumer<T> consumer) {
receiver.forEach(consumer);
return receiver;
}
public static <T> Iterable<T> eachWithIndex(Iterable<T> receiver, ObjIntConsumer<T> consumer) {
/**
* Iterates through an iterable type, passing each item and the item's index
* (a counter starting at zero) to the given consumer.
*/
public static <T> Object eachWithIndex(Iterable<T> receiver, ObjIntConsumer<T> consumer) {
int count = 0;
for (T t : receiver) {
consumer.accept(t, count++);
@ -71,6 +96,9 @@ public class Augmentation {
return receiver;
}
/**
* Used to determine if the given predicate is valid (i.e. returns true for all items in this iterable).
*/
public static <T> boolean every(Iterable<T> receiver, Predicate<T> predicate) {
for (T t : receiver) {
if (predicate.test(t) == false) {
@ -80,6 +108,10 @@ public class Augmentation {
return true;
}
/**
* Iterates through the Iterable transforming items using the supplied function and
* collecting any non-null results.
*/
public static <T,U> List<U> findResults(Iterable<T> receiver, Function<T,U> filter) {
List<U> list = new ArrayList<>();
for (T t: receiver) {
@ -91,6 +123,9 @@ public class Augmentation {
return list;
}
/**
* Sorts all Iterable members into groups determined by the supplied mapping function.
*/
public static <T,U> Map<U,List<T>> groupBy(Iterable<T> receiver, Function<T,U> mapper) {
Map<U,List<T>> map = new LinkedHashMap<>();
for (T t : receiver) {
@ -105,6 +140,10 @@ public class Augmentation {
return map;
}
/**
* Concatenates the toString() representation of each item in this Iterable,
* with the given String as a separator between each item.
*/
public static <T> String join(Iterable<T> receiver, String separator) {
StringBuilder sb = new StringBuilder();
for (T t : receiver) {
@ -116,6 +155,9 @@ public class Augmentation {
return sb.toString();
}
/**
* Sums the result of applying a function to each item of an Iterable.
*/
public static <T> double sum(Iterable<T> receiver, ToDoubleFunction<T> function) {
double sum = 0;
for (T t : receiver) {
@ -123,4 +165,246 @@ public class Augmentation {
}
return sum;
}
// some groovy methods on collection
// see http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/Collection.html
/**
* Iterates through this collection transforming each entry into a new value using
* the function, returning a list of transformed values.
*/
public static <T,U> List<U> collect(Collection<T> receiver, Function<T,U> function) {
List<U> list = new ArrayList<>();
for (T t : receiver) {
list.add(function.apply(t));
}
return list;
}
/**
* Iterates through this collection transforming each entry into a new value using
* the function, adding the values to the specified collection.
*/
public static <T,U> Object collect(Collection<T> receiver, Collection<U> collection, Function<T,U> function) {
for (T t : receiver) {
collection.add(function.apply(t));
}
return collection;
}
/**
* Finds the first value matching the predicate, or returns null.
*/
public static <T> T find(Collection<T> receiver, Predicate<T> predicate) {
for (T t : receiver) {
if (predicate.test(t)) {
return t;
}
}
return null;
}
/**
* Finds all values matching the predicate, returns as a list
*/
public static <T> List<T> findAll(Collection<T> receiver, Predicate<T> predicate) {
List<T> list = new ArrayList<>();
for (T t : receiver) {
if (predicate.test(t)) {
list.add(t);
}
}
return list;
}
/**
* Iterates through the collection calling the given function for each item
* but stopping once the first non-null result is found and returning that result.
* If all results are null, null is returned.
*/
public static <T,U> Object findResult(Collection<T> receiver, Function<T,U> function) {
return findResult(receiver, null, function);
}
/**
* Iterates through the collection calling the given function for each item
* but stopping once the first non-null result is found and returning that result.
* If all results are null, defaultResult is returned.
*/
public static <T,U> Object findResult(Collection<T> receiver, Object defaultResult, Function<T,U> function) {
for (T t : receiver) {
U value = function.apply(t);
if (value != null) {
return value;
}
}
return defaultResult;
}
/**
* Splits all items into two collections based on the predicate.
* The first list contains all items which match the closure expression. The second list all those that don't.
*/
public static <T> List<List<T>> split(Collection<T> receiver, Predicate<T> predicate) {
List<T> matched = new ArrayList<>();
List<T> unmatched = new ArrayList<>();
List<List<T>> result = new ArrayList<>(2);
result.add(matched);
result.add(unmatched);
for (T t : receiver) {
if (predicate.test(t)) {
matched.add(t);
} else {
unmatched.add(t);
}
}
return result;
}
// some groovy methods on map
// see http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/Map.html
/**
* Iterates through this map transforming each entry into a new value using
* the function, returning a list of transformed values.
*/
public static <K,V,T> List<T> collect(Map<K,V> receiver, BiFunction<K,V,T> function) {
List<T> list = new ArrayList<>();
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
list.add(function.apply(kvPair.getKey(), kvPair.getValue()));
}
return list;
}
/**
* Iterates through this map transforming each entry into a new value using
* the function, adding the values to the specified collection.
*/
public static <K,V,T> Object collect(Map<K,V> receiver, Collection<T> collection, BiFunction<K,V,T> function) {
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
collection.add(function.apply(kvPair.getKey(), kvPair.getValue()));
}
return collection;
}
/** Counts the number of occurrences which satisfy the given predicate from inside this Map */
public static <K,V> int count(Map<K,V> receiver, BiPredicate<K,V> predicate) {
int count = 0;
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
if (predicate.test(kvPair.getKey(), kvPair.getValue())) {
count++;
}
}
return count;
}
/** Iterates through a Map, passing each item to the given consumer. */
public static <K,V> Object each(Map<K,V> receiver, BiConsumer<K,V> consumer) {
receiver.forEach(consumer);
return receiver;
}
/**
* Used to determine if the given predicate is valid (i.e. returns true for all items in this map).
*/
public static <K,V> boolean every(Map<K,V> receiver, BiPredicate<K,V> predicate) {
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
if (predicate.test(kvPair.getKey(), kvPair.getValue()) == false) {
return false;
}
}
return true;
}
/**
* Finds the first entry matching the predicate, or returns null.
*/
public static <K,V> Map.Entry<K,V> find(Map<K,V> receiver, BiPredicate<K,V> predicate) {
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
if (predicate.test(kvPair.getKey(), kvPair.getValue())) {
return kvPair;
}
}
return null;
}
/**
* Finds all values matching the predicate, returns as a map.
*/
public static <K,V> Map<K,V> findAll(Map<K,V> receiver, BiPredicate<K,V> predicate) {
// try to preserve some properties of the receiver (see the groovy javadocs)
final Map<K,V> map;
if (receiver instanceof TreeMap) {
map = new TreeMap<>();
} else {
map = new LinkedHashMap<>();
}
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
if (predicate.test(kvPair.getKey(), kvPair.getValue())) {
map.put(kvPair.getKey(), kvPair.getValue());
}
}
return map;
}
/**
* Iterates through the map calling the given function for each item
* but stopping once the first non-null result is found and returning that result.
* If all results are null, null is returned.
*/
public static <K,V,T> Object findResult(Map<K,V> receiver, BiFunction<K,V,T> function) {
return findResult(receiver, null, function);
}
/**
* Iterates through the map calling the given function for each item
* but stopping once the first non-null result is found and returning that result.
* If all results are null, defaultResult is returned.
*/
public static <K,V,T> Object findResult(Map<K,V> receiver, Object defaultResult, BiFunction<K,V,T> function) {
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
T value = function.apply(kvPair.getKey(), kvPair.getValue());
if (value != null) {
return value;
}
}
return defaultResult;
}
/**
* Iterates through the map transforming items using the supplied function and
* collecting any non-null results.
*/
public static <K,V,T> List<T> findResults(Map<K,V> receiver, BiFunction<K,V,T> filter) {
List<T> list = new ArrayList<>();
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
T result = filter.apply(kvPair.getKey(), kvPair.getValue());
if (result != null) {
list.add(result);
}
}
return list;
}
/**
* Sorts all Map members into groups determined by the supplied mapping function.
*/
public static <K,V,T> Map<T,Map<K,V>> groupBy(Map<K,V> receiver, BiFunction<K,V,T> mapper) {
Map<T,Map<K,V>> map = new LinkedHashMap<>();
for (Map.Entry<K,V> kvPair : receiver.entrySet()) {
T mapped = mapper.apply(kvPair.getKey(), kvPair.getValue());
Map<K,V> results = map.get(mapped);
if (results == null) {
// try to preserve some properties of the receiver (see the groovy javadocs)
if (receiver instanceof TreeMap) {
results = new TreeMap<>();
} else {
results = new LinkedHashMap<>();
}
map.put(mapped, results);
}
results.put(kvPair.getKey(), kvPair.getValue());
}
return map;
}
}

View File

@ -350,10 +350,10 @@ public final class Def {
}
throw new IllegalArgumentException("Unknown call [" + call + "] with [" + arity + "] arguments.");
}
ref = new FunctionRef(clazz, interfaceMethod, handle, captures);
ref = new FunctionRef(clazz, interfaceMethod, handle, captures.length);
} else {
// whitelist lookup
ref = new FunctionRef(clazz, type, call, captures);
ref = new FunctionRef(clazz, type, call, captures.length);
}
final CallSite callSite;
if (ref.needsBridges()) {

View File

@ -257,7 +257,7 @@ public final class Definition {
final org.objectweb.asm.Type type;
if (augmentation) {
assert java.lang.reflect.Modifier.isStatic(modifiers);
type = org.objectweb.asm.Type.getType(Augmentation.class);
type = WriterConstants.AUGMENTATION_TYPE;
} else {
type = owner.type;
}
@ -825,7 +825,7 @@ public final class Definition {
reflect = implClass.getMethod(name, params);
} catch (NoSuchMethodException exception) {
throw new IllegalArgumentException("Method [" + name +
"] not found for class [" + owner.clazz.getName() + "]" +
"] not found for class [" + implClass.getName() + "]" +
" with arguments " + Arrays.toString(params) + ".");
}

View File

@ -53,10 +53,10 @@ public class FunctionRef {
* @param expected interface type to implement.
* @param type the left hand side of a method reference expression
* @param call the right hand side of a method reference expression
* @param captures captured arguments
* @param numCaptures number of captured arguments
*/
public FunctionRef(Definition.Type expected, String type, String call, Class<?>... captures) {
this(expected, expected.struct.getFunctionalMethod(), lookup(expected, type, call, captures.length > 0), captures);
public FunctionRef(Definition.Type expected, String type, String call, int numCaptures) {
this(expected, expected.struct.getFunctionalMethod(), lookup(expected, type, call, numCaptures > 0), numCaptures);
}
/**
@ -64,13 +64,16 @@ public class FunctionRef {
* @param expected interface type to implement
* @param method functional interface method
* @param impl implementation method
* @param captures captured arguments
* @param numCaptures number of captured arguments
*/
public FunctionRef(Definition.Type expected, Definition.Method method, Definition.Method impl, Class<?>... captures) {
public FunctionRef(Definition.Type expected, Definition.Method method, Definition.Method impl, int numCaptures) {
// e.g. compareTo
invokedName = method.name;
// e.g. (Object)Comparator
invokedType = MethodType.methodType(expected.clazz, captures);
MethodType implType = impl.getMethodType();
// only include captured parameters as arguments
invokedType = MethodType.methodType(expected.clazz,
implType.dropParameterTypes(numCaptures, implType.parameterCount()));
// e.g. (Object,Object)int
interfaceMethodType = method.getMethodType().dropParameterTypes(0, 1);
@ -90,6 +93,9 @@ public class FunctionRef {
// owner == null: script class itself
ownerIsInterface = false;
owner = WriterConstants.CLASS_TYPE.getInternalName();
} else if (impl.augmentation) {
ownerIsInterface = false;
owner = WriterConstants.AUGMENTATION_TYPE.getInternalName();
} else {
ownerIsInterface = impl.owner.clazz.isInterface();
owner = impl.owner.type.getInternalName();
@ -98,7 +104,7 @@ public class FunctionRef {
implMethod = impl.handle;
// remove any prepended captured arguments for the 'natural' signature.
samMethodType = adapt(interfaceMethodType, impl.getMethodType().dropParameterTypes(0, captures.length));
samMethodType = adapt(interfaceMethodType, impl.getMethodType().dropParameterTypes(0, numCaptures));
}
/**
@ -106,11 +112,14 @@ public class FunctionRef {
* <p>
* This will <b>not</b> set implMethodASM. It is for runtime use only.
*/
public FunctionRef(Definition.Type expected, Definition.Method method, MethodHandle impl, Class<?>... captures) {
public FunctionRef(Definition.Type expected, Definition.Method method, MethodHandle impl, int numCaptures) {
// e.g. compareTo
invokedName = method.name;
// e.g. (Object)Comparator
invokedType = MethodType.methodType(expected.clazz, captures);
MethodType implType = impl.type();
// only include captured parameters as arguments
invokedType = MethodType.methodType(expected.clazz,
implType.dropParameterTypes(numCaptures, implType.parameterCount()));
// e.g. (Object,Object)int
interfaceMethodType = method.getMethodType().dropParameterTypes(0, 1);
@ -119,7 +128,7 @@ public class FunctionRef {
implMethodASM = null;
// remove any prepended captured arguments for the 'natural' signature.
samMethodType = adapt(interfaceMethodType, impl.type().dropParameterTypes(0, captures.length));
samMethodType = adapt(interfaceMethodType, impl.type().dropParameterTypes(0, numCaptures));
}
/**

View File

@ -73,6 +73,8 @@ public final class WriterConstants {
public final static Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
public static final Type AUGMENTATION_TYPE = Type.getType(Augmentation.class);
/**
* A Method instance for {@linkplain Pattern#compile}. This isn't available from Definition because we intentionally don't add it there
* so that the script can't create regexes without this syntax. Essentially, our static regex syntax has a monopoly on building regexes

View File

@ -76,7 +76,7 @@ public class ECapturingFunctionRef extends AExpression implements ILambda {
// static case
if (captured.type.sort != Definition.Sort.DEF) {
try {
ref = new FunctionRef(expected, captured.type.name, call, captured.type.clazz);
ref = new FunctionRef(expected, captured.type.name, call, 1);
} catch (IllegalArgumentException e) {
throw createError(e);
}

View File

@ -76,10 +76,10 @@ public class EFunctionRef extends AExpression implements ILambda {
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
"to [" + expected.name + "], function not found");
}
ref = new FunctionRef(expected, interfaceMethod, implMethod);
ref = new FunctionRef(expected, interfaceMethod, implMethod, 0);
} else {
// whitelist lookup
ref = new FunctionRef(expected, type, call);
ref = new FunctionRef(expected, type, call, 0);
}
} catch (IllegalArgumentException e) {
throw createError(e);

View File

@ -175,11 +175,7 @@ public class ELambda extends AExpression implements ILambda {
} else {
defPointer = null;
try {
Class<?> captureClasses[] = new Class<?>[captures.size()];
for (int i = 0; i < captures.size(); i++) {
captureClasses[i] = captures.get(i).type.clazz;
}
ref = new FunctionRef(expected, interfaceMethod, desugared.method, captureClasses);
ref = new FunctionRef(expected, interfaceMethod, desugared.method, captures.size());
} catch (IllegalArgumentException e) {
throw createError(e);
}

View File

@ -52,6 +52,13 @@ class Iterable -> java.lang.Iterable {
Spliterator spliterator()
# some adaptations of groovy methods
boolean any*(Predicate)
def each*(Consumer)
def eachWithIndex*(ObjIntConsumer)
boolean every*(Predicate)
List findResults*(Function)
Map groupBy*(Function)
String join*(String)
double sum*(ToDoubleFunction)
}
# Readable: i/o

View File

@ -39,6 +39,15 @@ class Collection -> java.util.Collection extends Iterable {
Stream stream()
def[] toArray()
def[] toArray(def[])
# some adaptations of groovy methods
List collect*(Function)
def collect*(Collection,Function)
def find*(Predicate)
List findAll*(Predicate)
def findResult*(Function)
def findResult*(def,Function)
List split*(Predicate)
}
class Comparator -> java.util.Comparator {
@ -152,6 +161,19 @@ class Map -> java.util.Map {
void replaceAll(BiFunction)
int size()
Collection values()
# some adaptations of groovy methods
List collect*(BiFunction)
def collect*(Collection,BiFunction)
int count*(BiPredicate)
def each*(BiConsumer)
boolean every*(BiPredicate)
Map.Entry find*(BiPredicate)
Map findAll*(BiPredicate)
def findResult*(BiFunction)
def findResult*(def,BiFunction)
List findResults*(BiFunction)
Map groupBy*(BiFunction)
}
class Map.Entry -> java.util.Map$Entry {

View File

@ -19,17 +19,160 @@
package org.elasticsearch.painless;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class AugmentationTests extends ScriptTestCase {
@AwaitsFix(bugUrl = "rmuir is working on this")
public void testStatic() {
assertEquals(1, exec("ArrayList l = new ArrayList(); l.add(1); return l.getLength();"));
assertEquals(1, exec("ArrayList l = new ArrayList(); l.add(1); return l.length;"));
}
public void testSubclass() {
assertEquals(1, exec("List l = new ArrayList(); l.add(1); return l.getLength();"));
assertEquals(1, exec("List l = new ArrayList(); l.add(1); return l.length;"));
}
public void testDef() {
assertEquals(1, exec("def l = new ArrayList(); l.add(1); return l.getLength();"));
assertEquals(1, exec("def l = new ArrayList(); l.add(1); return l.length;"));
}
public void testCapturingReference() {
assertEquals(1, exec("int foo(Supplier t) { return t.get() }" +
"ArrayList l = new ArrayList(); l.add(1);" +
"return foo(l::getLength);"));
assertEquals(1, exec("int foo(Supplier t) { return t.get() }" +
"List l = new ArrayList(); l.add(1);" +
"return foo(l::getLength);"));
assertEquals(1, exec("int foo(Supplier t) { return t.get() }" +
"def l = new ArrayList(); l.add(1);" +
"return foo(l::getLength);"));
}
public void testIterable_Any() {
assertEquals(true, exec("List l = new ArrayList(); l.add(1); l.any(x -> x == 1)"));
assertEquals(true,
exec("List l = new ArrayList(); l.add(1); l.any(x -> x == 1)"));
}
public void testIterable_Each() {
assertEquals(1,
exec("List l = new ArrayList(); l.add(1); List l2 = new ArrayList(); l.each(l2::add); return l2.size()"));
}
public void testIterable_EachWithIndex() {
assertEquals(0,
exec("List l = new ArrayList(); l.add(2); Map m = new HashMap(); l.eachWithIndex(m::put); return m.get(2)"));
}
public void testIterable_Every() {
assertEquals(false, exec("List l = new ArrayList(); l.add(1); l.add(2); l.every(x -> x == 1)"));
}
public void testIterable_FindResults() {
assertEquals(1,
exec("List l = new ArrayList(); l.add(1); l.add(2); l.findResults(x -> x == 1 ? x : null).size()"));
}
public void testIterable_GroupBy() {
assertEquals(2,
exec("List l = new ArrayList(); l.add(1); l.add(-1); l.groupBy(x -> x < 0 ? 'negative' : 'positive').size()"));
}
public void testIterable_Join() {
assertEquals("test,ing",
exec("List l = new ArrayList(); l.add('test'); l.add('ing'); l.join(',')"));
}
public void testIterable_Sum() {
assertEquals(5.0D,
exec("List l = new ArrayList(); l.add(1); l.add(2); l.sum(x -> x + 1)"));
}
public void testCollection_Collect() {
assertEquals(Arrays.asList(2, 3),
exec("List l = new ArrayList(); l.add(1); l.add(2); l.collect(x -> x + 1)"));
assertEquals(asSet(2, 3),
exec("List l = new ArrayList(); l.add(1); l.add(2); l.collect(new HashSet(), x -> x + 1)"));
}
public void testCollection_Find() {
assertEquals(2,
exec("List l = new ArrayList(); l.add(1); l.add(2); return l.find(x -> x == 2)"));
}
public void testCollection_FindAll() {
assertEquals(Arrays.asList(2),
exec("List l = new ArrayList(); l.add(1); l.add(2); return l.findAll(x -> x == 2)"));
}
public void testCollection_FindResult() {
assertEquals("found",
exec("List l = new ArrayList(); l.add(1); l.add(2); return l.findResult(x -> x > 1 ? 'found' : null)"));
assertEquals("notfound",
exec("List l = new ArrayList(); l.add(1); l.add(2); return l.findResult('notfound', x -> x > 10 ? 'found' : null)"));
}
public void testCollection_Split() {
assertEquals(Arrays.asList(Arrays.asList(2), Arrays.asList(1)),
exec("List l = new ArrayList(); l.add(1); l.add(2); return l.split(x -> x == 2)"));
}
public void testMap_Collect() {
assertEquals(Arrays.asList("one1", "two2"),
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; m.collect((key,value) -> key + value)"));
assertEquals(asSet("one1", "two2"),
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; m.collect(new HashSet(), (key,value) -> key + value)"));
}
public void testMap_Count() {
assertEquals(1,
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; m.count((key,value) -> value == 2)"));
}
public void testMap_Each() {
assertEquals(2,
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; Map m2 = new TreeMap(); m.each(m2::put); return m2.size()"));
}
public void testMap_Every() {
assertEquals(false,
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; m.every((key,value) -> value == 2)"));
}
public void testMap_Find() {
assertEquals("two",
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; return m.find((key,value) -> value == 2).key"));
}
public void testMap_FindAll() {
assertEquals(Collections.singletonMap("two", 2),
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; return m.findAll((key,value) -> value == 2)"));
}
public void testMap_FindResult() {
assertEquals("found",
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; return m.findResult((key,value) -> value == 2 ? 'found' : null)"));
assertEquals("notfound",
exec("Map m = new TreeMap(); m.one = 1; m.two = 2; " +
"return m.findResult('notfound', (key,value) -> value == 10 ? 'found' : null)"));
}
public void testMap_FindResults() {
assertEquals(Arrays.asList("negative", "positive"),
exec("Map m = new TreeMap(); m.a = -1; m.b = 1; " +
"return m.findResults((key,value) -> value < 0 ? 'negative' : 'positive')"));
}
public void testMap_GroupBy() {
Map<String,Map<String,Integer>> expected = new HashMap<>();
expected.put("negative", Collections.singletonMap("a", -1));
expected.put("positive", Collections.singletonMap("b", 1));
assertEquals(expected,
exec("Map m = new TreeMap(); m.a = -1; m.b = 1; " +
"return m.groupBy((key,value) -> value < 0 ? 'negative' : 'positive')"));
}
}