Render contextual rows when filtered.
git-svn-id: http://google-refine.googlecode.com/svn/trunk@51 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
parent
755b01c2c4
commit
98a16ca500
@ -9,6 +9,11 @@ import com.metaweb.gridworks.model.Row;
|
||||
|
||||
public class ConjunctiveFilteredRows implements FilteredRows {
|
||||
final protected List<RowFilter> _rowFilters = new LinkedList<RowFilter>();
|
||||
final protected boolean _contextual;
|
||||
|
||||
public ConjunctiveFilteredRows(boolean contextual) {
|
||||
_contextual = contextual;
|
||||
}
|
||||
|
||||
public void add(RowFilter rowFilter) {
|
||||
_rowFilters.add(rowFilter);
|
||||
@ -16,6 +21,14 @@ public class ConjunctiveFilteredRows implements FilteredRows {
|
||||
|
||||
@Override
|
||||
public void accept(Project project, RowVisitor visitor) {
|
||||
if (_contextual) {
|
||||
contextualAccept(project, visitor);
|
||||
} else {
|
||||
simpleAccept(project, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
protected void simpleAccept(Project project, RowVisitor visitor) {
|
||||
for (int i = 0; i < project.rows.size(); i++) {
|
||||
Row row = project.rows.get(i);
|
||||
|
||||
@ -28,7 +41,42 @@ public class ConjunctiveFilteredRows implements FilteredRows {
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
visitor.visit(project, i, row);
|
||||
visitor.visit(project, i, row, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void contextualAccept(Project project, RowVisitor visitor) {
|
||||
int lastVisitedRow = -1;
|
||||
|
||||
for (int i = 0; i < project.rows.size(); i++) {
|
||||
Row row = project.rows.get(i);
|
||||
|
||||
boolean ok = true;
|
||||
for (RowFilter rowFilter : _rowFilters) {
|
||||
if (!rowFilter.filterRow(project, i, row)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
if (row.contextRows != null && lastVisitedRow < i - 1) {
|
||||
for (int contextRowIndex : row.contextRows) {
|
||||
if (contextRowIndex > lastVisitedRow) {
|
||||
visitor.visit(
|
||||
project,
|
||||
contextRowIndex,
|
||||
project.rows.get(contextRowIndex),
|
||||
true
|
||||
);
|
||||
lastVisitedRow = contextRowIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visitor.visit(project, i, row, false);
|
||||
lastVisitedRow = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,12 +25,12 @@ public class Engine implements Jsonizable {
|
||||
_project = project;
|
||||
}
|
||||
|
||||
public FilteredRows getAllFilteredRows() {
|
||||
return getFilteredRows(null);
|
||||
public FilteredRows getAllFilteredRows(boolean contextual) {
|
||||
return getFilteredRows(null, contextual);
|
||||
}
|
||||
|
||||
public FilteredRows getFilteredRows(Facet except) {
|
||||
ConjunctiveFilteredRows cfr = new ConjunctiveFilteredRows();
|
||||
public FilteredRows getFilteredRows(Facet except, boolean contextual) {
|
||||
ConjunctiveFilteredRows cfr = new ConjunctiveFilteredRows(contextual);
|
||||
for (Facet facet : _facets) {
|
||||
if (facet != except) {
|
||||
RowFilter rowFilter = facet.getRowFilter();
|
||||
@ -68,7 +68,7 @@ public class Engine implements Jsonizable {
|
||||
|
||||
public void computeFacets() throws JSONException {
|
||||
for (Facet facet : _facets) {
|
||||
FilteredRows filteredRows = getFilteredRows(facet);
|
||||
FilteredRows filteredRows = getFilteredRows(facet, false);
|
||||
|
||||
facet.computeChoices(_project, filteredRows);
|
||||
}
|
||||
|
@ -4,6 +4,6 @@ import com.metaweb.gridworks.model.Project;
|
||||
import com.metaweb.gridworks.model.Row;
|
||||
|
||||
public interface RowVisitor {
|
||||
public boolean visit(Project project, int rowIndex, Row row);
|
||||
public boolean visit(Project project, int rowIndex, Row row, boolean contextual);
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ public class ExpressionNominalRowGrouper implements RowVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Project project, int rowIndex, Row row) {
|
||||
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) {
|
||||
if (_cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(_cellIndex);
|
||||
if (cell != null) {
|
||||
|
@ -24,7 +24,7 @@ public class ExpressionNumericRowBinner implements RowVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Project project, int rowIndex, Row row) {
|
||||
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) {
|
||||
if (_cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(_cellIndex);
|
||||
if (cell != null) {
|
||||
|
@ -51,7 +51,7 @@ public class ExportRowsCommand extends Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Project project, int rowIndex, Row row) {
|
||||
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) {
|
||||
boolean first = true;
|
||||
for (Column column : project.columnModel.columns) {
|
||||
if (first) {
|
||||
@ -74,7 +74,7 @@ public class ExportRowsCommand extends Command {
|
||||
}
|
||||
}.init(writer);
|
||||
|
||||
FilteredRows filteredRows = engine.getAllFilteredRows();
|
||||
FilteredRows filteredRows = engine.getAllFilteredRows(true);
|
||||
filteredRows.accept(project, visitor);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@ -94,7 +94,7 @@ public class ExportRowsCommand extends Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Project project, int rowIndex, Row row) {
|
||||
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) {
|
||||
boolean r = false;
|
||||
|
||||
if (total >= start && total < start + limit) {
|
||||
|
@ -40,17 +40,28 @@ public class GetRowsCommand extends Command {
|
||||
RowAccumulator acc = new RowAccumulator(start, limit) {
|
||||
JSONWriter writer;
|
||||
Properties options;
|
||||
Properties extra;
|
||||
|
||||
public RowAccumulator init(JSONWriter writer, Properties options) {
|
||||
this.writer = writer;
|
||||
this.options = options;
|
||||
|
||||
this.extra = new Properties();
|
||||
this.extra.put("contextual", true);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean internalVisit(int rowIndex, Row row) {
|
||||
public boolean internalVisit(int rowIndex, Row row, boolean contextual) {
|
||||
try {
|
||||
if (contextual) {
|
||||
options.put("extra", extra);
|
||||
} else {
|
||||
options.remove("extra");
|
||||
}
|
||||
options.put("rowIndex", rowIndex);
|
||||
|
||||
row.write(writer, options);
|
||||
} catch (JSONException e) {
|
||||
}
|
||||
@ -58,7 +69,7 @@ public class GetRowsCommand extends Command {
|
||||
}
|
||||
}.init(writer, options);
|
||||
|
||||
FilteredRows filteredRows = engine.getAllFilteredRows();
|
||||
FilteredRows filteredRows = engine.getAllFilteredRows(true);
|
||||
|
||||
writer.key("rows"); writer.array();
|
||||
filteredRows.accept(project, acc);
|
||||
@ -89,17 +100,19 @@ public class GetRowsCommand extends Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Project project, int rowIndex, Row row) {
|
||||
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) {
|
||||
boolean r = false;
|
||||
|
||||
if (total >= start && total < start + limit) {
|
||||
r = internalVisit(rowIndex, row);
|
||||
r = internalVisit(rowIndex, row, contextual);
|
||||
}
|
||||
if (!contextual) {
|
||||
total++;
|
||||
}
|
||||
total++;
|
||||
return r;
|
||||
}
|
||||
|
||||
protected boolean internalVisit(int rowIndex, Row row) {
|
||||
protected boolean internalVisit(int rowIndex, Row row, boolean contextual) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ public class ColumnModel implements Serializable, Jsonizable {
|
||||
final public List<ColumnGroup> columnGroups = new LinkedList<ColumnGroup>();
|
||||
|
||||
private int _maxCellIndex;
|
||||
private int _keyCellIndex;
|
||||
private int _keyColumnIndex;
|
||||
|
||||
transient protected Map<String, Column> _nameToColumn;
|
||||
transient protected Map<Integer, Column> _cellIndexToColumn;
|
||||
@ -45,13 +45,13 @@ public class ColumnModel implements Serializable, Jsonizable {
|
||||
return ++_maxCellIndex;
|
||||
}
|
||||
|
||||
public void setKeyCellIndex(int keyCellIndex) {
|
||||
public void setKeyColumnIndex(int keyColumnIndex) {
|
||||
// TODO: check validity of new cell index, e.g., it's not in any group
|
||||
this._keyCellIndex = keyCellIndex;
|
||||
this._keyColumnIndex = keyColumnIndex;
|
||||
}
|
||||
|
||||
public int getKeyCellIndex() {
|
||||
return _keyCellIndex;
|
||||
public int getKeyColumnIndex() {
|
||||
return _keyColumnIndex;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
@ -79,7 +79,7 @@ public class ColumnModel implements Serializable, Jsonizable {
|
||||
}
|
||||
writer.endArray();
|
||||
|
||||
writer.key("keyCellIndex"); writer.value(getKeyCellIndex());
|
||||
writer.key("keyCellIndex"); writer.value(getKeyColumnIndex());
|
||||
writer.key("columnGroups");
|
||||
writer.array();
|
||||
for (ColumnGroup g : _rootColumnGroups) {
|
||||
|
@ -4,8 +4,11 @@ import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import com.metaweb.gridworks.expr.ExpressionUtils;
|
||||
import com.metaweb.gridworks.history.History;
|
||||
import com.metaweb.gridworks.process.ProcessManager;
|
||||
|
||||
@ -35,10 +38,118 @@ public class Project implements Serializable {
|
||||
protected void internalInitialize() {
|
||||
processManager = new ProcessManager();
|
||||
|
||||
computeContext();
|
||||
recomputeRowContextDependencies();
|
||||
}
|
||||
|
||||
protected void computeContext() {
|
||||
// TODO
|
||||
static protected class Group {
|
||||
int[] cellIndices;
|
||||
int keyCellIndex;
|
||||
}
|
||||
|
||||
public void recomputeRowContextDependencies() {
|
||||
List<Group> keyedGroups = new ArrayList<Group>();
|
||||
|
||||
keyedGroups.add(createRootKeyedGroup());
|
||||
for (ColumnGroup group : columnModel.columnGroups) {
|
||||
if (group.keyColumnIndex >= 0) {
|
||||
Group keyedGroup = new Group();
|
||||
keyedGroup.keyCellIndex = columnModel.columns.get(group.keyColumnIndex).getCellIndex();
|
||||
keyedGroup.cellIndices = new int[group.columnSpan - 1];
|
||||
|
||||
int c = 0;
|
||||
for (int i = 0; i < group.columnSpan; i++) {
|
||||
int columnIndex = group.startColumnIndex + i;
|
||||
if (columnIndex != group.keyColumnIndex) {
|
||||
int cellIndex = columnModel.columns.get(columnIndex).getCellIndex();
|
||||
keyedGroup.cellIndices[c++] = cellIndex;
|
||||
}
|
||||
}
|
||||
|
||||
keyedGroups.add(keyedGroup);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(keyedGroups, new Comparator<Group>() {
|
||||
@Override
|
||||
public int compare(Group o1, Group o2) {
|
||||
return o2.cellIndices.length - o1.cellIndices.length; // larger groups first
|
||||
}
|
||||
});
|
||||
|
||||
int[] lastNonBlankRowsByGroup = new int[keyedGroups.size()];
|
||||
for (int i = 0; i < lastNonBlankRowsByGroup.length; i++) {
|
||||
lastNonBlankRowsByGroup[i] = -1;
|
||||
}
|
||||
|
||||
for (int r = 0; r < rows.size(); r++) {
|
||||
Row row = rows.get(r);
|
||||
row.contextRowSlots = null;
|
||||
row.contextCellSlots = null;
|
||||
|
||||
for (int g = 0; g < keyedGroups.size(); g++) {
|
||||
Group group = keyedGroups.get(g);
|
||||
|
||||
if (ExpressionUtils.isBlank(row.getCellValue(group.keyCellIndex))) {
|
||||
int contextRowIndex = lastNonBlankRowsByGroup[g];
|
||||
if (contextRowIndex >= 0) {
|
||||
for (int dependentCellIndex : group.cellIndices) {
|
||||
if (!ExpressionUtils.isBlank(row.getCellValue(dependentCellIndex))) {
|
||||
setRowDependency(
|
||||
row,
|
||||
dependentCellIndex,
|
||||
contextRowIndex,
|
||||
group.keyCellIndex
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lastNonBlankRowsByGroup[g] = r;
|
||||
}
|
||||
}
|
||||
|
||||
if (row.contextRowSlots != null) {
|
||||
row.contextRows = new ArrayList<Integer>();
|
||||
for (int index : row.contextRowSlots) {
|
||||
if (index >= 0) {
|
||||
row.contextRows.add(index);
|
||||
}
|
||||
}
|
||||
Collections.sort(row.contextRows);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Group createRootKeyedGroup() {
|
||||
int count = columnModel.getMaxCellIndex();
|
||||
|
||||
Group rootKeyedGroup = new Group();
|
||||
|
||||
rootKeyedGroup.cellIndices = new int[count - 1];
|
||||
rootKeyedGroup.keyCellIndex = columnModel.columns.get(columnModel.getKeyColumnIndex()).getCellIndex();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (i < rootKeyedGroup.keyCellIndex) {
|
||||
rootKeyedGroup.cellIndices[i] = i;
|
||||
} else if (i > rootKeyedGroup.keyCellIndex) {
|
||||
rootKeyedGroup.cellIndices[i - 1] = i;
|
||||
}
|
||||
}
|
||||
return rootKeyedGroup;
|
||||
}
|
||||
|
||||
protected void setRowDependency(Row row, int cellIndex, int contextRowIndex, int contextCellIndex) {
|
||||
int count = columnModel.getMaxCellIndex() + 1;
|
||||
if (row.contextRowSlots == null || row.contextCellSlots == null) {
|
||||
row.contextRowSlots = new int[count];
|
||||
row.contextCellSlots = new int[count];
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
row.contextRowSlots[i] = -1;
|
||||
row.contextCellSlots[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
row.contextRowSlots[cellIndex] = contextRowIndex;
|
||||
row.contextCellSlots[cellIndex] = contextCellIndex;
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,9 @@ public class Row implements Serializable, HasFields, Jsonizable {
|
||||
public boolean starred;
|
||||
final public List<Cell> cells;
|
||||
|
||||
transient public List<Integer> contextRows;
|
||||
transient public List<Integer> contextCells;
|
||||
transient public List<Integer> contextRows;
|
||||
transient public int[] contextRowSlots;
|
||||
transient public int[] contextCellSlots;
|
||||
|
||||
public Row(int cellCount) {
|
||||
cells = new ArrayList<Cell>(cellCount);
|
||||
@ -128,6 +129,15 @@ public class Row implements Serializable, HasFields, Jsonizable {
|
||||
if (options.containsKey("rowIndex")) {
|
||||
writer.key("i"); writer.value(options.get("rowIndex"));
|
||||
}
|
||||
if (options.containsKey("extra")) {
|
||||
Properties extra = (Properties) options.get("extra");
|
||||
if (extra != null) {
|
||||
for (Object key : extra.keySet()) {
|
||||
writer.key((String) key);
|
||||
writer.value(extra.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.endObject();
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ public class ApproveNewReconOperation extends EngineDependentMassCellOperation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Project project, int rowIndex, Row row) {
|
||||
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) {
|
||||
if (cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(cellIndex);
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class ApproveReconOperation extends EngineDependentMassCellOperation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Project project, int rowIndex, Row row) {
|
||||
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) {
|
||||
if (cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(cellIndex);
|
||||
if (cell.recon != null && cell.recon.candidates.size() > 0) {
|
||||
|
@ -63,7 +63,7 @@ public class ColumnAdditionOperation extends EngineDependentOperation {
|
||||
|
||||
List<CellAtRow> cellsAtRows = new ArrayList<CellAtRow>(project.rows.size());
|
||||
|
||||
FilteredRows filteredRows = engine.getAllFilteredRows();
|
||||
FilteredRows filteredRows = engine.getAllFilteredRows(false);
|
||||
filteredRows.accept(project, createRowVisitor(project, cellsAtRows));
|
||||
|
||||
String description = createDescription(column, cellsAtRows);
|
||||
@ -108,7 +108,7 @@ public class ColumnAdditionOperation extends EngineDependentOperation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Project project, int rowIndex, Row row) {
|
||||
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) {
|
||||
if (cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(cellIndex);
|
||||
if (cell.value != null) {
|
||||
|
@ -50,7 +50,7 @@ public class DiscardReconOperation extends EngineDependentMassCellOperation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Project project, int rowIndex, Row row) {
|
||||
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) {
|
||||
if (cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(cellIndex);
|
||||
|
||||
|
@ -38,7 +38,7 @@ abstract public class EngineDependentMassCellOperation extends EngineDependentOp
|
||||
|
||||
List<CellChange> cellChanges = new ArrayList<CellChange>(project.rows.size());
|
||||
|
||||
FilteredRows filteredRows = engine.getAllFilteredRows();
|
||||
FilteredRows filteredRows = engine.getAllFilteredRows(false);
|
||||
filteredRows.accept(project, createRowVisitor(project, cellChanges));
|
||||
|
||||
String description = createDescription(column, cellChanges);
|
||||
|
@ -60,7 +60,7 @@ public class ReconOperation extends EngineDependentOperation {
|
||||
|
||||
List<ReconEntry> entries = new ArrayList<ReconEntry>(project.rows.size());
|
||||
|
||||
FilteredRows filteredRows = engine.getAllFilteredRows();
|
||||
FilteredRows filteredRows = engine.getAllFilteredRows(false);
|
||||
filteredRows.accept(project, new RowVisitor() {
|
||||
int cellIndex;
|
||||
List<ReconEntry> entries;
|
||||
@ -72,7 +72,7 @@ public class ReconOperation extends EngineDependentOperation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Project project, int rowIndex, Row row) {
|
||||
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) {
|
||||
if (cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(cellIndex);
|
||||
if (cell.value != null) {
|
||||
|
@ -62,7 +62,7 @@ public class TextTransformOperation extends EngineDependentMassCellOperation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Project project, int rowIndex, Row row) {
|
||||
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) {
|
||||
if (cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(cellIndex);
|
||||
if (cell.value != null) {
|
||||
|
@ -250,6 +250,10 @@ DataTableView.prototype.render = function() {
|
||||
var tr = table.insertRow(table.rows.length);
|
||||
tr.className = (r % 2) == 1 ? "odd" : "even";
|
||||
|
||||
if ("contextual" in row && row.contextual) {
|
||||
$(tr).addClass("contextual");
|
||||
}
|
||||
|
||||
var td = tr.insertCell(tr.cells.length);
|
||||
$(td).html((row.i + 1) + ".");
|
||||
|
||||
|
@ -14,6 +14,10 @@ table.data-table tr.even {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
table.data-table tr.contextual {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
table.data-table td.column-header {
|
||||
background: #ddd;
|
||||
cursor: pointer;
|
||||
|
Loading…
Reference in New Issue
Block a user