This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/master by this push:
     new 7b36d71  CAMEL-12332: camel-csv - Add support for ordered Map in 
unmarshal
7b36d71 is described below

commit 7b36d71ced0c6afaa63f0b327f11a86e0bd66587
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Wed Mar 7 16:43:35 2018 +0100

    CAMEL-12332: camel-csv - Add support for ordered Map in unmarshal
---
 .../camel/model/dataformat/CsvDataFormat.java      | 18 +++++++++-
 .../camel-csv/src/main/docs/csv-dataformat.adoc    |  5 +--
 .../apache/camel/dataformat/csv/CsvDataFormat.java | 21 ++++++++++++
 .../camel/dataformat/csv/CsvRecordConverters.java  | 40 ++++++++++++++++++++++
 .../camel/dataformat/csv/CsvUnmarshaller.java      |  4 ++-
 .../camel/dataformat/csv/CsvUnmarshalTest.java     | 37 ++++++++++++++++++++
 .../csv/springboot/CsvDataFormatConfiguration.java | 18 ++++++++--
 7 files changed, 137 insertions(+), 6 deletions(-)

diff --git 
a/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java 
b/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java
index 7eeb27c..42ca8e9 100644
--- 
a/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java
+++ 
b/camel-core/src/main/java/org/apache/camel/model/dataformat/CsvDataFormat.java
@@ -91,6 +91,8 @@ public class CsvDataFormat extends DataFormatDefinition {
     @XmlAttribute
     private Boolean useMaps;
     @XmlAttribute
+    private Boolean useOrderedMaps;
+    @XmlAttribute
     private String recordConverterRef;
 
     public CsvDataFormat() {
@@ -187,6 +189,9 @@ public class CsvDataFormat extends DataFormatDefinition {
         if (useMaps != null) {
             setProperty(camelContext, dataFormat, "useMaps", useMaps);
         }
+        if (useOrderedMaps != null) {
+            setProperty(camelContext, dataFormat, "useOrderedMaps", 
useOrderedMaps);
+        }
         if (ObjectHelper.isNotEmpty(recordConverterRef)) {
             Object recordConverter = 
CamelContextHelper.mandatoryLookup(camelContext, recordConverterRef);
             setProperty(camelContext, dataFormat, "recordConverter", 
recordConverter);
@@ -438,12 +443,23 @@ public class CsvDataFormat extends DataFormatDefinition {
     }
 
     /**
-     * Whether the unmarshalling should produce maps for the lines values 
instead of lists. It requires to have header (either defined or collected).
+     * Whether the unmarshalling should produce maps (HashMap)for the lines 
values instead of lists. It requires to have header (either defined or 
collected).
      */
     public void setUseMaps(Boolean useMaps) {
         this.useMaps = useMaps;
     }
 
+    public Boolean getUseOrderedMaps() {
+        return useOrderedMaps;
+    }
+
+    /**
+     * Whether the unmarshalling should produce ordered maps (LinkedHashMap) 
for the lines values instead of lists. It requires to have header (either 
defined or collected).
+     */
+    public void setUseOrderedMaps(Boolean useOrderedMaps) {
+        this.useOrderedMaps = useOrderedMaps;
+    }
+
     public String getRecordConverterRef() {
         return recordConverterRef;
     }
diff --git a/components/camel-csv/src/main/docs/csv-dataformat.adoc 
b/components/camel-csv/src/main/docs/csv-dataformat.adoc
index 436ac90..18c2853 100644
--- a/components/camel-csv/src/main/docs/csv-dataformat.adoc
+++ b/components/camel-csv/src/main/docs/csv-dataformat.adoc
@@ -12,7 +12,7 @@ exported/imported by Excel.
 ### Options
 
 // dataformat options: START
-The CSV dataformat supports 27 options which are listed below.
+The CSV dataformat supports 28 options which are listed below.
 
 
 
@@ -43,7 +43,8 @@ The CSV dataformat supports 27 options which are listed below.
 | trim | false | Boolean | Sets whether or not to trim leading and trailing 
blanks.
 | trailingDelimiter | false | Boolean | Sets whether or not to add a trailing 
delimiter.
 | lazyLoad | false | Boolean | Whether the unmarshalling should produce an 
iterator that reads the lines on the fly or if all the lines must be read at 
one.
-| useMaps | false | Boolean | Whether the unmarshalling should produce maps 
for the lines values instead of lists. It requires to have header (either 
defined or collected).
+| useMaps | false | Boolean | Whether the unmarshalling should produce maps 
(HashMap) for the lines values instead of lists. It requires to have header 
(either defined or collected).
+| useOrderedMaps | false | Boolean | Whether the unmarshalling should produce 
ordered maps (LinkedHashMap) for the lines values instead of lists. It requires 
to have header (either defined or collected).
 | recordConverterRef |  | String | Refers to a custom CsvRecordConverter to 
lookup from the registry to use.
 | contentTypeHeader | false | Boolean | Whether the data format should set the 
Content-Type header with the type from the data format if the data format is 
capable of doing so. For example application/xml for data formats marshalling 
to XML, or application/json for data formats marshalling to JSon etc.
 |===
diff --git 
a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java
 
b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java
index 41c9529..c6f1e1a 100644
--- 
a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java
+++ 
b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java
@@ -64,6 +64,7 @@ public class CsvDataFormat extends ServiceSupport implements 
DataFormat, DataFor
     // Unmarshal options
     private boolean lazyLoad;
     private boolean useMaps;
+    private boolean useOrderedMaps;
     private CsvRecordConverter<?> recordConverter;
 
     private volatile CsvMarshaller marshaller;
@@ -674,6 +675,26 @@ public class CsvDataFormat extends ServiceSupport 
implements DataFormat, DataFor
     }
 
     /**
+     * Indicates whether or not the unmarshalling should produce ordered maps 
instead of lists.
+     *
+     * @return {@code true} for maps, {@code false} for lists
+     */
+    public boolean isUseOrderedMaps() {
+        return useOrderedMaps;
+    }
+
+    /**
+     * Sets whether or not the unmarshalling should produce ordered maps 
instead of lists.
+     *
+     * @param useOrderedMaps {@code true} for maps, {@code false} for lists
+     * @return Current {@code CsvDataFormat}, fluent API
+     */
+    public CsvDataFormat setUseOrderedMaps(boolean useOrderedMaps) {
+        this.useOrderedMaps = useOrderedMaps;
+        return this;
+    }
+
+    /**
      * Gets the record converter to use. If {@code null} then it will use 
{@link CsvDataFormat#isUseMaps()} for finding
      * the proper converter.
      *
diff --git 
a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvRecordConverters.java
 
b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvRecordConverters.java
index 120de7d..4884cbc 100644
--- 
a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvRecordConverters.java
+++ 
b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvRecordConverters.java
@@ -16,7 +16,10 @@
  */
 package org.apache.camel.dataformat.csv;
 
+import java.lang.reflect.Field;
 import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -71,4 +74,41 @@ final class CsvRecordConverters {
             return record.toMap();
         }
     }
+
+    /**
+     * Returns a converter that transforms the CSV record into an ordered map.
+     *
+     * @return converter that transforms the CSV record into an ordered map
+     */
+    public static CsvRecordConverter<Map<String, String>> 
orderedMapConverter() {
+        return OrderedMapCsvRecordConverter.SINGLETON;
+    }
+
+    private static class OrderedMapCsvRecordConverter implements 
CsvRecordConverter<Map<String, String>> {
+        private static final OrderedMapCsvRecordConverter SINGLETON = new 
OrderedMapCsvRecordConverter();
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public Map<String, String> convertRecord(CSVRecord record) {
+            Map<String, String> answer = new LinkedHashMap<>();
+
+            // use reflection because CSVRecord does not return maps ordered
+            try {
+                Field field = record.getClass().getDeclaredField("mapping");
+                field.setAccessible(true);
+                Map<String, Integer> mapping = (Map<String, Integer>) 
field.get(record);
+                if (mapping != null) {
+                    for (Object o : mapping.entrySet()) {
+                        Map.Entry<String, Integer> entry = (Map.Entry) o;
+                        int col = entry.getValue();
+                        answer.put(entry.getKey(), record.get(col));
+                    }
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+                // ignore
+            }
+            return answer;
+        }
+    }
 }
diff --git 
a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvUnmarshaller.java
 
b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvUnmarshaller.java
index da0ed54..9bde056 100644
--- 
a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvUnmarshaller.java
+++ 
b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvUnmarshaller.java
@@ -45,7 +45,7 @@ abstract class CsvUnmarshaller {
 
     public static CsvUnmarshaller create(CSVFormat format, CsvDataFormat 
dataFormat) {
         // If we want to use maps, thus the header must be either fixed or 
automatic
-        if (dataFormat.isUseMaps() && format.getHeader() == null) {
+        if ((dataFormat.isUseMaps() || dataFormat.isUseOrderedMaps()) && 
format.getHeader() == null) {
             format = format.withHeader();
         }
         // If we want to skip the header record it must automatic otherwise 
it's not working
@@ -72,6 +72,8 @@ abstract class CsvUnmarshaller {
     private static CsvRecordConverter<?> extractConverter(CsvDataFormat 
dataFormat) {
         if (dataFormat.getRecordConverter() != null) {
             return dataFormat.getRecordConverter();
+        } else if (dataFormat.isUseOrderedMaps()) {
+            return CsvRecordConverters.orderedMapConverter();
         } else if (dataFormat.isUseMaps()) {
             return CsvRecordConverters.mapConverter();
         } else {
diff --git 
a/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvUnmarshalTest.java
 
b/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvUnmarshalTest.java
index a39b70f..b7323c4 100644
--- 
a/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvUnmarshalTest.java
+++ 
b/components/camel-csv/src/test/java/org/apache/camel/dataformat/csv/CsvUnmarshalTest.java
@@ -17,6 +17,9 @@
 package org.apache.camel.dataformat.csv;
 
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -92,6 +95,35 @@ public class CsvUnmarshalTest extends CamelTestSupport {
         assertEquals(2, body.size());
         assertEquals(asMap("A", "1", "B", "2", "C", "3"), body.get(0));
         assertEquals(asMap("A", "one", "B", "two", "C", "three"), body.get(1));
+        // should be unordered map
+        Map map = (Map) body.get(0);
+        assertIsInstanceOf(HashMap.class, map);
+    }
+
+    @Test
+    public void shouldUseOrderedMaps() throws Exception {
+        output.expectedMessageCount(1);
+
+        template.sendBody("direct:orderedmap", CSV_SAMPLE);
+        output.assertIsSatisfied();
+
+        List<?> body = assertIsInstanceOf(List.class, 
output.getExchanges().get(0).getIn().getBody());
+        assertEquals(2, body.size());
+        assertEquals(asMap("A", "1", "B", "2", "C", "3"), body.get(0));
+        assertEquals(asMap("A", "one", "B", "two", "C", "three"), body.get(1));
+
+        Map map = (Map) body.get(0);
+        assertIsInstanceOf(LinkedHashMap.class, map);
+        Iterator<Map.Entry> it = map.entrySet().iterator();
+        Map.Entry e = it.next();
+        assertEquals("A", e.getKey());
+        assertEquals("1", e.getValue());
+        e = it.next();
+        assertEquals("B", e.getKey());
+        assertEquals("2", e.getValue());
+        e = it.next();
+        assertEquals("C", e.getKey());
+        assertEquals("3", e.getValue());
     }
 
     @Test
@@ -146,6 +178,11 @@ public class CsvUnmarshalTest extends CamelTestSupport {
                         .unmarshal(new CsvDataFormat().setUseMaps(true))
                         .to("mock:output");
 
+                // Use ordered maps
+                from("direct:orderedmap")
+                        .unmarshal(new CsvDataFormat().setUseOrderedMaps(true))
+                        .to("mock:output");
+
                 // Use lazy load and maps
                 from("direct:lazy_map")
                         .unmarshal(new 
CsvDataFormat().setLazyLoad(true).setUseMaps(true))
diff --git 
a/platforms/spring-boot/components-starter/camel-csv-starter/src/main/java/org/apache/camel/dataformat/csv/springboot/CsvDataFormatConfiguration.java
 
b/platforms/spring-boot/components-starter/camel-csv-starter/src/main/java/org/apache/camel/dataformat/csv/springboot/CsvDataFormatConfiguration.java
index f01a3ea..f480eb4 100644
--- 
a/platforms/spring-boot/components-starter/camel-csv-starter/src/main/java/org/apache/camel/dataformat/csv/springboot/CsvDataFormatConfiguration.java
+++ 
b/platforms/spring-boot/components-starter/camel-csv-starter/src/main/java/org/apache/camel/dataformat/csv/springboot/CsvDataFormatConfiguration.java
@@ -132,12 +132,18 @@ public class CsvDataFormatConfiguration
      */
     private Boolean lazyLoad = false;
     /**
-     * Whether the unmarshalling should produce maps for the lines values
-     * instead of lists. It requires to have header (either defined or
+     * Whether the unmarshalling should produce maps (HashMap) for the lines
+     * values instead of lists. It requires to have header (either defined or
      * collected).
      */
     private Boolean useMaps = false;
     /**
+     * Whether the unmarshalling should produce ordered maps (LinkedHashMap) 
for
+     * the lines values instead of lists. It requires to have header (either
+     * defined or collected).
+     */
+    private Boolean useOrderedMaps = false;
+    /**
      * Refers to a custom CsvRecordConverter to lookup from the registry to 
use.
      */
     private String recordConverterRef;
@@ -349,6 +355,14 @@ public class CsvDataFormatConfiguration
         this.useMaps = useMaps;
     }
 
+    public Boolean getUseOrderedMaps() {
+        return useOrderedMaps;
+    }
+
+    public void setUseOrderedMaps(Boolean useOrderedMaps) {
+        this.useOrderedMaps = useOrderedMaps;
+    }
+
     public String getRecordConverterRef() {
         return recordConverterRef;
     }

-- 
To stop receiving notification emails like this one, please contact
davscl...@apache.org.

Reply via email to