001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.dataformat.csv;
018
019 import java.io.InputStream;
020 import java.io.InputStreamReader;
021 import java.io.OutputStream;
022 import java.io.OutputStreamWriter;
023 import java.io.Writer;
024 import java.util.ArrayList;
025 import java.util.Arrays;
026 import java.util.List;
027 import java.util.Map;
028 import java.util.Set;
029
030 import org.apache.camel.Exchange;
031 import org.apache.camel.converter.IOConverter;
032 import org.apache.camel.spi.DataFormat;
033 import org.apache.camel.util.ExchangeHelper;
034 import org.apache.commons.csv.CSVParser;
035 import org.apache.commons.csv.CSVStrategy;
036 import org.apache.commons.csv.writer.CSVConfig;
037 import org.apache.commons.csv.writer.CSVField;
038 import org.apache.commons.csv.writer.CSVWriter;
039
040 /**
041 * CSV Data format.
042 * <p/>
043 * By default, columns are autogenerated in the resulting CSV. Subsequent
044 * messages use the previously created columns with new fields being added at
045 * the end of the line. Thus, field order is the same from message to message.
046 * Autogeneration can be disabled. In this case, only the fields defined in
047 * csvConfig are written on the output.
048 *
049 * @version
050 */
051 public class CsvDataFormat implements DataFormat {
052 private CSVStrategy strategy = CSVStrategy.DEFAULT_STRATEGY;
053 private CSVConfig config = new CSVConfig();
054 private boolean autogenColumns = true;
055 private String delimiter;
056
057 public void marshal(Exchange exchange, Object object, OutputStream outputStream) throws Exception {
058 if (delimiter != null) {
059 config.setDelimiter(delimiter.charAt(0));
060 }
061
062 OutputStreamWriter out = new OutputStreamWriter(outputStream, IOConverter.getCharsetName(exchange));
063 CSVWriter csv = new CSVWriter(config);
064 csv.setWriter(out);
065
066 try {
067 List list = ExchangeHelper.convertToType(exchange, List.class, object);
068 if (list != null) {
069 for (Object child : list) {
070 Map row = ExchangeHelper.convertToMandatoryType(exchange, Map.class, child);
071 doMarshalRecord(exchange, row, out, csv);
072 }
073 } else {
074 Map row = ExchangeHelper.convertToMandatoryType(exchange, Map.class, object);
075 doMarshalRecord(exchange, row, out, csv);
076 }
077 } finally {
078 out.close();
079 }
080 }
081
082 private void doMarshalRecord(Exchange exchange, Map row, Writer out, CSVWriter csv) throws Exception {
083 if (autogenColumns) {
084 // no specific config has been set so lets add fields
085 Set set = row.keySet();
086 updateFieldsInConfig(set, exchange);
087 }
088 csv.writeRecord(row);
089 }
090
091 public Object unmarshal(Exchange exchange, InputStream inputStream) throws Exception {
092 InputStreamReader in = new InputStreamReader(inputStream, IOConverter.getCharsetName(exchange));
093 if (delimiter != null) {
094 strategy.setDelimiter(delimiter.charAt(0));
095 }
096
097 try {
098 CSVParser parser = new CSVParser(in, strategy);
099 List<List<String>> list = new ArrayList<List<String>>();
100 while (true) {
101 String[] strings = parser.getLine();
102 if (strings == null) {
103 break;
104 }
105 List<String> line = Arrays.asList(strings);
106 list.add(line);
107 }
108 if (list.size() == 1) {
109 return list.get(0);
110 } else {
111 return list;
112 }
113 } finally {
114 in.close();
115 }
116 }
117
118 public String getDelimiter() {
119 return delimiter;
120 }
121
122 public void setDelimiter(String delimiter) {
123 if (delimiter != null && delimiter.length() > 1) {
124 throw new IllegalArgumentException("Delimiter must have a length of one!");
125 }
126 this.delimiter = delimiter;
127 }
128
129 public CSVConfig getConfig() {
130 return config;
131 }
132
133 public void setConfig(CSVConfig config) {
134 this.config = config;
135 }
136
137 public CSVStrategy getStrategy() {
138 return strategy;
139 }
140
141 public void setStrategy(CSVStrategy strategy) {
142 this.strategy = strategy;
143 }
144
145 public boolean isAutogenColumns() {
146 return autogenColumns;
147 }
148
149 /**
150 * Auto generate columns.
151 *
152 * @param autogenColumns set to false to disallow column autogeneration (default true)
153 */
154 public void setAutogenColumns(boolean autogenColumns) {
155 this.autogenColumns = autogenColumns;
156 }
157
158 private synchronized void updateFieldsInConfig(Set set, Exchange exchange) {
159 for (Object value : set) {
160 if (value != null) {
161 String text = exchange.getContext().getTypeConverter().convertTo(String.class, value);
162 // do not add field twice
163 if (config.getField(text) == null) {
164 CSVField field = new CSVField(text);
165 config.addField(field);
166 }
167 }
168 }
169 }
170 }