001package gudusoft.gsqlparser.util.csv;
002
003import java.io.*;
004import java.nio.charset.Charset;
005
006public class CsvWriter {
007
008    private Writer outWriter;
009    private char sepChar = ',';
010    private char recDelimiterChar = '\n';
011    private char textQuoteChar = '"';
012    private boolean enableTextQuote = true;
013    private boolean forceTextQuote = false;
014    private char commentSymbol = '#';
015    private int escMode = ESCAPE_MODE_DOUBLED;
016    private boolean isFirstColumn = true;
017    private boolean isClosed = false;
018    private boolean customRecDelimiter = false;
019
020    public static final int ESCAPE_MODE_DOUBLED = 1;
021    public static final int ESCAPE_MODE_BACKSLASH = 2;
022
023    public CsvWriter(String fileName, char delimiter, Charset charset) {
024        if (fileName == null || charset == null) {
025            throw new IllegalArgumentException("Parameters cannot be null");
026        }
027        try {
028            this.outWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName), charset));
029        } catch (IOException e) {
030            throw new RuntimeException(e);
031        }
032        this.sepChar = delimiter;
033    }
034
035    public CsvWriter(String fileName) {
036        this(fileName, ',', Charset.forName("ISO-8859-1"));
037    }
038
039    public CsvWriter(Writer writer, char delimiter) {
040        if (writer == null) throw new IllegalArgumentException("Writer cannot be null");
041        this.outWriter = writer;
042        this.sepChar = delimiter;
043    }
044
045    public CsvWriter(OutputStream stream, char delimiter, Charset charset) {
046        this(new OutputStreamWriter(stream, charset), delimiter);
047    }
048
049    public char getDelimiter() { return sepChar; }
050    public void setDelimiter(char delimiter) { this.sepChar = delimiter; }
051
052    public char getRecordDelimiter() { return recDelimiterChar; }
053    public void setRecordDelimiter(char recDelimiter) {
054        this.recDelimiterChar = recDelimiter;
055        this.customRecDelimiter = true;
056    }
057
058    public char getTextQualifier() { return textQuoteChar; }
059    public void setTextQualifier(char textQualifier) { this.textQuoteChar = textQualifier; }
060
061    public boolean getUseTextQualifier() { return enableTextQuote; }
062    public void setUseTextQualifier(boolean useTextQualifier) { this.enableTextQuote = useTextQualifier; }
063
064    public int getEscapeMode() { return escMode; }
065    public void setEscapeMode(int escapeMode) { this.escMode = escapeMode; }
066
067    public char getComment() { return commentSymbol; }
068    public void setComment(char comment) { this.commentSymbol = comment; }
069
070    public boolean getForceQualifier() { return forceTextQuote; }
071    public void setForceQualifier(boolean forceQualifier) { this.forceTextQuote = forceQualifier; }
072
073    public void write(String content, boolean preserveSpaces) throws IOException {
074        checkClosed();
075        if (content == null) content = "";
076
077        if (!isFirstColumn) outWriter.write(sepChar);
078
079        boolean quote = forceTextQuote;
080        String value = preserveSpaces ? content : content.trim();
081
082        if (enableTextQuote && !quote) {
083            if (value.indexOf(sepChar) >= 0 || value.indexOf(textQuoteChar) >= 0 ||
084                    (!customRecDelimiter && (value.indexOf('\n') >= 0 || value.indexOf('\r') >= 0)) ||
085                    (customRecDelimiter && value.indexOf(recDelimiterChar) >= 0) ||
086                    (isFirstColumn && value.length() > 0 && value.charAt(0) == commentSymbol) ||
087                    (isFirstColumn && value.isEmpty())
088            ) {
089                quote = true;
090            }
091        }
092
093        if (quote) {
094            outWriter.write(textQuoteChar);
095            value = escapeValue(value);
096        } else if (escMode == ESCAPE_MODE_BACKSLASH) {
097            value = escapeWithBackslash(value);
098        }
099
100        outWriter.write(value);
101
102        if (quote) outWriter.write(textQuoteChar);
103
104        isFirstColumn = false;
105    }
106
107    public void write(String content) throws IOException {
108        write(content, false);
109    }
110
111    public void writeComment(String commentText) throws IOException {
112        checkClosed();
113        outWriter.write(commentSymbol);
114        outWriter.write(commentText);
115        outWriter.write(customRecDelimiter ? recDelimiterChar : '\n');
116        isFirstColumn = true;
117    }
118
119    public void writeRecord(String[] values, boolean preserveSpaces) throws IOException {
120        if (values != null && values.length > 0) {
121            for (String val : values) {
122                write(val, preserveSpaces);
123            }
124            endRecord();
125        }
126    }
127
128    public void writeRecord(String[] values) throws IOException {
129        writeRecord(values, false);
130    }
131
132    public void endRecord() throws IOException {
133        checkClosed();
134        outWriter.write(customRecDelimiter ? recDelimiterChar : '\n');
135        isFirstColumn = true;
136    }
137
138    public void flush() throws IOException { outWriter.flush(); }
139
140    public void close() {
141        if (!isClosed) {
142            try {
143                outWriter.close();
144            } catch (Exception ignored) {}
145            isClosed = true;
146        }
147    }
148
149    private void checkClosed() throws IOException {
150        if (isClosed) throw new IOException("CsvWriter already closed.");
151    }
152
153    private String escapeValue(String val) {
154        if (escMode == ESCAPE_MODE_DOUBLED) {
155            return val.replace("" + textQuoteChar, "" + textQuoteChar + textQuoteChar);
156        }
157        return val;
158    }
159
160    private String escapeWithBackslash(String val) {
161        val = val.replace("\\", "\\\\")
162                .replace("" + textQuoteChar, "\\" + textQuoteChar)
163                .replace("" + sepChar, "\\" + sepChar);
164        if (!customRecDelimiter) {
165            val = val.replace("\n", "\\n").replace("\r", "\\r");
166        } else {
167            val = val.replace("" + recDelimiterChar, "\\" + recDelimiterChar);
168        }
169        if (isFirstColumn && val.length() > 0 && val.charAt(0) == commentSymbol) {
170            val = "\\" + val;
171        }
172        return val;
173    }
174}