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

com.day.cq.reporting.Data Maven / Gradle / Ivy

/*
 * Copyright 1997-2010 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

package com.day.cq.reporting;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.io.JSONWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 

This class represents the data of a report.

* *

Instances of this class may be used concurrently (through caches), so the following * policy must be followed:

* *
    *
  • During the creation of a report, the class must only be used from a single thread. *
  • *
  • After the data has been completely retrieved (and before adding it to a cache), it * must be "compacted" (using {@link #compact}).
  • *
  • After being compacted, the data object is considered immutable. All methods * that change state are then considered to throw {@link IllegalStateException}s.
  • *
*/ public abstract class Data { /** * Logger */ protected static final Logger log = LoggerFactory.getLogger(Data.class); /** * The report */ protected Report report; /** * Data rows */ protected final List rows; /** * Column totals */ protected final Map columnTotals; /** * Flag that determines if the data has grouped columns */ protected final boolean hasGroupedColumns; /** * Flag that determines if the data has already been compacted. */ protected boolean isCompacted; /** * List of the report's column (available after compacting) */ protected List columns; /** * Version of report data (0 - CQ 5.4; 1 - CQ 5.5) */ protected int reportingVersion; public Data(Report report) { this.report = report; this.hasGroupedColumns = this.report.hasGroupedColumns(); this.columnTotals = new HashMap(); this.rows = new ArrayList(16); this.isCompacted = false; this.reportingVersion = (report instanceof ReportExtensions ? ((ReportExtensions) report).getReportingVersion() : 0); } /** * Ensures that the data is still mutable. Throws an {@link IllegalStateException} * otherwise. */ protected void ensureMutable() { if (this.isCompacted) { throw new IllegalStateException( "Report data is already compacted and therefore immutable."); } } /** * Ensures that the report data is immutable; throws an {@link IllegalStateException} * otherwise. */ protected void ensureImmutable() { if (!this.isCompacted) { throw new IllegalStateException( "Report data must be compacted before writing to JSON"); } } /** * Determines if the report data has of grouped columns. * * @return true if the report contains data from grouped columns */ public boolean hasGroupedColumns() { return this.hasGroupedColumns; } /** * Adds the specified row of data. * * @param rowToAdd The row to add */ public void addRow(DataRow rowToAdd) { this.ensureMutable(); log.debug("Adding row; value: {}", rowToAdd); this.rows.add(rowToAdd); } /** * Gets a row by its index. * * @param rowIndex The index * @return The row */ public DataRow getRowAt(int rowIndex) { return this.rows.get(rowIndex); } /** * Gets the number of rows. * * @return Number of rows */ public int getRowCnt() { return this.rows.size(); } /** * Gets an iterator over the columns of the report data. * * @return The iterator */ public Iterator getColumns() { this.ensureImmutable(); return this.columns.iterator(); } /** * Adds the specified column total. * * @param col The column * @param total The total value */ public void addColumnTotal(Column col, CellValue total) { this.ensureMutable(); this.columnTotals.put(col, total); } /** * Gets the total value of the specified column. * * @param col The (aggregated) column to determine the total value for * @return The total value of the specified column */ public CellValue getColumnTotal(Column col) { return this.columnTotals.get(col); } /** * This class must called after the data has been calculated completely, no further * changes have to be made and the */ public void compact() { this.ensureMutable(); this.isCompacted = true; Iterator cols = this.report.getColumnIterator(); this.columns = new ArrayList(); while (cols.hasNext()) { this.columns.add(cols.next()); } ((ArrayList) this.columns).trimToSize(); this.report = null; ((ArrayList) this.rows).trimToSize(); for (DataRow row : this.rows) { row.compact(); } } /** * Use this method to process each data row with the specified {@link Processor}s. * * @param processors The array of processing modules to execute. Note that if one of the * modules declares a row to be deleted, the modules specified at * higher array indices will not be executed on that row. */ public void postProcess(Processor[] processors) { this.ensureMutable(); log.debug("Postprocessing result data"); for (int r = this.getRowCnt() - 1; r >= 0; r--) { log.debug("Postprocessing row #{}", r); DataRow row = this.rows.get(r); for (Processor processor : processors) { log.debug("Applying processor {}", processor.getClass().getSimpleName()); if (processor.processRow(row)) { log.debug("Processor requested removal of record {}; removing record", row); this.rows.remove(r); } } } } /** * Sorts the result data by the specified column. * * @param sortingColumn The column to sort by * @param sortingDirection The sorting direction */ public void sortByColumn(final Column sortingColumn, Sorting.Direction sortingDirection) { this.ensureMutable(); final boolean isAscending = (sortingDirection == Sorting.Direction.ASCENDING); log.debug("Sorting result for column {}", sortingColumn.getName()); Collections.sort(this.rows, new Comparator() { public int compare(DataRow o1, DataRow o2) { int cmpResult; String resultProp = sortingColumn.getName(); CellValue val1 = o1.get(resultProp); CellValue val2 = o2.get(resultProp); if (val1 == null) { if (val2 == null) { cmpResult = 0; } else { cmpResult = -1; } } else if (val2 == null) { cmpResult = 1; } else { // TODO ugly hack, please help (cause: page titles can be Longs)!!! try { cmpResult = val1.compareTo(val2); } catch (IllegalStateException e) { return val1.toString().compareTo(val2.toString()); } } if (!isAscending) { cmpResult = -cmpResult; } return cmpResult; } }); } /** * Use this method to process each data row with the specified {@link Processor}. * * @param processor The processing module */ public void postProcess(Processor processor) { this.ensureMutable(); this.postProcess(new Processor[] { processor } ); } /** *

Gets the interal reporting version the report was created for.

* *

This can be used to ensure backwards compatibility with reports that were created * for different CQ versions if some default behaviour had to be changed.

* * @return The version of reporting the report has been created for (0 - CQ 5.4; 1 - CQ * 5.5) * @since 5.5 */ public int getReportingVersion() { return this.reportingVersion; } /** * Creates a suitable {@link ChartData} object for this report data. * * @param limit Number of data to be returned for the chart * @return The corresponding chart data */ public abstract ChartData createChartData(int limit); /** * Writes the type definition for each column to the specified {@link JSONWriter}. * * @param writer The writer to stream the data to * @throws JSONException if writing the type definition has failed */ public abstract void writeTypesJSON(JSONWriter writer) throws JSONException; /** * Writes the sort information for the report to the specified {@link JSONWriter}. * * @param writer The writer to stream the data to * @throws JSONException if writing the sort info has failed */ public abstract void writeSortInfoJSON(JSONWriter writer) throws JSONException; /** * Writes the result to the specified {@link JSONWriter}. * * @param writer The writer to stream the data to * @param locale The locale to be used for formatting data * @param start The first record to be streamed; null to stream from the * beginning * @param limit The maximum number of records to be streamed; null to * stream to the end * @throws JSONException if writing the result has failed */ public abstract void writeDataJSON(JSONWriter writer, Locale locale, Integer start, Integer limit) throws JSONException; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy