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

net.sf.jett.tag.AggTag Maven / Gradle / Ivy

package net.sf.jett.tag;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.apache.poi.ss.usermodel.RichTextString;

import net.sf.jagg.AggregateFunction;
import net.sf.jagg.Aggregation;
import net.sf.jagg.Aggregator;
import net.sf.jagg.exception.JaggException;
import net.sf.jagg.model.AggregateValue;

import net.sf.jett.exception.TagParseException;
import net.sf.jett.transform.BlockTransformer;
import net.sf.jett.util.AttributeUtil;

/**
 * 

An AggTag represents possibly many aggregate values * calculated from a List of values already exposed to the * context. It uses jAgg functionality and exposes the results * and AggregateFunctions used for display later.

* *
Attributes: *
    *
  • Inherits all attributes from {@link BaseTag}.
  • *
  • items (required): List
  • *
  • aggs (required): String
  • *
  • aggsVar (optional): String
  • *
  • valuesVar (required): String
  • *
  • groupBy (optional): String
  • *
  • parallel (optional): int
  • *
  • useMsd (optional): boolean
  • *
  • rollup (optional): int[]
  • *
  • rollups (optional): int[][]
  • *
  • cube (optional): int[]
  • *
  • groupingSets (optional): int[][]
  • *
* * @author Randy Gettman */ public class AggTag extends BaseTag { /** * Attribute that specifies the List of items to aggregate. */ public static final String ATTR_ITEMS = "items"; /** * Attribute that specifies the List of AggregateFunctions to use. */ public static final String ATTR_AGGS = "aggs"; /** * Attribute that specifies the name of the List of exposed * aggregate functions. */ public static final String ATTR_AGGS_VAR = "aggsVar"; /** * Attribute that specifies name of the List of exposed * aggregation values. */ public static final String ATTR_VALUES_VAR = "valuesVar"; /** * Attribute that specifies the List of group-by properties. */ public static final String ATTR_GROUP_BY = "groupBy"; /** * Attribute that specifies the degree of parallelism to use. */ public static final String ATTR_PARALLEL = "parallel"; /** * Attribute that specifies whether to use Multiset Discrimination instead * of sorting during aggregation processing. This means that the results * will not be sorted by the "group by" properties. It defaults to * false -- don't use Multiset Discrimination, use sorting. * @since 0.4.0 */ public static final String ATTR_USE_MSD = "useMsd"; /** * Attribute that specifies a rollup that should occur on the results. * Specify a List of 0-based integer indexes that reference the * original list of properties. E.g. when grouping by two properties, * prop1 and prop2, specifying a List * of {0} specifies a rollup on prop1. * @since 0.4.0 * @see #ATTR_GROUP_BY */ public static final String ATTR_ROLLUP = "rollup"; /** * Attribute that specifies multiple rollups that should occur on the * results. Specify a List of Lists of 0-based * integer indexes that reference the original list of properties. E.g. * when grouping by three properties, * prop1, prop2, and prop3, * specifying a List of {{0}, {1, 2}} specifies a * rollup on prop1, and a separate rollup on prop2 * and prop3. * @since 0.4.0 * @see #ATTR_GROUP_BY */ public static final String ATTR_ROLLUPS = "rollups"; /** * Attribute that specifies a data cube that should occur on the results. * Specify a List of 0-based integer indexes that reference the * original list of properties. E.g. when grouping by three properties, * prop1, prop2, and prop3, * specifying a List of {0, 1} specifies a cube on * prop1 and prop2. * @since 0.4.0 * @see #ATTR_GROUP_BY */ public static final String ATTR_CUBE = "cube"; /** * Attribute that specifies the exact grouping sets that should occur on the * results. Specify a List of Lists of 0-based * integer indexes that reference the original list of properties. E.g. * when grouping by three properties, prop1, * prop2, and prop3, specifying a * List of {{0}, {1, 2}, {}} specifies a * grouping set on prop1, a separate grouping set on * prop2 and prop3, and a third grouping set on no * properties (a grand total). * @since 0.4.0 * @see #ATTR_GROUP_BY */ public static final String ATTR_GROUPING_SETS = "groupingSets"; private static final List REQ_ATTRS = new ArrayList<>(Arrays.asList(ATTR_ITEMS, ATTR_AGGS, ATTR_VALUES_VAR)); private static final List OPT_ATTRS = new ArrayList<>(Arrays.asList(ATTR_AGGS_VAR, ATTR_GROUP_BY, ATTR_PARALLEL, ATTR_USE_MSD, ATTR_ROLLUP, ATTR_ROLLUPS, ATTR_CUBE, ATTR_GROUPING_SETS)); private List myList = null; private List myAggs = null; private String myAggsVar = null; private String myValuesVar = null; private Aggregation myAggregation; /** * Returns this Tag's name. * @return This Tag's name. */ @Override public String getName() { return "agg"; } /** * Returns a List of required attribute names. * @return A List of required attribute names. */ @Override protected List getRequiredAttributes() { List reqAttrs = new ArrayList<>(super.getRequiredAttributes()); reqAttrs.addAll(REQ_ATTRS); return reqAttrs; } /** * Returns a List of optional attribute names. * @return A List of optional attribute names. */ @Override protected List getOptionalAttributes() { List optAttrs = new ArrayList<>(super.getOptionalAttributes()); optAttrs.addAll(OPT_ATTRS); return optAttrs; } /** * Validates the attributes for this Tag. The "items" * attribute must be a List. The "parallel" attribute must be * a positive integer (defaults to 1). The "aggs" attribute must be a * semicolon-separated list of valid Aggregator specification * strings. The "valuesVar" attribute must be a string that indicates the * name to which the aggregate values will be exposed in the * Map of beans. The "aggsVar" attribute must be a string that * indicates the name of the List that contains all created * AggregateFunctions and to which that will be exposed in the * Map of beans. The "groupBy" attribute must be a semicolon- * separated list of properties with which to "group" aggregated * calculations (defaults to no "group by" properties). The "agg" tag must * have a body. */ @SuppressWarnings("unchecked") @Override public void validateAttributes() throws TagParseException { super.validateAttributes(); if (isBodiless()) throw new TagParseException("Agg tags must have a body. Bodiless agg tag found" + getLocation()); TagContext context = getContext(); Map beans = context.getBeans(); Map attributes = getAttributes(); myList = AttributeUtil.evaluateObject(this, attributes.get(ATTR_ITEMS), beans, ATTR_ITEMS, List.class, null); List aggsList = AttributeUtil.evaluateList(this, attributes.get(ATTR_AGGS), beans, null); myAggs = new ArrayList<>(aggsList.size()); for (String aggSpec : aggsList) myAggs.add(Aggregator.getAggregator(aggSpec)); myAggsVar = AttributeUtil.evaluateString(this, attributes.get(ATTR_AGGS_VAR), beans, null); myValuesVar = AttributeUtil.evaluateString(this, attributes.get(ATTR_VALUES_VAR), beans, null); List groupByProps = AttributeUtil.evaluateList(this, attributes.get(ATTR_GROUP_BY), beans, new ArrayList()); int parallelism = AttributeUtil.evaluatePositiveInt(this, attributes.get(ATTR_PARALLEL), beans, ATTR_PARALLEL, 1); boolean useMsd = AttributeUtil.evaluateBoolean(this, attributes.get(ATTR_USE_MSD), beans, false); RichTextString rtsRollup = attributes.get(ATTR_ROLLUP); RichTextString rtsRollups = attributes.get(ATTR_ROLLUPS); RichTextString rtsCube = attributes.get(ATTR_CUBE); RichTextString rtsGroupingSets = attributes.get(ATTR_GROUPING_SETS); AttributeUtil.ensureAtMostOneExists(this, Arrays.asList(rtsRollup, rtsRollups, rtsCube, rtsGroupingSets), Arrays.asList(ATTR_ROLLUP, ATTR_ROLLUPS, ATTR_CUBE, ATTR_GROUPING_SETS)); List rollup = AttributeUtil.evaluateIntegerArray(this, rtsRollup, beans, null); List cube = AttributeUtil.evaluateIntegerArray(this, attributes.get(ATTR_CUBE), beans, null); List> rollups = AttributeUtil.evaluateIntegerArrayArray(this, rtsRollups, beans, null); List> groupingSets = AttributeUtil.evaluateIntegerArrayArray(this, rtsGroupingSets, beans, null); Aggregation.Builder builder = new Aggregation.Builder() .setAggregators(myAggs) .setParallelism(parallelism) .setUseMsd(useMsd); if (groupByProps != null) builder.setProperties(groupByProps); if (rollup != null) builder.setRollup(rollup); else if (cube != null) builder.setCube(cube); else if (rollups != null) builder.setRollups(rollups); else if (groupingSets != null) builder.setGroupingSets(groupingSets); try { myAggregation = builder.build(); } catch (JaggException e) { throw new TagParseException("AggTag: Problem executing jAgg call: " + getLocation() + ": " + e.getMessage(), e); } catch (RuntimeException e) { throw new TagParseException("AggTag: RuntimeException caught during jAgg execution" + getLocation() + ": " + e.getMessage(), e); } } /** * Run a "group by" operation on the specified AggregateFunctions, get * the results, and expose the aggregate values and the * AggregateFunctions used. * @return Whether the first Cell in the Block * associated with this Tag was processed. */ @Override public boolean process() { TagContext context = getContext(); Map beans = context.getBeans(); List> aggValues = myAggregation.groupBy(myList); beans.put(myValuesVar, aggValues); if (myAggsVar != null) beans.put(myAggsVar, myAggs); BlockTransformer transformer = new BlockTransformer(); transformer.transform(context, getWorkbookContext()); return true; } }