Saiku导出excel指标列无线条以及0与空值显示问题(三十二)
Saiku导出excel指标列无线条以及0与空值显示问题
描述: 数据库中字段值为0 ,与数据库中字段值为 null 时 ,saiku会将为0 以及为 null 的数据都不展示出来,但是我们其实希望数据库中为0 的数据展示位0 ,为null的时候就是null 可以不展示。
table展示0与null有两个点需要处理:
1. 展示在页面上的table数据
2.导出的excel中的数据(导出excel还有个格式问题,指标信息没有线条分割,维度信息正常)
1.我们先来解决一下怎么让(0与null)数据在页面中展示正常
在saiku的SaikuTableRenderer.js文件中 添加為0的數據顯示問題 (修改後為0的DATA_CELL也會正常顯示為0),line825-828
if (val == " " || val == '') {
val=0;
}
添加位置可参考下图: (SaikuTableRenderer.js)
后台查询接口对于0值返回的结果是 " ",所以我就用了以上判断,可自己debug调试哦。
2.导出excel数据时问题处理 后台导出excel使用POI插件
>>>添加线条:
ExcelWorksheetBuilder.java ---> line 154 添加 setCellBordersColor(numberCS);
添加位置可参考下图: (ExcelWorksheetBuilder.java)
>>>处理0与null值的问题
1.在FlattenedCellSetFormatter.java中修改cellvalue的值为0.
(这里if里面的判断条件是我debug出来的哦,当数据为空的时候,cellvalue的值时空的,当数据为0的时候,cellvalue的值是 | |style=white ,当数据有其他值的时候(例如33),cellvalue的值为 |33 |style=white)
2.接下来就是导出时的样式设置了
在ExcelWorksheetBuilder.java中添加新的样式设定,专门对于cellvalue的值为0.(红色框框中为添加内容)
3.在 ExcelWorksheetBuilder.java的 initCellStyles()方法中添加对 number0CS的初始化
4.在 ExcelWorksheetBuilder.java的addExcelTableRows()方法中对0值的添加自定义的样式处理。
(这里说明一下为什么还要判断一下 formatString的值是否为 #,### ,因为有其他样式的时候0值不会被忽略掉,比如 formatString= #,#00.00% 这种百分比形式时,0值对应的值就会为 0.00%)
红色框框为改动后的内容,之前这个位置的代码为: applyCellFormating(cell,dataCell);
至此,我们对于table渲染0值与空值的问题就处理好了,导出的excel指标数据也会有线条啦~ 重新打包编译,启动saiku即可~
ps: 顺便提一下iframe嵌入saiku url的问题,会有那种偶然性的 bug,大概是网络问题 导致js加载出问题了,然后嵌入的iframe展示高度就会变得很短。
处理方案: 在saiku的 saiku.table.css中設置.workspace_results 樣式的最小高度為708 min-height
最后提供完整的文件信息:
SaikuTableRenderer.js
function SaikuTableRenderer(data, options) {
this._data = data;
this._options = _.extend({}, SaikuRendererOptions, options);
} function getAxisLevelsName(data, axisName) {
var queryData = data.query.queryModel.axes[axisName].hierarchies;
var len = queryData.length;
var arrLevels = []; for (var i = 0; i < len; i++) {
for (var level in queryData[i].levels) {
if (queryData[i].levels.hasOwnProperty(level)) {
if (Settings.COLUMN_TITLE_TABLE_USE_LEVEL_CAPTION_NAME) {
arrLevels.push(queryData[i].levels[level].caption);
}
else {
arrLevels.push(level);
}
}
}
} return arrLevels;
} function setStyleNegativeNumber(value) {
var className = ''; if (Settings.STYLE_NEGATIVE_NUMBER && parseFloat(value) < 0) {
className = ' style_negative_number ';
} return className;
} function getAxisSize(data, axisName) {
var queryData = data.query.queryModel.axes[axisName].hierarchies;
var len = queryData.length;
var axisSize = 0; for (var i = 0; i < len; i++) {
axisSize += _.size(queryData[i].levels);
} return axisSize;
} function getDomColumnsLevelsName(htmlObject) {
var $htmlObject = $(htmlObject.closest('.workspace')
.find('.workspace_fields')
.find('.columns.axis_fields')
.find('.hierarchy')
.find('.d_level'));
var arrLevels = []; $.each($htmlObject, function(key, level) {
if ($(level).attr('style') === 'display: list-item;') {
if (Settings.COLUMN_TITLE_TABLE_USE_LEVEL_CAPTION_NAME) {
arrLevels.push($(level).find('.level').attr('title'));
}
else {
arrLevels.push($(level).find('.level').attr('level'));
}
}
}); return arrLevels;
} /*table render method*/
SaikuTableRenderer.prototype.render = function(data, options) {
var self = this;
if (data) {
this._data = data;
}
if (options) {
this._options = _.extend({}, SaikuRendererOptions, options);
} if (typeof this._data == "undefined") {
return;
} if (this._data != null && this._data.error != null) {
return;
}
if (this._data == null || (this._data.cellset && this._data.cellset.length === 0)) {
return;
} this.hideEmpty = this._options.hideEmpty; if (this._options.htmlObject) {
// $(this._options.htmlObject).stickyTableHeaders("destroy"); // in case we have some left over scrollers
if (self._options.hasOwnProperty('batch')) {
$(self._options.htmlObject).parent().parent().unbind('scroll');
} _.defer(function(that) {
if (self._options.hasOwnProperty('batch') && !self._options.hasOwnProperty('batchSize')) {
self._options['batchSize'] = 1000;
} // the key method to render data by table form. 20190423
var html = self.internalRender(self._data, self._options);
$(self._options.htmlObject).html(html);
// Render the totals summary
$('#totals_summary').remove(); // Remove one previous totals div, if present
$(self._options.htmlObject).after(self.renderSummary(data)); // Render the new summary // $(self._options.htmlObject).stickyTableHeaders( { container: self._options.htmlObject.parent().parent(), fixedOffset: self._options.htmlObject.parent().parent().offset().top }); _.defer(function(that) {
if (self._options.hasOwnProperty('batch') && self._options.hasBatchResult) {
var batchRow = 0;
var batchIsRunning = false;
var batchIntervalSize = self._options.hasOwnProperty('batchIntervalSize') ? self._options.batchIntervalSize : 20;
var batchIntervalTime = self._options.hasOwnProperty('batchIntervalTime') ? self._options.batchIntervalTime : 20; var len = self._options.batchResult.length; var batchInsert = function() {
// maybe add check for reach table bottom - ($('.workspace_results').scrollTop() , $('.workspace_results table').height()
if (!batchIsRunning && len > 0 && batchRow < len) {
batchIsRunning = true;
var batchContent = "";
var startb = batchRow;
for (var i = 0; batchRow < len && i < batchIntervalSize ; i++, batchRow++) {
batchContent += self._options.batchResult[batchRow];
}
if (batchRow > startb) {
$(self._options.htmlObject).append( $(batchContent));
}
batchIsRunning = false;
}
if (batchRow >= len) {
$(self._options.htmlObject).parent().parent().unbind('scroll');
}
}; var lazyBatchInsert = _.debounce(batchInsert, batchIntervalTime);
$(self._options.htmlObject).parent().parent().scroll(function () {
lazyBatchInsert();
});
}
});
return html;
});
} else {
var html = this.internalRender(this._data, self._options);
return html;
} }; SaikuTableRenderer.prototype.clear = function(data, options) {
var self = this;
if (this._options && this._options.htmlObject && this._options.hasOwnProperty('batch')) {
$(self._options.htmlObject).parent().parent().unbind('scroll');
} }; SaikuTableRenderer.prototype.processData = function(data, options) {
this._hasProcessed = true;
}; function genTotalDataCells(currentIndex, cellIndex, scanSums, scanIndexes, lists) {
var contents = '';
var lists = lists[ROWS]; for (var i = scanSums.length - 1; i >= 0; i--) {
if (currentIndex == scanSums[i]) {
var currentListNode = lists[i][scanIndexes[i]];
for (var m = 0; m < currentListNode.cells.length; m++) {
contents += '<td class="data total">' + currentListNode.cells[m][cellIndex].value + '</td>';
} scanIndexes[i]++;
if (scanIndexes[i] < lists[i].length)
scanSums[i] += lists[i][scanIndexes[i]].width;
}
} return contents;
} function genTotalHeaderCells(currentIndex, bottom, scanSums, scanIndexes, lists, wrapContent) {
var contents = '';
for (var i = bottom; i >= 0; i--) {
if (currentIndex == scanSums[i]) {
var currentListNode = lists[i][scanIndexes[i]];
var cssClass;
if (i == 0 && bottom == 1)
cssClass = "col";
else if (i == bottom)
cssClass = "col_total_corner";
else if (i == bottom - 1 && currentListNode.captions)
cssClass = "col_total_first";
else cssClass = "col_null"; for (var m = 0; m < currentListNode.cells.length; m++) {
var text = ' ';
if (bottom == lists.length - 1) {
if (currentListNode.captions) {
text = lists[i][scanIndexes[i]].captions[m];
}
if (i == 0 && scanIndexes[i] == 0) {
if (currentListNode.captions)
text += " ";
else text = "";
text += (wrapContent ? "<span class='i18n'>Grand Total</span>" : "Grand Total");
}
}
contents += '<th class="' + cssClass + '">'
+ (wrapContent ? '<div>' + text + '</div>' : text ) + '</th>';
}
scanIndexes[i]++;
if (scanIndexes[i] < lists[i].length)
scanSums[i] += lists[i][scanIndexes[i]].width;
}
}
return contents;
} function totalIntersectionCells(currentIndex, bottom, scanSums, scanIndexes, lists) {
var contents = '';
for (var i = bottom; i >= 0; i--) {
if (currentIndex == scanSums[i]) {
var currentListNode = lists[i][scanIndexes[i]];
var cssClass = "data total";
for (var m = 0; m < currentListNode.cells.length; m++) {
var text = ' ';
contents += '<td class="' + cssClass + '">' + text + '</td>';
}
scanIndexes[i]++;
if (scanIndexes[i] < lists[i].length)
scanSums[i] += lists[i][scanIndexes[i]].width;
}
}
return contents;
} function isNextTotalsRow(currentIndex, scanSums, scanIndexes, totalsLists, wrapContent) {
var colLists = totalsLists[COLUMNS];
var colScanSums = scanSums[COLUMNS];
var colScanIndexes = scanIndexes[COLUMNS];
var bottom = colLists.length - 2;
var contents = -1;
for (var i = bottom; i >= 0; i--) {
if (currentIndex == colScanSums[i]) {
for (var m = 0; m < colLists[i][colScanIndexes[i]].cells.length; m++) {
contents += '<tr>';
for (var j = 0; j <= bottom; j++) {
var cssClass;
var text = ' ';
if (i == 0 && j == 0)
cssClass = 'row';
else if (i == j + 1){
cssClass = 'row_total_corner';
return j;
}
else if (i == j && colLists[i][colScanIndexes[i]].captions) {
cssClass = 'row_total_first';
} else if (i < j + 1)
cssClass = 'row_total';
else
cssClass = 'row_null';
if (j == bottom ) {
if (colLists[i][colScanIndexes[i]].captions) {
text = colLists[i][colScanIndexes[i]].captions[m];
}
if (i == 0 && colScanIndexes[i] == 0) {
if (colLists[i][colScanIndexes[i]].captions)
text += " ";
else text = "";
text += (wrapContent ? "<span class='i18n'>Grand Total</span>" : "Grand Total");
}
} }
}
}
}
return -1;
} function genTotalHeaderRowCells(currentIndex, scanSums, scanIndexes, totalsLists, wrapContent) {
var colLists = totalsLists[COLUMNS];
var colScanSums = scanSums[COLUMNS];
var colScanIndexes = scanIndexes[COLUMNS];
var bottom = colLists.length - 2;
var contents = '';
for (var i = bottom; i >= 0; i--) {
if (currentIndex == colScanSums[i]) {
for (var m = 0; m < colLists[i][colScanIndexes[i]].cells.length; m++) {
contents += '<tr>';
for (var j = 0; j <= bottom; j++) {
var cssClass;
var text = ' ';
if (i == 0 && j == 0)
cssClass = 'row';
else if (i == j + 1)
cssClass = 'row_total_corner';
else if (i == j && colLists[i][colScanIndexes[i]].captions) {
cssClass = 'row_total_first';
} else if (i < j + 1)
cssClass = 'row_total';
else
cssClass = 'row_null';
if (j == bottom ) {
if (colLists[i][colScanIndexes[i]].captions) {
text = colLists[i][colScanIndexes[i]].captions[m];
}
if (i == 0 && colScanIndexes[i] == 0) {
if (colLists[i][colScanIndexes[i]].captions)
text += " ";
else text = "";
text += (wrapContent ? "<span class='i18n'>Grand Total</span>" : "Grand Total");
}
}
contents += '<th class="' + cssClass + '">'
+ (wrapContent ? '<div>' + text + '</div>' : text ) + '</th>'; } var scanIndexes = {};
var scanSums = {}; if (totalsLists[ROWS]) {
for (var z = 0; z < totalsLists[ROWS].length; z++) {
scanIndexes[z] = 0;
scanSums[z] = totalsLists[ROWS][z][scanIndexes[z]].width;
}
} for (var k = 0; k < colLists[i][colScanIndexes[i]].cells[m].length; k++) {
contents += '<td class="data total">' + colLists[i][colScanIndexes[i]].cells[m][k].value + '</td>'; if (totalsLists[ROWS]) {
contents += totalIntersectionCells(k + 1, totalsLists[ROWS].length - 1, scanSums, scanIndexes, totalsLists[ROWS]);
}
} contents += '</tr>';
} colScanIndexes[i]++; if (colScanIndexes[i] < colLists[i].length) {
colScanSums[i] += colLists[i][colScanIndexes[i]].width;
}
}
}
return contents;
} var ROWS = "ROWS";
var COLUMNS = "COLUMNS"; function nextParentsDiffer(data, row, col) {
while (row-- > 0) {
if (data[row][col].properties.uniquename != data[row][col + 1].properties.uniquename)
return true;
}
return false;
} function topParentsDiffer(data, row, col) {
while (col-- > 0)
if (data[row][col].properties.uniquename != data[row - 1][col].properties.uniquename)
return true;
return false;
} /**
* This function is intended to traverse the totals arrays and cleanup empty
* totals. This will optimize the query result on screen, displaying just the
* needed cells.
* @param dirs The direction array ['ROWS', 'COLUMNS']
* @param totalsLists The totals from allData.rowTotalsLists and allData.colTotalsLists.
*/
function cleanupTotals(dirs, totalsLists) {
// For each direction (ROWS/COLUMNS)
for (var dirIndex = 0; dirIndex < dirs.length; dirIndex++) {
var dir = dirs[dirIndex]; // If there are defined totals
if (totalsLists[dir]) {
var isEmpty = true; // A flag to indicate if this total is empty
for (var row = 0; row < totalsLists[dir].length; row++) {
var totalsInfoArray = totalsLists[dir][row];
for (var totalIndex = 0; totalIndex < totalsInfoArray.length; totalIndex++) {
var cells = totalsLists[dir][row][totalIndex].cells;
for (var cellIndex = 0; cellIndex < cells.length; cellIndex++) {
var cellArray = cells[cellIndex];
// For each total cell
for (var i = 0; i < cellArray.length; i++) {
var cell = cellArray[i];
// If it contains a value different from empty
if (cell.value !== '-') {
isEmpty = false; // So, this total is not empty
}
}
}
}
} if (isEmpty) { // If this total is empty
totalsLists[dir] = null; // Remove it
}
}
}
} /*the main method to render data by table form. 20190423*/
SaikuTableRenderer.prototype.internalRender = function(allData, options) {
var tableContent = "";
var rowContent = "";
var data = allData.cellset; var newRowContent = '';
var arrRowData = [];
var objRowData = []; var table = data ? data : [];
var colSpan;
var colValue;
var isHeaderLowestLvl;
var isBody = false;
var firstColumn;
var isLastColumn, isLastRow;
var nextHeader;
var processedRowHeader = false;
var lowestRowLvl = 0;
var rowGroups = [];
var batchSize = null;
var batchStarted = false;
var isColHeader = false, isColHeaderDone = false;
var resultRows = [];
var wrapContent = true;
if (options) {
batchSize = options.hasOwnProperty('batchSize') ? options.batchSize : null;
wrapContent = options.hasOwnProperty('wrapContent') ? options.wrapContent : true;
}
var totalsLists = {};
totalsLists[COLUMNS] = allData.rowTotalsLists;
totalsLists[ROWS] = allData.colTotalsLists; var scanSums = {};
var scanIndexes = {}; var dirs = [ROWS, COLUMNS]; var hasMeasures = allData.query && allData.query.queryModel && allData.query.queryModel.details
? allData.query.queryModel.details.measures.length
: 0; if (typeof this._options.htmlObject === 'object' &&
Settings.ALLOW_AXIS_COLUMN_TITLE_TABLE &&
hasMeasures > 0 &&
allData.query.type === 'QUERYMODEL' &&
allData.query.queryModel.details.axis === 'COLUMNS' &&
allData.query.queryModel.details.location === 'BOTTOM') { var arrColumnTitleTable = getAxisLevelsName(allData, COLUMNS);
var arrDomColumnTitleTable = getDomColumnsLevelsName(this._options.htmlObject);
var colspanColumnTitleTable = getAxisSize(allData, ROWS);
var auxColumnTitleTable = 0; if (arrColumnTitleTable.length === arrDomColumnTitleTable.length) {
arrColumnTitleTable = arrDomColumnTitleTable;
}
else {
arrColumnTitleTable = _.intersection(arrDomColumnTitleTable, arrColumnTitleTable);
}
} for (var i = 0; i < dirs.length; i++) {
scanSums[dirs[i]] = new Array();
scanIndexes[dirs[i]] = new Array();
} // Here we cleaup the empty totals
cleanupTotals(dirs, totalsLists); if (totalsLists[COLUMNS]) {
for (var i = 0; i < totalsLists[COLUMNS].length; i++) {
scanIndexes[COLUMNS][i] = 0;
scanSums[COLUMNS][i] = totalsLists[COLUMNS][i][scanIndexes[COLUMNS][i]].width;
}
} var headerFlag=true;// add this flag to solve the bug when same data to merge。 20190423
var cubename = allData.query.cube.name;
for (var row = 0, rowLen = table.length; row < rowLen; row++) {
var rowShifted = row - allData.topOffset;
colSpan = 1;
colValue = "";
isHeaderLowestLvl = false;
isLastColumn = false;
isLastRow = false;
isColHeader = false;
var headerSame = false; if (totalsLists[ROWS]) {
for (var i = 0; i < totalsLists[ROWS].length; i++) {
scanIndexes[ROWS][i] = 0;
scanSums[ROWS][i] = totalsLists[ROWS][i][scanIndexes[ROWS][i]].width;
}
} rowWithOnlyEmptyCells = true; //默认行数据为空时不展展示,如果改回来 将此条值置为true即可 //var paramsURI = Saiku.URLParams.paramsURI(); //get the param from url
//var showEmptyRows = paramsURI.showEmptyRows;
//if(showEmptyRows != null && showEmptyRows != undefined && showEmptyRows != '' && showEmptyRows=='yes') {
if(cubename == "SummaryKPI_2018_ext" ||cubename == "SummaryKPI_2019_ext" ||cubename == "SummaryKPI_2019_Dynamic"){
rowWithOnlyEmptyCells = false; //設置行數據為空的時候也顯示數據! 20190425 for summaryKPI Data
}
rowContent = "<tr>";
var header = null; if (row === 0) {
rowContent = "<thead>" + rowContent;
} if (typeof this._options.htmlObject === 'object' &&
Settings.ALLOW_AXIS_COLUMN_TITLE_TABLE &&
hasMeasures > 0 &&
allData.query.type === 'QUERYMODEL' &&
allData.query.queryModel.details.axis === 'COLUMNS' &&
allData.query.queryModel.details.location === 'BOTTOM' &&
auxColumnTitleTable < arrColumnTitleTable.length) { rowContent += '<th class="row_header" style="text-align: right;" colspan="' + colspanColumnTitleTable + '" title="' + arrColumnTitleTable[auxColumnTitleTable] + '">'
+ (wrapContent ? '<div>' + arrColumnTitleTable[auxColumnTitleTable] + '</div>' : arrColumnTitleTable[auxColumnTitleTable])
+ '</th>'; auxColumnTitleTable += 1;
} //循環遍歷data 將帶小數的年、月去掉小數位; 例如 2018.0 轉換為 2018
for (var col = 0, colLen = table[row].length; col < colLen; col++) {
var tmpRow = data[row][col];
if(tmpRow.properties.hierarchy=="[年].[年]" || tmpRow.properties.hierarchy=="[月].[月]"){
var tmpValue = tmpRow.value;
if(tmpValue != null && tmpValue.indexOf('.')>-1){
tmpValue = tmpValue.substr(0,tmpValue.indexOf('.'));//去掉小數位
data[row][col].value = tmpValue;
}
}
} for (var col = 0, colLen = table[row].length; col < colLen; col++) { var colShifted = col - allData.leftOffset;
header = data[row][col]; if (header.type === "COLUMN_HEADER") {
isColHeader = true;
} // If the cell is a column header and is null (top left of table)
if (header.type === "COLUMN_HEADER" && header.value === "null" && (firstColumn == null || col < firstColumn)) {
if (((!Settings.ALLOW_AXIS_COLUMN_TITLE_TABLE || (Settings.ALLOW_AXIS_COLUMN_TITLE_TABLE && allData.query.queryModel.details.location !== 'BOTTOM')) || hasMeasures === 0) ||
allData.query.type === 'MDX') {
rowContent += '<th class="all_null"> </th>';
}
} // If the cell is a column header and isn't null (column header of table)
else if (header.type === "COLUMN_HEADER") {
if (firstColumn == null) {
firstColumn = col;
}
if (table[row].length == col+1)
isLastColumn = true;
else
nextHeader = data[row][col+1]; if (isLastColumn) {
// Last column in a row...
if (header.value == "null") {
rowContent += '<th class="col_null"> </th>';
} else {
if (totalsLists[ROWS])
colSpan = totalsLists[ROWS][row + 1][scanIndexes[ROWS][row + 1]].span;
rowContent += '<th class="col" style="text-align: center;" colspan="' + colSpan + '" title="' + header.value + '">'
+ (wrapContent ? '<div rel="' + row + ":" + col +'">' + header.value + '</div>' : header.value)
+ '</th>';
} } else {
// All the rest...
var groupChange = (col > 1 && row > 1 && !isHeaderLowestLvl && col > firstColumn) ?
data[row-1][col+1].value != data[row-1][col].value || data[row-1][col+1].properties.uniquename != data[row-1][col].properties.uniquename
: false; var maxColspan = colSpan > 999 ? true : false;
if (header.value != nextHeader.value || nextParentsDiffer(data, row, col) || isHeaderLowestLvl || groupChange || maxColspan) {
if (header.value == "null") {
rowContent += '<th class="col_null" colspan="' + colSpan + '"> </th>';
} else {
if (totalsLists[ROWS])
colSpan = totalsLists[ROWS][row + 1][scanIndexes[ROWS][row + 1]].span;
rowContent += '<th class="col" style="text-align: center;" colspan="' + (colSpan == 0 ? 1 : colSpan) + '" title="' + header.value + '">'
+ (wrapContent ? '<div rel="' + row + ":" + col +'">' + header.value + '</div>' : header.value)
+ '</th>';
}
colSpan = 1;
} else {
colSpan++;
}
}
if (totalsLists[ROWS])
rowContent += genTotalHeaderCells(col - allData.leftOffset + 1, row + 1, scanSums[ROWS], scanIndexes[ROWS], totalsLists[ROWS], wrapContent);
} // If the cell is a row header and is null (grouped row header)
else if (header.type === "ROW_HEADER" && header.value === "null") {
rowContent += '<th class="row_null"> </th>';
} // If the cell is a row header and isn't null (last row header)
else if (header.type === "ROW_HEADER") {
if (lowestRowLvl == col)
isHeaderLowestLvl = true;
else
nextHeader = data[row][col+1]; var previousRow = data[row - 1];
var nextRow = data[row + 1];
// when same set fixed value is false ,It means the same data will not merge。table data will show row by row.20190423
//var same=false; /*judge the current value and previousRow value,
if equals ,all set comeback,set the headerFlag is true,
we can judge the data as usual. 20190423*/
if(header.value !== previousRow[col].value){
headerFlag =true;
} /*judge the tableContent include value or not, if include ,
set the headerFlag value is true to avoid repeat datas showed in table.20190423*/
if(tableContent.indexOf(header.value) > -1 ){
headerFlag =true;
} /*add headerFlag to judge the data is same ,then control the data merge wheather or not.20190423 */
var same = !headerSame && !isHeaderLowestLvl && (col == 0 || !topParentsDiffer(data, row, col)) && header.value === previousRow[col].value && headerFlag;
headerSame = !same;
var sameAsPrevValue = false;
if(Settings.ALLOW_TABLE_DATA_COLLAPSE){
if (row > 0 && row < rowLen - 1) {
if (totalsLists[ROWS] == null || (col <= colLen - totalsLists[ROWS].length - 1)) {
var checkOther = true;
if (totalsLists[COLUMNS] && rowShifted >= 0 && col <= isNextTotalsRow(rowShifted + 1, scanSums, scanIndexes, totalsLists, wrapContent)) {
sameAsPrevValue = true;
checkOther = false;
}
if (checkOther && nextRow[col].value == header.value) {
if (col > 0) {
for (var j = 0; j < col; j++) {
if (nextRow[j].value == data[row][j].value) {
sameAsPrevValue = true;
} else {
sameAsPrevValue = false;
break;
}
}
} else {
sameAsPrevValue = true;
}
}
}
} else if(row > 0 && row == rowLen - 1) {
if (totalsLists[COLUMNS] && rowShifted >= 0 && col <= isNextTotalsRow(rowShifted + 1, scanSums, scanIndexes, totalsLists, wrapContent)) {
sameAsPrevValue = true;
}
}
}
var value = (same ? "<div> </div>" : '<div rel="' + row + ":" + col + '">'
+ (sameAsPrevValue && Settings.ALLOW_TABLE_DATA_COLLAPSE ? '<span class="expander expanded" style="cursor: pointer;">▼</span>' : '' ) + header.value + '</div>');
if (!wrapContent) {
value = (same ? " " : header.value );
}
var tipsy = "";
/* var tipsy = ' original-title="';
if (!same && header.metaproperties) {
for (key in header.metaproperties) {
if (key.substring(0,1) != "$" && key.substring(1,2).toUpperCase() != key.substring(1,2)) {
tipsy += "<b>" + safe_tags_replace(key) + "</b> : " + safe_tags_replace(header.metaproperties[key]) + "<br>";
}
}
}
tipsy += '"';
*/
var cssclass = (same ? "row_null" : "row");
var colspan = 0; if (!isHeaderLowestLvl && (typeof nextHeader == "undefined" || nextHeader.value === "null")) {
colspan = 1;
var group = header.properties.dimension;
var level = header.properties.level;
var groupWidth = (group in rowGroups ? rowGroups[group].length - rowGroups[group].indexOf(level) : 1);
for (var k = col + 1; colspan < groupWidth && k <= (lowestRowLvl+1) && data[row][k] !== "null"; k++) {
colspan = k - col;
}
col = col + colspan -1;
} /*when the content is to long ,we will set new line to show it.*/
// eg value: <div rel="3:0">新業務及保單行政部</div>
if(cssclass == "row" && value.length>0){
var startPos = value.indexOf('>'); //find start position of the tag. eg: <div rel="3:0">
var endPos = value.lastIndexOf('<'); //find end position of the tag. eg: </div>
var tmpValue = value.substr( startPos+1 ,endPos-startPos-1); // get the content value. eg: 新業務及保單行政部
//将value值每隔40个字自动加上换行符
//each 40 character add one <br> tag to get new line.
if(tmpValue.length>120){
tmpValue = tmpValue.substr(0,40)+"<br/>"+tmpValue.substr(40,40)+"<br/>"+tmpValue.substr(80,40)+"<br/>"+tmpValue.substr(120,tmpValue.length-120);
}else if(tmpValue.length>80){
tmpValue = tmpValue.substr(0,40)+"<br/>"+tmpValue.substr(40,40)+"<br/>"+tmpValue.substr(80,tmpValue.length-80);
}else if(tmpValue.length>40){
tmpValue = tmpValue.substr(0,40)+"<br/>"+tmpValue.substr(40,tmpValue.length-40);
} // compared with old value, this value only add <br> tag for show data in table more beautiful.
value = value.substr(0,startPos+1) + tmpValue + value.substr(endPos,value.length-endPos);
}
rowContent += '<th class="' + cssclass + '" ' + (colspan > 0 ? ' colspan="' + colspan + '"' : "") + tipsy + '>' + value + '</th>';
}
else if (header.type === "ROW_HEADER_HEADER") {
var hierName = function(data) {
var hier = data.properties.hierarchy;
var name = hier.replace(/[\[\]]/gi, '').split('.')[1]
? hier.replace(/[\[\]]/gi, '').split('.')[1]
: hier.replace(/[\[\]]/gi, '').split('.')[0]; return name;
};
var arrPosRowData = []; if (_.contains(arrRowData, header.value)) {
for (var i = 0; i < arrRowData.length; i++) {
if (arrRowData[i] === header.value) {
arrPosRowData.push(i);
}
} arrPosRowData.push(col);
} rowContent += '<th class="row_header">' + (wrapContent ? '<div>' + header.value + '</div>' : header.value) + '</th>'; arrRowData.push(header.value);
objRowData.push({
name: header.value,
hierName: hierName(header) + '/' + header.value
}); isHeaderLowestLvl = true;
processedRowHeader = true;
lowestRowLvl = col;
if (header.properties.hasOwnProperty("dimension")) {
var group = header.properties.dimension;
if (!(group in rowGroups)) {
rowGroups[group] = [];
}
rowGroups[group].push(header.properties.level);
} if (arrPosRowData.length > 0) {
var aux = 0; rowContent = '<tr>'; if (row === 0) {
rowContent = '<thead>' + rowContent;
} for (var i = 0; i < objRowData.length; i++) {
if (arrPosRowData[aux] === i) {
newRowContent += '<th class="row_header">' + (wrapContent ? '<div>' + objRowData[i].hierName + '</div>' : objRowData[i].hierName) + '</th>';
aux += 1;
}
else {
newRowContent += '<th class="row_header">' + (wrapContent ? '<div>' + objRowData[i].name + '</div>' : objRowData[i].name) + '</th>';
}
} rowContent += newRowContent;
}
} // If the cell is a normal data cell
else if (header.type === "DATA_CELL") {
batchStarted = true;
var color = "";
var val = _.isEmpty(header.value) ? Settings.EMPTY_VALUE_CHARACTER : header.value;
var arrow = ""; if (header.properties.hasOwnProperty('image')) {
var img_height = header.properties.hasOwnProperty('image_height') ? " height='" + header.properties.image_height + "'" : "";
var img_width = header.properties.hasOwnProperty('image_width') ? " width='" + header.properties.image_width + "'" : "";
val = "<img " + img_height + " " + img_width + " style='padding-left: 5px' src='" + header.properties.image + "' border='0'>";
} // Just apply formatting to non-empty cells
if (val !== '-' && val !== '' && header.properties.hasOwnProperty('style')) {
color = " style='background-color: " + header.properties.style + "' ";
}
if (header.properties.hasOwnProperty('link')) {
val = "<a target='__blank' href='" + header.properties.link + "'>" + val + "</a>";
}
if (header.properties.hasOwnProperty('arrow')) {
arrow = "<img height='10' width='10' style='padding-left: 5px' src='./images/arrow-" + header.properties.arrow + ".gif' border='0'>";
} if (val !== '-' && val !== '') {
rowWithOnlyEmptyCells = false;
} //设置saiku为0的数据正常显示0 ,不显示为空
if (val == " " || val == '') {
val=0;
} rowContent += '<td class="data" ' + color + '>'
+ (wrapContent ? '<div class="datadiv '+ setStyleNegativeNumber(header.properties.raw) + '" alt="' + header.properties.raw + '" rel="' + header.properties.position + '">' : "")
+ val + arrow
+ (wrapContent ? '</div>' : '') + '</td>';
if (totalsLists[ROWS])
rowContent += genTotalDataCells(colShifted + 1, rowShifted, scanSums[ROWS], scanIndexes[ROWS], totalsLists, wrapContent);
}
}
rowContent += "</tr>"; // Change it to let hideEmpty true by default
if (options.hideEmpty && header.type === "DATA_CELL" && rowWithOnlyEmptyCells) {
/*when data_cell is null,set the headerFlag is false ,
to fix the problem data merge inccrrect.
*/
headerFlag=false;
rowContent = '';
} var totals = "";
if (totalsLists[COLUMNS] && rowShifted >= 0) {
totals += genTotalHeaderRowCells(rowShifted + 1, scanSums, scanIndexes, totalsLists, wrapContent);
}
if (batchStarted && batchSize) {
if (row <= batchSize) {
if (!isColHeader && !isColHeaderDone) {
tableContent += "</thead><tbody>";
isColHeaderDone = true;
}
tableContent += rowContent;
if (totals.length > 0) {
tableContent += totals;
} } else {
resultRows.push(rowContent);
if (totals.length > 0) {
resultRows.push(totals);
} }
} else {
if (!isColHeader && !isColHeaderDone) {
tableContent += "</thead><tbody>";
isColHeaderDone = true;
}
tableContent += rowContent;
if (totals.length > 0) {
tableContent += totals;
}
}
}
if (options) {
options['batchResult'] = resultRows;
options['hasBatchResult'] = resultRows.length > 0;
}
return "<table>" + tableContent + "</tbody></table>";
}; SaikuTableRenderer.prototype.renderSummary = function(data) {
if (data && data.query) {
var hasSomethingToRender = false;
var measures = data.query.queryModel.details
? data.query.queryModel.details.measures
: [];
var summaryData = {}; for (var i = 0; i < measures.length; i++) {
var m = measures[i];
if (m.aggregators) {
for (var j = 0; j < m.aggregators.length; j++) {
var a = m.aggregators[j];
if (a.indexOf('_') > 0) {
var tokens = a.split('_');
var aggregator = tokens[0];
var axis = tokens[1]; if (aggregator !== 'nil' && aggregator !== 'not') {
hasSomethingToRender = true;
aggregator = aggregator.capitalizeFirstLetter();
if (!(axis in summaryData)) summaryData[axis] = [];
summaryData[axis].push(m.name + ": " + aggregator);
}
}
}
}
} if (hasSomethingToRender) {
var summary = "<div id='totals_summary'><br/>"; $.each(summaryData, function(key, aggregators) {
summary += "<h3>" + key.capitalizeFirstLetter();
for (var i = 0; i < aggregators.length; i++) {
summary += "<br/> " + aggregators[i];
}
summary += "</h3>";
}); return summary + "</div>";
}
} return "";
}; String.prototype.capitalizeFirstLetter = function() {
return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
}
ExcelWorksheetBuilder.java
package org.saiku.service.util.export.excel; import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFPalette;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.olap4j.metadata.Measure;
import org.saiku.olap.dto.resultset.AbstractBaseCell;
import org.saiku.olap.dto.resultset.CellDataSet;
import org.saiku.olap.dto.resultset.DataCell;
import org.saiku.olap.dto.resultset.MemberCell;
import org.saiku.olap.query2.ThinHierarchy;
import org.saiku.olap.query2.ThinLevel;
import org.saiku.olap.query2.ThinMember;
import org.saiku.olap.util.SaikuProperties;
import org.saiku.service.olap.totals.TotalNode;
import org.saiku.service.olap.totals.aggregators.TotalAggregator;
import org.saiku.service.util.exception.SaikuServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List; /**
* Created with IntelliJ IDEA. User: sramazzina Date: 21/06/12 Time: 7.35 To
* change this template use File | Settings | File Templates.
*/ public class ExcelWorksheetBuilder { private static final String BASIC_SHEET_FONT_FAMILY = "Arial";
private static final short BASIC_SHEET_FONT_SIZE = 11;
private static final String EMPTY_STRING = "";
private static final String CSS_COLORS_CODE_PROPERTIES = "css-colors-codes.properties"; private int maxRows = -1;
private int maxColumns = -1; private AbstractBaseCell[][] rowsetHeader;
private AbstractBaseCell[][] rowsetBody; private Map<Integer, TotalAggregator[][]> rowScanTotals;
private Map<Integer, TotalAggregator[][]> colScanTotals; private CellDataSet table;
private Workbook excelWorkbook;
private Sheet workbookSheet;
private String sheetName;
private int topLeftCornerWidth;
private int topLeftCornerHeight;
private CellStyle basicCS;
private CellStyle totalsCS;
private CellStyle numberCS;
private CellStyle number0CS; //use this cellstyle when cell value is 0
private CellStyle lighterHeaderCellCS;
private List<ThinHierarchy> queryFilters;
private Map<String, Integer> colorCodesMap; private int nextAvailableColorCode = 41;
private Properties cssColorCodesProperties; private HSSFPalette customColorsPalette;
private ExcelBuilderOptions options; private static final Logger log = LoggerFactory.getLogger(ExcelWorksheetBuilder.class); public ExcelWorksheetBuilder(CellDataSet table, List<ThinHierarchy> filters, ExcelBuilderOptions options) {
init(table, filters, options);
} private void init(CellDataSet table, List<ThinHierarchy> filters, ExcelBuilderOptions options) {
this.table = table;
this.options = options;
queryFilters = filters;
maxRows = SpreadsheetVersion.EXCEL2007.getMaxRows();
maxColumns = SpreadsheetVersion.EXCEL2007.getMaxColumns(); if ("xls".equals(SaikuProperties.webExportExcelFormat)) {
HSSFWorkbook wb = new HSSFWorkbook();
customColorsPalette = wb.getCustomPalette();
excelWorkbook = wb;
maxRows = SpreadsheetVersion.EXCEL97.getMaxRows();
maxColumns = SpreadsheetVersion.EXCEL97.getMaxColumns();
} else if ("xlsx".equals(SaikuProperties.webExportExcelFormat)) {
excelWorkbook = new XSSFWorkbook();
} else {
excelWorkbook = new XSSFWorkbook();
} colorCodesMap = new HashMap<>();
this.sheetName = options.sheetName;
rowsetHeader = table.getCellSetHeaders();
rowsetBody = table.getCellSetBody(); topLeftCornerWidth = findTopLeftCornerWidth();
topLeftCornerHeight = findTopLeftCornerHeight(); initCellStyles(); // Row totals and subtotals
rowScanTotals = new HashMap<>();
colScanTotals = new HashMap<>();
scanRowAndColumnAggregations(table.getRowTotalsLists(), rowScanTotals, table.getColTotalsLists(), colScanTotals);
} private void initCellStyles() { Font font = excelWorkbook.createFont();
font.setFontHeightInPoints((short) BASIC_SHEET_FONT_SIZE);
font.setFontName(BASIC_SHEET_FONT_FAMILY); basicCS = excelWorkbook.createCellStyle();
basicCS.setFont(font);
basicCS.setAlignment(HorizontalAlignment.LEFT);
basicCS.setVerticalAlignment(VerticalAlignment.TOP);
setCellBordersColor(basicCS); Font totalsFont = excelWorkbook.createFont();
totalsFont.setFontHeightInPoints((short) BASIC_SHEET_FONT_SIZE);
totalsFont.setBold(true);
totalsFont.setFontName(BASIC_SHEET_FONT_FAMILY); totalsCS = excelWorkbook.createCellStyle();
totalsCS.setFont(totalsFont);
totalsCS.setAlignment(HorizontalAlignment.RIGHT);
setCellBordersColor(totalsCS); // Setting the default styling for number cells
numberCS = excelWorkbook.createCellStyle();
numberCS.setFont(font);
numberCS.setAlignment(HorizontalAlignment.RIGHT); /*
* justasg: Let's set default format, used if measure has no format at
* all. More info:
* http://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/
* BuiltinFormats.html#getBuiltinFormat(int) If we don't have default
* format, it will output values up to maximum detail, i.e.
* 121212.3456789 and we like them as 121,212.346
*/
DataFormat fmt = excelWorkbook.createDataFormat();
short dataFormat = fmt.getFormat(SaikuProperties.webExportExcelDefaultNumberFormat);
numberCS.setDataFormat(dataFormat);
setCellBordersColor(numberCS); // Setting the default format for cell value is 0
number0CS = excelWorkbook.createCellStyle();
number0CS.setFont(font);
number0CS.setAlignment(HorizontalAlignment.RIGHT);
setCellBordersColor(number0CS); Font headerFont = excelWorkbook.createFont();
headerFont.setFontHeightInPoints((short) BASIC_SHEET_FONT_SIZE);
headerFont.setFontName(BASIC_SHEET_FONT_FAMILY);
headerFont.setBold(true); lighterHeaderCellCS = excelWorkbook.createCellStyle();
lighterHeaderCellCS.setFont(headerFont);
lighterHeaderCellCS.setAlignment(HorizontalAlignment.CENTER);
lighterHeaderCellCS.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
lighterHeaderCellCS.setFillPattern(FillPatternType.SOLID_FOREGROUND);
setCellBordersColor(lighterHeaderCellCS); CellStyle darkerHeaderCellCS = excelWorkbook.createCellStyle();
darkerHeaderCellCS.setFont(headerFont);
darkerHeaderCellCS.setAlignment(HorizontalAlignment.CENTER);
darkerHeaderCellCS.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.getIndex());
darkerHeaderCellCS.setFillPattern(FillPatternType.SOLID_FOREGROUND);
setCellBordersColor(darkerHeaderCellCS); } //更改指标分割线条颜色为 黑色20190528
private void setCellBordersColor(CellStyle style) { style.setBorderBottom(BorderStyle.THIN);
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderTop(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderLeft(BorderStyle.THIN);
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
/*style.setBorderBottom(BorderStyle.THIN);
style.setBottomBorderColor(IndexedColors.GREY_80_PERCENT.getIndex());
style.setBorderTop(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.GREY_80_PERCENT.getIndex());
style.setBorderLeft(BorderStyle.THIN);
style.setLeftBorderColor(IndexedColors.GREY_80_PERCENT.getIndex());
style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.GREY_80_PERCENT.getIndex());*/
} public byte[] build() throws SaikuServiceException { Long start = (new Date()).getTime();
int startRow = initExcelSheet();
Long init = (new Date()).getTime();
int lastHeaderRow = buildExcelTableHeader(startRow);
Long header = (new Date()).getTime();
addExcelTableRows(lastHeaderRow);
addTotalsSummary(lastHeaderRow);
Long content = (new Date()).getTime();
finalizeExcelSheet(startRow);
Long finalizing = (new Date()).getTime(); log.debug("Init: " + (init - start) + "ms header: " + (header - init) + "ms content: " + (content - header)
+ "ms finalizing: " + (finalizing - content) + "ms ");
ByteArrayOutputStream bout = new ByteArrayOutputStream(); try {
excelWorkbook.write(bout);
} catch (IOException e) {
throw new SaikuServiceException("Error creating excel export for query", e);
}
return bout.toByteArray();
} private void checkRowLimit(int rowIndex) {
if ((rowIndex + 1) > maxRows) {
log.warn("Excel sheet is truncated, only outputting " + maxRows + " rows of " + (rowIndex + 1));
}
} private void addTotalsSummary(int startingRow) {
int rowIndex = startingRow + rowsetBody.length + 2; // Lines offset after data, in order to add summary
checkRowLimit(rowIndex); // Columns summary
if (colScanTotals.keySet().size() > 0) {
Row row = workbookSheet.createRow(rowIndex);
Cell cell = row.createCell(0);
cell.setCellStyle(lighterHeaderCellCS);
cell.setCellValue("Columns"); for (Integer colKey : colScanTotals.keySet()) {
TotalAggregator[][] colAggregator = colScanTotals.get(colKey); if (colAggregator == null) continue; for (int x = 0; x < colAggregator.length; x++) {
rowIndex++;
checkRowLimit(rowIndex); Measure measure = this.table.getSelectedMeasures()[x]; TotalAggregator agg = colAggregator[x][0];
row = workbookSheet.createRow(rowIndex); // Measure name
cell = row.createCell(0);
cell.setCellStyle(lighterHeaderCellCS);
cell.setCellValue(measure.getCaption() + ":"); // Measure aggregator
cell = row.createCell(1);
cell.setCellStyle(basicCS);
cell.setCellValue(agg.getClass().getSimpleName().substring(0, 3));
}
}
} // Rows summary
if (rowScanTotals.keySet().size() > 0) {
rowIndex++;
checkRowLimit(rowIndex); Row row = workbookSheet.createRow(rowIndex);
Cell cell = row.createCell(0);
cell.setCellStyle(lighterHeaderCellCS);
cell.setCellValue("Rows"); for (Integer rowKey : rowScanTotals.keySet()) {
TotalAggregator[][] rowAggregator = rowScanTotals.get(rowKey); if (rowAggregator == null) continue; for (int x = 0; x < rowAggregator.length; x++) {
for (int y = 0; y < this.table.getSelectedMeasures().length; y++) {
rowIndex++;
checkRowLimit(rowIndex); Measure measure = this.table.getSelectedMeasures()[y];
TotalAggregator agg = rowAggregator[x][y]; row = workbookSheet.createRow(rowIndex); // Measure name
cell = row.createCell(0);
cell.setCellStyle(lighterHeaderCellCS);
cell.setCellValue(measure.getCaption() + ":"); // Measure aggregator
cell = row.createCell(1);
cell.setCellStyle(basicCS);
cell.setCellValue(agg.getClass().getSimpleName().substring(0, 3));
}
}
}
} } private void finalizeExcelSheet(int startRow) { boolean autoSize = (rowsetBody != null && rowsetBody.length > 0 && rowsetBody.length < 10000
&& rowsetHeader != null && rowsetHeader.length > 0 && rowsetHeader[0].length < 200); if (autoSize) {
log.warn("Skipping auto-sizing columns, more than 10000 rows and/or 200 columns");
} Long start = (new Date()).getTime();
if (autoSize) {
//Detect max column size
int maxColumnsSize = rowsetBody[0].length;
if (!colScanTotals.isEmpty()) {
maxColumnsSize = Collections.max(colScanTotals.keySet()) + 1;
}
// Autosize columns
for (int i = 0; i < maxColumns && i < maxColumnsSize; i++) {
workbookSheet.autoSizeColumn(i);
}
}
Long end = (new Date()).getTime();
log.debug("Autosizing: " + (end - start) + "ms");
// Freeze the header columns
int headerWidth = rowsetHeader.length;
workbookSheet.createFreezePane(0, startRow + headerWidth, 0, startRow + headerWidth);
} private int initExcelSheet() {
// Main Workbook Sheet
if (StringUtils.isNotBlank(options.sheetName)) {
workbookSheet = excelWorkbook.createSheet(this.sheetName);
} else {
workbookSheet = excelWorkbook.createSheet();
}
initSummarySheet();
return 0;
} private void initSummarySheet() { // Main Workbook Sheet
Sheet summarySheet = excelWorkbook.createSheet("Summary page"); int row = 1; Row sheetRow = summarySheet.createRow((int) row);
Cell cell = sheetRow.createCell(0);
String todayDate = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date());
cell.setCellValue("Export date and time: " + todayDate);
summarySheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 2));
row = row + 2; sheetRow = summarySheet.createRow((int) row);
cell = sheetRow.createCell(0);
cell.setCellValue("Dimension");
cell = sheetRow.createCell(1);
cell.setCellValue("Level");
cell = sheetRow.createCell(2);
cell.setCellValue("Filter Applied");
row++; if (queryFilters != null) {
for (ThinHierarchy item : queryFilters) {
for (ThinLevel s : item.getLevels().values()) {
for (ThinMember i : s.getSelection().getMembers()) {
sheetRow = summarySheet.createRow((short) row);
cell = sheetRow.createCell(0);
cell.setCellValue(item.getCaption());
cell = sheetRow.createCell(1);
cell.setCellValue(s.getCaption());
cell = sheetRow.createCell(2);
cell.setCellValue(i.getCaption());
row++;
}
}
}
} row += 2; int rowLength = (rowsetBody != null) ? rowsetBody.length : 0;
int columnCount = (rowsetHeader != null && rowsetHeader.length > 0) ? rowsetHeader[0].length : 0;
int headerLength = (rowsetHeader != null) ? rowsetHeader.length : 0; if (columnCount > maxColumns) {
sheetRow = summarySheet.createRow((int) row);
cell = sheetRow.createCell(0);
cell.setCellValue("Excel sheet is truncated, only contains " + maxColumns + " columns of " + (columnCount));
summarySheet.addMergedRegion(new CellRangeAddress(row, row, 0, 10));
row++;
} if ((headerLength + rowLength) > maxRows) {
sheetRow = summarySheet.createRow((int) row);
cell = sheetRow.createCell(0);
cell.setCellValue(
"Excel sheet is truncated, only contains " + maxRows + " rows of " + (headerLength + rowLength));
summarySheet.addMergedRegion(new CellRangeAddress(row, row, 0, 10));
row++;
} row++; sheetRow = summarySheet.createRow((int) row);
cell = sheetRow.createCell(0);
cell.setCellValue(SaikuProperties.webExportExcelPoweredBy);
summarySheet.addMergedRegion(new CellRangeAddress(row, row, 0, 10)); // Autosize columns for summary sheet
for (int i = 0; i < 5; i++) {
summarySheet.autoSizeColumn(i);
}
} private void addExcelTableRows(int startingRow) { Row sheetRow = null;
Cell cell = null;
Map<Integer, String> tmpCellUniqueValueByColumn = new HashMap<>();
Map<Integer, Map<Integer, Boolean>> mergeRowsByColumn = new HashMap<>(); if ((startingRow + rowsetBody.length) > maxRows) {
log.warn("Excel sheet is truncated, only outputting " + maxRows + " rows of "
+ (rowsetBody.length + startingRow));
}
if (rowsetBody.length > 0 && rowsetBody[0].length > maxColumns) {
log.warn("Excel sheet is truncated, only outputting " + maxColumns + " columns of "
+ (rowsetBody[0].length));
} int rowCount = startingRow; for (int x = 0; (x + startingRow) < maxRows && x < rowsetBody.length; x++) { int excelRowIndex = x + startingRow;
sheetRow = workbookSheet.createRow(excelRowIndex); int column = 0;
for (int y = 0; y < maxColumns && y < rowsetBody[x].length; y++) {
cell = sheetRow.createCell(column); AbstractBaseCell baseCell = rowsetBody[x][y]; //Detect merge cells
findMergeCells(baseCell, excelRowIndex, y, mergeRowsByColumn, tmpCellUniqueValueByColumn); String value = baseCell.getFormattedValue(); if (value == null && options.repeatValues) {
// If the row cells has a null values it means the value is
// repeated in the data internally
// but not in the interface. To properly format the Excel
// export file we need that value so we
// get it from the same position in the prev row
value = workbookSheet.getRow(sheetRow.getRowNum() - 1).getCell(column).getStringCellValue();
} cell.setCellStyle(basicCS);
cell.setCellValue(value);
// Use rawNumber only is there is a formatString
if (rowsetBody[x][y] instanceof DataCell) {
DataCell dataCell = (DataCell) rowsetBody[x][y];
String formatString = dataCell.getFormatString();
if ((dataCell.getRawNumber() != null) && (formatString != null) && !formatString.trim().isEmpty()) {
Number numberValue = dataCell.getRawNumber();
cell.setCellValue(numberValue.doubleValue());
if(numberValue.doubleValue() != 0.0 || !formatString.trim().equals("#,###")){//不等于0.0时 就设置样式
applyCellFormatting(cell, dataCell);
}else{
cell.setCellStyle(number0CS);
}
}
} //Set column sub totalstotals
column = setColTotalAggregationCell(colScanTotals, sheetRow, x, column, true, false); //Set column grand totals
if (y == rowsetBody[x].length - 1) {
setColTotalAggregationCell(colScanTotals, sheetRow, x, column - 1, true, x == 0);
}
} // Set row sub totals
startingRow = setRowTotalAggregationCell(rowScanTotals, startingRow, x, false);
rowCount = startingRow + x;
} //Set row grand totals
setRowTotalAggregationCell(rowScanTotals, rowCount, 0, true); //Add merge cells
addMergedRegions(mergeRowsByColumn);
} private void scanRowAndColumnAggregations(List<TotalNode>[] rowTotalsLists, Map<Integer, TotalAggregator[][]> rowScanTotals, List<TotalNode>[] colTotalsLists, Map<Integer, TotalAggregator[][]> colScanTotals) {
if (rowTotalsLists != null) {
for (List<TotalNode> totalNodes : rowTotalsLists) {
//Scan row totals
scanAggregations(true, totalNodes, rowScanTotals);
}
}
if (colTotalsLists != null) {
for (List<TotalNode> totalNodes : colTotalsLists) {
//Scan Columns grand totals
scanAggregations(false, totalNodes, colScanTotals);
}
}
} private void scanAggregations(boolean row, List<TotalNode> totalNodes, Map<Integer, TotalAggregator[][]> scanSums) {
if (totalNodes != null && (!totalNodes.isEmpty())) {
int index;
if (row) {
index = rowsetHeader.length - 2;
} else {
index = detectColumnStartIndex();
}
for (TotalNode n : totalNodes) {
TotalAggregator[][] tg = n.getTotalGroups();
if (tg.length > 0) {
if (n.getSpan() > n.getWidth()) {
index += n.getSpan();
} else {
index += n.getWidth();
}
index++;
scanSums.put(index, tg);
}
}
}
} private int setRowTotalAggregationCell(Map<Integer, TotalAggregator[][]> scanTotals, int startIndex, int subIndex, boolean grandTotal) {
if (!scanTotals.isEmpty()) {
int row = subIndex + startIndex;
TotalAggregator[][] aggregatorsTable = scanTotals.get(row);
if (aggregatorsTable != null) {
//Create totals row
Row sheetRow = workbookSheet.createRow(row + 1); //Detect column start index
int startColumnIndex = detectColumnStartIndex(); if (grandTotal) {
setGrandTotalLabel(sheetRow, startColumnIndex, false);
} for (TotalAggregator[] aggregators : aggregatorsTable) { int column = startColumnIndex; for (TotalAggregator aggregator : aggregators) { //Calculate column sub total index
column = setColTotalAggregationCell(colScanTotals, null, -1, column, false, false); //Create row totals cell
Cell cell = sheetRow.createCell(column);
String value = aggregator.getFormattedValue();
cell.setCellValue(value);
cell.setCellStyle(totalsCS);
}
}
startIndex++;
}
}
return startIndex;
} /**
* @return columns data start index
*/
private int detectColumnStartIndex() {
int index = 0;
if (rowsetBody.length > 0) {
for (AbstractBaseCell cell : rowsetBody[0]) {
if (cell instanceof MemberCell) {
index++;
}
}
index--;
}
return index;
} private int setColTotalAggregationCell(Map<Integer, TotalAggregator[][]> scanTotals, Row sheetRow, int x, int column, boolean setValue, boolean grandTotal) {
column++; if (!scanTotals.isEmpty()) {
TotalAggregator[][] aggregatorsTable = scanTotals.get(column); if (aggregatorsTable != null) {
if (setValue) {
if (grandTotal) {
setGrandTotalLabel(sheetRow.getRowNum() - 1, column, true);
} for (TotalAggregator[] aggregators : aggregatorsTable) {
Cell cell = sheetRow.createCell(column);
String value = aggregators[x].getFormattedValue();
cell.setCellValue(value);
cell.setCellStyle(totalsCS);
column++;
}
}
}
} return column;
} private void setGrandTotalLabel(int x, int y, boolean header) {
Row sheetRow = workbookSheet.getRow(x);
if (sheetRow != null) {
setGrandTotalLabel(sheetRow, y, header);
}
} private void setGrandTotalLabel(Row sheetRow, int y, boolean header) {
Cell cell = sheetRow.createCell(y);
//TODO i18n
String value = "Grand Total";
if (header) {
fillHeaderCell(sheetRow, value, y);
} else {
cell.setCellValue(value);
cell.setCellStyle(basicCS);
}
} /**
* Apply exact number format to excel Cell from its DataCell. Caller checks
* the DataCell rawNumber and formatString are correct.
*
* @param cell The excel cell to apply formatting
* @param dataCell The source
*/
private void applyCellFormatting(Cell cell, DataCell dataCell) {
/*
* Previously, the CellStyles were being kept on a hash map for reuse,
* but the key used was just the formatString (not considering the
* colours), so, if many cells shared the same formatString but using
* different colours, all those cells would have the last cell colour.
*/
String formatString = dataCell.getFormatString();
CellStyle numberCSClone = excelWorkbook.createCellStyle(); numberCSClone.cloneStyleFrom(numberCS); try {
formatString = FormatUtil.getFormatString(formatString);
DataFormat fmt = excelWorkbook.createDataFormat();
short dataFormat = fmt.getFormat(formatString);
numberCSClone.setDataFormat(dataFormat);
} catch (Exception ex) { } // Check for cell background
Map<String, String> properties = dataCell.getProperties(); // Just style the cell if it contains a value
if (dataCell.getRawNumber() != null && properties.containsKey("style")) {
String colorCode = properties.get("style");
short colorCodeIndex = getColorFromCustomPalette(colorCode); if (colorCodeIndex != -1) {
numberCSClone.setFillForegroundColor(colorCodeIndex);
numberCSClone.setFillPattern(FillPatternType.SOLID_FOREGROUND);
} else if (customColorsPalette == null) {
try { if (cssColorCodesProperties != null && cssColorCodesProperties.containsKey(colorCode)) {
colorCode = cssColorCodesProperties.getProperty(colorCode);
} int redCode = Integer.parseInt(colorCode.substring(1, 3), 16);
int greenCode = Integer.parseInt(colorCode.substring(3, 5), 16);
int blueCode = Integer.parseInt(colorCode.substring(5, 7), 16); numberCSClone.setFillPattern(FillPatternType.SOLID_FOREGROUND); ((XSSFCellStyle) numberCSClone).setFillForegroundColor(
new XSSFColor(new java.awt.Color(redCode, greenCode, blueCode)));
((XSSFCellStyle) numberCSClone).setFillBackgroundColor(
new XSSFColor(new java.awt.Color(redCode, greenCode, blueCode)));
} catch (Exception e) {
// we tried to set the color, no luck, lets continue
// without
} }
} else {
numberCSClone.setFillForegroundColor(numberCS.getFillForegroundColor());
numberCSClone.setFillBackgroundColor(numberCS.getFillBackgroundColor());
} cell.setCellStyle(numberCSClone);
} private short getColorFromCustomPalette(String style) { short returnedColorIndex = -1;
InputStream is = null; if (colorCodesMap.containsKey(style)) {
returnedColorIndex = colorCodesMap.get(style).shortValue();
} else {
try { if (cssColorCodesProperties == null) {
is = getClass().getResourceAsStream(CSS_COLORS_CODE_PROPERTIES);
if (is != null) {
cssColorCodesProperties = new Properties();
cssColorCodesProperties.load(is);
}
} String colorCode = cssColorCodesProperties.getProperty(style);
if (colorCode != null) {
try {
int redCode = Integer.parseInt(colorCode.substring(1, 3), 16);
int greenCode = Integer.parseInt(colorCode.substring(3, 5), 16);
int blueCode = Integer.parseInt(colorCode.substring(5, 7), 16);
if (customColorsPalette != null) {
customColorsPalette.setColorAtIndex((byte) nextAvailableColorCode, (byte) redCode,
(byte) greenCode, (byte) blueCode);
returnedColorIndex = customColorsPalette.getColor(nextAvailableColorCode).getIndex();
colorCodesMap.put(style, (int) returnedColorIndex);
} else {
return -1;
}
nextAvailableColorCode++;
} catch (Exception e) {
// we tried to set the color, no luck, lets continue
// without
return -1;
}
}
} catch (IOException e) {
log.error("IO Exception", e);
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
log.error("IO Exception", e);
}
} } return returnedColorIndex; // To change body of created methods use File
// | Settings | File Templates.
} private int buildExcelTableHeader(int startRow) { Row sheetRow = null;
int x = 0;
int y = 0;
int startSameFromPos = 0;
int mergedCellsWidth = 0;
boolean isLastHeaderRow = false;
boolean isLastColumn = false;
String nextHeader = EMPTY_STRING;
String currentHeader = EMPTY_STRING;
ArrayList<ExcelMergedRegionItemConfig> mergedItemsConfig = new ArrayList<>(); for (x = 0; x < rowsetHeader.length; x++) { sheetRow = workbookSheet.createRow((int) x + startRow); nextHeader = EMPTY_STRING;
isLastColumn = false;
startSameFromPos = 0;
mergedCellsWidth = 0; if (x + 1 == rowsetHeader.length) {
isLastHeaderRow = true;
} int column = 0;
for (y = 0; y < maxColumns && y < rowsetHeader[x].length; y++) { currentHeader = rowsetHeader[x][y].getFormattedValue();
if (currentHeader != null) {
if (rowsetHeader[x].length == y + 1) {
isLastColumn = true;
} else {
nextHeader = rowsetHeader[x][y + 1].getFormattedValue();
} manageColumnHeaderDisplay(sheetRow, x, column, currentHeader); if (!isLastHeaderRow) {
if (nextHeader != null && !nextHeader.equals(currentHeader) || isLastColumn) {
manageCellsMerge(column, x + startRow, mergedCellsWidth + 1, startSameFromPos,
mergedItemsConfig);
startSameFromPos = column + 1;
mergedCellsWidth = 0;
} else if (nextHeader != null && nextHeader.equals(currentHeader)) {
mergedCellsWidth++;
}
}
} else {
startSameFromPos++;
} //Set sub total column space
int nextColumn = setColTotalAggregationCell(colScanTotals, sheetRow, x, column, false, false);
if (column != nextColumn - 1) {
startSameFromPos++;
}
column = nextColumn;
}
// Manage the merge condition on exit from columns scan
if (!isLastHeaderRow)
manageCellsMerge(y - 1, x, mergedCellsWidth + 1, startSameFromPos, mergedItemsConfig);
} if (topLeftCornerHeight > 0 && topLeftCornerWidth > 0) {
workbookSheet.addMergedRegion(
new CellRangeAddress(startRow, startRow + topLeftCornerHeight - 1, 0, topLeftCornerWidth - 1));
} if (mergedItemsConfig.size() > 0) {
for (ExcelMergedRegionItemConfig item : mergedItemsConfig) {
int lastCol = item.getStartX() + item.getWidth() - 1;
lastCol = lastCol >= maxColumns ? maxColumns - 1 : lastCol;
workbookSheet.addMergedRegion(new CellRangeAddress(item.getStartY(),
item.getStartY() + item.getHeight(), item.getStartX(), lastCol));
}
} return x + startRow;
} private void manageColumnHeaderDisplay(Row sheetRow, int x, int y, String currentHeader) {
if (topLeftCornerHeight > 0 && x >= topLeftCornerHeight) {
fillHeaderCell(sheetRow, currentHeader, y);
} else if ((topLeftCornerHeight > 0 && x < topLeftCornerHeight)
&& (topLeftCornerWidth > 0 && y >= topLeftCornerWidth)) {
fillHeaderCell(sheetRow, currentHeader, y);
} else if (topLeftCornerHeight == 0 && topLeftCornerWidth == 0)
fillHeaderCell(sheetRow, currentHeader, y);
} private void manageCellsMerge(int rowPos, int colPos, int width, int startSameFromPos,
ArrayList<ExcelMergedRegionItemConfig> mergedItemsConfig) { ExcelMergedRegionItemConfig foundItem = null;
boolean itemGetFromList = false; if (width == 1)
return; for (ExcelMergedRegionItemConfig item : mergedItemsConfig) {
if (item.getStartY() == colPos && item.getStartX() == rowPos) {
foundItem = item;
itemGetFromList = true;
}
} if (foundItem == null)
foundItem = new ExcelMergedRegionItemConfig(); foundItem.setHeight(0);
foundItem.setWidth(width);
foundItem.setStartX(startSameFromPos);
foundItem.setStartY(colPos);
if (mergedItemsConfig.isEmpty() || !itemGetFromList)
mergedItemsConfig.add(foundItem);
} private void fillHeaderCell(Row sheetRow, String formattedValue, int y) {
Cell cell = sheetRow.createCell(y);
cell.setCellValue(formattedValue);
cell.setCellStyle(lighterHeaderCellCS);
} /**
* Find the width in cells of the top left corner of the table
*
* @return
*/
private int findTopLeftCornerWidth() { int width = 0;
int x = 0;
boolean exit = (rowsetHeader.length < 1 || rowsetHeader[0][0].getRawValue() != null);
String cellValue = null; for (x = 0; (!exit && rowsetHeader[0].length > x); x++) { cellValue = rowsetHeader[0][x].getRawValue();
if (cellValue == null) {
width = x + 1;
} else {
exit = true;
}
} return width;
} /**
* Find the height in cells of the top left corner of the table
*
* @return
*/
private int findTopLeftCornerHeight() {
return rowsetHeader.length > 0 ? rowsetHeader.length - 1 : 0;
} /**
* @param mergeRowsByColumn merged indexes
*/
private void addMergedRegions(Map<Integer, Map<Integer, Boolean>> mergeRowsByColumn) {
if (mergeRowsByColumn != null) {
for (Map.Entry<Integer, Map<Integer, Boolean>> e : mergeRowsByColumn.entrySet()) { int col = e.getKey(); Map<Integer, Boolean> rows = e.getValue(); if (rows != null) { int mergeCount = 1;
for (Map.Entry<Integer, Boolean> rowEntry : rows.entrySet()) { int row = rowEntry.getKey(); boolean current = rowEntry.getValue(); Boolean next = rows.get(rowEntry.getKey() + 1); if (current) {
if (next == null || !next) {
workbookSheet.addMergedRegion(new CellRangeAddress(row - mergeCount, row, col, col));
}
mergeCount++;
} else {
mergeCount = 1;
}
}
}
}
}
} /**
* @param baseCell current cell
* @param excelRowIndex row index
* @param y column
* @param mergeRowsByColumn merge indexes store
* @param tmpCellUniqueValueByColumn tmp map to compare previews value(max possible value = columns size)
*/
private void findMergeCells(AbstractBaseCell baseCell,
int excelRowIndex,
int y,
Map<Integer, Map<Integer, Boolean>> mergeRowsByColumn,
Map<Integer, String> tmpCellUniqueValueByColumn) {
if (baseCell instanceof MemberCell) { MemberCell memberCell = (MemberCell) baseCell; Map<Integer, Boolean> rowMerge = mergeRowsByColumn.get(y);
if (rowMerge == null) {
rowMerge = new TreeMap<>();
mergeRowsByColumn.put(y, rowMerge);
} //Compare preview and current cells
String previousValue = tmpCellUniqueValueByColumn.get(y); Map<Integer, Boolean> previousColumn = mergeRowsByColumn.get(y - 1); boolean merge = previousValue != null && previousValue.equals(memberCell.getUniqueName()); if (previousColumn != null) {
Boolean previewColumnCellmergeValue = previousColumn.get(excelRowIndex);
if ((previewColumnCellmergeValue != null) && (!previewColumnCellmergeValue) && merge) {
merge = false;
}
}
rowMerge.put(excelRowIndex, merge); tmpCellUniqueValueByColumn.put(y, memberCell.getUniqueName());
}
}
}
FlattenedCellSetFormatter.java
/*
* Copyright 2012 OSBI Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.saiku.olap.util.formatter; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map; import org.olap4j.Cell;
import org.olap4j.CellSet;
import org.olap4j.CellSetAxis;
import org.olap4j.Position;
import org.olap4j.impl.CoordinateIterator;
import org.olap4j.impl.Olap4jUtil;
import org.olap4j.metadata.Level;
import org.olap4j.metadata.Member;
import org.olap4j.metadata.Property;
import org.saiku.olap.dto.resultset.DataCell;
import org.saiku.olap.dto.resultset.Matrix;
import org.saiku.olap.dto.resultset.MemberCell;
import org.saiku.olap.util.SaikuProperties; public class FlattenedCellSetFormatter implements ICellSetFormatter { /**
* Description of an axis.
*/
private static class AxisInfo {
final List<AxisOrdinalInfo> ordinalInfos; /**
* Creates an AxisInfo.
*
* @param ordinalCount Number of hierarchies on this axis
*/
AxisInfo(final int ordinalCount) {
ordinalInfos = new ArrayList<>(ordinalCount); // For each index from 0 to the number of hierarchies ...
for (int i = 0; i < ordinalCount; i++) {
// Associate an AxisOrdinalInfo instance
ordinalInfos.add(new AxisOrdinalInfo());
}
} /**
* Returns the number of matrix columns required by this axis. The sum of
* the width of the hierarchies on this axis.
*
* @return Width of axis
*/
public int getWidth() {
int width = 0;
for (final AxisOrdinalInfo info : ordinalInfos) {
width += info.getWidth();
}
return width;
}
} /**
* Description of a particular hierarchy mapped to an axis.
*/
private static class AxisOrdinalInfo {
private final List<Integer> depths = new ArrayList<>();
private final Map<Integer,Level> depthLevel = new HashMap<>(); public int getWidth() {
return depths.size();
} public List<Integer> getDepths() {
return depths;
} public Level getLevel(Integer depth) {
return depthLevel.get(depth);
} public void addLevel(Integer depth, Level level) {
depthLevel.put(depth, level);
}
} /**
* Returns an iterator over cells in a result.
*/
private static Iterable<Cell> cellIter(final int[] pageCoords, final CellSet cellSet) {
return new Iterable<Cell>() {
public Iterator<Cell> iterator() {
final int[] axisDimensions = new int[cellSet.getAxes().size() - pageCoords.length];
assert pageCoords.length <= axisDimensions.length;
for (int i = 0; i < axisDimensions.length; i++) {
final CellSetAxis axis = cellSet.getAxes().get(i);
axisDimensions[i] = axis.getPositions().size();
}
final CoordinateIterator coordIter = new CoordinateIterator(axisDimensions, true);
return new Iterator<Cell>() {
public boolean hasNext() {
return coordIter.hasNext();
} public Cell next() {
final int[] ints = coordIter.next();
final AbstractList<Integer> intList = new AbstractList<Integer>() {
@Override
public Integer get(final int index) {
return index < ints.length ? ints[index] : pageCoords[index - ints.length];
} @Override
public int size() {
return pageCoords.length + ints.length;
}
};
return cellSet.getCell(intList);
} public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
} private Matrix matrix; private final List<Integer> ignorex = new ArrayList<>();
private final List<Integer> ignorey = new ArrayList<>(); /**
* This is the main method of a cellset formatter, it receives a cellset as
* input and converts it on a matrix, a bidimensional representation of query
* values, arranged in a xy cartesian coordinate system.
* @param cellSet
* @return
*/
public Matrix format(final CellSet cellSet) {
// Compute how many rows are required to display the columns axis.
final CellSetAxis columnsAxis; // If the axes are not empty, the first one is the column axis
if (cellSet.getAxes().size() > 0) {
// As a convention, the columns axis is associated with the index 0
columnsAxis = cellSet.getAxes().get(0);
} else {
columnsAxis = null;
} final AxisInfo columnsAxisInfo = computeAxisInfo(columnsAxis); // Compute how many columns are required to display the rows axis.
final CellSetAxis rowsAxis; // If there are more than one axis, the second one is the rows axis
if (cellSet.getAxes().size() > 1) {
// As a convention, the rows axis is associated with the index 1
rowsAxis = cellSet.getAxes().get(1);
} else {
rowsAxis = null;
} final AxisInfo rowsAxisInfo = computeAxisInfo(rowsAxis); if (cellSet.getAxes().size() > 2) {
final int[] dimensions = new int[cellSet.getAxes().size() - 2];
for (int i = 2; i < cellSet.getAxes().size(); i++) {
final CellSetAxis cellSetAxis = cellSet.getAxes().get(i);
dimensions[i - 2] = cellSetAxis.getPositions().size();
}
for (final int[] pageCoords : CoordinateIterator.iterate(dimensions)) {
matrix = formatPage(cellSet, pageCoords, columnsAxis, columnsAxisInfo, rowsAxis, rowsAxisInfo);
}
} else {
matrix = formatPage(cellSet, new int[] {}, columnsAxis, columnsAxisInfo, rowsAxis, rowsAxisInfo);
} return matrix;
} /**
* Computes a description of an axis. Each axis is composed by many positions,
* each position is then composed by many members. A member is a 'point' on a
* dimension of a cube. Every member belongs to a Level of a Hierarchy. The
* member's depth is its distance to the root member.
*
* @param axis Axis
* @return Description of axis
*/
private AxisInfo computeAxisInfo(final CellSetAxis axis) {
if (axis == null) {
return new AxisInfo(0);
} // An axis info is created by informing the number of hierarchies of axis
final AxisInfo axisInfo = new AxisInfo(axis.getAxisMetaData().getHierarchies().size());
int p = -1; // For each axis position
for (final Position position : axis.getPositions()) {
++p;
int k = -1; // For each member of the axis
for (final Member member : position.getMembers()) {
++k; // Fetch the AxisOrdinalInfo instance of the position index k
final AxisOrdinalInfo axisOrdinalInfo = axisInfo.ordinalInfos.get(k); // We avoid duplicating information for members with the same depth
if (!axisOrdinalInfo.getDepths().contains(member.getDepth())) {
axisOrdinalInfo.getDepths().add(member.getDepth());
// For each depth of the hiearchy, add its level
axisOrdinalInfo.addLevel(member.getDepth(), member.getLevel());
Collections.sort(axisOrdinalInfo.depths);
}
}
} // The axisInfo object, contains a collection of the hiearchy's levels
// sorted by their depths.
return axisInfo;
} /**
* Formats a two-dimensional page.
*
* @param cellSet
* Cell set
* @param pageCoords
* Print writer
* @param pageCoords
* Coordinates of page [page, chapter, section, ...]
* @param columnsAxis
* Columns axis
* @param columnsAxisInfo
* Description of columns axis
* @param rowsAxis
* Rows axis
* @param rowsAxisInfo
* Description of rows axis
*/
private Matrix formatPage(final CellSet cellSet, final int[] pageCoords, final CellSetAxis columnsAxis,
final AxisInfo columnsAxisInfo, final CellSetAxis rowsAxis, final AxisInfo rowsAxisInfo) { // Figure out the dimensions of the blank rectangle in the top left
// corner.
final int yOffset = columnsAxisInfo.getWidth();
final int xOffsset = rowsAxisInfo.getWidth(); // Populate a string matrix
final Matrix matrix = new Matrix(xOffsset + (columnsAxis == null ? 1 : columnsAxis.getPositions().size()),
yOffset + (rowsAxis == null ? 1 : rowsAxis.getPositions().size())); // Populate corner
List<Level> levels = new ArrayList<>();
if (rowsAxis != null && rowsAxis.getPositions().size() > 0) {
// We assume that every position contains members with same levels,
// so, we just need the first position to retrieve this information.
Position p = rowsAxis.getPositions().get(0); for (int m = 0; m < p.getMembers().size(); m++) {
AxisOrdinalInfo a = rowsAxisInfo.ordinalInfos.get(m); // For each member's depth of the first position, add its level
for (Integer depth : a.getDepths()) {
levels.add(a.getLevel(depth));
}
} for (int x = 0; x < xOffsset; x++) {
Level xLevel = levels.get(x);
String s = xLevel.getCaption();
for (int y = 0; y < yOffset; y++) {
final MemberCell memberInfo = new MemberCell(false, x > 0);
if (y == yOffset-1) {
memberInfo.setRawValue(s);
memberInfo.setFormattedValue(s);
memberInfo.setProperty("__headertype", "row_header_header");
memberInfo.setProperty("levelindex", "" + levels.indexOf(xLevel));
memberInfo.setHierarchy(xLevel.getHierarchy().getUniqueName());
memberInfo.setParentDimension(xLevel.getDimension().getName());
memberInfo.setLevel(xLevel.getUniqueName());
}
matrix.set(x, y, memberInfo);
} }
}
// Populate matrix with cells representing axes
populateAxis(matrix, columnsAxis, columnsAxisInfo, true, xOffsset);
populateAxis(matrix, rowsAxis, rowsAxisInfo, false, yOffset); // TODO - why did we do this in the first place??? HERE BE DRAGONS
// int headerwidth = matrix.getMatrixWidth();
// if (headerwidth > 2) {
// for(int yy=matrix.getMatrixHeight(); yy > matrix.getOffset() ; yy--) {
// for(int xx=0; xx < headerwidth-1;xx++) {
// if (matrix.get(xx,yy-1) != null && matrix.get(xx,yy) != null && matrix.get(xx,yy-1).getRawValue() != null
// && matrix.get(xx,yy-1).getRawValue().equals(matrix.get(xx, yy).getRawValue()))
// {
// matrix.set(xx, yy, new MemberCell());
// }
// else {
// break;
// }
// }
// }
// } // Populate cell values
int newyOffset = yOffset;
int newxOffset = xOffsset;
List<Integer> donex = new ArrayList<>();
List<Integer> doney = new ArrayList<>();
for (final Cell cell : cellIter(pageCoords, cellSet)) {
final List<Integer> coordList = cell.getCoordinateList();
int y = newyOffset;
int x = newxOffset;
if (coordList.size() > 0) {
if (coordList.get(0) == 0) {
newxOffset = xOffsset;
donex = new ArrayList<>();
}
x = newxOffset;
if (coordList.size() > 0)
x += coordList.get(0);
y = newyOffset;
if (coordList.size() > 1)
y += coordList.get(1); boolean stop = false;
if (coordList.size() > 0 && ignorex.contains(coordList.get(0))) {
if (!donex.contains(coordList.get(0))) {
newxOffset--;
donex.add(coordList.get(0));
}
stop = true;
}
if (coordList.size() > 1 && ignorey.contains(coordList.get(1))) {
if (!doney.contains(coordList.get(1))) {
newyOffset--;
doney.add(coordList.get(1));
}
stop = true;
}
if (stop) {
continue;
}
} final DataCell cellInfo = new DataCell(true, false, coordList);
cellInfo.setCoordinates(cell.getCoordinateList()); if (cell.getValue() != null) {
try {
cellInfo.setRawNumber(cell.getDoubleValue());
} catch (Exception e1) {
}
}
String cellValue = cell.getFormattedValue(); // First try to get a
// formatted value if (cellValue == null || cellValue.equals("null")) { //$NON-NLS-1$
cellValue =""; //$NON-NLS-1$
} //0值处理 为"null"的时候 ,cellValue.startsWith("| |style=")表示原值为0,直接让其为0 if (cellValue.startsWith("| |style=")) { //$NON-NLS-1$
cellValue ="0";
cellInfo.setRawValue(cellValue);
} if ( cellValue.length() < 1) {
final Object value = cell.getValue();
if (value == null || value.equals("null")) //$NON-NLS-1$
cellValue = ""; //$NON-NLS-1$
else {
try {
// TODO this needs to become query / execution specific
DecimalFormat myFormatter = new DecimalFormat(SaikuProperties.formatDefautNumberFormat); //$NON-NLS-1$
DecimalFormatSymbols dfs = new DecimalFormatSymbols(SaikuProperties.locale);
myFormatter.setDecimalFormatSymbols(dfs);
cellValue = myFormatter.format(cell.getValue());
}
catch (Exception e) {
// TODO: handle exception
}
}
// the raw value
} // Format string is relevant for Excel export
// xmla cells can throw an error on this
try {
String formatString = (String) cell.getPropertyValue(Property.StandardCellProperty.FORMAT_STRING);
if (formatString != null && !formatString.startsWith("|")) {
cellInfo.setFormatString(formatString);
} else {
formatString = formatString.substring(1, formatString.length());
cellInfo.setFormatString(formatString.substring(0, formatString.indexOf("|")));
}
} catch (Exception e) {
// we tried
} Map<String, String> cellProperties = new HashMap<>();
String val = Olap4jUtil.parseFormattedCellValue(cellValue, cellProperties); //注釋掉properties style=white設置,防止導出的excel 指標信息無線條
/*if (!cellProperties.isEmpty()) {
cellInfo.setProperties(cellProperties);
}*/
cellInfo.setFormattedValue(val);
matrix.set(x, y, cellInfo);
}
return matrix; } /**
* Populates cells in the matrix corresponding to a particular axis.
*
* @param matrix
* Matrix to populate
* @param axis
* Axis
* @param axisInfo
* Description of axis
* @param isColumns
* True if columns, false if rows
* @param oldoffset
* Ordinal of first cell to populate in matrix
*/
private void populateAxis(final Matrix matrix, final CellSetAxis axis, final AxisInfo axisInfo,
final boolean isColumns, final int oldoffset) {
if (axis == null) {
return;
} int offset = oldoffset; final Member[] prevMembers = new Member[axisInfo.getWidth()];
final MemberCell[] prevMemberInfo = new MemberCell[axisInfo.getWidth()];
final Member[] members = new Member[axisInfo.getWidth()]; // For each axis' position
for (int i = 0; i < axis.getPositions().size(); i++) {
final int x = offset + i;
final Position position = axis.getPositions().get(i);
int yOffset = 0;
final List<Member> memberList = position.getMembers();
boolean stop = false; // For each position's member
for (int j = 0; j < memberList.size(); j++) {
Member member = memberList.get(j);
final AxisOrdinalInfo ordinalInfo = axisInfo.ordinalInfos.get(j);
List<Integer> depths = ordinalInfo.depths;
Collections.sort(depths); // If it is not the last member (the one with the highest depth)
if (member.getDepth() < Collections.max(depths)) {
stop = true; if (isColumns) {
ignorex.add(i);
} else {
ignorey.add(i);
} break;
} if (ordinalInfo.getDepths().size() > 0 && member.getDepth() < ordinalInfo.getDepths().get(0)) {
break;
} // It stores each position's member in members array sorted by its depth
final int y = yOffset + ordinalInfo.depths.indexOf(member.getDepth());
members[y] = member;
yOffset += ordinalInfo.getWidth();
} if (stop) {
offset--;
continue;
} boolean expanded = false;
boolean same = true; for (int y = 0; y < members.length; y++) {
final MemberCell memberInfo = new MemberCell();
final Member member = members[y]; // The index of the member on its position
int index = memberList.indexOf(member); if (index >= 0) {
final AxisOrdinalInfo ordinalInfo = axisInfo.ordinalInfos.get(index);
int depth_i = ordinalInfo.getDepths().indexOf(member.getDepth());
if (depth_i > 0) {
expanded = true;
}
} memberInfo.setExpanded(expanded);
same = same && i > 0 && Olap4jUtil.equal(prevMembers[y], member); if (member != null) {
if (x - 1 == offset)
memberInfo.setLastRow(true); matrix.setOffset(oldoffset);
memberInfo.setRawValue(member.getUniqueName());
memberInfo.setFormattedValue(member.getCaption()); // First try to get a formatted value
memberInfo.setParentDimension(member.getDimension().getName());
memberInfo.setUniquename(member.getUniqueName());
memberInfo.setHierarchy(member.getHierarchy().getUniqueName());
memberInfo.setLevel(member.getLevel().getUniqueName());
// try {
// memberInfo.setChildMemberCount(member.getChildMemberCount());
// } catch (OlapException e) {
// e.printStackTrace();
// throw new RuntimeException(e);
// }
// NamedList<Property> values = member.getLevel().getProperties();
// for(int j=0; j<values.size();j++){
// String val;
// try {
// val = member.getPropertyFormattedValue(values.get(j));
// } catch (OlapException e) {
// e.printStackTrace();
// throw new RuntimeException(e);
// }
// memberInfo.setProperty(values.get(j).getCaption(), val);
// } // if (y > 0) {
// for (int previ = y-1; previ >= 0;previ--) {
// if(prevMembers[previ] != null) {
// memberInfo.setRightOf(prevMemberInfo[previ]);
// memberInfo.setRightOfDimension(prevMembers[previ].getDimension().getName());
// previ = -1;
// }
// }
// } // if (member.getParentMember() != null)
// memberInfo.setParentMember(member.getParentMember().getUniqueName()); } else {
memberInfo.setRawValue(null);
memberInfo.setFormattedValue(null);
memberInfo.setParentDimension(null);
} if (isColumns) {
memberInfo.setRight(false);
memberInfo.setSameAsPrev(same);
if (member != null)
memberInfo.setParentDimension(member.getDimension().getName());
matrix.set(x, y, memberInfo);
} else {
memberInfo.setRight(false);
memberInfo.setSameAsPrev(false);
matrix.set(y, x, memberInfo);
} int x_parent = isColumns ? x : y-1;
int y_parent = isColumns ? y-1 : x; if (index >= 0) {
final AxisOrdinalInfo ordinalInfo = axisInfo.ordinalInfos.get(index);
int depth_i = ordinalInfo.getDepths().indexOf(member.getDepth());
while (depth_i > 0) {
depth_i--;
int parentDepth = (ordinalInfo.getDepths().get(depth_i));
Member parent = member.getParentMember();
while (parent != null && parent.getDepth() > parentDepth) {
parent = parent.getParentMember();
}
final MemberCell pInfo = new MemberCell();
if (parent != null) {
pInfo.setRawValue(parent.getUniqueName());
pInfo.setFormattedValue(parent.getCaption()); // First try to get a formatted value
pInfo.setParentDimension(parent.getDimension().getName());
pInfo.setHierarchy(parent.getHierarchy().getUniqueName());
pInfo.setUniquename(parent.getUniqueName());
pInfo.setLevel(parent.getLevel().getUniqueName());
} else {
pInfo.setRawValue("");
pInfo.setFormattedValue(""); // First try to get a formatted value
pInfo.setParentDimension(member.getDimension().getName());
pInfo.setHierarchy(member.getHierarchy().getUniqueName());
pInfo.setLevel(member.getLevel().getUniqueName());
pInfo.setUniquename("");
}
matrix.set(x_parent, y_parent, pInfo);
if (isColumns) {
y_parent--;
} else {
x_parent--;
}
}
} prevMembers[y] = member;
prevMemberInfo[y] = memberInfo;
members[y] = null;
}
}
}
}
saiku.table.css
.workspace_results {
overflow: auto;
margin-right: 10px;
margin-left: 10px;
padding-top: 10px;
line-height: 1.3;
min-height: 708px;
} .workspace_results table {
width: auto;
margin-bottom: 0;
table-layout: fixed;
border-spacing: 0;
border-collapse: separate;
border-right: 1px solid #d5d5d5;
border-bottom: 1px solid #d5d5d5;
} .workspace_results th,
.workspace_results td {
padding: 3px 9px 3px 4px;
} .workspace_results td {
font-size: 10px;
} .workspace_results table th {
margin-bottom: 0;
font-weight: 500;
border-left: 1px solid #d5d5d5;
white-space: normal;
} .workspace_results table th.all_null {
border: none;
text-align: center;
} .workspace_results table th.col {
border-top: 1px solid #d5d5d5;
background: #f9f9f9;
text-align: left;
} .workspace_results table th.col_total_corner {
border-top: 1px solid #d5d5d5;
border-bottom: 1px none #d5d5d5;
border-left: 1px none #d5d5d5;
background: #f9f9f9;
text-align: left;
} .workspace_results table th.col_total_first {
border-top: 1px solid #d5d5d5;
border-left: 1px solid #d5d5d5;
background: #f9f9f9;
text-align: left;
} .workspace_results table th.col_null {
background: #f9f9f9;
} .workspace_results table th.row {
border-top: 1px solid #d5d5d5;
background: #f9f9f9;
text-align: left;
white-space: nowrap;
} .workspace_results table th.row_total {
border-top: 1px solid #d5d5d5;
border-left: 1px none #d5d5d5;
background: #f9f9f9;
text-align: left;
} .workspace_results table th.row_total_first {
border-top: 1px solid #d5d5d5;
border-left: 1px solid #d5d5d5;
background: #f9f9f9;
text-align: left;
} .workspace_results table th.row_total_corner {
border-top: 1px none #d5d5d5;
border-right: 1px none #d5d5d5;
border-bottom: 1px none #d5d5d5;
border-left: 1px solid #d5d5d5;
background: #f9f9f9;
text-align: left;
} .workspace_results table th.row_header {
border-top: 1px solid #d5d5d5;
/*border-bottom: 1px solid #d5d5d5;*/
background: #ececec;
} .workspace_results table th.row_null {
background: #f9f9f9;
} .workspace_results table td.data {
border-top: 1px solid #d5d5d5;
border-left: 1px solid #d5d5d5;
text-align: right;
white-space: nowrap;
} .workspace_results table td.total {
font-weight: bold;
background: #fbfbfb;
} .workspace_results table tbody tr:hover {
background: #edf4fa !important;
} .workspace_results table td.cellhighlight:hover {
background: #c2d5e2 !important;
} .workspace_results table td.cellhighlight {
cursor: pointer;
} .workspace_results .headerhighlight th.row:hover,
.workspace_results .headerhighlight th.col:hover {
background: #c2d5e2 !important;
} .workspace_results .headerhighlight th.row,
.workspace_results .headerhighlight th.col {
cursor: pointer;
} .workspace_results table td.selected,
.workspace_results table td.ui-selected,
.workspace_results table td.ui-selecting {
background: #c2d5e2 !important;
} /* For some weird reason you can not have all css queries
separated by commas, and have just one of these: */ .workspace_results:fullscreen {
width: 100%;
height: 100%;
background-color: #fff;
font-weight: 500;
} .workspace_results:-moz-full-screen {
width: 100%;
height: 100%;
background-color: #fff;
} .workspace_results:-webkit-full-screen {
width: 100%;
height: 100%;
background-color: #fff;
} .workspace_results:fullscreen table {
margin: 0 auto;
font-size: 200%;
font-weight: 500;
} .workspace_results: table td {
font-size: 100%;
} .workspace_results:-webkit-full-screen table {
margin: 0 auto;
font-size: 200%;
font-weight: 500;
} .workspace_results:-webkit-full-screen table td {
font-size: 100%;
} .workspace_results:-moz-full-screen table {
margin: 0 auto;
font-size: 200%;
} .workspace_results:-moz-full-screen table td {
font-size: 100%;
}
Saiku导出excel指标列无线条以及0与空值显示问题(三十二)的更多相关文章
- saiku导出excel单元格格式与中文列宽自动适应
在saiku导出excel后打开发现单元格的整数也显示为小数,并且含有中文的列宽没有自动适应,解决办法如下: 打开ExcelWorksheetBuilder.java文件,找到applyCellFor ...
- C# 导出 Excel 数字列出现‘0’的解决办法
在DataGird的中某一列全是数字并且长度大于15的字符,在导出excel时数字列第15-18位全部为0. 解决办法:在需导出数字列前加入英文字符状态的单引号(‘ ), 如: <asp:Tem ...
- C#导出Excel动态列
一.用StreamWrite流对象,导出Excel 1. string _sPath = GenerateSalaryMonthlyReport(dgvSalarySum); System.Diagn ...
- C#导出带有格式的Excel(列宽,合并单元格,显示边框线,加背景颜色等)
源地址:http://blog.sina.com.cn/s/blog_74f702e60101au55.html 导出excel相关设置:http://blog.csdn.net/wanmingtom ...
- POI导出Excel(xls、xlsx均可以,也支持图片)——(三)
Jar包
- php导出excel不知道列数 php26进制函数
function num2Letter($num) { $num = intval($num); if ($num <= 0) return false; $letterArr = array( ...
- NPOI导出EXCEL后公式无结果,公式重新计算开关
但其实这个选项是可以控制的.在NPOI中,这个属性叫做XSSFSheet.ForceFormulaRecalculation. 打开该选项的话,代码为 sheet1.ForceFormulaRecal ...
- 三十二、MySQL 导出数据
MySQL 导出数据 MySQL中你可以使用SELECT...INTO OUTFILE语句来简单的导出数据到文本文件上. 使用 SELECT ... INTO OUTFILE 语句导出数据 以下实例中 ...
- 聊聊高并发(三十二)实现一个基于链表的无锁Set集合
Set表示一种没有反复元素的集合类,在JDK里面有HashSet的实现,底层是基于HashMap来实现的.这里实现一个简化版本号的Set,有下面约束: 1. 基于链表实现.链表节点依照对象的hashC ...
随机推荐
- 每日命令:(9)touch
linux的touch命令不常用,一般在使用make的时候可能会用到,用来修改文件时间戳,或者新建一个不存在的文件. 1.命令格式: touch [选项]... 文件... 2.命令参数: -a ...
- react入门-----(jsx语法,在react中获取真实的dom节点)
1.jsx语法 var names = ['Alice', 'Emily', 'Kate']; <!-- HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 JSX 的 ...
- vue 刷新当前页面的时候重新调用新的cookie
data() { return{ AdminToken: this.getCookie('token'), } }, updated() { //刷新当前页面的时候重新调用新的cookie this. ...
- 【Codeforces 1009C】Annoying Present
[链接] 我是链接,点我呀:) [题意] 题意 [题解] 其实就是让你最后这n个数字的和最大. 加上的x没有关系.因为肯定都是加上n个x 所以直接加上就可以了 主要在于如何选取j 显然我们要找到一个位 ...
- 【Codeforces 140C】New Year Snowmen
[链接] 我是链接,点我呀:) [题意] 题意 [题解] 每次都选择剩余个数最多的3个不同数字组成一组. 优先消耗剩余个数多的数字 这样能尽量让剩余的数字总数比较多,从而更加可能得到更多的3个组合 [ ...
- 【Codeforces 711C】Coloring Trees
[链接] 我是链接,点我呀:) [题意] 连续相同的数字分为一段 你可以改变其中0为1~m中的某个数字(改变成不同数字需要不同花费) 问你最后如果要求分成恰好k段的话,最少需要多少花费 [题解] dp ...
- Codeforces 121A Lucky Sum
Lucky Sum Time Limit: 2000ms Memory Limit: 262144KB This problem will be judged on CodeForces. Origi ...
- Codeforces Round #235 (Div. 2)
A. Vanya and Cards time limit per test 1 second memory limit per test 256 megabytes input standard i ...
- js 发布订阅模式
//发布订阅模式 class EventEmiter{ constructor(){ //维护一个对象 this._events={ } } on(eventName,callback){ if( t ...
- java 源码分析1 -String
1. String的本质是一个 char数组,实现了CharSequence 接口, /** The value is used for character storage. */ private f ...