Skip to content

Commit

Permalink
Append implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
jdunkerley committed Jul 4, 2022
1 parent 34ac335 commit da29efe
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.enso.table.data.column.storage;

import java.util.BitSet;
import java.util.Comparator;

import org.enso.table.data.column.operation.map.MapOpStorage;
import org.enso.table.data.column.operation.map.MapOperation;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.enso.table.data.column.storage;

import java.util.BitSet;
import java.util.Comparator;

import org.enso.table.data.column.builder.object.NumericBuilder;
import org.enso.table.data.column.operation.map.MapOpStorage;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.enso.table.data.column.storage;

import java.util.*;
import java.util.BitSet;
import java.util.OptionalLong;
import java.util.stream.LongStream;

import org.enso.table.data.column.builder.object.NumericBuilder;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.enso.table.data.column.storage;

import org.enso.table.data.mask.OrderMask;

import java.util.BitSet;

public class NullStorage extends Storage {
private final int size;

public NullStorage(int size) {
this.size = size;
}

@Override
public int size() {
return this.size;
}

@Override
public int countMissing() {
return this.size;
}

@Override
public long getType() { return Type.OBJECT; }

@Override
public boolean isNa(long idx) {
return true;
}

@Override
public Object getItemBoxed(int idx) {
return null;
}

@Override
protected boolean isOpVectorized(String name) {
return false;
}

@Override
protected Storage runVectorizedMap(String name, Object argument) {
return null;
}

@Override
protected Storage runVectorizedZip(String name, Storage argument) {
return null;
}

@Override
public Storage mask(BitSet mask, int cardinality) { return new NullStorage(cardinality); }

@Override
public Storage applyMask(OrderMask mask) { return new NullStorage(mask.getPositions().length); }

@Override
public Storage countMask(int[] counts, int total) {
return null;
}

@Override
public Storage slice(int offset, int limit) {
return new NullStorage(Math.min(size - offset, limit));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import org.enso.table.data.mask.OrderMask;

import java.util.BitSet;
import java.util.Comparator;

/** A column storing arbitrary objects. */
public class ObjectStorage extends Storage {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.enso.table.data.column.storage;

import java.lang.reflect.Array;
import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;

/**
* Wraps a storage in a list. Used for exposing a polyglot array interface back to Enso. This list
Expand Down Expand Up @@ -70,10 +73,9 @@ public boolean containsAll(Collection<?> c) {
@SuppressWarnings("rawtypes")
@Override
public boolean equals(Object obj) {
if (!(obj instanceof List)) {
if (!(obj instanceof List that)) {
return false;
}
List that = (List) obj;
if (that.size() != size()) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.enso.table.data.column.storage;

import java.util.BitSet;
import java.util.Comparator;
import org.enso.table.data.column.builder.object.StringBuilder;
import org.enso.table.data.column.operation.map.MapOpStorage;
import org.enso.table.data.column.operation.map.MapOperation;
Expand Down
57 changes: 57 additions & 0 deletions std-bits/table/src/main/java/org/enso/table/util/ColumnMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.enso.table.util;

import org.enso.table.data.column.storage.NullStorage;
import org.enso.table.data.table.Column;
import org.enso.table.data.table.Table;
import org.enso.table.problems.Problem;
import org.enso.table.problems.WithProblems;
import org.enso.table.util.problems.ColumnCountMismatch;
import org.enso.table.util.problems.ColumnNameMismatch;

import java.util.*;

public class ColumnMapper {
public static WithProblems<Table> MapColumnsByName(Table table, String[] columnNames) {
Column[] columns = new Column[columnNames.length];
Set<String> extras = new HashSet<>(Arrays.asList(columnNames));
Set<String> missing = new HashSet<>();

for (int i = 0; i < columnNames.length; i++) {
String name = columnNames[i];
Column column = table.getColumnByName(name);
if (column == null) {
missing.add(name);
NullStorage storage = new NullStorage(columns[0].getStorage().size());
columns[i] = new Column(java.util.UUID.randomUUID().toString(), storage);
} else {
extras.remove(name);
columns[i] = column;
}
}

Table newTable = new Table(columns);
if (missing.isEmpty() && extras.isEmpty()) {
return new WithProblems<>(newTable, Collections.emptyList());
} else {
Problem problem = new ColumnNameMismatch(missing.toArray(String[]::new), extras.toArray(String[]::new));
return new WithProblems<>(newTable, List.of(problem));
}
}

public static WithProblems<Table> MapColumnsByIndex(Table table, int columnCount) {
Column[] columns = table.getColumns();
if (columns.length == columnCount) {
return new WithProblems<>(table, Collections.emptyList());
}

Column[] newColumns = new Column[columnCount];
System.arraycopy(columns, 0, newColumns, 0, Math.min(columnCount, columns.length));
if (columns.length < columnCount) {
NullStorage storage = new NullStorage(columns[0].getStorage().size());
Arrays.fill(newColumns, columns.length, columnCount, new Column(java.util.UUID.randomUUID().toString(), storage));
}

Problem problem = new ColumnCountMismatch(columnCount, columns.length);
return new WithProblems<>(new Table(newColumns), List.of(problem));
}
}
44 changes: 39 additions & 5 deletions std-bits/table/src/main/java/org/enso/table/write/ExcelWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import org.enso.table.excel.ExcelRange;
import org.enso.table.excel.ExcelRow;
import org.enso.table.excel.ExcelSheet;
import org.enso.table.problems.WithProblems;
import org.enso.table.util.ColumnMapper;

import java.time.LocalDate;
import java.time.LocalDateTime;
Expand Down Expand Up @@ -121,8 +123,8 @@ public static void writeTableToRange(Workbook workbook, ExcelRange range, Existi
hasHeaders(sheet, expanded.getTopRow(), expanded.getLeftColumn(), expanded.getRightColumn());

if ((existingDataMode == ExistingDataMode.APPEND_BY_NAME || existingDataMode == ExistingDataMode.APPEND_BY_INDEX) &&
!checkExistingRange(workbook, expanded, false, sheet)) {
throw new ExistingDataException("Range already exists, and appended not currently implemented.");
checkExistingRange(workbook, expanded, false, sheet)) {
appendRangeWithTable(workbook, range, existingDataMode, table, rowLimit, headers, sheet, expanded);
} else {
updateRangeWithTable(workbook, expanded, range.isSingleCell(), existingDataMode, table, rowLimit, headers, sheet);
}
Expand All @@ -137,6 +139,38 @@ public static Workbook createWorkbook(boolean xls_format) {
return xls_format ? new HSSFWorkbook() : new XSSFWorkbook();
}

private static void appendRangeWithTable(Workbook workbook, ExcelRange range, ExistingDataMode existingDataMode, Table table, Long rowLimit, ExcelHeaders.HeaderBehavior headers, ExcelSheet sheet, ExcelRange expanded) throws RangeExceededException, ExistingDataException {
// Map Table
WithProblems<Table> mappedTable = switch (existingDataMode) {
case APPEND_BY_INDEX -> ColumnMapper.MapColumnsByIndex(table, expanded.getColumnCount());
case APPEND_BY_NAME -> {
if (headers == ExcelHeaders.HeaderBehavior.EXCEL_COLUMN_NAMES) {
throw new IllegalArgumentException("Cannot append by name when headers are not present in the existing data.");
}
String[] currentHeaders = sheet.get(expanded.getTopRow()).getCellsAsText(expanded.getLeftColumn(), expanded.getRightColumn());
yield ColumnMapper.MapColumnsByName(table, currentHeaders);
}
default ->
throw new IllegalArgumentException("The existing data mode '" + existingDataMode + "' is not supported.");
};

// Adjust output range to new area.
if (range.isSingleCell()) {
int bottomRow = expanded.getBottomRow();
int requiredRows = Math.min(table.rowCount(), rowLimit == null ? Integer.MAX_VALUE : rowLimit.intValue());
expanded = new ExcelRange(expanded.getSheetName(), bottomRow + 1, expanded.getLeftColumn(), bottomRow + requiredRows, expanded.getRightColumn());
} else {
int finalRow = expanded.getLastRow(sheet);
if (finalRow == expanded.getBottomRow()) {
throw new RangeExceededException("The range is already full.");
}

expanded = new ExcelRange(expanded.getSheetName(), finalRow + 1, expanded.getLeftColumn(), expanded.getBottomRow(), expanded.getRightColumn());
}

updateRangeWithTable(workbook, expanded, false, existingDataMode, table, rowLimit, ExcelHeaders.HeaderBehavior.EXCEL_COLUMN_NAMES, sheet);
}

private static void updateRangeWithTable(Workbook workbook, ExcelRange range, boolean singleCell, ExistingDataMode existingDataMode, Table table, Long rowLimit, ExcelHeaders.HeaderBehavior headers, ExcelSheet sheet)
throws RangeExceededException, ExistingDataException {
boolean writeHeaders = headers == ExcelHeaders.HeaderBehavior.USE_FIRST_ROW_AS_HEADERS;
Expand All @@ -160,7 +194,7 @@ private static void updateRangeWithTable(Workbook workbook, ExcelRange range, bo
}

// Check or Clear Current Range
if (!checkExistingRange(workbook, range, existingDataMode == ExistingDataMode.REPLACE, sheet)) {
if (checkExistingRange(workbook, range, existingDataMode == ExistingDataMode.REPLACE, sheet)) {
throw new ExistingDataException("Range is not empty, and cannot be replaced in current mode.");
}

Expand All @@ -182,14 +216,14 @@ private static boolean checkExistingRange(Workbook workbook, ExcelRange range, b
if (clear) {
cell.setBlank();
} else {
return false;
return true;
}
}
}
}
}

return true;
return false;
}

private static void writeTableToSheet(Workbook workbook, Sheet sheet, int firstRow, int firstColumn, Table table, Long rowLimit, boolean headers) {
Expand Down

0 comments on commit da29efe

Please sign in to comment.