public abstract class Accumulator extends LazyUndoable implements Scopeable, Finalizable
An accumulator may derive its data from one of the following sources:
BaseDataType
instance -- note:
the reference provided to the accumulator must not be reassigned,
it must be updated via its assign
method only);
data source
instance,
resolved once per iteration;
The accumulator is updated by calling the iterate(boolean)
method once per
loop iteration. This is either done manually by client code, or the
accumulator can be added to a query
.
In the latter case, the iterate
method is called directly by
the query.
A query may call a variant of the iterate
method which
accepts a defer
argument. If this argument is
true
, the data bundle(s) of the Accumulator instance is
not updated immediately, but instead is updated only on a subsequent call
to the no-argument variant of accumulate
. This alternate
mechanism is used in cases where the accumulation must occur conditionally
or otherwise cannot be triggered automatically by the query.
Each accumulator instance manages one or more Accumulator.DataBundle
s to gather and report its statistic. Concrete
implementations must implement this inner interface and implement the
createDataBundle()
abstract method to return a new instance of
their implementation-specific data bundle. An accumulator will always
have one, primary data bundle, which will track results across the full
loop iteration (i.e., across all break groups). Additional data bundles
may be added for each break group category the accumulator supports using
the addBreakGroup(com.goldencode.p2j.util.Resolvable)
method. If an accumulator is associated with
a query containing a matching break group category, the data bundle will
be reset automatically whenever the query results enter a new break group.
The mechanism to retrieve the statistic(s) calculated by a concrete accumulator implementation is specific to that implementation.
Modifier and Type | Class and Description |
---|---|
protected static interface |
Accumulator.DataBundle
This interface must be implemented by concrete subclasses.
|
private static class |
Accumulator.ExternalDataSource
A data source implementation which expects to be fed its data from an
external source.
|
Modifier and Type | Field and Description |
---|---|
private java.util.Deque<java.lang.Boolean> |
accumExecutedStack
Stack with status of the ACCUM statement execution.
|
protected static int |
BASE_WEIGHT
Base weight for all subclass relative weight values
|
private java.util.Deque<java.lang.Boolean> |
blockExecutedStack
Stack with status of "block with ACCUM statement" execution.
|
private java.util.Map<Resolvable,java.lang.Boolean> |
breakMap
Mapping of break groups to "new group" status for next iteration
|
private java.util.Deque<java.util.LinkedHashMap<Resolvable,Accumulator.DataBundle>> |
bundleStack
Stack with maps of data bundles by break group.
|
private Resolvable |
dataSource
Abstraction of data source from which data values are accumulated
|
private boolean |
deferred
Is accumulator operating in deferred iteration mode?
|
private boolean |
enabled
Is accumulator enabled for the next iteration in its loop?
|
private java.util.Deque<Accumulator.DataBundle> |
entryStack
Stack with the entry values for each block.
|
private java.util.Deque<Accumulator.DataBundle> |
fixedDataStack
Stack with the fixed reported value.
|
private java.util.Deque<java.lang.Boolean> |
ifExecutedStack
Stack with status of "IF with enclosed ACCUM statement" execution.
|
private java.util.Deque<java.lang.Boolean> |
ifStack
Stack with the IF statement execution status - true values mean that
an IF statement is currently being executed.
|
private boolean |
lastOfGroup
Is the most recent accumulation the last of the first break group?
|
private java.util.Deque<java.util.LinkedHashMap<Resolvable,java.lang.Boolean>> |
pendingResetStack
Stack with maps of data bundles to pending reset flag.
|
private java.util.Deque<Accumulator.DataBundle> |
pendingStack
Stack with pending accumulation values for each block.
|
private static int |
POSTPONE_NONE
Value used to mark the IF statement that it doesn't need to postpone
anything.
|
private static int |
POSTPONE_RESET
Value used to mark the IF statement that it needs to reset the
accumulator, when the enclosed accumulator did not execute.
|
private static int |
POSTPONE_UNKNOWN
Value used to mark the IF statement that it needs to set the accumulator
to UNKNOWN, when the enclosed accumulator did not execute.
|
private java.util.Deque<java.lang.Integer> |
postponeStack
Stack with the postpone values for IF cases.
|
private boolean |
subgroupOnly
Does accumulator produce "grand" results or subgroup results only?
|
private java.util.Deque<java.lang.Boolean> |
subOnlyStack
Track sub-only mode - an accumulate function can retrieve a sub-only
accumulator only if it is in the same block as the accumulate statement.
|
private java.util.Deque<java.lang.Boolean> |
topLevelStack
Stack with each parent block type (top-level or not).
|
private boolean |
uninitialized
Is accumulator in uninitialized safe mode?
|
private java.util.Deque<java.lang.Boolean> |
usageStack
Stack with usage status for this accumulator.
|
Modifier | Constructor and Description |
---|---|
protected |
Accumulator()
Constructor which stores no data source.
|
protected |
Accumulator(BaseDataType variable)
Constructor which stores a mutable variable reference as the
accumulator's data source.
|
protected |
Accumulator(java.lang.Class<? extends BaseDataType> cls)
Constructor which stores no data source.
|
protected |
Accumulator(Resolvable dataSource,
boolean register)
Constructor which stores a
Resolvable as the accumulator's
data source. |
Modifier and Type | Method and Description |
---|---|
void |
accumulate()
This method may be called one or more times per loop iteration, either
directly by client code, or indirectly by a query with which this
accumulator is associated.
|
void |
accumulate(BaseDataType datum)
This method may be called one or more times per loop iteration, either
directly by client code, or indirectly by a query with which this
accumulator is associated.
|
private void |
accumulate(Resolvable key,
boolean newGroup,
BaseDataType datum)
Apply a piece of accumulated data to the appropriate data bundle,
resetting the bundle first if we are entering a new break group.
|
void |
addBreakGroup(Resolvable breakGroupKey)
Track a new break group category within this accumulator.
|
void |
assign(Undoable value)
Sets the state of this instance based on the state of the passed instance.
|
java.util.Iterator<Resolvable> |
breakGroupKeys()
Get an iterator of the break group keys for all break group categories
being managed by this accumulator.
|
protected BaseDataType |
calculate(Resolvable breakGroupKey)
Calculate the accumulator's result for the specified break group.
|
protected abstract Accumulator.DataBundle |
createDataBundle()
Create and return a new instance of an implementation-specific data
bundle.
|
protected BaseDataType |
createUnknown()
Convenience method to create a new instance of the data source's
return data type, which represents the unknown value.
|
Undoable |
deepCopy()
This is a form of a copy constructor that makes a deep copy of the
current instance and returns this copy as a new instance of the same
class.
|
void |
deleted()
Provides a notification that the external program scope in which the object is registered is
is being deleted and the object's reference will be lost after this method is called.
|
void |
finished()
Cleanup that must occur at the end of the block to which the frame is
scoped.
|
Resolvable |
getDataSource()
Get the resolvable data source which feeds this accumulator.
|
Resolvable |
getFirstBreakGroupKey()
Get the first, non-
null break group key registered with
this accumulator. |
abstract BaseDataType |
getFullResult()
Get the full result of all accumulations so far for this accumulator,
across all break groups.
|
abstract java.lang.String |
getLabel()
Get the label associated with this accumulator type, for use in the UI.
|
abstract BaseDataType |
getSubResult()
Get the current subgroup result for the first registered break group in
this accumulator.
|
protected java.lang.Class<?> |
getType()
Get the data type returned by this accumulator's data source.
|
abstract int |
getWeight()
Get the relative weight associated with this accumulator type, for use
in sorting accumulator instances of different types according to their
natural display order.
|
void |
ifEnded()
This method is used to notify the accumulator that a IF statement which
contains the ACCUM statement or a DISPLAY with aggregate phrase has
ended.
|
boolean |
isLastOfGroup()
Report whether the most recent accumulation performed by this instance
represents the last accumulation to be performed for the current break
group.
|
boolean |
isSubgroupOnly()
Indicate whether this accumulator instance is meant to report full
results or subgroup results only.
|
boolean |
isSubOnly()
Get current state of sub-only mode, for the current
block.
|
protected boolean |
isUndoable()
Check if this instance is undoable.
|
protected boolean |
isUninitialized()
Indicate whether this accumulator is in uninitialized, safe mode.
|
void |
iterate()
Provides a notification that the block whose scope in which the object
is registered is about to iterate and attempt another pass.
|
void |
iterate(boolean defer)
This method must be called exactly once per loop iteration, either
directly by client code, or by a query with which this accumulator is
associated.
|
void |
iterate(java.util.Map<Resolvable,java.lang.Boolean> breakMap,
boolean defer)
This method must be called exactly once per loop iteration, either
directly by client code, or by a query with which this accumulator is
associated.
|
private void |
postpone(int what)
Internal method to save the postponed action type, when an IF statement
which encloses an accumulation statement is started.
|
void |
postponeReset()
When an IF statement which contains the ACCUM statement or DISPLAY with
aggregate phrase is started (and also the accumulation statement is not
also enclosed within a REPEAT/DO TRANSACTION/FOR block), the accumulator
must be reset when the enclosed accumulation did not execute.
This method will call postpone(int) with the parameter set to
POSTPONE_RESET . |
void |
postponeUnknown()
When an IF statement which contains the ACCUM statement or DISPLAY with
aggregate phrase is started (and the accumulation statement is also
enclosed within a REPEAT/DO TRANSACTION/FOR block), the accumulator must
be set to UNKNOWN when the enclosed accumulation did not execute.
When there are enclosed 2 or more accumulation statements and at least one is also enclosed within a block as specified above, then setting the accumulator to UNKNOWN has precedence against resetting the accumulator. |
void |
reset()
When an accumulator is used inside a block, a call to this method must
be made - this is because, if the block is not notified that it uses
this accumulator, then the accumulation value for the parent block
will not be updated when the child block is finished.
|
protected void |
resetData()
Reset all data managed by this accumulator to default values.
|
void |
retry()
Provides a notification that the block whose scope in which the object
is registered is about to retry and re-attempt the current iteration.
|
void |
scopeDeleted()
Provides notification that the external procedure scope has been deleted.
|
void |
scopeFinished()
Process a notification that a scope is about to be exited.
|
void |
scopeStart()
Process a notification that a new block scope has been entered.
|
void |
setLastOfGroup(boolean lastOfGroup)
Update this accumulator's current state to remember that the most
recent accumulation performed by this instance represents the last such
accumulation to be performed for the current break group.
|
void |
setSubgroupOnly(boolean subgroupOnly)
Set whether this accumulator instance is meant to report full results
or subgroup results only.
|
void |
setSubOnly()
Turn sub-only mode for the current block.
|
changed, checkUndoable, checkUndoable, checkUndoable, getTransLevel, isGlobal, markUndoable, popBlock, rollback, setGlobal
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
initFailure
protected static final int BASE_WEIGHT
private static final int POSTPONE_NONE
private static final int POSTPONE_RESET
private static final int POSTPONE_UNKNOWN
private final Resolvable dataSource
private java.util.Deque<java.util.LinkedHashMap<Resolvable,Accumulator.DataBundle>> bundleStack
private java.util.Deque<java.util.LinkedHashMap<Resolvable,java.lang.Boolean>> pendingResetStack
private java.util.Deque<Accumulator.DataBundle> pendingStack
private java.util.Deque<java.lang.Boolean> usageStack
private java.util.Deque<java.lang.Boolean> topLevelStack
private java.util.Deque<java.lang.Boolean> accumExecutedStack
private java.util.Deque<java.lang.Boolean> blockExecutedStack
private java.util.Deque<java.lang.Boolean> ifExecutedStack
private java.util.Deque<Accumulator.DataBundle> entryStack
private java.util.Deque<java.lang.Integer> postponeStack
private java.util.Deque<java.lang.Boolean> ifStack
private java.util.Deque<Accumulator.DataBundle> fixedDataStack
private java.util.Map<Resolvable,java.lang.Boolean> breakMap
private boolean uninitialized
private boolean deferred
private boolean enabled
private boolean subgroupOnly
private boolean lastOfGroup
private java.util.Deque<java.lang.Boolean> subOnlyStack
protected Accumulator()
accumulate
method. The accumulator is registered with the AccumulatorManager and
the TransactionManager in the next scope.
This c'tor variant must only be used for the CountAccumulator type, as this is the only aggregator which does not require knowledge about the expression type.
protected Accumulator(java.lang.Class<? extends BaseDataType> cls)
accumulate
method. The accumulator is registered with the AccumulatorManager and
the TransactionManager in the next scope.cls
- Data type of the value being accumulated.protected Accumulator(BaseDataType variable)
variable
- Mutable variable from which data is collected.protected Accumulator(Resolvable dataSource, boolean register)
Resolvable
as the accumulator's
data source. When an iteration occurs, the accumulator gathers its data
by resolving the current value of the data source.dataSource
- Data source from which data is collected.register
- This flag will be false
only if a copy object is instantiated.
Because we use the copy only for roll-back purposes, we do not need to add it to
the TransactionManager or AccumulatorManager.public boolean isSubOnly()
true
if sub-only mode is active.public void setSubOnly()
public final void addBreakGroup(Resolvable breakGroupKey)
breakGroupKey
- Key which will be used to manage and retrieve results for the
specified break group.public final boolean isLastOfGroup()
false
is returned.true
if the most recent accumulation represents
the last for the current break group; else false
.setLastOfGroup(boolean)
public final void setLastOfGroup(boolean lastOfGroup)
public
, it is intended that this method is
only invoked by the persistence layer of the P2J runtime, and not by
application code.lastOfGroup
- true
if the most recent accumulation represents
the last for the current break group; else false
.isLastOfGroup()
public final boolean isSubgroupOnly()
true
if this instance should report subgroup
results only; false
if it should also report
full results (e.g., grand totals, etc.).setSubgroupOnly(boolean)
public final void setSubgroupOnly(boolean subgroupOnly)
subgroupOnly
- true
if this instance should report subgroup
results only; false
if it should also report
full results (e.g., grand totals, etc.).isSubgroupOnly()
public abstract BaseDataType getFullResult()
public abstract BaseDataType getSubResult()
null
if no subgroups are being tracked by this
accumulator.public abstract java.lang.String getLabel()
public abstract int getWeight()
public final Resolvable getDataSource()
public final void iterate(boolean defer)
Upon invocation, if defer
is false
, it
resolves the current value from the accumulator's data source, then
performs an implementation-specific action to update the accumulator's
internal state.
This implementation assumes break groups are disabled.
defer
- true
to defer accumulation until the next
invocation of the no-argument accumulate()
method;
false
to perform accumulation immediately.accumulate()
public final void iterate(java.util.Map<Resolvable,java.lang.Boolean> breakMap, boolean defer)
defer
is false
, it
resolves the current value from the accumulator's data source, then
performs an implementation-specific action to update the accumulator's
internal state. Each data bundle being managed by this accumulator
(including the primary bundle and each break group bundle), is updated
with the data value being accumulated.
If a data bundle must be reset, because the result set being iterated entered a new break group for that bundle's break group category, this is done before the new data item is accumulated.
breakMap
- A map of break group category keys to boolean values, where
the boolean values indicate whether the current loop iteration
marks entry into a new break group for the associated category.
For every key with a true
value, the associated
break group data bundle is reset.defer
- true
to defer accumulation until the next
invocation of the no-argument accumulate()
method;
false
to perform accumulation immediately.accumulate()
public final void accumulate()
Upon invocation, it checks whether iteration is enabled at the current time. If so, it resolves the current value from the accumulator's data source, then performs an implementation-specific action to update the accumulator's internal state. Each data bundle being managed by this accumulator (including the primary bundle and each break group bundle), is updated with the data value being accumulated. If iteration is not currently enabled (i.e., we are operating in deferred mode, and the driving query has not enabled iteration for the current loop pass), this method returns immediately.
If a data bundle must be reset, because the result set being iterated entered a new break group for that bundle's break group category, this is done before the new data item is accumulated.
public final void accumulate(BaseDataType datum)
Upon invocation, it checks whether iteration is enabled at the current time. If so, it resolves the current value from the accumulator's data source, then performs an implementation-specific action to update the accumulator's internal state. Each data bundle being managed by this accumulator (including the primary bundle and each break group bundle), is updated with the data value being accumulated. If iteration is not currently enabled (i.e., we are operating in deferred mode, and the driving query has not enabled iteration for the current loop pass), this method returns immediately.
If a data bundle must be reset, because the result set being iterated entered a new break group for that bundle's break group category, this is done before the new data item is accumulated.
This method can be invoked even if the dataSource
is not an
instance of class Accumulator.ExternalDataSource
; this is needed because
the accumulator may be used standalone (by calling the
accumulate()
method) or used inside a DISPLAY with aggregate
phrase statement (and will be called by
GenericFrame
when it will display the
value).
The only situation when the dataSource
will be an instance of
class Accumulator.ExternalDataSource
is when the accumulator is used only
with a DISPLAY with aggregate phrase statement - in this case, the
default c'tor will be used to instantiate the accumulator.
datum
- The data item to be accumulated.public final java.util.Iterator<Resolvable> breakGroupKeys()
public final Resolvable getFirstBreakGroupKey()
null
break group key registered with
this accumulator. If no break group keys have been registered, return
null
.null
key or null
.public void reset()
public void ifEnded()
POSTPONE_UNKNOWN
public void postponeReset()
postpone(int)
with the parameter set to
POSTPONE_RESET
.public void postponeUnknown()
postpone(int)
with the parameter set to
POSTPONE_UNKNOWN
.public void iterate()
When a new iteration is performed, if this accumulator is retrieved before any more accumulation, then the reported value is the accumulation value for the enclosing block.
iterate
in interface Finalizable
public void finished()
finished
in interface Finalizable
public void deleted()
This is a no-op for accumulators.
deleted
in interface Finalizable
public void retry()
retry
in interface Finalizable
public void scopeFinished()
scopeFinished
in interface Scopeable
public void scopeDeleted()
This is a no-op for accumulators.
scopeDeleted
in interface Scopeable
public void scopeStart()
scopeStart
in interface Scopeable
public Undoable deepCopy()
public void assign(Undoable value)
protected BaseDataType createUnknown()
protected java.lang.Class<?> getType()
protected boolean isUninitialized()
true
if uninitialized, else false
.protected void resetData()
protected BaseDataType calculate(Resolvable breakGroupKey)
breakGroupKey
is retrieved and
is asked to calculate a result.breakGroupKey
- Key which identifies a particular break group category.protected abstract Accumulator.DataBundle createDataBundle()
private void accumulate(Resolvable key, boolean newGroup, BaseDataType datum)
key
- Data bundle key.newGroup
- true
to indicate that the current loop iteration
marks entry into a new break group; else false
.datum
- The data item to be accumulated.private void postpone(int what)
what
- The type of postponed action.protected boolean isUndoable()
isUndoable
in class LazyUndoable
true
, as the accumulators are always undoable.