/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.camel.dataformat.flatpack; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.util.List; import java.util.Map; import java.util.Map.Entry; import net.sf.flatpack.DataSet; import net.sf.flatpack.DefaultParserFactory; import net.sf.flatpack.Parser; import net.sf.flatpack.ParserFactory; import net.sf.flatpack.writer.DelimiterWriterFactory; import net.sf.flatpack.writer.FixedWriterFactory; import net.sf.flatpack.writer.Writer; import org.apache.camel.Exchange; import org.apache.camel.component.flatpack.DataSetList; import org.apache.camel.spi.DataFormat; import org.apache.camel.spi.DataFormatName; import org.apache.camel.support.ServiceSupport; import org.apache.camel.util.IOHelper; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.ResourceHelper; import org.jdom.JDOMException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Flatpack DataFormat. * <p/> * This data format supports two operations: * <ul> * <li>marshal = from <tt>List<Map<String, Object>></tt> to <tt>OutputStream</tt> (can be converted to String)</li> * <li>unmarshal = from <tt>InputStream</tt> (such as a File) to {@link org.apache.camel.component.flatpack.DataSetList}. * </ul> * <b>Notice:</b> The Flatpack library does currently not support header and trailers for the marshal operation. * * @version */ public class FlatpackDataFormat extends ServiceSupport implements DataFormat, DataFormatName { private static final Logger LOG = LoggerFactory.getLogger(FlatpackDataFormat.class); private ParserFactory parserFactory = DefaultParserFactory.getInstance(); private char delimiter = ','; private char textQualifier = '"'; private boolean ignoreFirstRecord = true; private boolean fixed; private boolean allowShortLines; private boolean ignoreExtraColumns; private String definition; @Override public String getDataFormatName() { return "flatback"; } @SuppressWarnings("unchecked") public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception { ObjectHelper.notNull(graph, "The object to marshal must be provided"); List<Map<String, Object>> data = (List<Map<String, Object>>) graph; if (data.isEmpty()) { LOG.warn("No data to marshal as the list is empty"); return; } Map<String, Object> firstRow = data.get(0); Writer writer = createWriter(exchange, firstRow, stream); try { boolean first = true; writer.printHeader(); for (Map<String, Object> row : data) { if (ignoreFirstRecord && first) { // skip first row first = false; continue; } for (Entry<String, Object> entry : row.entrySet()) { writer.addRecordEntry(entry.getKey(), entry.getValue()); } writer.nextRecord(); } writer.printFooter(); } finally { writer.flush(); writer.close(); } } public Object unmarshal(Exchange exchange, InputStream stream) throws Exception { InputStreamReader reader = new InputStreamReader(stream, IOHelper.getCharsetName(exchange)); try { Parser parser = createParser(exchange, reader); DataSet dataSet = parser.parse(); return new DataSetList(dataSet); } finally { reader.close(); } } @Override protected void doStart() throws Exception { // noop } @Override protected void doStop() throws Exception { // noop } // Properties //------------------------------------------------------------------------- public String getDefinition() { return definition; } public void setDefinition(String definition) { this.definition = definition; } public boolean isFixed() { return fixed; } public void setFixed(boolean fixed) { this.fixed = fixed; } public char getDelimiter() { return delimiter; } public void setDelimiter(char delimiter) { this.delimiter = delimiter; } public boolean isIgnoreFirstRecord() { return ignoreFirstRecord; } public void setIgnoreFirstRecord(boolean ignoreFirstRecord) { this.ignoreFirstRecord = ignoreFirstRecord; } public char getTextQualifier() { return textQualifier; } public void setTextQualifier(char textQualifier) { this.textQualifier = textQualifier; } public ParserFactory getParserFactory() { return parserFactory; } public void setParserFactory(ParserFactory parserFactory) { this.parserFactory = parserFactory; } public boolean isAllowShortLines() { return this.allowShortLines; } /** * Allows for lines to be shorter than expected and ignores the extra characters */ public void setAllowShortLines(boolean allowShortLines) { this.allowShortLines = allowShortLines; } /** * Allows for lines to be longer than expected and ignores the extra characters */ public void setIgnoreExtraColumns(boolean ignoreExtraColumns) { this.ignoreExtraColumns = ignoreExtraColumns; } public boolean isIgnoreExtraColumns() { return ignoreExtraColumns; } // Implementation methods //------------------------------------------------------------------------- protected Parser createParser(Exchange exchange, Reader bodyReader) throws IOException { if (isFixed()) { InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(exchange.getContext(), getDefinition()); InputStreamReader reader = new InputStreamReader(is, IOHelper.getCharsetName(exchange)); Parser parser = getParserFactory().newFixedLengthParser(reader, bodyReader); if (allowShortLines) { parser.setHandlingShortLines(true); parser.setIgnoreParseWarnings(true); } if (ignoreExtraColumns) { parser.setIgnoreExtraColumns(true); parser.setIgnoreParseWarnings(true); } return parser; } else { if (ObjectHelper.isEmpty(getDefinition())) { return getParserFactory().newDelimitedParser(bodyReader, delimiter, textQualifier); } else { InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(exchange.getContext(), getDefinition()); InputStreamReader reader = new InputStreamReader(is, IOHelper.getCharsetName(exchange)); Parser parser = getParserFactory().newDelimitedParser(reader, bodyReader, delimiter, textQualifier, ignoreFirstRecord); if (allowShortLines) { parser.setHandlingShortLines(true); parser.setIgnoreParseWarnings(true); } if (ignoreExtraColumns) { parser.setIgnoreExtraColumns(true); parser.setIgnoreParseWarnings(true); } return parser; } } } private Writer createWriter(Exchange exchange, Map<String, Object> firstRow, OutputStream stream) throws JDOMException, IOException { if (isFixed()) { InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(exchange.getContext(), getDefinition()); InputStreamReader reader = new InputStreamReader(is, IOHelper.getCharsetName(exchange)); FixedWriterFactory factory = new FixedWriterFactory(reader); return factory.createWriter(new OutputStreamWriter(stream, IOHelper.getCharsetName(exchange))); } else { if (getDefinition() == null) { DelimiterWriterFactory factory = new DelimiterWriterFactory(delimiter, textQualifier); // add columns from the keys in the data map as the columns must be known for (String key : firstRow.keySet()) { factory.addColumnTitle(key); } return factory.createWriter(new OutputStreamWriter(stream, IOHelper.getCharsetName(exchange))); } else { InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(exchange.getContext(), getDefinition()); InputStreamReader reader = new InputStreamReader(is, IOHelper.getCharsetName(exchange)); DelimiterWriterFactory factory = new DelimiterWriterFactory(reader, delimiter, textQualifier); return factory.createWriter(new OutputStreamWriter(stream, IOHelper.getCharsetName(exchange))); } } } }