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; + } + +}