src.org.python.modules._csv.PyWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jython-standalone Show documentation
Show all versions of jython-standalone Show documentation
Jython is an implementation of the high-level, dynamic, object-oriented
language Python written in 100% Pure Java, and seamlessly integrated with
the Java platform. It thus allows you to run Python on any Java platform.
/* Copyright (c)2017 Jython Developers */
package org.python.modules._csv;
import org.python.core.Py;
import org.python.core.PyException;
import org.python.core.PyFloat;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PyType;
import org.python.core.PyUnicode;
import org.python.core.Traverseproc;
import org.python.core.Visitproc;
import org.python.expose.ExposedType;
import org.python.expose.ExposedMethod;
import org.python.expose.ExposedGet;
/**
* CSV file writer.
*
* Analogous to CPython's _csv.c::WriterObj struct.
*/
@ExposedType(name = "_csv.writer", doc = PyWriter.writer_doc)
public class PyWriter extends PyObject implements Traverseproc {
public static final String writer_doc = "CSV writer\n\n"//
+ "Writer objects are responsible for generating tabular data\n"
+ "in CSV format from sequence input.\n";
public static final PyType TYPE = PyType.fromClass(PyWriter.class);
/** Parsing dialect. */
@ExposedGet
public PyDialect dialect;
/** Output lines writer callable. */
private PyObject writeline;
/** Buffer for parser.join. */
private StringBuffer rec;
/** Length of record. */
private int rec_len = 0;
/** Number of fields in record. */
private int num_fields = 0;
/** Whether field should be quoted during a join. */
private boolean quoted = false;
public PyWriter(PyObject writeline, PyDialect dialect) {
this.writeline = writeline;
this.dialect = dialect;
}
public static PyString __doc__writerows = Py.newString(//
"writerows(sequence of sequences)\n\n"
+ "Construct and write a series of sequences to a csv file. Non-string\n"
+ "elements will be converted to string.");
public void writerows(PyObject seqseq) {
writer_writerows(seqseq);
}
@ExposedMethod
final void writer_writerows(PyObject seqseq) {
PyObject row_iter;
PyObject row_obj;
boolean result;
row_iter = seqseq.__iter__();
if (row_iter == null) {
throw _csv.Error("writerows() argument must be iterable");
}
while ((row_obj = row_iter.__iternext__()) != null) {
result = writerow(row_obj);
if (!result) {
break;
}
}
}
public static PyString __doc__writerow = Py.newString(//
"writerow(sequence)\n\n"
+ "Construct and write a CSV record from a sequence of fields. Non-string\n"
+ "elements will be converted to string.");
public boolean writerow(PyObject seq) {
return writer_writerow(seq);
}
@ExposedMethod
final boolean writer_writerow(PyObject seq) {
int len;
int i;
if (!seq.isSequenceType()) {
throw _csv.Error("sequence expected");
}
len = seq.__len__();
if (len < 0) {
return false;
}
// Join all fields in internal buffer.
join_reset();
for (i = 0; i < len; i++) {
PyObject field;
boolean append_ok;
quoted = false;
field = seq.__getitem__(i);
if (field == null) {
return false;
}
switch (dialect.quoting) {
case QUOTE_NONNUMERIC:
try {
field.__float__();
} catch (PyException ex) {
quoted = true;
}
break;
case QUOTE_ALL:
quoted = true;
break;
default:
quoted = false;
}
if (field instanceof PyUnicode) {
// Unicode fields get the default encoding (must yield U16 bytes).
append_ok = join_append(((PyString) field).encode(), len == 1);
} else if (field instanceof PyString) {
// Not unicode, so must be U16 bytes.
append_ok = join_append(field.toString(), len == 1);
} else if (field == Py.None) {
append_ok = join_append("", len == 1);
} else {
PyObject str;
// XXX: in 3.x this check can go away and we can just always use __str__
if (field.getClass() == PyFloat.class) {
str = field.__repr__();
} else {
str = field.__str__();
}
if (str == null) {
return false;
}
append_ok = join_append(str.toString(), len == 1);
}
if (!append_ok) {
return false;
}
}
// Add line terminator.
if (!join_append_lineterminator()) {
return false;
}
writeline.__call__(new PyString(rec.toString()));
return true;
}
private void join_reset() {
rec_len = 0;
num_fields = 0;
quoted = false;
rec = new StringBuffer();
}
private boolean join_append_lineterminator() {
rec.append(dialect.lineterminator);
return true;
}
private boolean join_append(String field, boolean quote_empty) {
int rec_len;
rec_len = join_append_data(field, quote_empty, false);
if (rec_len < 0) {
return false;
}
this.rec_len = join_append_data(field, quote_empty, true);
num_fields++;
return true;
}
/**
* This method behaves differently depending on the value of copy_phase: if copy_phase is false,
* then the method determines the new record length. If copy_phase is true then the new field is
* appended to the record.
*/
private int join_append_data(String field, boolean quote_empty, boolean copy_phase) {
int i;
// If this is not the first field we need a field separator.
if (num_fields > 0) {
addChar(dialect.delimiter, copy_phase);
}
// Handle preceding quote
if (copy_phase && quoted) {
addChar(dialect.quotechar, copy_phase);
}
// parsing below is based on _csv.c which expects all strings to be terminated
// with a nul byte.
field += '\0';
// Copy/count field data.
for (i = 0;; i++) {
char c = field.charAt(i);
boolean want_escape = false;
if (c == '\0') {
break;
}
if (c == dialect.delimiter || c == dialect.escapechar || c == dialect.quotechar
|| dialect.lineterminator.indexOf(c) > -1) {
if (dialect.quoting == QuoteStyle.QUOTE_NONE) {
want_escape = true;
} else {
if (c == dialect.quotechar) {
if (dialect.doublequote) {
addChar(dialect.quotechar, copy_phase);
} else {
want_escape = true;
}
}
if (!want_escape) {
quoted = true;
}
}
if (want_escape) {
if (dialect.escapechar == '\0') {
throw _csv.Error("need to escape, but no escapechar set");
}
addChar(dialect.escapechar, copy_phase);
}
}
// Copy field character into record buffer.
addChar(c, copy_phase);
}
// If field is empty check if it needs to be quoted.
if (i == 0 && quote_empty) {
if (dialect.quoting == QuoteStyle.QUOTE_NONE) {
throw _csv.Error("single empty field record must be quoted");
} else {
quoted = true;
}
}
// Handle final quote character on field.
if (quoted) {
if (copy_phase) {
addChar(dialect.quotechar, copy_phase);
} else {
// Didn't know about leading quote until we found it necessary in field
// data - compensate for it now.
rec_len += 2;
}
}
return rec_len;
}
private void addChar(char c, boolean copy_phase) {
if (copy_phase) {
rec.append(c);
}
rec_len++;
}
/* Traverseproc implementation */
@Override
public int traverse(Visitproc visit, Object arg) {
if (dialect != null) {
int retVal = visit.visit(dialect, arg);
if (retVal != 0) {
return retVal;
}
}
return writeline != null ? visit.visit(writeline, arg) : 0;
}
@Override
public boolean refersDirectlyTo(PyObject ob) {
return ob != null && (ob == dialect || ob == writeline);
}
}