From ee14955605185343894810b53c37941cfef9d192 Mon Sep 17 00:00:00 2001 From: David Huynh Date: Fri, 30 Jul 2010 02:25:58 +0000 Subject: [PATCH] Added new command Transpose Cells in Columns into Rows. git-svn-id: http://google-refine.googlecode.com/svn/trunk@1111 7d457c2a-affb-35e4-300a-418c747d4874 --- .../metaweb/gridworks/GridworksServlet.java | 1 + .../cell/TransposeColumnsIntoRowsCommand.java | 42 +++++ .../model/changes/MassRowColumnChange.java | 139 ++++++++++++++ .../TransposeColumnsIntoRowsOperation.java | 171 ++++++++++++++++++ .../core/scripts/project/browsing-engine.js | 4 + .../data-table/data-table-column-header-ui.js | 77 ++++++++ .../transpose-columns-into-rows.html | 38 ++++ 7 files changed, 472 insertions(+) create mode 100644 main/src/com/metaweb/gridworks/commands/cell/TransposeColumnsIntoRowsCommand.java create mode 100644 main/src/com/metaweb/gridworks/model/changes/MassRowColumnChange.java create mode 100644 main/src/com/metaweb/gridworks/operations/cell/TransposeColumnsIntoRowsOperation.java create mode 100644 main/webapp/modules/core/scripts/views/data-table/transpose-columns-into-rows.html diff --git a/main/src/com/metaweb/gridworks/GridworksServlet.java b/main/src/com/metaweb/gridworks/GridworksServlet.java index 82bc51089..11e84ba5b 100644 --- a/main/src/com/metaweb/gridworks/GridworksServlet.java +++ b/main/src/com/metaweb/gridworks/GridworksServlet.java @@ -71,6 +71,7 @@ public class GridworksServlet extends Butterfly { {"mass-edit", "com.metaweb.gridworks.commands.cell.MassEditCommand"}, {"join-multi-value-cells", "com.metaweb.gridworks.commands.cell.JoinMultiValueCellsCommand"}, {"split-multi-value-cells", "com.metaweb.gridworks.commands.cell.SplitMultiValueCellsCommand"}, + {"transpose-columns-into-rows", "com.metaweb.gridworks.commands.cell.TransposeColumnsIntoRowsCommand"}, {"add-column", "com.metaweb.gridworks.commands.column.AddColumnCommand"}, {"remove-column", "com.metaweb.gridworks.commands.column.RemoveColumnCommand"}, diff --git a/main/src/com/metaweb/gridworks/commands/cell/TransposeColumnsIntoRowsCommand.java b/main/src/com/metaweb/gridworks/commands/cell/TransposeColumnsIntoRowsCommand.java new file mode 100644 index 000000000..1b9238bc0 --- /dev/null +++ b/main/src/com/metaweb/gridworks/commands/cell/TransposeColumnsIntoRowsCommand.java @@ -0,0 +1,42 @@ +package com.metaweb.gridworks.commands.cell; + +import java.io.IOException; +import java.util.Properties; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.metaweb.gridworks.commands.Command; +import com.metaweb.gridworks.model.AbstractOperation; +import com.metaweb.gridworks.model.Project; +import com.metaweb.gridworks.operations.cell.TransposeColumnsIntoRowsOperation; +import com.metaweb.gridworks.process.Process; + +public class TransposeColumnsIntoRowsCommand extends Command { + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + try { + Project project = getProject(request); + + String startColumnName = request.getParameter("startColumnName"); + int columnCount = Integer.parseInt(request.getParameter("columnCount")); + String combinedColumnName = request.getParameter("combinedColumnName"); + + boolean prependColumnName = Boolean.parseBoolean(request.getParameter("prependColumnName")); + String separator = request.getParameter("separator"); + boolean ignoreBlankCells = Boolean.parseBoolean(request.getParameter("ignoreBlankCells")); + + AbstractOperation op = new TransposeColumnsIntoRowsOperation( + startColumnName, columnCount, combinedColumnName, prependColumnName, separator, ignoreBlankCells); + + Process process = op.createProcess(project, new Properties()); + + performProcessAndRespond(request, response, project, process); + } catch (Exception e) { + respondException(response, e); + } + } +} diff --git a/main/src/com/metaweb/gridworks/model/changes/MassRowColumnChange.java b/main/src/com/metaweb/gridworks/model/changes/MassRowColumnChange.java new file mode 100644 index 000000000..76951fac2 --- /dev/null +++ b/main/src/com/metaweb/gridworks/model/changes/MassRowColumnChange.java @@ -0,0 +1,139 @@ +package com.metaweb.gridworks.model.changes; + +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import com.metaweb.gridworks.history.Change; +import com.metaweb.gridworks.model.Column; +import com.metaweb.gridworks.model.Project; +import com.metaweb.gridworks.model.Row; +import com.metaweb.gridworks.util.Pool; + +public class MassRowColumnChange implements Change { + final protected List _newColumns; + final protected List _newRows; + protected List _oldColumns; + protected List _oldRows; + + public MassRowColumnChange(List newColumns, List newRows) { + _newColumns = newColumns; + _newRows = newRows; + } + + public void apply(Project project) { + synchronized (project) { + _oldColumns = new ArrayList(project.columnModel.columns); + _oldRows = new ArrayList(project.rows); + + project.columnModel.columns.clear(); + project.columnModel.columns.addAll(_newColumns); + + project.rows.clear(); + project.rows.addAll(_newRows); + + project.update(); + } + } + + public void revert(Project project) { + synchronized (project) { + project.columnModel.columns.clear(); + project.columnModel.columns.addAll(_oldColumns); + + project.rows.clear(); + project.rows.addAll(_oldRows); + + project.update(); + } + } + + public void save(Writer writer, Properties options) throws IOException { + writer.write("newColumnCount="); writer.write(Integer.toString(_newColumns.size())); writer.write('\n'); + for (Column column : _newColumns) { + column.save(writer); + writer.write('\n'); + } + writer.write("oldColumnCount="); writer.write(Integer.toString(_oldColumns.size())); writer.write('\n'); + for (Column column : _oldColumns) { + column.save(writer); + writer.write('\n'); + } + writer.write("newRowCount="); writer.write(Integer.toString(_newRows.size())); writer.write('\n'); + for (Row row : _newRows) { + row.save(writer, options); + writer.write('\n'); + } + writer.write("oldRowCount="); writer.write(Integer.toString(_oldRows.size())); writer.write('\n'); + for (Row row : _oldRows) { + row.save(writer, options); + writer.write('\n'); + } + writer.write("/ec/\n"); // end of change marker + } + + static public Change load(LineNumberReader reader, Pool pool) throws Exception { + List oldColumns = null; + List newColumns = null; + + List oldRows = null; + List newRows = null; + + String line; + while ((line = reader.readLine()) != null && !"/ec/".equals(line)) { + int equal = line.indexOf('='); + CharSequence field = line.subSequence(0, equal); + + if ("oldRowCount".equals(field)) { + int count = Integer.parseInt(line.substring(equal + 1)); + + oldRows = new ArrayList(count); + for (int i = 0; i < count; i++) { + line = reader.readLine(); + if (line != null) { + oldRows.add(Row.load(line, pool)); + } + } + } else if ("newRowCount".equals(field)) { + int count = Integer.parseInt(line.substring(equal + 1)); + + newRows = new ArrayList(count); + for (int i = 0; i < count; i++) { + line = reader.readLine(); + if (line != null) { + newRows.add(Row.load(line, pool)); + } + } + } else if ("oldColumnCount".equals(field)) { + int count = Integer.parseInt(line.substring(equal + 1)); + + oldColumns = new ArrayList(count); + for (int i = 0; i < count; i++) { + line = reader.readLine(); + if (line != null) { + oldColumns.add(Column.load(line)); + } + } + } else if ("newColumnCount".equals(field)) { + int count = Integer.parseInt(line.substring(equal + 1)); + + newColumns = new ArrayList(count); + for (int i = 0; i < count; i++) { + line = reader.readLine(); + if (line != null) { + newColumns.add(Column.load(line)); + } + } + } + } + + MassRowColumnChange change = new MassRowColumnChange(newColumns, newRows); + change._oldColumns = oldColumns; + change._oldRows = oldRows; + + return change; + } +} diff --git a/main/src/com/metaweb/gridworks/operations/cell/TransposeColumnsIntoRowsOperation.java b/main/src/com/metaweb/gridworks/operations/cell/TransposeColumnsIntoRowsOperation.java new file mode 100644 index 000000000..518240774 --- /dev/null +++ b/main/src/com/metaweb/gridworks/operations/cell/TransposeColumnsIntoRowsOperation.java @@ -0,0 +1,171 @@ +package com.metaweb.gridworks.operations.cell; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONWriter; + +import com.metaweb.gridworks.history.HistoryEntry; +import com.metaweb.gridworks.model.AbstractOperation; +import com.metaweb.gridworks.model.Cell; +import com.metaweb.gridworks.model.Column; +import com.metaweb.gridworks.model.Project; +import com.metaweb.gridworks.model.Row; +import com.metaweb.gridworks.model.changes.MassRowColumnChange; +import com.metaweb.gridworks.operations.OperationRegistry; + +public class TransposeColumnsIntoRowsOperation extends AbstractOperation { + final protected String _startColumnName; + final protected int _columnCount; + final protected String _combinedColumnName; + final protected boolean _prependColumnName; + final protected String _separator; + final protected boolean _ignoreBlankCells; + + static public AbstractOperation reconstruct(Project project, JSONObject obj) throws Exception { + return new TransposeColumnsIntoRowsOperation( + obj.getString("startColumnName"), + obj.getInt("columnCount"), + obj.getString("combinedColumnName"), + obj.getBoolean("prependColumnName"), + obj.getString("separator"), + obj.getBoolean("ignoreBlankCells") + ); + } + + public TransposeColumnsIntoRowsOperation( + String startColumnName, + int columnCount, + String combinedColumnName, + boolean prependColumnName, + String separator, + boolean ignoreBlankCells + ) { + _startColumnName = startColumnName; + _columnCount = columnCount; + _combinedColumnName = combinedColumnName; + _prependColumnName = prependColumnName; + _separator = separator; + _ignoreBlankCells = ignoreBlankCells; + } + + public void write(JSONWriter writer, Properties options) + throws JSONException { + + writer.object(); + writer.key("op"); writer.value(OperationRegistry.s_opClassToName.get(this.getClass())); + writer.key("description"); writer.value("Transpose cells in " + _columnCount + " column(s) starting with " + _startColumnName + " into rows"); + writer.key("startColumnName"); writer.value(_startColumnName); + writer.key("columnCount"); writer.value(_columnCount); + writer.key("combinedColumnName"); writer.value(_combinedColumnName); + writer.key("prependColumnName"); writer.value(_prependColumnName); + writer.key("separator"); writer.value(_separator); + writer.key("ignoreBlankCells"); writer.value(_ignoreBlankCells); + writer.endObject(); + } + + protected String getBriefDescription(Project project) { + return "Transpose cells in " + _columnCount + " column(s) starting with " + _startColumnName + " into rows"; + } + + @Override + protected HistoryEntry createHistoryEntry(Project project, long historyEntryID) throws Exception { + List newColumns = new ArrayList(); + List oldColumns = project.columnModel.columns; + + int columnsLeftToTranspose = _columnCount; + int startColumnIndex = oldColumns.size(); + for (int c = 0; c < oldColumns.size(); c++) { + Column column = oldColumns.get(c); + if (columnsLeftToTranspose == 0) { + // This column is beyond the columns to transpose + + Column newColumn = new Column(newColumns.size(), column.getOriginalHeaderLabel()); + newColumn.setName(column.getName()); + + newColumns.add(newColumn); + } else if (columnsLeftToTranspose < _columnCount) { + // This column is a column to transpose, but not the first + // nothing to do + + columnsLeftToTranspose--; + } else if (_startColumnName.equals(column.getName())) { + // This is the first column to transpose + + startColumnIndex = c; + + String columnName = _combinedColumnName != null && _combinedColumnName.length() > 0 ? _combinedColumnName : column.getName(); + Column newColumn = new Column(newColumns.size(), columnName); + + newColumns.add(newColumn); + + columnsLeftToTranspose--; + } else { + // This column is before all columns to transpose + + Column newColumn = new Column(newColumns.size(), column.getOriginalHeaderLabel()); + newColumn.setName(column.getName()); + + newColumns.add(newColumn); + } + } + + + List oldRows = project.rows; + List newRows = new ArrayList(oldRows.size() * _columnCount); + for (int r = 0; r < oldRows.size(); r++) { + Row oldRow = project.rows.get(r); + Row firstNewRow = new Row(newColumns.size()); + + newRows.add(firstNewRow); + + int transposedCells = 0; + for (int c = 0; c < oldColumns.size(); c++) { + Column column = oldColumns.get(c); + Cell cell = oldRow.getCell(column.getCellIndex()); + + if (c < startColumnIndex) { + firstNewRow.setCell(c, cell); + } else if (c == startColumnIndex || c < startColumnIndex + _columnCount) { + Cell newCell; + + if (cell == null || cell.value == null) { + if (_prependColumnName && !_ignoreBlankCells) { + newCell = new Cell(column.getName() + _separator, null); + } else { + continue; + } + } else if (_prependColumnName) { + newCell = new Cell(column.getName() + _separator + cell.value, null); + } else { + newCell = cell; + } + + if (transposedCells == 0) { + firstNewRow.setCell(startColumnIndex, newCell); + } else { + Row newRow = new Row(newColumns.size()); + + newRow.setCell(startColumnIndex, newCell); + newRows.add(newRow); + } + + transposedCells++; + } else { + firstNewRow.setCell(c - _columnCount + 1, cell); + } + } + } + + return new HistoryEntry( + historyEntryID, + project, + getBriefDescription(null), + this, + new MassRowColumnChange(newColumns, newRows) + ); + } +} diff --git a/main/webapp/modules/core/scripts/project/browsing-engine.js b/main/webapp/modules/core/scripts/project/browsing-engine.js index 63048db32..79ce16a1b 100644 --- a/main/webapp/modules/core/scripts/project/browsing-engine.js +++ b/main/webapp/modules/core/scripts/project/browsing-engine.js @@ -127,6 +127,10 @@ BrowsingEngine.prototype._updateFacetOrder = function() { this._facets = newFacets; }; +BrowsingEngine.prototype.getMode = function() { + return this._elmts.modeSelectors.find("input:checked")[0].value; +}; + BrowsingEngine.prototype.getJSON = function(keepUnrestrictedFacets, except) { var a = { facets: [], diff --git a/main/webapp/modules/core/scripts/views/data-table/data-table-column-header-ui.js b/main/webapp/modules/core/scripts/views/data-table/data-table-column-header-ui.js index a3700fb19..a8c177cb1 100644 --- a/main/webapp/modules/core/scripts/views/data-table/data-table-column-header-ui.js +++ b/main/webapp/modules/core/scripts/views/data-table/data-table-column-header-ui.js @@ -320,6 +320,15 @@ DataTableColumnHeaderUI.prototype._createMenuForColumnHeader = function(elmt) { } ] }, + { + label: "Transpose", + submenu: [ + { + label: "Cells Across Columns into Rows", + click: function() { self._doTransposeColumnsIntoRows(); } + } + ] + }, {}, ( this._dataTableView._getSortingCriterionForColumn(this._column.name) == null ? @@ -1083,6 +1092,74 @@ DataTableColumnHeaderUI.prototype._doSplitColumn = function() { footerElmts.cancelButton.click(dismiss); }; +DataTableColumnHeaderUI.prototype._doTransposeColumnsIntoRows = function() { + var self = this; + var dialog = $(DOM.loadHTML("core", "scripts/views/data-table/transpose-columns-into-rows.html")); + + var elmts = DOM.bind(dialog); + elmts.dialogHeader.text('Transpose Cells Across Columns into Rows'); + + var level = DialogSystem.showDialog(dialog); + var dismiss = function() { + DialogSystem.dismissUntil(level - 1); + }; + + var columns = theProject.columnModel.columns; + + elmts.cancelButton.click(function() { dismiss(); }); + elmts.okButton.click(function() { + var config = { + startColumnName: elmts.fromColumnSelect[0].value, + columnCount: elmts.toColumnSelect[0].value, + combinedColumnName: $.trim(elmts.combinedColumnNameInput[0].value), + prependColumnName: elmts.prependColumnNameCheckbox[0].checked, + separator: elmts.separatorInput[0].value, + ignoreBlankCells: elmts.ignoreBlankCellsCheckbox[0].checked + }; + + Gridworks.postProcess( + "transpose-columns-into-rows", + config, + null, + { modelsChanged: true } + ); + dismiss(); + }); + + for (var i = 0; i < columns.length; i++) { + var column = columns[i]; + var option = $('