Interface EssCubeView

All Superinterfaces:
EssGrid
All Known Implementing Classes:
EssCubeViewImpl

public interface EssCubeView extends EssGrid
A live, navigable ad hoc grid on a cube - the REST analog of the Java API's IEssCubeView. Every operation re-executes the view against the server and replaces the grid held by this view with the server's response.

This class describes actions and cells to the Essbase REST API and renders back whatever grid the engine returns - it does not model what a given action "should" do to the grid, or compute a request shaped to produce some intended result. The exceptions are all mechanical, not semantic - cases where the wire format itself has more than one field capable of naming a cell, and only one of them actually reaches the engine's implementation for a given kind of cell or action:

  • zoomOut(int, int), keepOnly(int, int) and removeOnly(int, int) are sent as a "ranges" request rather than "coordinates" like the other operations, because the server silently no-ops "coordinates" for those actions (200 OK, grid unchanged) - "ranges" is simply the only field that reaches their implementation at all.
  • A range is [startRow, startColumn, rowCount, columnCount], not two corners. Established live: on a grid of Qtr1..Qtr4, Year, a keepOnly at Qtr2 (row 3) sent as [3, 0, 3, 0] keeps Qtr2, Qtr3 and Qtr4 - three rows starting at row 3 - while [3, 0, 1, 1] keeps Qtr2 alone.
  • zoomIn(int, int) tries "ranges" first and falls back to "coordinates" only if the server rejects that shape outright (an engine-level error, not a 200). This isn't a guess based on inspecting the cell: "coordinates" turns out not to do a literal (row, col) grid lookup at all for a cell already on a real axis (as opposed to a POV placeholder dimension shown as a header but not yet on any axis) - it addresses POV placeholder dimensions by column, order-insensitively (coordinates [0,1], [1,0], [0,2], and [2,0] all landed on whichever POV dimension's column matched the nonzero value, never on the literal on-axis cell). Confirmed live: zooming in on "Year" (on the row axis at column 0) via "coordinates" silently expanded "Market" (an unrelated POV dimension) instead - not an error, just the wrong dimension, which is exactly why trying "ranges" first and reacting to an actual rejection is the only sound way to pick between the two: "coordinates" can't be trusted to fail loudly when it's wrong.
Beyond those field-selection adjustments, no attempt is made to reverse, expand, or otherwise infer "the range that will produce a particular outcome" - whatever the server does with the literal single-cell description is authoritative. In practice this means, for instance, that zoomOut(int, int) reliably collapses a cleanly-targeted dimension back to its total, but on a member row that the engine doesn't accept for that request shape (or once more than one dimension is zoomed in at once), it may do less than you'd expect, more than you'd expect, or return an engine-level error - that's the engine's answer to the given description, not a bug in this layer.

EssGrid.getCell(int, int), getCellType(int, int), setMembers(java.util.List<com.appliedolap.essbase.EssCubeView.MemberPlacement>), zoomIn(int, int), zoomOut(int, int), keepOnly(int, int), and refresh() are verified against a live server (see EssCubeViewIT) in at least their most direct case - the grid content is asserted to actually change, not just that the call doesn't throw.

removeOnly(int, int) is verified live, single cell and rectangle. It previously was not, and was documented here as unusable because every attempt answered "This operation would generate a nonsensical report" - that was the range format, not the action: a single cell sent as [row, col, row, col] describes a block of row by col cells, which for most positions really is nonsensical. It is ordinary once the range says what it means.

pivot(int, int) is verified live and documented on the method: it takes a source column and a destination column, not a pair of cells as the old four-argument form assumed.

