Support for transforming results into something else.

This commit is contained in:
Samppa Saarela 2011-09-21 10:47:08 +03:00
parent 1b0b572b66
commit 4034f5fcef
8 changed files with 408 additions and 208 deletions

View File

@ -5,6 +5,7 @@
*/
package com.mysema.query;
/**
* Executes query on a Projectable and transforms results into T. This can be used for example
* to group projected columns or to filter out duplicate results.

View File

@ -29,6 +29,4 @@ public interface Group {
<K, V> Map<K, V> getMap(Expression<K> key, Expression<V> value);
<K, V> Map<K, V> getMap(Expression<K> key, Class<V> valueType);
}

View File

@ -14,6 +14,8 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import org.apache.commons.collections15.Transformer;
import com.mysema.commons.lang.CloseableIterator;
import com.mysema.commons.lang.Pair;
import com.mysema.query.Projectable;
@ -33,6 +35,32 @@ import com.mysema.query.types.Expression;
@SuppressWarnings("unchecked")
public class GroupBy<S> implements ResultTransformer<Map<S, Group>> {
private static class GList<T> extends AbstractGroupColumnDefinition<T, List<T>>{
public GList(Expression<T> expr) {
super(expr);
}
@Override
public GroupColumn<List<T>> createGroupColumn() {
return new GroupColumn<List<T>>() {
private final List<T> list = new ArrayList<T>();
@Override
public void add(Object o) {
list.add((T) o);
}
@Override
public List<T> get() {
return list;
}
};
}
}
private static class GMap<K, V> extends AbstractGroupColumnDefinition<Pair<K, V>, Map<K, V>>{
public GMap(QPair<K, V> qpair) {
@ -60,6 +88,35 @@ public class GroupBy<S> implements ResultTransformer<Map<S, Group>> {
}
}
private static class GOne<T> extends AbstractGroupColumnDefinition<T, T>{
public GOne(Expression<T> expr) {
super(expr);
}
@Override
public GroupColumn<T> createGroupColumn() {
return new GroupColumn<T>() {
private boolean first = true;
private T val;
@Override
public void add(Object o) {
if (first) {
val = (T) o;
first = false;
}
}
@Override
public T get() {
return val;
}
};
}
}
private static class GSet<T> extends AbstractGroupColumnDefinition<T, Set<T>>{
public GSet(Expression<T> expr) {
@ -85,71 +142,93 @@ public class GroupBy<S> implements ResultTransformer<Map<S, Group>> {
};
}
}
private static class GList<T> extends AbstractGroupColumnDefinition<T, List<T>>{
public GList(Expression<T> expr) {
super(expr);
}
@Override
public GroupColumn<List<T>> createGroupColumn() {
return new GroupColumn<List<T>>() {
private final List<T> list = new ArrayList<T>();
@Override
public void add(Object o) {
list.add((T) o);
}
@Override
public List<T> get() {
return list;
}
};
}
}
private static class GOne<T> extends AbstractGroupColumnDefinition<T, T>{
public GOne(Expression<T> expr) {
super(expr);
}
@Override
public GroupColumn<T> createGroupColumn() {
return new GroupColumn<T>() {
private T val;
private boolean first = true;
@Override
public void add(Object o) {
if (first) {
val = (T) o;
first = false;
}
}
@Override
public T get() {
return val;
}
};
}
}
private final List<GroupColumnDefinition<?, ?>> columnDefinitions = new ArrayList<GroupColumnDefinition<?,?>>();
private final List<QPair<?, ?>> pairs = new ArrayList<QPair<?,?>>();
public static <T> GroupBy<T> create(Expression<T> expr) {
return new GroupBy<T>(expr);
}
public static <T> GroupBy<T> create(Expression<T> expr, Expression<?> first, Expression<?>... rest) {
return new GroupBy<T>(expr, first, rest);
}
private class GroupImpl implements Group {
private final Map<Expression<?>, GroupColumn<?>> groupColumns = new LinkedHashMap<Expression<?>, GroupColumn<?>>();
public GroupImpl() {
for (int i=0; i < columnDefinitions.size(); i++) {
GroupColumnDefinition<?, ?> coldef = columnDefinitions.get(i);
groupColumns.put(coldef.getExpression(), coldef.createGroupColumn());
}
}
void add(Object[] row) {
int i=0;
for (GroupColumn<?> groupColumn : groupColumns.values()) {
groupColumn.add(row[i]);
i++;
}
}
private <T, R> R get(Expression<T> expr) {
GroupColumn<R> col = (GroupColumn<R>) groupColumns.get(expr);
if (col != null) {
return col.get();
}
throw new NoSuchElementException(expr.toString());
}
@Override
public <T, R> R getGroup(GroupColumnDefinition<T, R> definition) {
Iterator<GroupColumn<?>> iter = groupColumns.values().iterator();
for (GroupColumnDefinition<?, ?> def : columnDefinitions) {
GroupColumn<?> groupColumn = iter.next();
if (def.equals(definition)) {
return (R) groupColumn.get();
}
}
throw new NoSuchElementException(definition.toString());
}
@Override
public <T> List<T> getList(Expression<T> expr) {
return this.<T, List<T>>get(expr);
}
public <K, V> Map<K, V> getMap(Expression<K> key, Expression<V> value) {
for (QPair<?, ?> pair : maps) {
if (pair.equals(key, value)) {
return (Map<K, V>) groupColumns.get(pair).get();
}
}
throw new NoSuchElementException("GMap(" + key + ", " + value + ")");
}
@Override
public <T> T getOne(Expression<T> expr) {
return this.<T, T>get(expr);
}
@Override
public <T> Set<T> getSet(Expression<T> expr) {
return this.<T, Set<T>>get(expr);
}
@Override
public Object[] toArray() {
List<Object> arr = new ArrayList<Object>(groupColumns.size());
for (GroupColumn<?> col : groupColumns.values()) {
arr.add(col.get());
}
return arr.toArray();
}
}
private final List<GroupColumnDefinition<?, ?>> columnDefinitions = new ArrayList<GroupColumnDefinition<?,?>>();
private final List<QPair<?, ?>> maps = new ArrayList<QPair<?,?>>();
public GroupBy(Expression<S> groupBy) {
withGroup(new GOne<S>(groupBy));
}
@ -170,29 +249,14 @@ public class GroupBy<S> implements ResultTransformer<Map<S, Group>> {
}
}
public GroupBy<S> withGroup(GroupColumnDefinition<?, ?> g) {
columnDefinitions.add(g);
return this;
private Expression<?>[] getExpressions() {
Expression<?>[] unwrapped = new Expression<?>[columnDefinitions.size()];
for (int i=0; i < columnDefinitions.size(); i++) {
unwrapped[i] = columnDefinitions.get(i).getExpression();
}
return unwrapped;
}
public <T> GroupBy<S> withSet(Expression<T> expr) {
return withGroup(new GSet<T>(expr));
}
public <T> GroupBy<S> withList(Expression<T> expr) {
return withGroup(new GList<T>(expr));
}
public <T> GroupBy<S> withOne(Expression<T> expr) {
return withGroup(new GOne<T>(expr));
}
public <K, V> GroupBy<S> withMap(Expression<K> key, Expression<V> value) {
QPair<K, V> qpair = new QPair<K, V>(key, value);
pairs.add(qpair);
return withGroup(new GMap<K, V>(qpair));
}
@Override
public Map<S, Group> transform(Projectable projectable) {
final Map<S, Group> groups = new LinkedHashMap<S, Group>();
@ -215,96 +279,31 @@ public class GroupBy<S> implements ResultTransformer<Map<S, Group>> {
return groups;
}
private Expression<?>[] getExpressions() {
Expression<?>[] unwrapped = new Expression<?>[columnDefinitions.size()];
for (int i=0; i < columnDefinitions.size(); i++) {
unwrapped[i] = columnDefinitions.get(i).getExpression();
}
return unwrapped;
public GroupBy<S> withGroup(GroupColumnDefinition<?, ?> g) {
columnDefinitions.add(g);
return this;
}
public <T> GroupBy<S> withList(Expression<T> expr) {
return withGroup(new GList<T>(expr));
}
private class GroupImpl implements Group {
private final Map<Expression<?>, GroupColumn<?>> groupColumns = new LinkedHashMap<Expression<?>, GroupColumn<?>>();
public GroupImpl() {
for (int i=0; i < columnDefinitions.size(); i++) {
GroupColumnDefinition<?, ?> coldef = columnDefinitions.get(i);
groupColumns.put(coldef.getExpression(), coldef.createGroupColumn());
}
}
@Override
public <T, R> R getGroup(GroupColumnDefinition<T, R> definition) {
Iterator<GroupColumn<?>> iter = groupColumns.values().iterator();
for (GroupColumnDefinition<?, ?> def : columnDefinitions) {
GroupColumn<?> groupColumn = iter.next();
if (def.equals(definition)) {
return (R) groupColumn.get();
}
}
throw new NoSuchElementException(definition.toString());
}
@Override
public <T> T getOne(Expression<T> expr) {
return this.<T, T>get(expr);
}
@Override
public <T> Set<T> getSet(Expression<T> expr) {
return this.<T, Set<T>>get(expr);
}
@Override
public <T> List<T> getList(Expression<T> expr) {
return this.<T, List<T>>get(expr);
}
private <T, R> R get(Expression<T> expr) {
GroupColumn<R> col = (GroupColumn<R>) groupColumns.get(expr);
if (col != null) {
return col.get();
}
throw new NoSuchElementException(expr.toString());
}
public <K, V> Map<K, V> getMap(Expression<K> key, Expression<V> value) {
for (QPair<?, ?> pair : pairs) {
if (pair.equals(key, value)) {
return (Map<K, V>) groupColumns.get(pair).get();
}
}
throw new NoSuchElementException("GMap(" + key + ", " + value + ")");
}
@Override
public <K, V> Map<K, V> getMap(Expression<K> key, Class<V> valueType) {
for (QPair<?, ?> pair : pairs) {
if (pair.equals(key, valueType)) {
return (Map<K, V>) groupColumns.get(pair).get();
}
}
throw new NoSuchElementException("GMap(" + key + ", " + valueType + ")");
}
void add(Object[] row) {
int i=0;
for (GroupColumn<?> groupColumn : groupColumns.values()) {
groupColumn.add(row[i]);
i++;
}
}
@Override
public Object[] toArray() {
List<Object> arr = new ArrayList<Object>(groupColumns.size());
for (GroupColumn<?> col : groupColumns.values()) {
arr.add(col.get());
}
return arr.toArray();
}
public <K, V> GroupBy<S> withMap(Expression<K> key, Expression<V> value) {
QPair<K, V> qpair = new QPair<K, V>(key, value);
maps.add(qpair);
return withGroup(new GMap<K, V>(qpair));
}
public <T> GroupBy<S> withOne(Expression<T> expr) {
return withGroup(new GOne<T>(expr));
}
public <T> GroupBy<S> withSet(Expression<T> expr) {
return withGroup(new GSet<T>(expr));
}
public <W> TransformerGroupBy<S, W> withTransformer(Transformer<? super Group, ? extends W> transformer) {
return TransformerGroupBy.create(this, transformer);
}
}

View File

@ -29,6 +29,10 @@ public final class QPair<K, V> extends ExpressionBase<Pair<K, V>> implements Fac
private final Expression<V> value;
public static <K, V> QPair<K, V> create(Expression<K> key, Expression<V> value) {
return new QPair<K, V>(key, value);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public QPair(Expression<K> key, Expression<V> value) {
super((Class) Pair.class);
@ -62,8 +66,7 @@ public final class QPair<K, V> extends ExpressionBase<Pair<K, V>> implements Fac
@Override
public int hashCode() {
int hashCode = key.hashCode();
return 31*hashCode + value.hashCode();
return 31*key.hashCode() + value.hashCode();
}
@Override

View File

@ -0,0 +1,30 @@
package com.mysema.query.group;
import java.util.Map;
import org.apache.commons.collections15.Transformer;
import com.mysema.query.Projectable;
import com.mysema.query.ResultTransformer;
public class TransformerGroupBy<K, V> implements ResultTransformer<Map<K, V>> {
private final GroupBy<K> groupBy;
private final Transformer<? super Group, ? extends V> transformer;
public static <K, V> TransformerGroupBy<K, V> create(GroupBy<K> groupBy, Transformer<? super Group, ? extends V> transformer) {
return new TransformerGroupBy<K, V>(groupBy, transformer);
}
public TransformerGroupBy(GroupBy<K> groupBy, Transformer<? super Group, ? extends V> transformer) {
this.groupBy = groupBy;
this.transformer = transformer;
}
@Override
public Map<K, V> transform(Projectable projectable) {
return TransformerMap.create(groupBy.transform(projectable), transformer);
}
}

View File

@ -0,0 +1,98 @@
package com.mysema.query.group;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.collection.TransformedCollection;
import org.apache.commons.collections15.set.TransformedSet;
public class TransformerMap<K, V, T> extends AbstractMap<K, T> {
private final Map<K, V> map;
private final Transformer<? super V, ? extends T> transformer;
public static <K, V, T> Map<K, T> create(Map<K, V> map, Transformer<? super V, ? extends T> transformer) {
return new TransformerMap<K, V, T>(map, transformer);
}
public TransformerMap(Map<K, V> map, Transformer<? super V, ? extends T> transformer) {
this.map = map;
this.transformer = transformer;
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
@SuppressWarnings("unchecked")
public boolean containsValue(Object value) {
return map.containsValue(transformer.transform((V) value));
}
public T get(Object key) {
return transformer.transform(map.get(key));
}
public T put(K key, T value) {
throw new UnsupportedOperationException();
}
public T remove(Object key) {
throw new UnsupportedOperationException();
}
public void putAll(Map<? extends K, ? extends T> m) {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}
public Set<K> keySet() {
return map.keySet();
}
public Collection<T> values() {
return TransformedCollection.decorate(map.values(), transformer);
}
public Set<java.util.Map.Entry<K, T>> entrySet() {
return TransformedSet.decorate(map.entrySet(), new Transformer<Map.Entry<K, V>, Map.Entry<K, T>>() {
@Override
public java.util.Map.Entry<K, T> transform(final Map.Entry<K, V> input) {
return new Map.Entry<K, T>() {
@Override
public K getKey() {
return input.getKey();
}
@Override
public T getValue() {
return transformer.transform(input.getValue());
}
@Override
public T setValue(T value) {
throw new UnsupportedOperationException();
}
};
}
});
}
}

View File

@ -1,6 +1,7 @@
package com.mysema.query.group;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.*;
import java.util.Arrays;
@ -8,14 +9,15 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections15.Transformer;
import org.junit.Test;
import com.mysema.commons.lang.CloseableIterator;
import com.mysema.commons.lang.IteratorAdapter;
import com.mysema.commons.lang.Pair;
import com.mysema.query.Projectable;
import com.mysema.query.group.GroupBy;
import com.mysema.query.support.AbstractProjectable;
import com.mysema.query.types.ConstructorExpression;
import com.mysema.query.types.Expression;
import com.mysema.query.types.expr.NumberExpression;
import com.mysema.query.types.expr.StringExpression;
@ -24,15 +26,15 @@ import com.mysema.query.types.path.StringPath;
public class GroupByTest {
private final NumberExpression<Integer> postId = new NumberPath<Integer>(Integer.class, "postId");
private static final NumberExpression<Integer> postId = new NumberPath<Integer>(Integer.class, "postId");
private final StringExpression postName = new StringPath("postName");
private static final StringExpression postName = new StringPath("postName");
private final NumberExpression<Integer> commentId = new NumberPath<Integer>(Integer.class, "commentId");
private static final NumberExpression<Integer> commentId = new NumberPath<Integer>(Integer.class, "commentId");
private final StringExpression commentText = new StringPath("commentText");
private static final StringExpression commentText = new StringPath("commentText");
private final GroupColumnDefinition<Integer, String> constant = new AbstractGroupColumnDefinition<Integer, String>(commentId) {
private static final GroupColumnDefinition<Integer, String> constant = new AbstractGroupColumnDefinition<Integer, String>(commentId) {
@Override
public GroupColumn<String> createGroupColumn() {
@ -49,18 +51,51 @@ public class GroupByTest {
};
}
};
private static final ConstructorExpression<Comment> qComment =
new ConstructorExpression<GroupByTest.Comment>(
GroupByTest.Comment.class,
new Class[] { Integer.class, String.class },
commentId, commentText
);
static class PostWithComments {
public Integer id;
public String name;
public Set<Integer> comments;
public PostWithComments(Integer id, String name, Set<Integer> comments) {
public static class Post {
public final Integer id;
public final String name;
public final Set<Comment> comments;
public Post(Integer id, String name, Set<Comment> comments) {
this.id = id;
this.name = name;
this.comments = comments;
}
}
public static class Comment {
public final Integer id;
public final String text;
public Comment(Integer id, String text) {
this.id = id;
this.text = text;
}
public int hashCode() {
return 31*id.hashCode() + text.hashCode();
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof Comment) {
Comment other = (Comment) o;
return this.id.equals(other.id) && this.text.equals(other.text);
} else {
return false;
}
}
}
private static <K, V> Pair<K, V> pair(K key, V value) {
return new Pair<K, V>(key, value);
}
private static final Projectable BASIC_RESULTS = projectable(
row(1, "post 1", 1, "comment 1"),
row(2, "post 2", 4, "comment 4"),
@ -71,10 +106,6 @@ public class GroupByTest {
row(null, "null post", 8, "comment 8"),
row(1, "post 1", 3, "comment 3")
);
private static <K, V> Pair<K, V> pair(K key, V value) {
return new Pair<K, V>(key, value);
}
private static final Projectable MAP_RESULTS = projectable(
row(1, "post 1", pair(1, "comment 1")),
@ -86,10 +117,21 @@ public class GroupByTest {
row(1, "post 1", pair(3, "comment 3"))
);
private static final Projectable POST_W_COMMENTS_RESULTS = projectable(
row(1, "post 1", comment(1)),
row(1, "post 1", comment(2)),
row(2, "post 2", comment(5)),
row(3, "post 3", comment(6)),
row(null, "null post", comment(7)),
row(null, "null post", comment(8)),
row(1, "post 1", comment(3))
);
@Test
public void Group_Order() {
Map<Integer, Group> results =
GroupBy.create(postId).withOne(postName).withSet(commentId).transform(BASIC_RESULTS);
GroupBy.create(postId).withOne(postName).withSet(commentId)
.transform(BASIC_RESULTS);
assertEquals(4, results.size());
}
@ -97,31 +139,34 @@ public class GroupByTest {
@Test
public void First_Set_And_List() {
Map<Integer, Group> results =
GroupBy.create(postId).withOne(postName).withSet(commentId).withList(commentText).transform(BASIC_RESULTS);
GroupBy.create(postId).withOne(postName).withSet(commentId).withList(commentText)
.transform(BASIC_RESULTS);
Group group = results.get(1);
assertEquals(toInt(1), group.getOne(postId));
assertEquals("post 1", group.getOne(postName));
assertEquals(toSet(1, 2, 3), group.getSet(commentId));
assertEquals(toInt(1), group.getOne(postId));
assertEquals("post 1", group.getOne(postName));
assertEquals(toSet(1, 2, 3), group.getSet(commentId));
assertEquals(Arrays.asList("comment 1", "comment 2", "comment 3"), group.getList(commentText));
}
@Test
public void Group_By_Null() {
Map<Integer, Group> results =
GroupBy.create(postId).withOne(postName).withSet(commentId).withList(commentText).transform(BASIC_RESULTS);
GroupBy.create(postId).withOne(postName).withSet(commentId).withList(commentText)
.transform(BASIC_RESULTS);
Group group = results.get(null);
assertNull(group.getOne(postId));
assertEquals("null post", group.getOne(postName));
assertEquals(toSet(7, 8), group.getSet(commentId));
assertEquals("null post", group.getOne(postName));
assertEquals(toSet(7, 8), group.getSet(commentId));
assertEquals(Arrays.asList("comment 7", "comment 8"), group.getList(commentText));
}
@Test
public void With_Constant_Column() {
Map<Integer, Group> results =
GroupBy.create(postId).withOne(postName).withGroup(constant).transform(BASIC_RESULTS);
GroupBy.create(postId).withOne(postName).withGroup(constant)
.transform(BASIC_RESULTS);
Group group = results.get(1);
assertEquals("constant", group.getGroup(constant));
@ -130,31 +175,54 @@ public class GroupByTest {
@Test
public void Map() {
Map<Integer, Group> results =
GroupBy.create(postId).withOne(postName).withMap(commentId, commentText).transform(MAP_RESULTS);
GroupBy.create(postId).withOne(postName).withMap(commentId, commentText)
.transform(MAP_RESULTS);
Group group = results.get(1);
Map<Integer, String> comments = group.getMap(commentId, commentText);
assertEquals(3, comments.size());
assertEquals("comment 2", comments.get(2));
assertEquals(comments, group.getMap(commentId, String.class));
assertEquals(comments, group.getMap(commentId, Object.class));
}
@Test
public void Array_Access() {
Map<Integer, Group> results =
GroupBy.create(postId).withOne(postName).withSet(commentId).withList(commentText).transform(BASIC_RESULTS);
GroupBy.create(postId).withOne(postName).withSet(commentId).withList(commentText)
.transform(BASIC_RESULTS);
Group group = results.get(1);
Object[] array = group.toArray();
assertEquals(toInt(1), array[0]);
assertEquals("post 1", array[1]);
assertEquals(toSet(1, 2, 3), array[2]);
assertEquals(toInt(1), array[0]);
assertEquals("post 1", array[1]);
assertEquals(toSet(1, 2, 3), array[2]);
assertEquals(Arrays.asList("comment 1", "comment 2", "comment 3"), array[3]);
}
@Test
public void Transform_Results() {
Map<Integer, Post> results =
GroupBy.create(postId, postName).withSet(qComment)
.withTransformer(new Transformer<Group, Post>() {
public Post transform(Group group) {
return new Post(group.getOne(postId), group.getOne(postName), group.getSet(qComment));
}
})
.transform(POST_W_COMMENTS_RESULTS);
Post post = results.get(1);
assertNotNull(post);
assertEquals(toInt(1), post.id);
assertEquals("post 1", post.name);
assertEquals(toSet(comment(1), comment(2), comment(3)), post.comments);
}
private static Comment comment(Integer id) {
return new Comment(id, "comment " + id);
}
private static Projectable projectable(final Object[]... rows) {
return new AbstractProjectable(){
public CloseableIterator<Object[]> iterate(Expression<?>[] args) {

View File

@ -1032,8 +1032,11 @@ public abstract class SelectBaseTest extends AbstractBaseTest{
.from(employee)
.innerJoin(employee._superiorIdKey, employee2);
Map<Integer, Group> results = new GroupBy<Integer>(employee.id, employee.firstname, employee.lastname)
.withMap(employee2.id, new QTuple(employee2.id, employee2.firstname, employee2.lastname))
QTuple subordinates = new QTuple(employee2.id, employee2.firstname, employee2.lastname);
// {id:1, firstname:"Mike", lastname:"Smith", subordinates: [{id:, firstname:, lastname:}, {id:, firstname:, lastname:}...]
Map<Integer, Group> results = GroupBy.create(employee.id, employee.firstname, employee.lastname)
.withMap(employee2.id, subordinates)
.transform(qry);
assertEquals(2, results.size());
@ -1043,7 +1046,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{
assertEquals("Mike", group.getOne(employee.firstname));
assertEquals("Smith", group.getOne(employee.lastname));
Map<Integer, Tuple> emps = group.getMap(employee2.id, Tuple.class);
Map<Integer, Tuple> emps = group.getMap(employee2.id, subordinates);
assertEquals(4, emps.size());
assertEquals("Steve", emps.get(12).get(employee2.firstname));
@ -1052,7 +1055,7 @@ public abstract class SelectBaseTest extends AbstractBaseTest{
assertEquals("Mary", group.getOne(employee.firstname));
assertEquals("Smith", group.getOne(employee.lastname));
emps = group.getMap(employee2.id, Tuple.class);
emps = group.getMap(employee2.id, subordinates);
assertEquals(4, emps.size());
assertEquals("Mason", emps.get(21).get(employee2.lastname));
}