Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.univocity.parsers.common.AbstractWriter Maven / Gradle / Ivy
Go to download
univocity's open source parsers for processing different text formats using a consistent API
/*******************************************************************************
* Copyright 2014 Univocity Software Pty Ltd
*
* 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 com.univocity.parsers.common;
import com.univocity.parsers.common.fields.*;
import com.univocity.parsers.common.input.*;
import com.univocity.parsers.common.processor.*;
import com.univocity.parsers.common.record.*;
import com.univocity.parsers.fixed.*;
import java.io.*;
import java.nio.charset.*;
import java.util.*;
/**
* The AbstractWriter class provides a common ground for all writers in univocity-parsers.
*
* It handles all settings defined by {@link CommonWriterSettings}, and delegates the writing algorithm implementation to its subclasses through the abstract method {@link AbstractWriter#processRow(Object[])}
*
* The following (absolutely required) attributes are exposed to subclasses:
*
* appender ({@link WriterCharAppender} ): the character writer that appends characters from a given input into an internal buffer
*
*
* @param The specific writer settings configuration class, which can potentially provide additional configuration options supported by the writer implementation.
* @author Univocity Software Pty Ltd - [email protected]
* @see com.univocity.parsers.csv.CsvWriter
* @see com.univocity.parsers.csv.CsvWriterSettings
* @see com.univocity.parsers.fixed.FixedWidthWriter
* @see com.univocity.parsers.fixed.FixedWidthWriterSettings
* @see com.univocity.parsers.tsv.TsvWriter
* @see com.univocity.parsers.tsv.TsvWriterSettings
* @see com.univocity.parsers.common.input.WriterCharAppender
* @see com.univocity.parsers.common.processor.RowWriterProcessor
*/
public abstract class AbstractWriter> {
@SuppressWarnings("rawtypes")
private final RowWriterProcessor writerProcessor;
private Writer writer;
private final boolean skipEmptyLines;
private final char comment;
private final WriterCharAppender rowAppender;
private final boolean isHeaderWritingEnabled;
private Object[] outputRow;
private int[] indexesToWrite;
private final char[] lineSeparator;
protected NormalizedString[] headers;
protected long recordCount = 0;
protected final String nullValue;
protected final String emptyValue;
protected final WriterCharAppender appender;
private final Object[] partialLine;
private int partialLineIndex = 0;
private Map> headerIndexes;
private int largestRowLength = -1;
protected boolean writingHeaders = false;
protected boolean[] headerTrimFlags;
private NormalizedString[] dummyHeaderRow;
protected boolean expandRows;
private boolean usingSwitch;
private boolean enableNewlineAfterRecord = true;
protected boolean usingNullOrEmptyValue;
protected final int whitespaceRangeStart;
private final boolean columnReorderingEnabled;
protected boolean ignoreLeading;
protected boolean ignoreTrailing;
private final CommonSettings internalSettings = new CommonSettings() {
@Override
protected DummyFormat createDefaultFormat() {
return DummyFormat.instance;
}
};
private final int errorContentLength;
/**
* All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be properly initialized.
* Important: by not providing an instance of {@link java.io.Writer} to this constructor, only the operations that write to Strings are available.
*
* @param settings the writer configuration
*/
public AbstractWriter(S settings) {
this((Writer) null, settings);
}
/**
* All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be properly initialized.
*
* @param file the output file that will be written with the format-specific records as defined by subclasses of {@link AbstractWriter}.
* @param settings the writer configuration
*/
public AbstractWriter(File file, S settings) {
this(ArgumentUtils.newWriter(file), settings);
}
/**
* All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be properly initialized.
*
* @param file the output file that will be written with the format-specific records as defined by subclasses of {@link AbstractWriter}.
* @param encoding the encoding of the file
* @param settings the writer configuration
*/
public AbstractWriter(File file, String encoding, S settings) {
this(ArgumentUtils.newWriter(file, encoding), settings);
}
/**
* All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be properly initialized.
*
* @param file the output file that will be written with the format-specific records as defined by subclasses of {@link AbstractWriter}.
* @param encoding the encoding of the file
* @param settings the writer configuration
*/
public AbstractWriter(File file, Charset encoding, S settings) {
this(ArgumentUtils.newWriter(file, encoding), settings);
}
/**
* All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be properly initialized.
*
* @param output the output stream that will be written with the format-specific records as defined by subclasses of {@link AbstractWriter}.
* @param settings the writer configuration
*/
public AbstractWriter(OutputStream output, S settings) {
this(ArgumentUtils.newWriter(output), settings);
}
/**
* All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be properly initialized.
*
* @param output the output stream that will be written with the format-specific records as defined by subclasses of {@link AbstractWriter}.
* @param encoding the encoding of the stream
* @param settings the writer configuration
*/
public AbstractWriter(OutputStream output, String encoding, S settings) {
this(ArgumentUtils.newWriter(output, encoding), settings);
}
/**
* All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be properly initialized.
*
* @param output the output stream that will be written with the format-specific records as defined by subclasses of {@link AbstractWriter}.
* @param encoding the encoding of the stream
* @param settings the writer configuration
*/
public AbstractWriter(OutputStream output, Charset encoding, S settings) {
this(ArgumentUtils.newWriter(output, encoding), settings);
}
/**
* All writers must support, at the very least, the settings provided by {@link CommonWriterSettings}. The AbstractWriter requires its configuration to be properly initialized.
*
* @param writer the output resource that will receive the format-specific records as defined by subclasses of {@link AbstractWriter}.
* @param settings the writer configuration
*/
public AbstractWriter(Writer writer, S settings) {
settings.autoConfigure();
this.ignoreLeading = settings.getIgnoreLeadingWhitespaces();
this.ignoreTrailing = settings.getIgnoreTrailingWhitespaces();
internalSettings.setMaxColumns(settings.getMaxColumns());
this.errorContentLength = settings.getErrorContentLength();
this.nullValue = settings.getNullValue();
this.emptyValue = settings.getEmptyValue();
this.lineSeparator = settings.getFormat().getLineSeparator();
this.comment = settings.getFormat().getComment();
this.skipEmptyLines = settings.getSkipEmptyLines();
this.writerProcessor = settings.getRowWriterProcessor();
this.usingSwitch = writerProcessor instanceof RowWriterProcessorSwitch;
this.expandRows = settings.getExpandIncompleteRows();
this.columnReorderingEnabled = settings.isColumnReorderingEnabled();
this.whitespaceRangeStart = settings.getWhitespaceRangeStart();
this.appender = new WriterCharAppender(settings.getMaxCharsPerColumn(), "", whitespaceRangeStart, settings.getFormat());
this.rowAppender = new WriterCharAppender(settings.getMaxCharsPerColumn(), "", whitespaceRangeStart, settings.getFormat());
this.writer = writer;
this.headers = NormalizedString.toIdentifierGroupArray(settings.getHeaders());
updateIndexesToWrite(settings);
this.partialLine = new Object[settings.getMaxColumns()];
this.isHeaderWritingEnabled = settings.isHeaderWritingEnabled();
if (writerProcessor instanceof DefaultConversionProcessor) {
DefaultConversionProcessor conversionProcessor = (DefaultConversionProcessor) writerProcessor;
conversionProcessor.context = null;
conversionProcessor.errorHandler = settings.getProcessorErrorHandler();
}
initialize(settings);
}
protected void enableNewlineAfterRecord(boolean enableNewlineAfterRecord) {
this.enableNewlineAfterRecord = enableNewlineAfterRecord;
}
/**
* Initializes the concrete implementation of this class with format-specific settings.
*
* @param settings the settings object specific to the format being written.
*/
protected abstract void initialize(S settings);
/**
* Update indexes to write based on the field selection provided by the user.
*/
private void updateIndexesToWrite(CommonSettings> settings) {
FieldSelector selector = settings.getFieldSelector();
if (selector != null) {
if (headers != null && headers.length > 0) {
indexesToWrite = selector.getFieldIndexes(headers);
if (columnReorderingEnabled) { //column reordering enabled?
int size = ArgumentUtils.removeAll(indexesToWrite, -1).length;
outputRow = new Object[size];
} else {
outputRow = new Object[headers.length];
}
} else if (!(selector instanceof FieldNameSelector) && !(selector instanceof ExcludeFieldNameSelector)) {
int rowLength = largestRowLength;
if ((selector instanceof FieldIndexSelector)) {
boolean gotLengthFromSelection = false;
for (Integer index : ((FieldIndexSelector) selector).get()) {
if (rowLength <= index) {
rowLength = index;
gotLengthFromSelection = true;
}
}
if (gotLengthFromSelection) {
rowLength++;
}
if (rowLength < largestRowLength) {
rowLength = largestRowLength;
}
} else {
rowLength = settings.getMaxColumns();
}
indexesToWrite = selector.getFieldIndexes(new NormalizedString[rowLength]); //generates a dummy header array - only the indexes matter so we are good
if (columnReorderingEnabled) { //column reordering enabled?
int size = ArgumentUtils.removeAll(indexesToWrite, -1).length;
outputRow = new Object[size];
} else {
outputRow = new Object[rowLength];
}
} else {
throw new IllegalStateException("Cannot select fields by name with no headers defined");
}
} else {
outputRow = null;
indexesToWrite = null;
}
}
/**
* Updates the selection of fields to write. This is useful if the input rows
* change during the writing process and their values need be allocated to specific columns.
*
* @param newFieldSelection the new selection of fields to write.
*/
public void updateFieldSelection(String... newFieldSelection) {
if (headers == null) {
throw new IllegalStateException("Cannot select fields by name. Headers not defined.");
}
internalSettings.selectFields(newFieldSelection);
updateIndexesToWrite(internalSettings);
}
/**
* Updates the selection of fields to write. This is useful if the input rows
* change during the writing process and their values need be allocated to specific columns.
*
* @param newFieldSelectionByIndex the new selection of fields to write.
*/
public void updateFieldSelection(Integer... newFieldSelectionByIndex) {
internalSettings.selectIndexes(newFieldSelectionByIndex);
updateIndexesToWrite(internalSettings);
}
/**
* Updates the selection of fields to exclude when writing. This is useful if the input rows
* change during the writing process and their values need be allocated to specific columns.
*
* @param fieldsToExclude the selection of fields to exclude from the output.
*/
public void updateFieldExclusion(String... fieldsToExclude) {
if (headers == null) {
throw new IllegalStateException("Cannot de-select fields by name. Headers not defined.");
}
internalSettings.excludeFields(fieldsToExclude);
updateIndexesToWrite(internalSettings);
}
/**
* Updates the selection of fields to exclude when writing. This is useful if the input rows
* change during the writing process and their values need be allocated to specific columns.
*
* @param fieldIndexesToExclude the selection of fields to exclude from the output.
*/
public void updateFieldExclusion(Integer... fieldIndexesToExclude) {
internalSettings.excludeIndexes(fieldIndexesToExclude);
updateIndexesToWrite(internalSettings);
}
/**
* Submits a row for processing by the format-specific implementation.
*
* @param row the data to be written for a single record in the output.
*/
private void submitRow(Object[] row) {
if (largestRowLength < row.length) {
largestRowLength = row.length;
}
if (writingHeaders) {
headerTrimFlags = new boolean[headers.length];
for (int i = 0; i < headers.length; i++) {
headerTrimFlags[i] = !headers[i].isLiteral();
}
}
processRow(row);
}
/**
* Format-specific implementation for writing a single record into the output.
*
* The AbstractWriter handles the initialization and processing of the output until it is ready to be written (generally, reorganizing it and passing it on to a {@link RowWriterProcessor}).
* It then delegates the record to the writer-specific implementation defined by {@link #processRow(Object[])}. In general, an implementation of {@link AbstractWriter#processRow(Object[])} will perform the following steps:
*
* Iterate over each object in the given input and convert it to the expected String representation.
* The conversion must happen using the provided {@link AbstractWriter#appender} object. The an individual value is processed, the {@link AbstractWriter#appendValueToRow()} method must be called.
* This will clear the accumulated value in {@link AbstractWriter#appender} and add it to the output row.
* Format specific separators and other characters must be introduced to the output row using {@link AbstractWriter#appendToRow(char)}
*
* Once the {@link #processRow(Object[])} method returns, a row will be written to the output with the processed information, and a newline will be automatically written after the given contents, unless this is a
* {@link com.univocity.parsers.fixed.FixedWidthWriter} whose {@link FixedWidthWriterSettings#getWriteLineSeparatorAfterRecord()} evaluates to {@code false}. The newline character sequence will conform to what is specified in {@link Format#getLineSeparator()}
* This cycle repeats until the writing process is stopped by the user or an error happens.
* In case of errors, the unchecked exception {@link TextWritingException} will be thrown and all resources in use will be closed automatically. The exception should contain the cause and more information about the output state when the error happened.
*
* @param row the data to be written to the output in the expected format.
* @see com.univocity.parsers.common.input.CharAppender
* @see com.univocity.parsers.common.CommonWriterSettings
*/
protected abstract void processRow(Object[] row);
/**
* Appends the processed sequence of characters in {@link AbstractWriter#appender} to the output row.
*/
protected final void appendValueToRow() {
rowAppender.append(appender);
}
/**
* Appends the given character to the output row.
*
* @param ch the character to append to the output row
*/
protected final void appendToRow(char ch) {
rowAppender.append(ch);
}
/**
* Appends the given character sequence to the output row
*
* @param chars the sequence of characters to append to the output row
*/
protected final void appendToRow(char[] chars) {
rowAppender.append(chars);
}
/**
* Writes the headers defined in {@link CommonSettings#getHeaders()}
* A {@link TextWritingException} will be thrown if no headers were defined or if records were already written to the output.
*/
public final void writeHeaders() {
writeHeaders(NormalizedString.toArray(this.headers));
}
/**
* Writes the given collection of headers to the output.
* A {@link TextWritingException} will be thrown if no headers were defined or if records were already written to the output.
*
* @param headers the headers to write to the output.
*/
public final void writeHeaders(Collection> headers) {
if (headers != null && headers.size() > 0) {
writeHeaders(headers.toArray(new String[headers.size()]));
} else {
throw throwExceptionAndClose("No headers defined.");
}
}
/**
* Writes the given collection of headers to the output.
* A {@link TextWritingException} will be thrown if no headers were defined or if records were already written to the output.
*
* @param headers the headers to write to the output.
*/
public final void writeHeaders(String... headers) {
if (recordCount > 0) {
throw throwExceptionAndClose("Cannot write headers after records have been written.", headers, null);
}
if (headers != null && headers.length > 0) {
writingHeaders = true;
if (columnReorderingEnabled && outputRow != null) {
fillOutputRow(headers);
headers = Arrays.copyOf(outputRow, outputRow.length, String[].class);
}
this.headers = NormalizedString.toIdentifierGroupArray(headers);
submitRow(headers);
internalWriteRow();
writingHeaders = false;
} else {
throw throwExceptionAndClose("No headers defined.", headers, null);
}
}
/**
* Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and writes them, then finally and closes the output
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param allRecords the records to be transformed by a {@link RowWriterProcessor} and then written to the output
*/
public final void processRecordsAndClose(Iterable> allRecords) {
try {
processRecords(allRecords);
} finally {
close();
}
}
/**
* Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and writes them, then finally and closes the output
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param allRecords the records to be transformed by a {@link RowWriterProcessor} and then written to the output
*/
public final void processRecordsAndClose(Object[] allRecords) {
try {
processRecords(allRecords);
} finally {
close();
}
}
/**
* Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and writes them.
* The output will remain open for further writing.
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param records the records to be transformed by a {@link RowWriterProcessor} and then written to the output
*/
public final void processRecords(Iterable> records) {
for (Object record : records) {
processRecord(record);
}
}
/**
* Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and writes them.
* The output will remain open for further writing.
* * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param records the records to transformed by a {@link RowWriterProcessor} and then written to the output
*/
public final void processRecords(Object[] records) {
for (Object record : records) {
processRecord(record);
}
}
/**
* Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and writes them.
* The output will remain open for further writing.
* * A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param records the records to transformed by a {@link RowWriterProcessor} and then written to the output
*/
public final void processRecords(T[] records) {
for (T record : records) {
processRecord(record);
}
}
/**
* Processes the data given for an individual record with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, then writes it.
* The output will remain open for further writing.
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param record the information of a single record to be transformed by a {@link RowWriterProcessor} and then written to the output
*/
public final void processRecord(Object... record) {
processRecord((Object) record);
}
public final void processRecord(T record) {
processRecord((Object) (record == null ? null : record.getValues()));
}
/**
* Processes the data given for an individual record with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, then writes it.
* The output will remain open for further writing.
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param record the information of a single record to be transformed by a {@link RowWriterProcessor} and then written to the output
*/
@SuppressWarnings("unchecked")
public final void processRecord(Object record) {
if (this.writerProcessor == null) {
String recordDescription;
if (record instanceof Object[]) {
recordDescription = Arrays.toString((Object[]) record);
} else {
recordDescription = String.valueOf(record);
}
String message = "Cannot process record '" + recordDescription + "' without a writer processor. Please define a writer processor instance in the settings or use the 'writeRow' methods.";
this.throwExceptionAndClose(message);
}
Object[] row;
try {
if (usingSwitch) {
dummyHeaderRow = ((RowWriterProcessorSwitch) writerProcessor).getHeaders(record);
if (dummyHeaderRow == null) {
dummyHeaderRow = this.headers;
}
row = writerProcessor.write(record, dummyHeaderRow, indexesToWrite);
} else {
row = writerProcessor.write(record, getRowProcessorHeaders(), indexesToWrite);
}
} catch (DataProcessingException e) {
e.setErrorContentLength(errorContentLength);
throw e;
}
if (row != null) {
writeRow(row);
}
}
private NormalizedString[] getRowProcessorHeaders() {
if (headers == null && indexesToWrite == null) {
return null;
}
return headers;
}
/**
* Iterates over all records, writes them and closes the output.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecordsAndClose(Iterable)} for that.
*
* @param Collection of objects containing values of a row
* @param allRows the rows to be written to the output
*/
public final > void writeRowsAndClose(Iterable allRows) {
try {
writeRows(allRows);
} finally {
close();
}
}
/**
* Iterates over all records, writes them and closes the output.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecordsAndClose(Object[])} for that.
*
* @param allRows the rows to be written to the output
*/
public final void writeRowsAndClose(Collection allRows) {
try {
writeRows(allRows);
} finally {
close();
}
}
/**
* Iterates over all records, writes them and closes the output.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecordsAndClose(Iterable)} for that.
*
* @param allRows the rows to be written to the output
*/
public final void writeStringRowsAndClose(Collection allRows) {
try {
writeStringRows(allRows);
} finally {
close();
}
}
/**
* Iterates over all records, writes them and closes the output.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecordsAndClose(Iterable)} for that.
*
* @param allRows the rows to be written to the output
*/
public final void writeRecordsAndClose(Collection extends Record> allRows) {
try {
writeRecords(allRows);
} finally {
close();
}
}
/**
* Iterates over all records, writes them and closes the output.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecordsAndClose(Object[])} for that.
*
* @param allRows the rows to be written to the output
*/
public final void writeRowsAndClose(Object[][] allRows) {
try {
writeRows(allRows);
} finally {
close();
}
}
/**
* Iterates over all records and writes them to the output.
* The output will remain open for further writing.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Object[])} for that.
*
* @param rows the rows to be written to the output
*/
public final void writeRows(Object[][] rows) {
for (Object[] row : rows) {
writeRow(row);
}
}
/**
* Iterates over all records and writes them to the output.
* The output will remain open for further writing.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that.
*
* @param Collection of objects containing values of a row
* @param rows the rows to be written to the output
*/
public final > void writeRows(Iterable rows) {
for (Collection> row : rows) {
writeRow(row);
}
}
/**
* Iterates over all records and writes them to the output.
* The output will remain open for further writing.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that.
*
* @param rows the rows to be written to the output
*/
public final void writeStringRows(Collection rows) {
for (String[] row : rows) {
writeRow(row);
}
}
/**
* Iterates over all records and writes them to the output.
* The output will remain open for further writing.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that.
*
* @param rows the rows to be written to the output
*/
public final void writeRecords(Collection extends Record> rows) {
for (Record row : rows) {
writeRecord(row);
}
}
/**
* Iterates over all records and writes them to the output.
* The output will remain open for further writing.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that.
*
* @param Collection of objects containing values of a row
* @param rows the rows to be written to the output
*/
public final > void writeStringRows(Iterable rows) {
for (Collection> row : rows) {
writeRow(row.toArray());
}
}
/**
* Iterates over all records and writes them to the output.
* The output will remain open for further writing.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that.
*
* @param rows the rows to be written to the output
*/
public final void writeRows(Collection rows) {
for (Object[] row : rows) {
writeRow(row);
}
}
/**
* Writes the data given for an individual record.
* The output will remain open for further writing.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that.
*
* @param row the information of a single record to be written to the output
*/
public final void writeRow(Collection> row) {
if (row == null) {
return;
}
writeRow(row.toArray());
}
/**
* Writes the data given for an individual record.
* The output will remain open for further writing.
* If the given data is null or empty, and {@link CommonSettings#getSkipEmptyLines()} is true, the input will be just ignored.
* If {@link CommonSettings#getSkipEmptyLines()} is false, then an empty row will be written to the output (as specified by {@link AbstractWriter#writeEmptyRow()}).
* In case of any errors, a {@link TextWritingException} will be thrown and the {@link java.io.Writer} given in the constructor will be closed.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that.
*
* @param row the information of a single record to be written to the output
*/
public final void writeRow(String[] row) {
writeRow((Object[]) row);
}
/**
* Writes the data given for an individual record.
* The output will remain open for further writing.
* If the given data is null or empty, and {@link CommonSettings#getSkipEmptyLines()} is true, the input will be just ignored.
* If {@link CommonSettings#getSkipEmptyLines()} is false, then an empty row will be written to the output (as specified by {@link AbstractWriter#writeEmptyRow()}).
* In case of any errors, a {@link TextWritingException} will be thrown and the {@link java.io.Writer} given in the constructor will be closed.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that.
*
* @param row the information of a single record to be written to the output
*/
public final void writeRecord(T row) {
if (row == null) {
if (skipEmptyLines) {
return;
} else {
writeEmptyRow();
return;
}
}
if (recordCount == 0 && isHeaderWritingEnabled && headers == null) {
String[] headers = row.getMetaData().headers();
if (headers != null) {
this.headers = NormalizedString.toArray(headers);
}
}
writeRow((Object[]) row.getValues());
}
/**
* Writes the data given for an individual record.
* The output will remain open for further writing.
* If the given data is null or empty, and {@link CommonSettings#getSkipEmptyLines()} is true, the input will be just ignored.
* If {@link CommonSettings#getSkipEmptyLines()} is false, then an empty row will be written to the output (as specified by {@link AbstractWriter#writeEmptyRow()}).
* In case of any errors, a {@link TextWritingException} will be thrown and the {@link java.io.Writer} given in the constructor will be closed.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that.
*
* @param row the information of a single record to be written to the output
*/
public final void writeRow(Object... row) {
try {
if (recordCount == 0 && isHeaderWritingEnabled && headers != null) {
writeHeaders();
}
if (row == null || (row.length == 0 && !expandRows)) {
if (skipEmptyLines) {
return;
} else {
writeEmptyRow();
return;
}
}
row = adjustRowLength(row);
submitRow(row);
internalWriteRow();
} catch (Throwable ex) {
throw throwExceptionAndClose("Error writing row.", row, ex);
}
}
protected Object[] expand(Object[] row, int length, Integer h2) {
if (row.length < length) {
return Arrays.copyOf(row, length);
} else if (h2 != null && row.length < h2) {
return Arrays.copyOf(row, h2);
}
if (length == -1 && h2 == null && row.length < largestRowLength) {
return Arrays.copyOf(row, largestRowLength);
}
return row;
}
/**
* Writes a plain (potentially free-text) String as a line to the output.
* A newline will automatically written after the given contents, unless this is a
* {@link com.univocity.parsers.fixed.FixedWidthWriter} whose
* {@link FixedWidthWriterSettings#getWriteLineSeparatorAfterRecord()} evaluates to {@code false}.
* The newline character sequence will conform to what is specified in {@link Format#getLineSeparator()}
* The writer implementation has no control over the format of this content.
* The output will remain open for further writing.
*
* @param row the line to be written to the output
*/
public final void writeRow(String row) {
try {
writer.write(row);
if (enableNewlineAfterRecord) {
writer.write(lineSeparator);
}
} catch (Throwable ex) {
throw throwExceptionAndClose("Error writing row.", row, ex);
}
}
/**
* Writes an empty line to the output, unless this is a {@link com.univocity.parsers.fixed.FixedWidthWriter} whose
* {@link FixedWidthWriterSettings#getWriteLineSeparatorAfterRecord()} evaluates to {@code false}.
* The newline character sequence will conform to what is specified in {@link Format#getLineSeparator()}
* The output will remain open for further writing.
*/
public final void writeEmptyRow() {
try {
if (enableNewlineAfterRecord) {
writer.write(lineSeparator);
}
} catch (Throwable ex) {
throw throwExceptionAndClose("Error writing empty row.", Arrays.toString(lineSeparator), ex);
}
}
/**
* Writes a comment row to the output.
* A newline will automatically written after the given contents, unless this is a
* {@link com.univocity.parsers.fixed.FixedWidthWriter} whose
* {@link FixedWidthWriterSettings#getWriteLineSeparatorAfterRecord()} evaluates to {@code false}.
* The newline character sequence will conform to what is specified in {@link Format#getLineSeparator()}
* The output will remain open for further writing.
*
* @param comment the contents to be written as a comment to the output
*/
public final void commentRow(String comment) {
writeRow(this.comment + comment);
}
/**
* Used when fields were selected and the input rows have a different order than the output. This method
* fills the internal #outputRow array with the values provided by the user in the correct order.
*
* @param row user-provided data which has to be rearranged to the expected record sequence before writing to the output.
*/
private void fillOutputRow(T[] row) {
if (!columnReorderingEnabled && row.length > outputRow.length) {
outputRow = Arrays.copyOf(outputRow, row.length);
}
if (indexesToWrite.length < row.length) {
for (int i = 0; i < indexesToWrite.length; i++) {
outputRow[indexesToWrite[i]] = row[indexesToWrite[i]];
}
} else {
for (int i = 0; i < row.length && i < indexesToWrite.length; i++) {
if (indexesToWrite[i] != -1) {
outputRow[indexesToWrite[i]] = row[i];
}
}
}
}
/**
* Writes the accumulated value of a record to the output, followed by a newline, and increases the record count.
* The newline character sequence will conform to what is specified in {@link Format#getLineSeparator()}
* The contents of {@link AbstractWriter#rowAppender} depend on the concrete implementation of {@link AbstractWriter#processRow(Object[])}
*/
private void internalWriteRow() {
try {
if (skipEmptyLines && rowAppender.length() == 0) {
return;
}
if (enableNewlineAfterRecord) {
rowAppender.appendNewLine();
}
rowAppender.writeCharsAndReset(writer);
recordCount++;
} catch (Throwable ex) {
throw throwExceptionAndClose("Error writing row.", rowAppender.getAndReset(), ex);
}
}
/**
* Identifies the starting character index of a value being written if leading whitespaces are to be discarded.
* Implementation note whitespaces are considered all characters where {@code ch <= ' '} evaluates to {@code true}
*
* @param whitespaceRangeStart starting range after which characters will be considered whitespace
* @param element the String to be scanned for leading whitespaces.
* @return the index of the first non-whitespace character in the given element.
*/
protected static int skipLeadingWhitespace(int whitespaceRangeStart, String element) {
if (element.isEmpty()) {
return 0;
}
for (int i = 0; i < element.length(); i++) {
char nextChar = element.charAt(i);
if (!(nextChar <= ' ' && whitespaceRangeStart < nextChar)) {
return i;
}
}
return element.length();
}
/**
* Flushes the {@link java.io.Writer} given in this class constructor.
* An IllegalStateException will be thrown in case of any errors, and the writer will be closed.
*/
public final void flush() {
try {
writer.flush();
} catch (Throwable ex) {
throw throwExceptionAndClose("Error flushing output.", rowAppender.getAndReset(), ex);
}
}
/**
* Closes the {@link java.io.Writer} given in this class constructor.
* An IllegalStateException will be thrown in case of any errors.
*/
public final void close() {
try {
this.headerIndexes = null;
if (writer != null) {
writer.close();
writer = null;
}
} catch (Throwable ex) {
throw new IllegalStateException("Error closing the output.", ex);
}
if (this.partialLineIndex != 0) {
throw new TextWritingException("Not all values associated with the last record have been written to the output. " +
"\n\tHint: use 'writeValuesToRow()' or 'writeValuesToString()' to flush the partially written values to a row.",
recordCount, getContent(Arrays.copyOf(partialLine, partialLineIndex)));
}
}
/**
* In case of any exceptions, a {@link TextWritingException} is thrown, and the output {@link java.io.Writer} is closed.
*
* @param message Description of the error
*/
private TextWritingException throwExceptionAndClose(String message) {
return throwExceptionAndClose(message, (Object[]) null, null);
}
/**
* In case of any exceptions, a {@link TextWritingException} is thrown, and the output {@link java.io.Writer} is closed.
*
* @param message Description of the error
* @param cause the exception to be wrapped by a {@link TextWritingException}
*/
private TextWritingException throwExceptionAndClose(String message, Throwable cause) {
return throwExceptionAndClose(message, (Object[]) null, cause);
}
/**
* In case of any exceptions, a {@link TextWritingException} is thrown, and the output {@link java.io.Writer} is closed.
*
* @param message Description of the error
* @param recordCharacters characters used to write to the output at the time the exception happened
* @param cause the exception to be wrapped by a {@link TextWritingException}
*/
private TextWritingException throwExceptionAndClose(String message, String recordCharacters, Throwable cause) {
try {
if (cause instanceof NullPointerException && writer == null) {
message = message + " No writer provided in the constructor of " + getClass().getName() + ". You can only use operations that write to Strings.";
}
throw new TextWritingException(message, recordCount, getContent(recordCharacters), cause);
} finally {
close();
}
}
/**
* In case of any exceptions, a {@link TextWritingException} is thrown, and the output {@link java.io.Writer} is closed.
*
* @param message Description of the error
* @param recordValues values used to write to the output at the time the exception happened
* @param cause the exception to be wrapped by a {@link TextWritingException}
*/
private TextWritingException throwExceptionAndClose(String message, Object[] recordValues, Throwable cause) {
try {
throw new TextWritingException(message, recordCount, getContent(recordValues), cause);
} finally {
try {
close();
} catch (Throwable t) {
//ignore and let original error go.
}
}
}
/**
* Converts a given object to its String representation for writing to a {@code String}
*
* If the object is null, then {@link AbstractWriter#nullValue} is returned.
* If the String representation of this object is an empty String, then {@link AbstractWriter#emptyValue} is returned
*
*
* @param element the object to be converted into a String.
* @return the String representation of the given object
*/
protected String getStringValue(Object element) {
usingNullOrEmptyValue = false;
if (element == null) {
usingNullOrEmptyValue = true;
return nullValue;
}
String string = String.valueOf(element);
if (string.isEmpty()) {
usingNullOrEmptyValue = true;
return emptyValue;
}
return string;
}
/**
* Writes a sequence of values to a row in memory. Subsequent calls to this method will add the given values in a new column of the same row, until {@link #writeValuesToRow} is called to flush
* all values accumulated and effectively write a new record to the output
*
* @param values the values to be written
*/
public final void addValues(Object... values) {
try {
System.arraycopy(values, 0, partialLine, partialLineIndex, values.length);
partialLineIndex += values.length;
} catch (Throwable t) {
throw throwExceptionAndClose("Error adding values to in-memory row", values, t);
}
}
/**
* Writes a sequence of Strings to a row in memory. Subsequent calls to this method will add the given values in a new column of the same row, until {@link #writeValuesToRow} is called to flush
* all values accumulated and effectively write a new record to the output
*
* @param values the values to be written
*/
public final void addStringValues(Collection values) {
if (values != null) {
try {
for (String o : values) {
partialLine[partialLineIndex++] = o;
}
} catch (Throwable t) {
throw throwExceptionAndClose("Error adding values to in-memory row", values.toArray(), t);
}
}
}
/**
* Writes a sequence of values to a row in memory. Subsequent calls to this method will add the given values in a new column of the same row, until {@link #writeValuesToRow} is called to flush
* all values accumulated and effectively write a new record to the output
*
* @param values the values to be written
*/
public final void addValues(Collection> values) {
if (values != null) {
try {
for (Object o : values) {
partialLine[partialLineIndex++] = o;
}
} catch (Throwable t) {
throw throwExceptionAndClose("Error adding values to in-memory row", values.toArray(), t);
}
}
}
/**
* Writes a value to a row in memory. Subsequent calls to this method will add the given values in a new column of the same row, until {@link #writeValuesToRow} is called to flush
* all values accumulated and effectively write a new record to the output
*
* @param value the value to be written
*/
public final void addValue(Object value) {
try {
partialLine[partialLineIndex++] = value;
} catch (Throwable t) {
throw throwExceptionAndClose("Error adding value to in-memory row", new Object[]{value}, t);
}
}
private void fillPartialLineToMatchHeaders() {
if (headers != null && partialLineIndex < headers.length) {
while (partialLineIndex < headers.length) {
partialLine[partialLineIndex++] = null;
}
}
}
/**
* Writes the contents accumulated in an internal in-memory row (using {@link #addValues(Object...) or #writeValue()} to a new record in the output.
*/
public final void writeValuesToRow() {
fillPartialLineToMatchHeaders();
writeRow(Arrays.copyOf(partialLine, partialLineIndex));
discardValues();
}
/**
* Writes a value to a row in memory. Subsequent calls to this method will add the given values in a new column of the same row, until {@link #writeValuesToRow} is called to flush
* all values accumulated and effectively write a new record to the output
*
* @param index the position in the row that should receive the value.
* @param value the value to be written
*/
public final void addValue(int index, Object value) {
if (index >= partialLine.length) {
throw throwExceptionAndClose("Cannot write '" + value + "' to index '" + index + "'. Maximum number of columns (" + partialLine.length + ") exceeded.", new Object[]{value}, null);
}
partialLine[index] = value;
if (partialLineIndex <= index) {
partialLineIndex = index + 1;
}
}
/**
* Writes a value to a row in memory. Subsequent calls to this method will add the given values in a new column of the same row, until {@link #writeValuesToRow} is called to flush
* all values accumulated and effectively write a new record to the output
*
* @param headerName the name of the column of the new row that should receive the value.
* @param value the value to be written
*/
public final void addValue(String headerName, Object value) {
addValue(getFieldIndex(headers, NormalizedString.valueOf(headerName), false), value);
}
private final void addValue(NormalizedString[] headersInContext, NormalizedString headerName, boolean ignoreOnMismatch, Object value) {
int index = getFieldIndex(headersInContext, headerName, ignoreOnMismatch);
if (index != -1) {
addValue(index, value);
}
}
/**
* Calculates the index of a header name in relation to the original {@link #headers} array defined in this writer
*
* @param headersInContext headers currently in use (they might change).
* @param headerName the name of the header whose position will be identified
* @param ignoreOnMismatch flag indicating that if the header is not found, no exception is to be thrown, and -1 should be returned instead.
* @return the position of the given header, or -1 if it's not found when ignoreOnMismatch is set to {@code true}
*/
private int getFieldIndex(NormalizedString[] headersInContext, NormalizedString headerName, boolean ignoreOnMismatch) {
if (headerIndexes == null) {
headerIndexes = new HashMap>();
}
Map indexes = headerIndexes.get(headersInContext);
if (indexes == null) {
indexes = new HashMap();
headerIndexes.put(headersInContext, indexes);
}
Integer index = indexes.get(headerName);
if (index == null) {
if (headersInContext == null) {
throw throwExceptionAndClose("Cannot calculate position of header '" + headerName + "' as no headers were defined.", null);
}
index = ArgumentUtils.indexOf(NormalizedString.toArray(headersInContext), NormalizedString.valueOf(headerName));
if (index == -1) {
if (!ignoreOnMismatch) {
throw throwExceptionAndClose("Header '" + headerName + "' could not be found. Defined headers are: " + Arrays.toString(headersInContext) + '.', null);
}
}
indexes.put(headerName, index);
}
return index;
}
/**
* Discards the contents written to the internal in-memory row (using {@link #addValues(Object...) or #writeValue()}.
*/
public final void discardValues() {
Arrays.fill(partialLine, 0, partialLineIndex, null);
partialLineIndex = 0;
}
/**
* Writes the headers defined in {@link CommonSettings#getHeaders()} to a {@code String}
*
* @return a formatted {@code String} containing the headers defined in {@link CommonSettings#getHeaders()}
*/
public final String writeHeadersToString() {
return writeHeadersToString(NormalizedString.toArray(this.headers));
}
/**
* Writes the given collection of headers to a {@code String}
* A {@link TextWritingException} will be thrown if no headers were defined.
*
* @param headers the headers to write to a {@code String}
* @return a formatted {@code String} containing the given headers
*/
public final String writeHeadersToString(Collection> headers) {
if (headers != null && headers.size() > 0) {
return writeHeadersToString(headers.toArray(new String[headers.size()]));
} else {
throw throwExceptionAndClose("No headers defined");
}
}
/**
* Writes the given collection of headers to a {@code String}
* A {@link TextWritingException} will be thrown if no headers were defined or if records were already written to a {@code String}
*
* @param headers the headers to write to a {@code String}
* @return a formatted {@code String} containing the given headers
*/
public final String writeHeadersToString(String... headers) {
if (headers != null && headers.length > 0) {
writingHeaders = true;
submitRow(headers);
writingHeaders = false;
this.headers = NormalizedString.toIdentifierGroupArray(headers);
return internalWriteRowToString();
} else {
throw throwExceptionAndClose("No headers defined.");
}
}
/**
* Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and writes them to a {@code List} of {@code String}.
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param records the records to be transformed by a {@link RowWriterProcessor} and then written to a {@code List} of {@code String}.
* @return a {@code List} containing the information transformed from the given records as formatted {@code String}s
*/
public final List processRecordsToString(Iterable> records) {
try {
List out = new ArrayList(1000);
for (Object record : records) {
out.add(processRecordToString(record));
}
return out;
} catch (Throwable t) {
throw throwExceptionAndClose("Unable process input records", t);
}
}
/**
* Iterates over all records, processes each one with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, and writes them them to a {@code List} of {@code String}.
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param records the records to transformed by a {@link RowWriterProcessor} and then written a {@code String}.
* @return a {@code List} containing the information transformed from the given records as formatted {@code String}s
*/
public final List processRecordsToString(Object[] records) {
try {
List out = new ArrayList(1000);
for (Object record : records) {
out.add(processRecordToString(record));
}
return out;
} catch (Throwable t) {
throw throwExceptionAndClose("Unable process input records", records, t);
}
}
/**
* Processes the data given for an individual record with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, then writes it to a {@code String}.
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param record the information of a single record to be transformed by a {@link RowWriterProcessor} and then written to a {@code String}.
* @return a formatted {@code String} containing the information transformed from the given record
*/
public final String processRecordToString(Object... record) {
return processRecordToString((Object) record);
}
/**
* Processes the data given for an individual record with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, then writes it to a {@code String}.
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param record the information of a single record to be transformed by a {@link RowWriterProcessor} and then written to a {@code String}.
* @return a formatted {@code String} containing the information transformed from the given record
*/
public final String processRecordToString(T record) {
return processRecordToString((Object) (record == null ? null : record.getValues()));
}
/**
* Processes the data given for an individual record with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}, then writes it.
* The output will remain open for further writing.
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param record the information of a single record to be transformed by a {@link RowWriterProcessor} and then written to a {@code String}.
* @return a formatted {@code String} containing the information transformed from the given record
*/
@SuppressWarnings("unchecked")
public final String processRecordToString(Object record) {
if (this.writerProcessor == null) {
throw throwExceptionAndClose("Cannot process record '" + record + "' without a writer processor. Please define a writer processor instance in the settings or use the 'writeRow' methods.");
}
try {
Object[] row = writerProcessor.write(record, getRowProcessorHeaders(), indexesToWrite);
if (row != null) {
return writeRowToString(row);
}
} catch (Throwable t) {
throw throwExceptionAndClose("Could not process record '" + record + "'", t);
}
return null;
}
/**
* Iterates over all records and writes them to a {@code List} of {@code String}.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Object[])} for that.
*
* @param rows the rows to be written to a {@code List} of {@code String}.
* @return a {@code List} containing the given rows as formatted {@code String}s
*/
public final List writeRowsToString(Object[][] rows) {
try {
List out = new ArrayList(rows.length);
for (Object[] row : rows) {
String string = writeRowToString(row);
if (string != null) {
out.add(string);
}
}
return out;
} catch (Throwable t) {
throw throwExceptionAndClose("Error writing input rows", t);
}
}
/**
* Iterates over all records and writes them to a {@code List} of {@code String}.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that.
*
* @param Collection of objects containing values of a row
* @param rows the rows to be written to a {@code List} of {@code String}.
* @return a {@code List} containing the given rows as formatted {@code String}s
*/
public final > List writeRowsToString(Iterable rows) {
try {
List out = new ArrayList(1000);
for (Collection> row : rows) {
out.add(writeRowToString(row));
}
return out;
} catch (Throwable t) {
throw throwExceptionAndClose("Error writing input rows", t);
}
}
/**
* Iterates over all records and writes them to a {@code List} of {@code String}.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that.
*
* @param Collection of objects containing values of a row
* @param rows the rows to be written to a {@code List} of {@code String}.
* @return a {@code List} containing the given rows as formatted {@code String}s
*/
public final > List writeStringRowsToString(Iterable rows) {
try {
List out = new ArrayList(1000);
for (Collection> row : rows) {
String string = writeRowToString(row);
if (string != null) {
out.add(string);
}
}
return out;
} catch (Throwable t) {
throw throwExceptionAndClose("Error writing input rows", t);
}
}
/**
* Iterates over all records and writes them to a {@code List} of {@code String}.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that.
*
* @param rows the rows to be written to a {@code List} of {@code String}.
* @return a {@code List} containing the given rows as formatted {@code String}s
*/
public final List writeRowsToString(Collection rows) {
try {
List out = new ArrayList(rows.size());
for (Object[] row : rows) {
out.add(writeRowToString(row));
}
return out;
} catch (Throwable t) {
throw throwExceptionAndClose("Error writing input rows", t);
}
}
/**
* Iterates over all records and writes them to a {@code List} of {@code String}.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that.
*
* @param rows the rows to be written to a {@code List} of {@code String}.
* @return a {@code List} containing the given rows as formatted {@code String}s
*/
public final List writeStringRowsToString(Collection rows) {
try {
List out = new ArrayList(rows.size());
for (String[] row : rows) {
out.add(writeRowToString(row));
}
return out;
} catch (Throwable t) {
throw throwExceptionAndClose("Error writing input rows", t);
}
}
/**
* Iterates over all records and writes them to a {@code List} of {@code String}.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecords(Iterable)} for that.
*
* @param rows the rows to be written to a {@code List} of {@code String}.
* @return a {@code List} containing the given rows as formatted {@code String}s
*/
public final List writeRecordsToString(Collection extends Record> rows) {
try {
List out = new ArrayList(rows.size());
for (Record row : rows) {
out.add(writeRecordToString(row));
}
return out;
} catch (Throwable t) {
throw throwExceptionAndClose("Error writing input rows", t);
}
}
/**
* Writes the data given for an individual record to a {@code String}.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that.
*
* @param row the information of a single record to be written to a {@code String}
* @return a formatted {@code String} containing the information of the given record
*/
public final String writeRowToString(Collection> row) {
try {
if (row == null) {
return null;
}
return writeRowToString(row.toArray());
} catch (Throwable t) {
throw throwExceptionAndClose("Error writing input row ", t);
}
}
/**
* Writes the data given for an individual record to a {@code String}.
* If the given data is null or empty, and {@link CommonSettings#getSkipEmptyLines()} is true, {@code null} will be returned
* In case of any errors, a {@link TextWritingException} will be thrown.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that.
*
* @param row the information of a single record to be written to a {@code String}.
* @return a formatted {@code String} containing the information of the given record
*/
public final String writeRowToString(String[] row) {
return writeRowToString((Object[]) row);
}
/**
* Writes the data given for an individual record to a {@code String}.
* If the given data is null or empty, and {@link CommonSettings#getSkipEmptyLines()} is true, {@code null} will be returned
* In case of any errors, a {@link TextWritingException} will be thrown.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that.
*
* @param row the information of a single record to be written to a {@code String}.
* @return a formatted {@code String} containing the information of the given record
*/
public final String writeRecordToString(T row) {
return writeRowToString((Object[]) (row == null ? null : row.getValues()));
}
/**
* Writes the data given for an individual record to a {@code String}.
* If the given data is null or empty, and {@link CommonSettings#getSkipEmptyLines()} is true, {@code null} will be returned
* In case of any errors, a {@link TextWritingException} will be thrown.
* Note this method will not use the {@link RowWriterProcessor}. Use {@link AbstractWriter#processRecord(Object)} for that.
*
* @param row the information of a single record to be written to a {@code String}.
* @return a formatted {@code String} containing the information of the given record
*/
public final String writeRowToString(Object... row) {
try {
if (row == null || (row.length == 0 && !expandRows)) {
if (skipEmptyLines) {
return null;
}
}
row = adjustRowLength(row);
submitRow(row);
return internalWriteRowToString();
} catch (Throwable ex) {
throw throwExceptionAndClose("Error writing row.", row, ex);
}
}
private Object[] adjustRowLength(Object[] row) {
if (outputRow != null) {
fillOutputRow(row);
row = outputRow;
} else if (expandRows) {
if (usingSwitch) {
row = expand(row, dummyHeaderRow == null ? -1 : dummyHeaderRow.length, headers == null ? null : headers.length);
dummyHeaderRow = null;
} else {
row = expand(row, headers == null ? -1 : headers.length, null);
}
}
return row;
}
/**
* Writes a comment row to a {@code String}
*
* @param comment the contents to be written as a comment to a {@code String}.
* @return a formatted {@code String} containing the comment.
*/
public final String commentRowToString(String comment) {
return writeRowToString(this.comment + comment);
}
/**
* Writes the accumulated value of a record to the output, followed by a newline, and increases the record count.
* The newline character sequence will conform to what is specified in {@link Format#getLineSeparator()}
* The contents of {@link AbstractWriter#rowAppender} depend on the concrete implementation of {@link AbstractWriter#processRow(Object[])}
*
* @return a formatted {@code String} containing the comment.
*/
private String internalWriteRowToString() {
if (skipEmptyLines && rowAppender.length() == 0) {
return null;
}
String out = rowAppender.getAndReset();
recordCount++;
return out;
}
/**
* Writes the contents accumulated in an internal in-memory row (using {@link #addValues(Object...) or #addValue()} as a {@code String}
*
* @return a formatted {@code String} containing the information accumulated in the internal in-memory row.
*/
public final String writeValuesToString() {
fillPartialLineToMatchHeaders();
String out = writeRowToString(Arrays.copyOf(partialLine, partialLineIndex));
discardValues();
return out;
}
/**
* Writes the contents accumulated in an internal in-memory row (using {@link #addValues(Object...) or #addValue()} to a new record in the output.
* The objects added to this row will be processed with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
* The output will remain open for further writing.
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*/
public final void processValuesToRow() {
fillPartialLineToMatchHeaders();
processRecord(Arrays.copyOf(partialLine, partialLineIndex));
discardValues();
}
/**
* Writes the contents accumulated in an internal in-memory row (using {@link #addValues(Object...) or #addValue()} to a {@code String}
* The objects added to this row will be processed with the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
* The output will remain open for further writing.
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @return a formatted {@code String} containing the result produced by the {@link RowWriterProcessor} using the values accumulated in internal in-memory row.
*/
public final String processValuesToString() {
fillPartialLineToMatchHeaders();
String out = processRecordToString(Arrays.copyOf(partialLine, partialLineIndex));
discardValues();
return out;
}
/**
* Returns the number of records written to the output so far
*
* @return the number of records written to the output so far
*/
public final long getRecordCount() {
return recordCount;
}
/**
* Writes values from an implementation of {@link java.util.Map} to a partial output record, ready to be
* written to the output.
*
* Values will be stored under a column identified by the headers. If no headers are defined,
* the keys of the map will be used to initialize an internal header row.
*
* A map of headers can be optionally provided to assign a name to the keys of the input map. This is useful when
* the input map has keys will generate unwanted header names.
*
* @param headerMapping an optional map associating keys of the rowData map with expected header names
* @param rowData the data to be written. Its keys will be used to form a header row in case no headers are available.
* @param type of the key in both rowData and headerMapping maps.
*/
private void writeValuesFromMap(Map headerMapping, Map rowData) {
try {
if (rowData != null && !rowData.isEmpty()) {
dummyHeaderRow = this.headers;
if (usingSwitch) {
dummyHeaderRow = ((RowWriterProcessorSwitch) writerProcessor).getHeaders(headerMapping, rowData);
if (dummyHeaderRow == null) {
dummyHeaderRow = this.headers;
}
}
if (dummyHeaderRow != null) {
if (headerMapping == null) {
for (Map.Entry, ?> e : rowData.entrySet()) {
addValue(dummyHeaderRow, NormalizedString.valueOf(e.getKey()), true, e.getValue());
}
} else {
for (Map.Entry, ?> e : rowData.entrySet()) {
String header = headerMapping.get(e.getKey());
if (header != null) {
addValue(dummyHeaderRow, NormalizedString.valueOf(header), true, e.getValue());
}
}
}
} else if (headerMapping != null) {
setHeadersFromMap(headerMapping, false);
writeValuesFromMap(headerMapping, rowData);
} else {
setHeadersFromMap(rowData, true);
writeValuesFromMap(null, rowData);
}
}
} catch (Throwable t) {
throw throwExceptionAndClose("Error processing data from input map", t);
}
}
/**
* Iterates over the keys of a map and builds an internal header row.
*
* @param map the input map whose keys will be used to generate headers for the output.
* @param keys indicates whether to take the map keys or values to build the header rows.
*/
private void setHeadersFromMap(Map, ?> map, boolean keys) {
this.headers = new NormalizedString[map.size()];
int i = 0;
for (Object header : keys ? map.keySet() : map.values()) {
headers[i++] = NormalizedString.valueOf(header);
}
}
/**
* Writes the values of a given map to a {@code String} formatted to according to the specified output format.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param rowData the map whose values will be used to generate a {@code String}.
* @return a {@code String} containing the given data as a formatted {@code String}
*/
public final String writeRowToString(Map, ?> rowData) {
return writeRowToString(null, (Map) rowData);
}
/**
* Writes the values of a given map into new output record
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param rowData the map whose values will be used to generate a new record
*/
public final void writeRow(Map, ?> rowData) {
writeRow(null, (Map) rowData);
}
/**
* Writes the values of a given map to a {@code String} formatted to according to the specified output format.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a {@code String}.
* @param the key type
* @return a {@code String} containing the given data as a formatted {@code String}
*/
public final String writeRowToString(Map headerMapping, Map rowData) {
writeValuesFromMap(headerMapping, rowData);
return writeValuesToString();
}
/**
* Writes the values of a given map into new output record
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a new record
* @param the key type
*/
public final void writeRow(Map headerMapping, Map rowData) {
writeValuesFromMap(headerMapping, rowData);
writeValuesToRow();
}
/**
* Writes the values of a given map to a {@code List} of {@code String} formatted to according to the specified output format.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @param the key type
* @param the iterable type
* @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one
* element of the iterators in the map.
*/
public final > List writeRowsToString(Map rowData) {
return writeRowsToString(null, rowData);
}
/**
* Writes the values of a given map to multiple output records
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
* @param the iterable type
*/
public final > void writeRows(Map rowData) {
writeRows(null, rowData, null, false);
}
/**
* Writes the values of a given map to a {@code List} of {@code String} formatted to according to the specified output format.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @param the key type
* @param the iterable type
* @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one
* element of the iterators in the map.
*/
public final > List writeRowsToString(Map headerMapping, Map rowData) {
List writtenRows = new ArrayList();
writeRows(headerMapping, rowData, writtenRows, false);
return writtenRows;
}
/**
* Writes the values of a given map to multiple output records
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
* @param the iterable type
*/
public final > void writeRows(Map headerMapping, Map rowData) {
writeRows(headerMapping, rowData, null, false);
}
/**
* Writes the values of a given map to a {@code List} of {@code String} formatted to according to the specified output format.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @param outputList an output {@code List} to fill with formatted {@code String}s, each {@code String} representing
* one successful iteration over at least one
* element of the iterators in the map.
* @param the key type
* @param the iterable type
*/
private > void writeRows(Map headerMapping, Map rowData, List outputList, boolean useRowProcessor) {
try {
Iterator[] iterators = new Iterator[rowData.size()];
Object[] keys = new Object[rowData.size()];
final Map rowValues = new LinkedHashMap(rowData.size());
if (outputList != null && headers == null) {
if (headerMapping != null) {
setHeadersFromMap(headerMapping, true);
} else {
setHeadersFromMap(rowData, true);
}
}
if (recordCount == 0 && headers != null && isHeaderWritingEnabled) {
outputList.add(writeHeadersToString());
}
int length = 0;
for (Map.Entry rowEntry : rowData.entrySet()) {
iterators[length] = rowEntry.getValue() == null ? null : rowEntry.getValue().iterator();
keys[length] = rowEntry.getKey();
rowValues.put(rowEntry.getKey(), null);
length++;
}
boolean nullsOnly;
do {
nullsOnly = true;
for (int i = 0; i < length; i++) {
Iterator> iterator = iterators[i];
boolean isNull = iterator == null || !iterator.hasNext();
nullsOnly &= isNull;
if (isNull) {
rowValues.put(keys[i], null);
} else {
rowValues.put(keys[i], iterator.next());
}
}
if (!nullsOnly) {
if (outputList == null) {
if (useRowProcessor) {
processRecord((Map) headerMapping, (Map) rowValues);
} else {
writeRow((Map) headerMapping, (Map) rowValues);
}
} else {
if (useRowProcessor) {
outputList.add(processRecordToString((Map) headerMapping, (Map) rowValues));
} else {
outputList.add(writeRowToString((Map) headerMapping, (Map) rowValues));
}
}
}
} while (!nullsOnly);
} catch (Throwable t) {
throw throwExceptionAndClose("Error processing input rows from map", t);
}
}
/**
* Writes the values of a given map to a {@code List} of {@code String} formatted to according to the specified output format.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @param the key type
* @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one
* element of the iterators in the map.
*/
public final List writeStringRowsToString(Map headerMapping, Map rowData) {
List writtenRows = new ArrayList();
writeRows(headerMapping, wrapStringArray(rowData), writtenRows, false);
return writtenRows;
}
/**
* Writes the values of a given map to a {@code List} of {@code String} formatted to according to the specified output format.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @param the key type
* @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one
* element of the iterators in the map.
*/
public final List writeRecordsToString(Map headerMapping, Map rowData) {
List writtenRows = new ArrayList();
writeRows(headerMapping, wrapRecordValues(rowData), writtenRows, false);
return writtenRows;
}
/**
* Writes the values of a given map to multiple output records
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
*/
public final void writeStringRows(Map headerMapping, Map rowData) {
writeRows(headerMapping, wrapStringArray(rowData), null, false);
}
/**
* Writes the values of a given map to multiple output records
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
*/
public final void writeRecords(Map headerMapping, Map rowData) {
writeRows(headerMapping, wrapRecordValues(rowData), null, false);
}
/**
* Writes the values of a given map to a {@code List} of {@code String} formatted to according to the specified output format.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @param the key type
* @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one
* element of the iterators in the map.
*/
public final List writeObjectRowsToString(Map headerMapping, Map rowData) {
List writtenRows = new ArrayList();
writeRows(headerMapping, wrapObjectArray(rowData), writtenRows, false);
return writtenRows;
}
/**
* Writes the values of a given map to multiple output records
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
*/
public final void writeObjectRows(Map headerMapping, Map rowData) {
writeRows(headerMapping, wrapObjectArray(rowData), null, false);
}
private Map> wrapObjectArray(Map rowData) {
Map> out = new LinkedHashMap>(rowData.size());
for (Map.Entry e : rowData.entrySet()) {
if (e.getValue() == null) {
out.put(e.getKey(), Collections.emptyList());
} else {
out.put(e.getKey(), Arrays.asList(e.getValue()));
}
}
return out;
}
private Map> wrapStringArray(Map rowData) {
Map> out = new LinkedHashMap>(rowData.size());
for (Map.Entry e : rowData.entrySet()) {
if (e.getValue() == null) {
out.put(e.getKey(), Collections.emptyList());
} else {
out.put(e.getKey(), Arrays.asList(e.getValue()));
}
}
return out;
}
private Map> wrapRecordValues(Map rowData) {
Map> out = new LinkedHashMap>(rowData.size());
for (Map.Entry e : rowData.entrySet()) {
if (e.getValue() == null) {
out.put(e.getKey(), Collections.emptyList());
} else {
out.put(e.getKey(), Arrays.asList(e.getValue().getValues()));
}
}
return out;
}
/**
* Writes the values of a given map to multiple output records and closes the output when finished.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
*/
public final void writeObjectRowsAndClose(Map headerMapping, Map rowData) {
try {
writeObjectRows(headerMapping, rowData);
} finally {
close();
}
}
/**
* Writes the values of a given map to multiple output records and closes the output when finished.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
*/
public final void writeStringRowsAndClose(Map headerMapping, Map rowData) {
try {
writeStringRows(headerMapping, rowData);
} finally {
close();
}
}
/**
* Writes the values of a given map to multiple output records and closes the output when finished.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
*/
public final void writeRecordsAndClose(Map headerMapping, Map rowData) {
try {
writeRecords(headerMapping, rowData);
} finally {
close();
}
}
/**
* Writes the values of a given map to multiple output records and closes the output when finished.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
*/
public final void writeObjectRowsAndClose(Map rowData) {
writeObjectRowsAndClose(null, rowData);
}
/**
* Writes the values of a given map to multiple output records and closes the output when finished.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
*/
public final void writeStringRowsAndClose(Map rowData) {
writeStringRowsAndClose(null, rowData);
}
/**
* Writes the values of a given map to multiple output records and closes the output when finished.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
*/
public final void writeRecordsAndClose(Map rowData) {
writeRecordsAndClose(null, rowData);
}
/**
* Writes the values of a given map to multiple output records and closes the output when finished.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
* @param the iterable type
*/
public final > void writeRowsAndClose(Map headerMapping, Map rowData) {
try {
writeRows(headerMapping, rowData);
} finally {
close();
}
}
/**
* Writes the values of a given map to multiple output records and closes the output when finished.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* Note this method will not use the {@link RowWriterProcessor}.
*
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
* @param the iterable type
*/
public final > void writeRowsAndClose(Map rowData) {
writeRowsAndClose(null, rowData);
}
/**
* Processes the values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}
* and writes the result into {@code String} formatted according to the specified output format.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @return a {@code String} containing the given data as a formatted {@code String}
*/
public final String processRecordToString(Map, ?> rowData) {
return processRecordToString(null, (Map) rowData);
}
/**
* Processes the values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}
* and writes the result into a new output record
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
*/
public final void processRecord(Map, ?> rowData) {
processRecord(null, (Map) rowData);
}
/**
* Processes the values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}
* and writes the result into {@code String} formatted according to the specified output format.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @param the key type
* @return a {@code String} containing the given data as a formatted {@code String}
*/
public final String processRecordToString(Map headerMapping, Map rowData) {
writeValuesFromMap(headerMapping, rowData);
return processValuesToString();
}
/**
* Processes the values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}
* and writes the result into a new output record
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @param the key type
*/
public final void processRecord(Map headerMapping, Map rowData) {
writeValuesFromMap(headerMapping, rowData);
processValuesToRow();
}
/**
* Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}
* and writes the result into a {@code List} of {@code String} formatted to according to the specified output format.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @param the key type
* @param the iterable type
* @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one
* element of the iterators in the map.
*/
public final > List processRecordsToString(Map rowData) {
return processRecordsToString(null, rowData);
}
/**
* Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()},
* then writes all values to the output .
*
* The output will remain open for further write operations.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
* @param the iterable type
*/
public final > void processRecords(Map rowData) {
writeRows(null, rowData, null, true);
}
/**
* Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}
* and writes the result into a {@code List} of {@code String} formatted to according to the specified output format.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @param the key type
* @param the iterable type
* @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one
* element of the iterators in the map.
*/
public final > List processRecordsToString(Map headerMapping, Map rowData) {
List writtenRows = new ArrayList();
writeRows(headerMapping, rowData, writtenRows, true);
return writtenRows;
}
/**
* Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()},
* then writes all values to the output .
*
* The output will remain open for further write operations.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
* @param the iterable type
*/
public final > void processRecords(Map headerMapping, Map rowData) {
writeRows(headerMapping, rowData, null, true);
}
/**
* Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}
* and writes the result into a {@code List} of {@code String} formatted to according to the specified output format.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @param the key type
* @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one
* element of the iterators in the map.
*/
public final List processObjectRecordsToString(Map rowData) {
return processObjectRecordsToString(null, rowData);
}
/**
* Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()}
* and writes the result into a {@code List} of {@code String} formatted to according to the specified output format.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a {@code List} of {@code String}.
* @param the key type
* @return a {@code List} of formatted {@code String}, each {@code String} representing one successful iteration over at least one
* element of the iterators in the map.
*/
public final List processObjectRecordsToString(Map headerMapping, Map rowData) {
List writtenRows = new ArrayList();
writeRows(headerMapping, wrapObjectArray(rowData), writtenRows, true);
return writtenRows;
}
/**
* Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()},
* then writes all values to the output .
*
* The output will remain open for further write operations.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
*/
public final void processObjectRecords(Map headerMapping, Map rowData) {
writeRows(headerMapping, wrapObjectArray(rowData), null, true);
}
/**
* Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()},
* then writes all values to the output and closes the writer.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
*/
public final void processObjectRecordsAndClose(Map headerMapping, Map rowData) {
try {
processObjectRecords(headerMapping, rowData);
} finally {
close();
}
}
/**
* Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()},
* then writes all values to the output and closes the writer.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
*/
public final void processObjectRecordsAndClose(Map rowData) {
processRecordsAndClose(null, wrapObjectArray(rowData));
}
/**
* Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()},
* then writes all values to the output and closes the writer.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param headerMapping a mapping associating the keys of the input map to their corresponding header names.
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
* @param the iterable type
*/
public final > void processRecordsAndClose(Map headerMapping, Map rowData) {
try {
processRecords(headerMapping, rowData);
} finally {
close();
}
}
/**
* Processes the data in all values of a map using the {@link RowWriterProcessor} provided by {@link CommonWriterSettings#getRowWriterProcessor()},
* then writes all values to the output and closes the writer.
*
* Each value is expected to be iterable and the result of this method will produce the number of records equal to the longest iterable.
*
* A new record will be created each time at least one {@link Iterator#hasNext()} returns {@code true}. {@code Null} will be written
* when a iterator has been fully read.
*
* A {@link TextWritingException} will be thrown if no {@link RowWriterProcessor} is provided by {@link CommonWriterSettings#getRowWriterProcessor()}.
*
* @param rowData the map whose values will be used to generate a number of output records
* @param the key type
* @param the iterable type
*/
public final > void processRecordsAndClose(Map rowData) {
processRecordsAndClose(null, rowData);
}
private Object[] getContent(Object[] tmp) {
return AbstractException.restrictContent(errorContentLength, tmp);
}
private String getContent(CharSequence tmp) {
return AbstractException.restrictContent(errorContentLength, tmp);
}
/**
* Checks whether the writer can remove trailing/leading whitespaces from a value being written.
* Applies to headers where names can be conflicting if trimmed for example {@code ' a '} and
* {@code 'a'} will become the same value if the spaces are removed)
*
* @param fieldIndex index of the field to be written.
* @return {@code true} if the value being written is not a header name, or it is a header name that won't
* conflict with another header if its surrounding whitespaces are trimmed.
*/
protected final boolean allowTrim(int fieldIndex) {
return !writingHeaders || fieldIndex >= headerTrimFlags.length || headerTrimFlags[fieldIndex];
}
}