All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.twisterrob.gradle.common.grouper.Grouper Maven / Gradle / Ivy

There is a newer version: 0.17
Show newest version
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> 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> getAt(@Nonnull String fieldName) { return (Grouper>) 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 by(@Nonnull String fieldName) { return new Grouper<>(list, representative, Collections.singletonList(fieldName), finalCollector); } @SuppressWarnings("unchecked") @Override 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); } } } 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