pivotToPov(int, int) moves a dimension into the POV, and needs a grid with two column dimensions to do anything at all - see the method for what is established and what is still guesswork. Setting a *data* cell's value is not implemented at all yet - the same dirty-cell/submit mechanism setMembers(java.util.List<com.appliedolap.essbase.EssCubeView.MemberPlacement>) uses was tried against data positions too, but unlike member positions, the write never stuck even against a confirmed leaf-level intersection.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Interface
    Description
    static final record 
    A new value for one data cell.
    static enum 
    A coarse classification of a cell as either a data position or a member/label.
    static final record 
    Where a dimension sits in the grid: in the POV, on a top/column axis, or on a left/row axis.
    static final record 
    Ad hoc display/navigation preferences, mirroring the classic "Essbase Options" dialog to the extent the REST wire format supports.
    static enum 
    How indented member rows/columns are, mirroring the classic ad hoc "Indentation" setting.
    static final record 
    A member to place at a specific grid position, replacing whichever member currently occupies that spot on its row or column axis tier.
    static final class 
    A rectangle of cells, given as two corners in either order.
    static enum 
    The server's zoom-in behavior when a plain zoomIn(int, int) is used (no explicit member set).
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final String
    The alias table name that means "label members with their own names".
  • Method Summary

    Modifier and Type
    Method
    Description
    default int
    cellIndex(int row, int column)
    The flat index of a cell, which is how the pivot actions address one.
    The alias table the grid's labels are in.
    getCellType(int row, int col)
    Classifies the cell at the given position as EssCubeView.CellType.DATA or EssCubeView.CellType.MEMBER.
    Says where every dimension currently sits, as the server reports it rather than as the sheet of labels implies.
    Reads the current session's ad hoc display/navigation preferences.
    void
    keepOnly(int row, int col)
    Keeps only the member at the given position.
    void
    keepOnly(int fromRow, int fromCol, int toRow, int toCol)
    Keeps only the members in a rectangle of cells, dropping their siblings.
    void
    Keeps only the members in several rectangles at once, dropping their siblings.
    void
    pivot(int fromCell)
    Moves a dimension to the front of the row axis, which is what a pivot with no destination does.
    void
    pivot(int fromCell, int toCell)
    Moves a dimension to somewhere else in the grid.
    void
    pivotToPov(int fromCell, int toCell)
    Moves a dimension into the POV.
    void
    Re-executes the view as-is, picking up any data changes made since it was last retrieved.
    void
    removeOnly(int row, int col)
    Removes the member at the given position, keeping the rest.
    void
    removeOnly(int fromRow, int fromCol, int toRow, int toCol)
    Removes the members in a rectangle of cells, keeping their siblings.
    void
    Removes the members in several rectangles at once, keeping their siblings.
    void
    setAliasTable(String aliasTable)
    Sets the alias table this grid's members are labelled from.
    void
    setLayout(String[][] sheet, int headerRows, int leftColumns)
    Rewrites the grid as a sheet of labels of any shape, saying where the data region begins.
    void
    Rewrites the grid as a sheet of labels and asks the server to interpret it.
    void
    Replaces the members at the given positions, in one request.
    void
    Replaces the current session's ad hoc display/navigation preferences.
    void
    Writes values back into the cube, in one request.
    void
    zoomIn(int row, int col)
    Zooms in on the member at the given position, using the server's default zoom-in preference.
    void
    Zooms in on the members in several rectangles at once.
    void
    zoomOut(int row, int col)
    Zooms out from the member at the given position.
    void
    Zooms out of the members in several rectangles at once.

    Methods inherited from interface com.appliedolap.essbase.EssGrid

    getCell, getColumns, getRows
  • Field Details

    • NO_ALIASES

      static final String NO_ALIASES
      The alias table name that means "label members with their own names".

      A sentinel rather than a table: the grid API has no flag for turning aliases off, and a name the cube does not have is an error, so this is the only way to ask for member names. Exactly this spelling - None, NONE and nOnE are all rejected like any other unknown table, which is itself the evidence that the lowercase one is deliberate rather than a cube that happens to have a table by that name.

      See Also:
  • Method Details

    • zoomIn

      void zoomIn(int row, int col)
      Zooms in on the member at the given position, using the server's default zoom-in preference. See the class-level javadoc: this tries "ranges" first and only falls back to "coordinates" if the server itself rejects that shape - not a guess based on inspecting the cell, but a reaction to what the server says about the same literal click either way.
      Parameters:
      row - the row of the member to zoom in on
      col - the column of the member to zoom in on
    • zoomOut

      void zoomOut(int row, int col)
      Zooms out from the member at the given position. See the class-level javadoc: this describes the click as-is and returns whatever the server does with it, rather than computing a request shaped to force a particular collapse.
      Parameters:
      row - the row of the member to zoom out from
      col - the column of the member to zoom out from
    • zoomIn

      void zoomIn(List<EssCubeView.Range> ranges)
      Zooms in on the members in several rectangles at once.

      One request, so every member expands against the same grid. Zooming them one at a time would work from a grid that the previous zoom had already reshaped, and the positions would no longer mean what they meant when they were picked.

      Parameters:
      ranges - the rectangles to zoom in on, in any order
    • zoomOut

      void zoomOut(List<EssCubeView.Range> ranges)
      Zooms out of the members in several rectangles at once.
      Parameters:
      ranges - the rectangles to zoom out of, in any order
    • keepOnly

      void keepOnly(int row, int col)
      Keeps only the member at the given position. See the class-level javadoc: this describes the click as-is and returns whatever the server does with it, rather than computing a request shaped to force a particular outcome.
      Parameters:
      row - the row of the member to keep
      col - the column of the member to keep
    • keepOnly

      void keepOnly(int fromRow, int fromCol, int toRow, int toCol)
      Keeps only the members in a rectangle of cells, dropping their siblings.

      The corners may be given in either order. What reaches the server is the rectangle they describe - a range there is a start plus a row and column count, not a pair of corners, which is a distinction worth knowing about only if you are reading the wire format.

      Parameters:
      fromRow - one corner's row
      fromCol - one corner's column
      toRow - the other corner's row
      toCol - the other corner's column
    • keepOnly

      void keepOnly(List<EssCubeView.Range> ranges)
      Keeps only the members in several rectangles at once, dropping their siblings.

      The rectangles need not touch, and this is the point of the method: keeping the first and third of four quarters is not a rectangle, and sending two operations would drop everything outside the first before the second was asked. Verified live - [[2,0,1,1],[4,0,1,1]] against Qtr1..Qtr4, Year keeps Qtr1 and Qtr3.

      Parameters:
      ranges - the rectangles to keep, in any order
    • removeOnly

      void removeOnly(int row, int col)
      Removes the member at the given position, keeping the rest.

      Not currently verified to work - every coordinate/range tried against a live server returns "This operation would generate a nonsensical report." See the class-level javadoc.

      Parameters:
      row - the row of the member to remove
      col - the column of the member to remove
    • removeOnly

      void removeOnly(int fromRow, int fromCol, int toRow, int toCol)
      Removes the members in a rectangle of cells, keeping their siblings.
      Parameters:
      fromRow - one corner's row
      fromCol - one corner's column
      toRow - the other corner's row
      toCol - the other corner's column
    • removeOnly

      void removeOnly(List<EssCubeView.Range> ranges)
      Removes the members in several rectangles at once, keeping their siblings.
      Parameters:
      ranges - the rectangles to remove, in any order
    • pivot

      void pivot(int fromCell, int toCell)
      Moves a dimension to somewhere else in the grid.

      Both arguments are flat cell indices, counted across the grid row by row: row * getColumns() + column, which cellIndex(int, int) will work out. They are not row/column pairs and not column numbers. Oracle's own examples are the authority here - the documented pivotToPOV call sends [8, 2] for a four-column grid, meaning the cell at row 2 column 0 and the one at row 0 column 2.

      This is the general move: name any cell holding a dimension, name the cell where it should go, and the grid comes back with it there. The source may be in the POV or on either axis, and the destination decides which region it lands in.

       // On Sample.Basic's default grid - Year down the side, Measures across the top, three in the POV:
       view.pivot(view.cellIndex(0, 1), view.cellIndex(1, 1));   // Product from the POV onto the top axis
       view.pivot(view.cellIndex(1, 1), view.cellIndex(0, 1));   // Measures off the top axis into the POV
       view.pivot(view.cellIndex(1, 1), view.cellIndex(1, 0));   // Measures off the top onto the row axis
       

      It refuses rather than corrupting the grid: taking the last dimension off an axis answers "Cannot pivot last row", and naming a data cell answers "Pivot starting point cannot be determined".

      Parameters:
      fromCell - the flat index of a cell holding the dimension to move
      toCell - the flat index of the cell to move it to
    • pivot

      void pivot(int fromCell)
      Moves a dimension to the front of the row axis, which is what a pivot with no destination does.

      The form Oracle's documentation shows, with a single coordinate. Works from the POV or from the column axis.

      Parameters:
      fromCell - the flat index of a cell holding the dimension to move
    • pivotToPov

      void pivotToPov(int fromCell, int toCell)
      Moves a dimension into the POV.

      Flat cell indices again, like pivot(int, int). The destination names where in the POV row it should sit.

      The axis it leaves cannot be emptied: pivoting away the only dimension on the row axis answers "Cannot pivot last row". Where the grid can afford it, Essbase fills the gap by bringing a POV dimension back out - pivoting the second of two row dimensions away leaves the first in place and pulls something from the POV in beside it.

      Parameters:
      fromCell - the flat index of a cell holding the dimension to move
      toCell - the flat index of the POV cell to move it to
    • cellIndex

      default int cellIndex(int row, int column)
      The flat index of a cell, which is how the pivot actions address one.
      Parameters:
      row - the cell's row
      column - the cell's column
      Returns:
      row * getColumns() + column
    • setLayout

      void setLayout(List<String> cells)
      Rewrites the grid as a sheet of labels and asks the server to interpret it.

      This is how a grid is rearranged. Not the pivot actions - this. Write the dimension names where you want them and send it; Essbase reads the sheet and answers with the grid that layout describes, data and all. It is what Smart View does, and it is why every operation carries the whole grid in the request rather than a handle to one.

       before                            after
       .      Product Market Scenario     .         Product Market Scenario
       .      Measures                    .         Year
       Year   105522                      Measures  105522
      
       view.setLayout(List.of(
               "", "Product", "Market", "Scenario",
               "", "Year",    "",       "",
               "Measures", "", "",      ""));
       

      That is the row-for-column rotation the pivot(int, int) action cannot do, and moving a POV dimension to the top works the same way. Reordering within an axis is the same idea: write the names in the order you want them.

      The sheet is row-major and must have exactly EssGrid.getRows() times EssGrid.getColumns() entries - the server indexes it by position and answers "Index n out of bounds" if it is short. Beyond that the rules are the engine's: a layout it cannot read comes back as "Your report heading cannot be interpreted", and a sheet whose shape implies more or fewer header rows than the one sent may come back reinterpreted rather than refused.

      Parameters:
      cells - the grid's labels, row by row, with empty strings for the blanks
    • setLayout

      void setLayout(String[][] sheet, int headerRows, int leftColumns)
      Rewrites the grid as a sheet of labels of any shape, saying where the data region begins.

      The difference from setLayout(List) is that this one can change the grid's structure - how many dimensions sit on the left, on top, and in the POV - and not just the order of what is already there. The single-argument form keeps the current grid's member/data partition and only rewrites the labels, which is enough to rotate a dimension from the left to the top but not to move a dimension between regions, because the server reads a label that lands in what it still believes is a data position as a heading it cannot interpret, and quietly answers with everything it could not place pushed into the POV.

      A grid has three regions, and headerRows and leftColumns are exactly the two numbers that divide them:

                       <- leftColumns ->
                    +----------------+----------------------+
        headerRows  |     blank      |  POV row, then one    |
                    |                |  row per top axis     |
                    +----------------+----------------------+
                    |  one column    |                       |
                    |  per left axis |      data region      |
                    +----------------+----------------------+
       

      Every cell above headerRows or left of leftColumns is a label. Of the rest, a cell carries data where both its column and its row do: a column does where the last header row - the innermost top axis, which names one member per data column - names something there, and a row does where the left axis names something on it. Leave the data cells empty; the server fills them.

      Deciding it per row and column rather than as a rectangle is what lets a sheet have a blank row or column ruled through it, the way a real template does.

      The POV does not have to be there: a grid with every dimension on an axis simply has no POV row, and headerRows is then just the number of top axes.

      Parameters:
      sheet - the grid's labels, row by row, with empty strings for blanks and data cells
      headerRows - how many rows precede the data region - the POV row, if there is one, plus one row per top/column axis
      leftColumns - how many columns the left/row axes occupy
    • getPlacements

      Says where every dimension currently sits, as the server reports it rather than as the sheet of labels implies.

      This is the authoritative read of a grid's structure and the one worth asserting against: a layout the server reinterpreted looks perfectly reasonable in the returned labels and is only obvious here, as a dimension that came back in the POV when it was asked for on an axis.

      The count is the cube's dimension count, always - every dimension is in exactly one region.

      Returns:
      one placement per dimension, in the server's order
    • getAliasTable

      String getAliasTable()
      The alias table the grid's labels are in.

      Worth asking, because a grid takes member names on the way in and hands back aliases: send a layout saying ALL and the answer says All Accounts. Anything comparing what it asked for against what it got needs to know which table did that.

      Returns:
      the alias table's name, such as Default
    • setAliasTable

      void setAliasTable(String aliasTable)
      Sets the alias table this grid's members are labelled from.

      Per request, not a stored preference: the alias table rides along on every grid action, so two grids open on the same connection can be reading different ones. That is unlike the rest of the grid preferences, which Essbase keeps per session and therefore shares between every grid a user has open.

      Null means the server's default, which is the Default table. A name the cube does not have is not ignored - the next action fails - so a name should come from EssCube.getAliasTables() or be NO_ALIASES. getAliasTable() reports what the last response was labelled from.

      Parameters:
      aliasTable - the alias table to label members from, or null for the server's default
    • refresh

      void refresh()
      Re-executes the view as-is, picking up any data changes made since it was last retrieved.
    • getCellType

      EssCubeView.CellType getCellType(int row, int col)
      Classifies the cell at the given position as EssCubeView.CellType.DATA or EssCubeView.CellType.MEMBER. Based on whether the cell's (undocumented by Oracle) types code is exactly "2" - verified, against a live server, to mark the data-cell position consistently whether or not the cell currently holds a real value (confirmed both with real data present, and later with every data cell blank/missing after a database reload). Deliberately compares by equality rather than treating types as a bitmask: code "7" (blank filler cells) also has bit 2 set, but is not a data position, so a bitwise check misclassifies it.
      Parameters:
      row - the row of the cell
      col - the column of the cell
      Returns:
      the cell's type
    • setMembers

      void setMembers(List<EssCubeView.MemberPlacement> placements)
      Replaces the members at the given positions, in one request. Verified live against a real server: this retargets an existing member-position on an existing axis tier to a different member (e.g. changing which Product shows in a given row) - it does not grow the grid to add rows or columns beyond what's already there, and the given member name must be valid for that position's dimension.
      Parameters:
      placements - the members to place, and where
    • submitData

      void submitData(List<EssCubeView.CellEdit> edits)
      Writes values back into the cube, in one request.

      The same mechanism as setMembers(java.util.List<com.appliedolap.essbase.EssCubeView.MemberPlacement>), which is not a coincidence: Essbase's grid has one submit action and what it writes depends on which cells were marked dirty. Members go in as labels, data as values, and both travel as edits to the grid the server already has.

      Positional, so there is nothing to resolve: the server knows what intersection each cell of its own grid is. Submitting a member cell's position is not rejected here but is not what this is for - use setMembers(java.util.List<com.appliedolap.essbase.EssCubeView.MemberPlacement>).

      The grid is refreshed by the submit, as every operation is, so a value the cube transforms on the way in - a member with a formula, an aggregation, a cell the writer cannot write - reads back as whatever it actually became rather than as what was sent.

      The empty string clears a cell; #Missing does not. Established live: submitting "" leaves the intersection with no value, and submitting #Missing - the classic spreadsheet word for the same thing, in any spelling - writes a literal zero, which is a different number that aggregates. Anything passing a value a person typed has to translate that word itself; this does not, because guessing which strings are words rather than values is the caller's business and getting it wrong silently corrupts a cube.

      Parameters:
      edits - the cells to write, and what to write in them
    • getPreferences

      EssCubeView.GridPreferences getPreferences()
      Reads the current session's ad hoc display/navigation preferences.
    • setPreferences

      void setPreferences(EssCubeView.GridPreferences preferences)
      Replaces the current session's ad hoc display/navigation preferences. See EssCubeView.GridPreferences: this takes effect for every subsequent grid operation on this connection, not just this view, and persists until changed again.
      Parameters:
      preferences - the preferences to apply