Merge remote-tracking branch 'origin/GT-3648-dragonmacher-csv-escape-quotes'

This commit is contained in:
ghidravore 2020-08-04 16:55:52 -04:00
commit e135e77f1f
2 changed files with 170 additions and 18 deletions

View File

@ -35,18 +35,25 @@ public final class GTableToCSV {
final static String TITLE = "Export to CSV";
public final static void writeCSV(File file, GTable table) {
ConvertTask task = new ConvertTask(file, table, table.getModel());
ConvertTask task = new ConvertTask(file, table);
new TaskLauncher(task, table, 0);
}
public final static void writeCSVUsingColunns(File file, GTable table,
List<Integer> selectedColumns) {
ConvertTask task = new ConvertTask(file, table, table.getModel(), selectedColumns);
ConvertTask task = new ConvertTask(file, table, selectedColumns);
new TaskLauncher(task, table, 0);
}
private final static void writeCSV(File file, GTable table, GTableColumnModel columnModel,
TableModel model, List<Integer> columns, TaskMonitor monitor) throws IOException {
final static void writeCSV(File file, GTable table,
List<Integer> columns, TaskMonitor monitor) throws IOException {
PrintWriter writer = new PrintWriter(file);
writeCSV(writer, table, columns, monitor);
}
final static void writeCSV(PrintWriter writer, GTable table,
List<Integer> columns, TaskMonitor monitor) {
List<TableColumn> tableColumns = null;
if (columns.isEmpty()) {
@ -56,7 +63,7 @@ public final class GTableToCSV {
tableColumns = getTableColumnsByIndex(table, columns);
}
PrintWriter writer = new PrintWriter(file);
TableModel model = table.getModel();
try {
writeColumnNames(writer, tableColumns, model, monitor);
writeNewLine(writer);
@ -142,7 +149,8 @@ public final class GTableToCSV {
final int column) {
final String[] result = new String[1];
try {
SwingUtilities.invokeAndWait(() -> result[0] = getTableCellValue(table, model, row, column));
SwingUtilities
.invokeAndWait(() -> result[0] = getTableCellValue(table, model, row, column));
}
catch (InterruptedException e) {
return null;
@ -301,14 +309,19 @@ public final class GTableToCSV {
* <p>
* Note: when importing into Excel, the quotes are stripped off.
*/
private final static void writeField(PrintWriter writer, String fieldValue, TaskMonitor monitor) {
private final static void writeField(PrintWriter writer, String fieldValue,
TaskMonitor monitor) {
writer.print("\"");
for (int i = 0; i < fieldValue.length(); ++i) {
if (monitor.isCancelled()) {
break;
}
if (fieldValue.charAt(i) == '"') {//embedded separator
writer.print("\"");
writer.print("\\\"");
}
else if (fieldValue.charAt(i) == ',') {
writer.print("\\,");
}
else {
writer.print(fieldValue.charAt(i));
@ -319,36 +332,30 @@ public final class GTableToCSV {
private static class ConvertTask extends Task {
private final GTable table;
private TableModel model;
private GTableColumnModel columnModel;
private File file;
private List<Integer> columns = new ArrayList<Integer>();
ConvertTask(File file, GTable table, TableModel model) {
ConvertTask(File file, GTable table) {
super(GTableToCSV.TITLE, true, true, true);
this.file = file;
this.table = table;
this.columnModel = (GTableColumnModel) table.getColumnModel();
this.model = model;
}
ConvertTask(File file, GTable table, TableModel model, List<Integer> columns) {
ConvertTask(File file, GTable table, List<Integer> columns) {
super(GTableToCSV.TITLE, true, true, true);
this.file = file;
this.table = table;
this.columns = columns;
this.columnModel = (GTableColumnModel) table.getColumnModel();
this.model = model;
}
@Override
public void run(TaskMonitor monitor) {
try {
GTableToCSV.writeCSV(file, table, columnModel, model, columns, monitor);
GTableToCSV.writeCSV(file, table, columns, monitor);
}
catch (IOException e) {
Msg.error(GTable.class.getName(), e.getMessage());
Msg.error(GTableToCSV.class.getName(), e.getMessage());
}
DockingWindowManager manager = DockingWindowManager.getInstance(table);

View File

@ -0,0 +1,145 @@
/* ###
* IP: GHIDRA
*
* 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 docking.widgets.table;
import static org.junit.Assert.*;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import org.junit.Test;
import ghidra.util.task.TaskMonitor;
public class GTableCSVTest {
@Test
public void testCSV_QuotesGetEscaped() {
AnyObjectTableModel<CSVRowObject> model =
new AnyObjectTableModel<>("MyModel", CSVRowObject.class,
"getName", "getDescription", "getNumber");
//@formatter:off
List<CSVRowObject> data = Arrays.asList(
new CSVRowObject("Bob", "Bobby", 11),
new CSVRowObject("Joan", "Joan has \"quoted\" text", 0),
new CSVRowObject("Sam", "\"Sam has a single quote text", 23),
new CSVRowObject("Time", "Tim is last", 33)
);
//@formatter:on
model.setModelData(data);
GTable table = new GTable(model);
List<Integer> columns = new ArrayList<>();
PrintWriterSpy writer = new PrintWriterSpy();
GTableToCSV.writeCSV(writer, table, columns, TaskMonitor.DUMMY);
assertRowValues(data, writer);
}
@Test
public void testCSV_CommasGetEscaped() {
AnyObjectTableModel<CSVRowObject> model =
new AnyObjectTableModel<>("MyModel", CSVRowObject.class,
"getName", "getDescription", "getNumber");
//@formatter:off
List<CSVRowObject> data = Arrays.asList(
new CSVRowObject("Bob", "Bobby", 11),
new CSVRowObject("Joan", "Joan has a comma, in her text", 0),
new CSVRowObject("Sam", ",Sam has a leading comma", 23),
new CSVRowObject("Time", "Tim is last", 33)
);
//@formatter:on
model.setModelData(data);
GTable table = new GTable(model);
List<Integer> columns = new ArrayList<>();
PrintWriterSpy writer = new PrintWriterSpy();
GTableToCSV.writeCSV(writer, table, columns, TaskMonitor.DUMMY);
assertRowValues(data, writer);
}
private void assertRowValues(List<CSVRowObject> data, PrintWriterSpy writer) {
String results = writer.toString();
String[] lines = results.split("\n");
for (int i = 1; i < lines.length; i++) {
int index = i - 1; // the first line is the header
CSVRowObject row = data.get(index);
String line = lines[i];
String[] columns = line.split("(?<!\\\\),");
String name = columns[0].replaceAll("\\\\,", ",");
name = name.replaceAll("\\\\\"", "\"");
assertEquals("\"" + row.getName() + "\"", name);
String description = columns[1].replaceAll("\\\\,", ",");
description = description.replaceAll("\\\\\"", "\"");
assertEquals("\"" + row.getDescription() + "\"", description);
String number = columns[2].replaceAll("\\\\,", ",");
number = number.replaceAll("\\\\\"", "\"");
assertEquals("\"" + row.getNumber() + "\"", number);
}
}
class CSVRowObject {
private String name;
private String description;
private int number;
CSVRowObject(String name, String description, int number) {
this.name = name;
this.description = description;
this.number = number;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public int getNumber() {
return number;
}
}
private class PrintWriterSpy extends PrintWriter {
private StringWriter stringWriter;
PrintWriterSpy() {
super(new StringWriter(), true);
stringWriter = ((StringWriter) out);
}
@Override
public String toString() {
return stringWriter.toString();
}
}
}