net.twisterrob.gradle.common.grouper.Grouper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of twister-quality-common Show documentation
Show all versions of twister-quality-common Show documentation
Shared classes between checkers. (Not to be consumed directly.)
package net.twisterrob.gradle.common.grouper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import groovy.lang.GroovyObjectSupport;
import groovy.lang.MissingPropertyException;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.plus;
import org.codehaus.groovy.runtime.NullObject;
/**
* A grouper for uniform lists. Non-uniform lists may misbehave depending on order due to
* {@linkplain AbstractGrouper#representative implementation details}.
*
* Usage from Java: {@code Grouper.create(asList(a,b,c)).getGrouper().by("f1").by("f2").group()}
*
* Usage from Groovy: {@code Grouper.create([a,b,c]).grouper['f1']['f2'].group()}
*
*/
public class Grouper extends AbstractGrouper {
public static @Nonnull Start create(@Nonnull List list) {
return create(list, Collectors.reducing(0, e -> 1, Integer::sum));
}
public static @Nonnull Start create(@Nonnull List list, @Nonnull Collector finalCollector) {
return new Start<>(Collections.unmodifiableList(list), findRepresentative(list), finalCollector);
}
private final @Nonnull List fields;
private Grouper(
@Nonnull List> list,
@Nonnull Object representative,
@Nonnull List fields,
@Nonnull Collector, ?, ?> finalCollector) {
super(list, representative, finalCollector);
this.fields = fields;
}
@Override
public @Nonnull Grouper> by(@Nonnull String fieldName) {
return new Grouper<>(list, representative, plus(fields, fieldName), finalCollector);
}
@Override
@SuppressWarnings("unchecked")
public @Nonnull Grouper> getAt(@Nonnull String fieldName) {
return (Grouper>) super.getAt(fieldName);
}
@SuppressWarnings("EmptyMethod") // Groovy needs this
@Override
public Object getProperty(String fieldName) {
return super.getProperty(fieldName);
}
@SuppressWarnings({"unchecked", "rawtypes"})
public @Nonnull Map group() {
List reversed = new ArrayList<>(fields);
Collections.reverse(reversed);
Collector collectors = finalCollector;
for (String field : reversed) {
Function access = obj -> DefaultGroovyMethods.getAt(obj, field);
collectors = Collectors.groupingBy(access, LinkedHashMap::new, collectors);
}
return (Map) list.stream().collect(collectors);
}
@Override
public String toString() {
return String.format("%d %s grouped on %s", this.list.size(), representative.getClass(), fields);
}
private static Object findRepresentative(@Nonnull Collection> list) {
Object found = DefaultGroovyMethods.find(list);
if (found != null) {
return found;
} else {
return NullObject.getNullObject();
}
}
public static class Start extends AbstractGrouper {
private Start(
@Nonnull List list,
@Nonnull Object representative,
@Nonnull Collector finalCollector) {
super(list, representative, finalCollector);
}
@SuppressWarnings("unchecked")
public @Nonnull List getList() {
return (List) list;
}
@Override
public @Nonnull Grouper, List> by(@Nonnull String fieldName) {
return new Grouper<>(list, representative, Collections.singletonList(fieldName), Collectors.toList());
}
@SuppressWarnings("unchecked")
public @Nonnull Counter count() {
return new Counter<>(getList(), representative, (Collector) finalCollector);
}
@SuppressWarnings("unchecked")
@Override
public @Nonnull Grouper, List> getAt(@Nonnull String fieldName) {
return (Grouper, List>) super.getAt(fieldName);
}
@SuppressWarnings("EmptyMethod") // Groovy needs this
@Override
public Object getProperty(String fieldName) {
return super.getProperty(fieldName);
}
}
public static class Counter extends AbstractGrouper {
private Counter(
@Nonnull List list,
@Nonnull Object representative,
@Nonnull Collector finalCollector) {
super(list, representative, finalCollector);
}
@Override
public @Nonnull Grouper, R> by(@Nonnull String fieldName) {
return new Grouper<>(list, representative, Collections.singletonList(fieldName), finalCollector);
}
@SuppressWarnings("unchecked")
@Override
public @Nonnull Grouper, R> getAt(@Nonnull String fieldName) {
return (Grouper, R>) super.getAt(fieldName);
}
@SuppressWarnings("EmptyMethod") // Groovy needs this
@Override
public Object getProperty(String fieldName) {
return super.getProperty(fieldName);
}
}
}
abstract class AbstractGrouper extends GroovyObjectSupport {
protected final @Nonnull List> list;
protected final @Nonnull Object representative;
protected final @Nonnull Collector, ?, ?> finalCollector;
protected AbstractGrouper(
@Nonnull List> list,
@Nonnull Object representative,
@Nonnull Collector, ?, ?> finalCollector) {
this.list = list;
this.representative = representative;
this.finalCollector = finalCollector;
}
/**
* for Groovy to have [] operator
*/
@SuppressWarnings("unused")
public @Nonnull Grouper, ?> getAt(@Nonnull String fieldName) {
return by(fieldName);
}
public @Nonnull abstract Grouper, ?> by(@Nonnull String fieldName);
/**
* for Groovy to have . operator, needs to be repeated in every type.
*/
@Override
public Object getProperty(String fieldName) {
if ("by".equals(fieldName)) {
return this;
}
if (representative == NullObject.getNullObject()) {
try {
// TODO the order of these is reversed here when on empty list, which could lead to different behavior
return super.getProperty(fieldName);
} catch (MissingPropertyException ex) {
return by(fieldName);
}
} else if (hasField(representative, fieldName)) {
return by(fieldName);
} else {
return super.getProperty(fieldName);
}
}
private static boolean hasField(Object obj, String fieldName) {
try {
DefaultGroovyMethods.getAt(obj, fieldName);
return true;
} catch (MissingPropertyException ex) {
// property not found
return false;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy