diff --git a/src/java/org/apache/commons/csv/writer/CSVConfig.java b/src/java/org/apache/commons/csv/writer/CSVConfig.java
new file mode 100644
index 00000000..ad5b0fd9
--- /dev/null
+++ b/src/java/org/apache/commons/csv/writer/CSVConfig.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.commons.csv.writer;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * The CSVConfig is used to configure the CSV writer
+ *
+ * @author Martin van den Bemt
+ * @version $Id: $
+ */
+public class CSVConfig {
+
+ /** specifies if it is a fixed width csv file **/
+ private boolean fixedWidth;
+ /** list of fields **/
+ private List fields;
+
+ /** Do no do any filling **/
+ public static final int FILLNONE = 0;
+ /** Fill content the the left. Mainly usable together with fixedWidth **/
+ public static final int FILLLEFT = 1;
+ /** Fill content to the right. Mainly usable together with fixedWidth **/
+ public static final int FILLRIGHT = 2;
+
+ /** The fill pattern */
+ private int fill;
+ /** The fill char. Defaults to a space */
+ private char fillChar = ' ';
+ /** The seperator character. Defaults to , */
+ private char delimiter = ',';
+ /** Should we ignore the delimiter. Defaults to false */
+ private boolean ignoreDelimiter = false;
+ /** the value delimiter. Defaults to " */
+ private char valueDelimiter = '"';
+ /** Should we ignore the value delimiter. Defaults to true */
+ private boolean ignoreValueDelimiter = true;
+ /** Specifies if we want to use a field header */
+ private boolean fieldHeader = false;
+ /** Specifies if the end of the line needs to be trimmed */
+ private boolean endTrimmed = false;
+ /**
+ *
+ */
+ public CSVConfig() {
+ super();
+ }
+
+ /**
+ * @return if the CSV file is fixedWidth
+ */
+ public boolean isFixedWidth() {
+ return fixedWidth;
+ }
+
+ /**
+ * Specify if the CSV file is fixed width.
+ * Defaults to false
+ * @param fixedWidth the fixedwidth
+ */
+ public void setFixedWidth(boolean fixedWidth) {
+ this.fixedWidth = fixedWidth;
+ }
+
+ public void addField(CSVField field) {
+ if (fields == null) {
+ fields = new ArrayList();
+ }
+ fields.add(field);
+ }
+
+ /**
+ * Set the fields that should be used by the writer.
+ * This will overwrite currently added fields completely!
+ * @param csvFields the csvfields array. If null it will do nothing
+ */
+ public void setFields(CSVField[] csvFields) {
+ if (csvFields == null) {
+ return;
+ }
+ fields = new ArrayList(Arrays.asList(csvFields));
+ }
+
+ /**
+ * Set the fields that should be used by the writer
+ * @param csvField a collection with fields. If null it will do nothing
+ */
+ public void setFields(Collection csvField) {
+ if (csvField == null) {
+ return;
+ }
+ fields = new ArrayList(csvField);
+ }
+
+ /**
+ * @return an array with the known fields (even if no fields are specified)
+ */
+ public CSVField[] getFields() {
+ CSVField[] csvFields = new CSVField[0];
+ if (fields != null) {
+ return (CSVField[]) fields.toArray(csvFields);
+ }
+ return csvFields;
+ }
+
+ public CSVField getField(String name) {
+ if (fields == null || name == null) {
+ return null;
+ }
+ for(int i = 0; i < fields.size(); i++) {
+ CSVField field = (CSVField) fields.get(i);
+ if (name.equals(field.getName())) {
+ return field;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the fill pattern.
+ */
+ public int getFill() {
+ return fill;
+ }
+
+ /**
+ * Set the fill pattern. Defaults to {@link #FILLNONE}
+ *
Other options are : {@link #FILLLEFT} and {@link #FILLRIGHT}
+ * @param fill the fill pattern.
+ */
+ public void setFill(int fill) {
+ this.fill = fill;
+ }
+
+ /**
+ *
+ * @return the fillchar. Defaults to a space.
+ */
+ public char getFillChar() {
+ return fillChar;
+ }
+
+ /**
+ * Set the fill char
+ * @param fillChar the fill char
+ */
+ public void setFillChar(char fillChar) {
+ this.fillChar = fillChar;
+ }
+
+ /**
+ * @return the delimeter used.
+ */
+ public char getDelimiter() {
+ return delimiter;
+ }
+
+ /**
+ * Set the delimiter to use
+ * @param delimiter the delimiter character.
+ */
+ public void setDelimiter(char delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ /**
+ * @return if the writer should ignore the delimiter character.
+ */
+ public boolean isDelimiterIgnored() {
+ return ignoreDelimiter;
+ }
+
+ /**
+ * Specify if the writer should ignore the delimiter.
+ * @param ignoreDelimiter defaults to false.
+ */
+ public void setIgnoreDelimiter(boolean ignoreDelimiter) {
+ this.ignoreDelimiter = ignoreDelimiter;
+ }
+
+ /**
+ * @return the value delimeter used. Defaults to "
+ */
+ public char getValueDelimiter() {
+ return valueDelimiter;
+ }
+
+ /**
+ * Set the value delimiter to use
+ * @param valueDelimiter the value delimiter character.
+ */
+ public void setValueDelimiter(char valueDelimiter) {
+ this.valueDelimiter = valueDelimiter;
+ }
+
+ /**
+ * @return if the writer should ignore the value delimiter character.
+ * Defaults to true.
+ */
+ public boolean isValueDelimiterIgnored() {
+ return ignoreValueDelimiter;
+ }
+
+ /**
+ * Specify if the writer should ignore the value delimiter.
+ * @param ignoreValueDelimiter defaults to false.
+ */
+ public void setIgnoreValueDelimiter(boolean ignoreValueDelimiter) {
+ this.ignoreValueDelimiter = ignoreValueDelimiter;
+ }
+
+ /**
+ * @return if a field header is used. Defaults to false
+ */
+ public boolean isFieldHeader() {
+ return fieldHeader;
+ }
+ /**
+ * Specify if you want to use a field header.
+ * @param fieldHeader true or false.
+ */
+ public void setFieldHeader(boolean fieldHeader) {
+ this.fieldHeader = fieldHeader;
+ }
+
+ /**
+ * TODO..
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj == null && !(obj instanceof CSVConfig)) {
+ return false;
+ }
+ return super.equals(obj);
+// CSVConfig config = (CSVConfig) obj;
+// getFill() == config.getFill()
+// getFields().equals(config.getFields())
+ }
+
+ /**
+ * Creates a config based on a stream. It tries to guess
+ * NOTE : The stream will be closed.
+ * @param inputStream the inputstream.
+ * @return the guessed config.
+ */
+ public static CSVConfig guessConfig(InputStream inputStream) {
+ return null;
+ }
+
+ /**
+ * @return if the end of the line should be trimmed. Default is false.
+ */
+ public boolean isEndTrimmed() {
+ return endTrimmed;
+ }
+
+ /**
+ * Specify if the end of the line needs to be trimmed. Defaults to false.
+ * @param endTrimmed
+ */
+ public void setEndTrimmed(boolean endTrimmed) {
+ this.endTrimmed = endTrimmed;
+ }
+
+
+}
diff --git a/src/java/org/apache/commons/csv/writer/CSVConfigGuesser.java b/src/java/org/apache/commons/csv/writer/CSVConfigGuesser.java
new file mode 100644
index 00000000..0a9f4808
--- /dev/null
+++ b/src/java/org/apache/commons/csv/writer/CSVConfigGuesser.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.commons.csv.writer;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+/**
+ * Tries to guess a config based on an InputStream.
+ *
+ * @author Martin van den Bemt
+ * @version $Id: $
+ */
+public class CSVConfigGuesser {
+
+ /** The stream to read */
+ private InputStream in;
+ /**
+ * if the file has a field header (need this info, to be able to guess better)
+ * Defaults to false
+ */
+ private boolean hasFieldHeader = false;
+ /** The found config */
+ protected CSVConfig config;
+
+ /**
+ *
+ */
+ public CSVConfigGuesser() {
+ this.config = new CSVConfig();
+ }
+
+ /**
+ * @param in the inputstream to guess from
+ */
+ public CSVConfigGuesser(InputStream in) {
+ this();
+ setInputStream(in);
+ }
+
+ public void setInputStream(InputStream in) {
+ this.in = in;
+ }
+
+ /**
+ * Allow override.
+ * @return the inputstream that was set.
+ */
+ protected InputStream getInputStream() {
+ return in;
+ }
+
+ /**
+ * Guess the config based on the first 10 (or less when less available)
+ * records of a CSV file.
+ *
+ * @return the guessed config.
+ */
+ public CSVConfig guess() {
+ try {
+ // tralalal
+ BufferedReader bIn = new BufferedReader(new InputStreamReader((getInputStream())));
+ String[] lines = new String[10];
+ String line = null;
+ int counter = 0;
+ while ( (line = bIn.readLine()) != null || counter > 10) {
+ lines[counter] = line;
+ counter++;
+ }
+ if (counter < 10) {
+ // remove nulls from the array, so we can skip the null checking.
+ String[] newLines = new String[counter];
+ System.arraycopy(lines, 0, newLines, 0, counter);
+ lines = newLines;
+ }
+ analyseLines(lines);
+ } catch(Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch(Exception e) {
+ // ignore exception.
+ }
+ }
+ }
+ CSVConfig conf = config;
+ // cleanup the config.
+ config = null;
+ return conf;
+ }
+
+ protected void analyseLines(String[] lines) {
+ guessFixedWidth(lines);
+ guessFieldSeperator(lines);
+ }
+
+ /**
+ * Guess if this file is fixedwidth.
+ * Just basing the fact on all lines being of the same length
+ * @param lines
+ */
+ protected void guessFixedWidth(String[] lines) {
+ int lastLength = 0;
+ // assume fixedlength.
+ config.setFixedWidth(true);
+ for (int i = 0; i < lines.length; i++) {
+ if (i == 0) {
+ lastLength = lines[i].length();
+ } else {
+ if (lastLength != lines[i].length()) {
+ config.setFixedWidth(false);
+ }
+ }
+ }
+ }
+
+
+ protected void guessFieldSeperator(String[] lines) {
+ if (config.isFixedWidth()) {
+ guessFixedWidthSeperator(lines);
+ return;
+ }
+ for (int i = 0; i < lines.length; i++) {
+ }
+ }
+
+ protected void guessFixedWidthSeperator(String[] lines) {
+ // keep track of the fieldlength
+ int previousMatch = -1;
+ for (int i = 0; i < lines[0].length(); i++) {
+ char last = ' ';
+ boolean charMatches = true;
+ for (int j = 0; j < lines.length; j++) {
+ if (j == 0) {
+ last = lines[j].charAt(i);
+ }
+ if (last != lines[j].charAt(i)) {
+ charMatches = false;
+ break;
+ }
+ }
+ if (charMatches) {
+ if (previousMatch == -1) {
+ previousMatch = 0;
+ }
+ CSVField field = new CSVField();
+ field.setName("field"+config.getFields().length+1);
+ field.setSize((i-previousMatch));
+ config.addField(field);
+ }
+ }
+ }
+ /**
+ *
+ * @return if the field uses a field header. Defaults to false.
+ */
+ public boolean hasFieldHeader() {
+ return hasFieldHeader;
+ }
+
+ /**
+ * Specify if the CSV file has a field header
+ * @param hasFieldHeader true or false
+ */
+ public void setHasFieldHeader(boolean hasFieldHeader) {
+ this.hasFieldHeader = hasFieldHeader;
+ }
+
+
+}
diff --git a/src/java/org/apache/commons/csv/writer/CSVField.java b/src/java/org/apache/commons/csv/writer/CSVField.java
new file mode 100644
index 00000000..c8730d72
--- /dev/null
+++ b/src/java/org/apache/commons/csv/writer/CSVField.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.commons.csv.writer;
+
+
+/**
+ *
+ * @author Martin van den Bemt
+ * @version $Id: $
+ */
+public class CSVField {
+
+ private String name;
+ private int size;
+ private int fill;
+ private boolean overrideFill;
+
+ /**
+ *
+ */
+ public CSVField() {
+ }
+
+ /**
+ * @param name the name of the field
+ */
+ public CSVField(String name) {
+ setName(name);
+ }
+
+ /**
+ * @param name the name of the field
+ * @param size the size of the field
+ */
+ public CSVField(String name, int size) {
+ setName(name);
+ setSize(size);
+ }
+
+ /**
+ * @return the name of the field
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name of the field
+ * @param name the name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ *
+ * @return the size of the field
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Set the size of the field.
+ * The size will be ignored when fixedwidth is set to false in the CSVConfig
+ * @param size the size of the field.
+ */
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+ /**
+ * @return the fill pattern.
+ */
+ public int getFill() {
+ return fill;
+ }
+
+ /**
+ * Sets overrideFill to true.
+ * @param fill the file pattern
+ */
+ public void setFill(int fill) {
+ overrideFill = true;
+ this.fill = fill;
+ }
+
+ /**
+ * Does this field override fill ?
+ *
+ * @return
+ */
+ public boolean overrideFill() {
+ return overrideFill;
+ }
+
+}
diff --git a/src/java/org/apache/commons/csv/writer/CSVWriter.java b/src/java/org/apache/commons/csv/writer/CSVWriter.java
new file mode 100644
index 00000000..6d0863bd
--- /dev/null
+++ b/src/java/org/apache/commons/csv/writer/CSVWriter.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.commons.csv.writer;
+
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Map;
+
+
+/**
+ * CSVWriter
+ *
+ * @author Martin van den Bemt
+ * @version $Id: $
+ */
+public class CSVWriter {
+
+ /** The CSV config **/
+ private CSVConfig config;
+ /** The writer **/
+ private Writer writer;
+ /**
+ *
+ */
+ public CSVWriter() {
+ }
+
+ public CSVWriter(CSVConfig config) {
+ setConfig(config);
+ }
+
+ public void writeRecord(Map map) {
+ CSVField[] fields = config.getFields();
+ try {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < fields.length; i++) {
+ String value = (String) map.get(fields[i].getName());
+ value = writeValue(fields[i], value);
+ sb.append(value);
+ if (!config.isDelimiterIgnored() && fields.length != (i+1)) {
+ sb.append(config.getDelimiter());
+ }
+ }
+ if (config.isEndTrimmed()) {
+ for (int i = sb.length()-1; i >= 0; i--) {
+ System.out.println("i : " + i);
+ if (Character.isWhitespace(sb.charAt(i))) {
+ sb.deleteCharAt(i);
+ } else {
+ break;
+ }
+ }
+ }
+ sb.append("\n");
+ String line = sb.toString();
+ writer.write(line);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ protected String writeValue(CSVField field, String value) throws Exception {
+ if (config.isFixedWidth()) {
+ if (value.length() < field.getSize()) {
+ int fillPattern = config.getFill();
+ if (field.overrideFill()) {
+ fillPattern = field.getFill();
+ }
+ StringBuffer sb = new StringBuffer();
+ int fillSize = (field.getSize() - value.length());
+ char[] fill = new char[fillSize];
+ Arrays.fill(fill, config.getFillChar());
+ if (fillPattern == CSVConfig.FILLLEFT) {
+ sb.append(fill);
+ sb.append(value);
+ value = sb.toString();
+ } else {
+ // defaults to fillpattern FILLRIGHT when fixedwidth is used
+ sb.append(value);
+ sb.append(fill);
+ value = sb.toString();
+ }
+ } else if (value.length() > field.getSize()) {
+ // value to big..
+ value = value.substring(0, field.getSize());
+ }
+ if (!config.isValueDelimiterIgnored()) {
+ // add the value delimiter..
+ value = config.getValueDelimiter()+value+config.getValueDelimiter();
+ }
+ }
+ return value;
+ }
+ /**
+ * @return the CVSConfig or null if not present
+ */
+ public CSVConfig getConfig() {
+ return config;
+ }
+
+ /**
+ * Set the CSVConfig
+ * @param config the CVSConfig
+ */
+ public void setConfig(CSVConfig config) {
+ this.config = config;
+ }
+
+ /**
+ * Set the writer to write the CSV file to.
+ * @param writer the writer.
+ */
+ public void setWriter(Writer writer) {
+ this.writer = writer;
+ }
+
+}