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.
/*******************************************************************************
* 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.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 BufferedWriter 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;
private String[] headers;
private 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;
private String[] dummyHeaderRow;
private boolean expandRows;
private boolean usingSwitch;
private boolean enableNewlineAfterRecord = true;
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();
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.appender = new WriterCharAppender(settings.getMaxCharsPerColumn(), "", settings.getFormat());
this.rowAppender = new WriterCharAppender(settings.getMaxCharsPerColumn(), "", settings.getFormat());
if (writer != null) {
if (writer instanceof BufferedWriter) {
this.writer = (BufferedWriter) writer;
} else {
this.writer = new BufferedWriter(writer);
}
} else {
this.writer = null;
}
this.headers = 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) {
outputRow = new Object[headers.length];
indexesToWrite = selector.getFieldIndexes(headers);
} 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();
}
outputRow = new Object[rowLength];
indexesToWrite = selector.getFieldIndexes(new String[rowLength]); //generates a dummy header array - only the indexes matter so we are good
} 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;
}
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);
}
/**
* 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(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;
submitRow(headers);
this.headers = headers;
writeRow();
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);
}
}
/**
* 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);
}
/**
* 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 String[] 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