1 package autotest.tko; 2 3 import autotest.common.StatusSummary; 4 import autotest.common.spreadsheet.Spreadsheet; 5 import autotest.common.spreadsheet.Spreadsheet.CellInfo; 6 import autotest.common.spreadsheet.Spreadsheet.Header; 7 import autotest.common.spreadsheet.Spreadsheet.HeaderImpl; 8 import autotest.common.table.DataSource.DataCallback; 9 import autotest.common.table.DataSource.Query; 10 import autotest.common.ui.NotifyManager; 11 12 import com.google.gwt.core.client.Duration; 13 import com.google.gwt.json.client.JSONArray; 14 import com.google.gwt.json.client.JSONObject; 15 import com.google.gwt.user.client.Command; 16 import com.google.gwt.user.client.DeferredCommand; 17 import com.google.gwt.user.client.IncrementalCommand; 18 19 import java.util.ArrayList; 20 import java.util.List; 21 22 public class SpreadsheetDataProcessor implements DataCallback { 23 private static final NotifyManager notifyManager = NotifyManager.getInstance(); 24 private static final int MAX_CELL_COUNT = 500000; 25 private static final int ROWS_PROCESSED_PER_ITERATION = 1000; 26 27 private Spreadsheet spreadsheet; 28 private TestGroupDataSource dataSource; 29 private int numTotalTests; 30 private CellInfo lastCellInfo; 31 32 private Header rowFields, columnFields; 33 private Command onFinished; 34 private Duration timer; 35 private Query currentQuery; 36 37 public static class TooManyCellsError extends Exception { 38 public int cellCount; 39 40 public TooManyCellsError(int cellCount) { 41 super(); 42 this.cellCount = cellCount; 43 } 44 } 45 46 private class ProcessDataCommand implements IncrementalCommand { 47 private int state = 0; 48 private List<JSONObject> counts; 49 private int currentRow = 0; 50 51 public ProcessDataCommand(List<JSONObject> counts) { 52 this.counts = counts; 53 } 54 55 public void processSomeRows() { 56 for (int i = 0; i < ROWS_PROCESSED_PER_ITERATION; i++, currentRow++) { 57 if (currentRow == counts.size()) { 58 state++; 59 return; 60 } 61 processRow(counts.get(currentRow).isObject()); 62 } 63 } 64 65 public boolean execute() { 66 switch (state) { 67 case 0: 68 notifyManager.setLoading(true); 69 numTotalTests = 0; 70 try { 71 processHeaders(); 72 } catch (TooManyCellsError exc) { 73 notifyManager.showError("Resulting spreadsheet contains " + exc.cellCount + 74 " cells, " + "exceeding maximum " + MAX_CELL_COUNT); 75 finalizeCommand(); 76 return false; 77 } 78 break; 79 case 1: 80 spreadsheet.prepareForData(); 81 break; 82 case 2: 83 processSomeRows(); 84 return true; 85 case 3: 86 // we must make the spreadsheet visible before rendering, or size computations 87 // won't work correctly 88 spreadsheet.setVisible(true); 89 break; 90 case 4: 91 spreadsheet.render(this); 92 state++; 93 return false; // render will re-add us to the command queue 94 case 5: 95 logTimer("Rendering"); 96 finalizeCommand(); 97 return false; 98 } 99 100 state++; 101 return true; 102 } 103 104 private void finalizeCommand() { 105 notifyManager.setLoading(false); 106 onFinished.execute(); 107 } 108 } 109 110 public SpreadsheetDataProcessor(Spreadsheet spreadsheet) { 111 this.spreadsheet = spreadsheet; 112 } 113 114 public void processHeaders() throws TooManyCellsError { 115 spreadsheet.setHeaderFields(rowFields, columnFields); 116 List<List<String>> rowHeaderValues = dataSource.getHeaderGroupValues(0); 117 List<List<String>> columnHeaderValues = dataSource.getHeaderGroupValues(1); 118 int totalCells = rowHeaderValues.size() * columnHeaderValues.size(); 119 if (totalCells > MAX_CELL_COUNT) { 120 throw new TooManyCellsError(totalCells); 121 } 122 for (List<String> header : rowHeaderValues) { 123 spreadsheet.addRowHeader(header); 124 } 125 for (List<String> header : columnHeaderValues) { 126 spreadsheet.addColumnHeader(header); 127 } 128 } 129 130 private void processRow(JSONObject group) { 131 JSONArray headerIndices = group.get("header_indices").isArray(); 132 int row = (int) headerIndices.get(0).isNumber().doubleValue(); 133 int column = (int) headerIndices.get(1).isNumber().doubleValue(); 134 CellInfo cellInfo = spreadsheet.getCellInfo(row, column); 135 StatusSummary statusSummary = StatusSummary.getStatusSummary( 136 group, 137 TestGroupDataSource.PASS_COUNT_FIELD, 138 TestGroupDataSource.COMPLETE_COUNT_FIELD, 139 TestGroupDataSource.INCOMPLETE_COUNT_FIELD, 140 TestGroupDataSource.GROUP_COUNT_FIELD); 141 numTotalTests += statusSummary.getTotal(); 142 cellInfo.contents = statusSummary.formatContents(); 143 cellInfo.cssClass = statusSummary.getCssClass(); 144 cellInfo.testCount = statusSummary.getTotal(); 145 cellInfo.testIndex = (int) group.get("test_idx").isNumber().doubleValue(); 146 lastCellInfo = cellInfo; 147 } 148 149 public void refresh(JSONObject condition, Command onFinished) { 150 timer = new Duration(); 151 this.onFinished = onFinished; 152 dataSource.query(condition, this); 153 } 154 155 public void onQueryReady(Query query) { 156 currentQuery = query; 157 query.getPage(null, null, null, this); 158 } 159 160 public void handlePage(List<JSONObject> data) { 161 logTimer("Server response"); 162 if (data.size() == 0) { 163 notifyManager.showMessage("No results for query"); 164 onFinished.execute(); 165 return; 166 } 167 168 DeferredCommand.addCommand(new ProcessDataCommand(data)); 169 } 170 171 public void handleTotalResultCount(int totalCount) {} 172 173 private void logTimer(String message) { 174 notifyManager.log(message + ": " + timer.elapsedMillis() + " ms"); 175 timer = new Duration(); 176 } 177 178 public int getNumTotalTests() { 179 return numTotalTests; 180 } 181 182 /** 183 * This is useful when there turns out to be only a single test return. 184 * @return the last CellInfo created. Should only really be called when there was only a single 185 * one. 186 */ 187 public CellInfo getLastCellInfo() { 188 assert numTotalTests == 1; 189 return lastCellInfo; 190 } 191 192 public void onError(JSONObject errorObject) { 193 onFinished.execute(); 194 } 195 196 public void setHeaders(List<HeaderField> rowFields, List<HeaderField> columnFields, 197 JSONObject queryParameters) { 198 this.rowFields = getHeaderSqlNames(rowFields); 199 this.columnFields = getHeaderSqlNames(columnFields); 200 201 List<List<String>> headerGroups = new ArrayList<List<String>>(); 202 headerGroups.add(this.rowFields); 203 headerGroups.add(this.columnFields); 204 dataSource.setHeaderGroups(headerGroups); 205 dataSource.setQueryParameters(queryParameters); 206 } 207 208 private Header getHeaderSqlNames(List<HeaderField> fields) { 209 Header header = new HeaderImpl(); 210 for (HeaderField field : fields) { 211 header.add(field.getSqlName()); 212 } 213 return header; 214 } 215 216 public void setDataSource(TestGroupDataSource dataSource) { 217 this.dataSource = dataSource; 218 } 219 220 public TestGroupDataSource getDataSource() { 221 return dataSource; 222 } 223 224 public Query getCurrentQuery() { 225 return currentQuery; 226 } 227 } 228