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.component.flatpack;
018    
019    import java.io.IOException;
020    import java.io.InputStream;
021    import java.io.InputStreamReader;
022    import java.io.OutputStream;
023    import java.io.OutputStreamWriter;
024    import java.io.Reader;
025    import java.util.List;
026    import java.util.Map;
027    
028    import net.sf.flatpack.DataSet;
029    import net.sf.flatpack.DefaultParserFactory;
030    import net.sf.flatpack.Parser;
031    import net.sf.flatpack.ParserFactory;
032    import net.sf.flatpack.writer.DelimiterWriterFactory;
033    import net.sf.flatpack.writer.FixedWriterFactory;
034    import net.sf.flatpack.writer.Writer;
035    import org.apache.camel.Exchange;
036    import org.apache.camel.converter.IOConverter;
037    import org.apache.camel.spi.DataFormat;
038    import org.apache.camel.util.ObjectHelper;
039    import org.apache.commons.logging.Log;
040    import org.apache.commons.logging.LogFactory;
041    import org.jdom.JDOMException;
042    import org.springframework.core.io.Resource;
043    
044    /**
045     * Flatpack DataFormat.
046     * <p/>
047     * This data format supports two operations:
048     * <ul>
049     * <li>marshal = from <tt>List&lt;Map&lt;String, Object&gt;&gt;</tt> to <tt>OutputStream</tt> (can be converted to String)</li>
050     * <li>unmarshal = from <tt>InputStream</tt> (such as a File) to {@link DataSetList}.
051     * </ul>
052     * <b>Notice:</b> The Flatpack library does currently not support header and trailers for the marshal operation.
053     *
054     * @version $Revision: 22872 $
055     */
056    public class FlatpackDataFormat implements DataFormat {
057        private static final transient Log LOG = LogFactory.getLog(FlatpackDataFormat.class);
058        private ParserFactory parserFactory = DefaultParserFactory.getInstance();
059        private char delimiter = ',';
060        private char textQualifier = '"';
061        private boolean ignoreFirstRecord = true;
062        private Resource definition;
063        private boolean fixed;
064    
065        @SuppressWarnings("unchecked")
066        public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception {
067            ObjectHelper.notNull(graph, "The object to marshal must be provided");
068    
069            List<Map<String, Object>> data = (List<Map<String, Object>>) graph;
070            if (data.isEmpty()) {
071                LOG.warn("No data to marshal as the list is empty");
072                return;
073            }
074            Map<String, Object> firstRow = data.get(0);
075    
076            Writer writer = createWriter(exchange, firstRow, stream);
077            try {
078                boolean first = true;
079                writer.printHeader();
080                for (Map<String, Object> row : data) {
081                    if (ignoreFirstRecord && first) {
082                        // skip first row
083                        first = false;
084                        continue;
085                    }
086                    for (String key : row.keySet()) {
087                        writer.addRecordEntry(key, row.get(key));
088                    }
089                    writer.nextRecord();
090                }
091                writer.printFooter();
092            } finally {
093                writer.flush();
094                writer.close();
095            }
096        }
097    
098        public Object unmarshal(Exchange exchange, InputStream stream) throws Exception {
099            InputStreamReader reader = new InputStreamReader(stream, IOConverter.getCharsetName(exchange));
100            try {
101                Parser parser = createParser(exchange, reader);
102                DataSet dataSet = parser.parse();
103                return new DataSetList(dataSet);
104            } finally {
105                reader.close();
106            }
107        }
108    
109        // Properties
110        //-------------------------------------------------------------------------
111    
112        public boolean isFixed() {
113            return fixed;
114        }
115    
116        public void setFixed(boolean fixed) {
117            this.fixed = fixed;
118        }
119    
120        public char getDelimiter() {
121            return delimiter;
122        }
123    
124        public void setDelimiter(char delimiter) {
125            this.delimiter = delimiter;
126        }
127    
128        public boolean isIgnoreFirstRecord() {
129            return ignoreFirstRecord;
130        }
131    
132        public void setIgnoreFirstRecord(boolean ignoreFirstRecord) {
133            this.ignoreFirstRecord = ignoreFirstRecord;
134        }
135    
136        public char getTextQualifier() {
137            return textQualifier;
138        }
139    
140        public void setTextQualifier(char textQualifier) {
141            this.textQualifier = textQualifier;
142        }
143    
144        public Resource getDefinition() {
145            return definition;
146        }
147    
148        public void setDefinition(Resource definition) {
149            this.definition = definition;
150        }
151    
152        public ParserFactory getParserFactory() {
153            return parserFactory;
154        }
155    
156        public void setParserFactory(ParserFactory parserFactory) {
157            this.parserFactory = parserFactory;
158        }
159    
160        // Implementation methods
161        //-------------------------------------------------------------------------
162    
163        protected Parser createParser(Exchange exchange, Reader bodyReader) throws IOException {
164            if (isFixed()) {
165                Resource resource = getDefinition();
166                ObjectHelper.notNull(resource, "resource");
167                return getParserFactory().newFixedLengthParser(new InputStreamReader(resource.getInputStream(), IOConverter.getCharsetName(exchange)), bodyReader);
168            } else {
169                Resource resource = getDefinition();
170                if (resource == null) {
171                    return getParserFactory().newDelimitedParser(bodyReader, delimiter, textQualifier);
172                } else {
173                    return getParserFactory().newDelimitedParser(new InputStreamReader(resource.getInputStream(), IOConverter.getCharsetName(exchange)), 
174                                                                 bodyReader, delimiter, textQualifier, ignoreFirstRecord);
175                }
176            }
177        }
178    
179        private Writer createWriter(Exchange exchange, Map<String, Object> firstRow, OutputStream stream) throws JDOMException, IOException {
180            if (isFixed()) {
181                Resource resource = getDefinition();
182                ObjectHelper.notNull(resource, "resource");
183                FixedWriterFactory factory = new FixedWriterFactory(new InputStreamReader(resource.getInputStream(), IOConverter.getCharsetName(exchange)));
184                return factory.createWriter(new OutputStreamWriter(stream, IOConverter.getCharsetName(exchange)));
185            } else {
186                Resource resource = getDefinition();
187                if (resource == null) {
188                    DelimiterWriterFactory factory = new DelimiterWriterFactory(delimiter, textQualifier);
189                    // add coulmns from the keys in the data map as the columns must be known
190                    for (String key : firstRow.keySet()) {
191                        factory.addColumnTitle(key);
192                    }
193                    return factory.createWriter(new OutputStreamWriter(stream, IOConverter.getCharsetName(exchange)));
194                } else {
195                    DelimiterWriterFactory factory = new DelimiterWriterFactory(new InputStreamReader(resource.getInputStream(), IOConverter.getCharsetName(exchange)), delimiter, textQualifier);
196                    return factory.createWriter(new OutputStreamWriter(stream, IOConverter.getCharsetName(exchange)));
197                }
198            }
199        }
200    }