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.spi.DataFormat;
032    import org.apache.camel.util.ExchangeHelper;
033    import org.apache.camel.util.ObjectHelper;
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 $Revision: 15125 $
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    
056        public void marshal(Exchange exchange, Object object, OutputStream outputStream) throws Exception {
057            ObjectHelper.notNull(config, "config");
058    
059            OutputStreamWriter out = new OutputStreamWriter(outputStream);
060            CSVWriter csv = new CSVWriter(config);
061            csv.setWriter(out);
062    
063            try {
064                List list = ExchangeHelper.convertToType(exchange, List.class, object);
065                if (list != null) {
066                    for (Object child : list) {
067                        Map row = ExchangeHelper.convertToMandatoryType(exchange, Map.class, child);
068                        doMarshalRecord(exchange, row, out, csv);
069                    }
070                } else {
071                    Map row = ExchangeHelper.convertToMandatoryType(exchange, Map.class, object);
072                    doMarshalRecord(exchange, row, out, csv);
073                }
074            } finally {
075                out.close();
076            }
077        }
078    
079        private void doMarshalRecord(Exchange exchange, Map row, Writer out, CSVWriter csv) throws Exception {
080            if (autogenColumns) {
081                // no specific config has been set so lets add fields
082                Set set = row.keySet();
083                updateFieldsInConfig(set, exchange);
084            }
085            csv.writeRecord(row);
086        }
087    
088        public Object unmarshal(Exchange exchange, InputStream inputStream) throws Exception {
089            InputStreamReader in = new InputStreamReader(inputStream);
090            try {
091                CSVParser parser = new CSVParser(in, getStrategy());
092                List<List<String>> list = new ArrayList<List<String>>();
093                while (true) {
094                    String[] strings = parser.getLine();
095                    if (strings == null) {
096                        break;
097                    }
098                    List<String> line = Arrays.asList(strings);
099                    list.add(line);
100                }
101                if (list.size() == 1) {
102                    return list.get(0);
103                } else {
104                    return list;
105                }
106            } finally {
107                in.close();
108            }
109        }
110    
111        public void setConfig(CSVConfig config) {
112            this.config = config;
113        }
114    
115        public CSVStrategy getStrategy() {
116            return strategy;
117        }
118    
119        public void setStrategy(CSVStrategy strategy) {
120            this.strategy = strategy;
121        }
122    
123        public boolean isAutogenColumns() {
124            return autogenColumns;
125        }
126    
127        /**
128         * Auto generate columns.
129         *
130         * @param autogenColumns set to false to disallow column autogeneration (default true)
131         */
132        public void setAutogenColumns(boolean autogenColumns) {
133            this.autogenColumns = autogenColumns;
134        }
135    
136        protected CSVConfig createConfig() {
137            return new CSVConfig();
138        }
139    
140        private synchronized void updateFieldsInConfig(Set set, Exchange exchange) {
141            for (Object value : set) {
142                if (value != null) {
143                    String text = exchange.getContext().getTypeConverter().convertTo(String.class, value);
144                    // do not add field twice
145                    if (config.getField(text) == null) {
146                        CSVField field = new CSVField(text);
147                        config.addField(field);
148                    }
149                }
150            }
151        }
152    
153    }