You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2018/02/01 08:36:57 UTC

[camel] 01/02: CAMEL-12192 support csv bindy skip fields

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

commit 024b059663be07eea6c4379c9114e7ca6563acd9
Author: longxu <ne...@gmail.com>
AuthorDate: Tue Jan 30 22:16:43 2018 +1100

    CAMEL-12192 support csv bindy skip fields
---
 .../camel/dataformat/bindy/BindyCsvFactory.java    | 188 ++++++++++++---------
 .../dataformat/bindy/csv/BindyCsvDataFormat.java   |  16 +-
 .../bindy/csv/BindyCsvSkipFieldTest.java           | 170 +++++++++++++++++++
 .../bindy/csv/BindyCsvSkipFieldTest-context.xml    |  34 ++++
 4 files changed, 328 insertions(+), 80 deletions(-)

diff --git a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyCsvFactory.java b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyCsvFactory.java
index 9906402..0015947 100755
--- a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyCsvFactory.java
+++ b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyCsvFactory.java
@@ -75,12 +75,20 @@ public class BindyCsvFactory extends BindyAbstractFactory implements BindyFactor
     private boolean allowEmptyStream;
     private boolean quotingEscaped;
     private boolean endWithLineBreak;
+    
+    private boolean isSkipField;
 
     public BindyCsvFactory(Class<?> type) throws Exception {
+        this(type, false);
+    }
+
+    public BindyCsvFactory(Class<?> type, boolean isSkipField) throws Exception {
         super(type);
 
         // initialize specific parameters of the csv model
         initCsvModel();
+        
+        this.isSkipField = isSkipField;
     }
 
     /**
@@ -174,105 +182,118 @@ public class BindyCsvFactory extends BindyAbstractFactory implements BindyFactor
 
             // Get DataField from model
             DataField dataField = dataFields.get(pos);
-            ObjectHelper.notNull(dataField, "No position " + pos + " defined for the field: " + data + ", line: " + line);
-
-            if (dataField.trim()) {
-                data = data.trim();
+            
+            // If a DataField can be skipped, it needs to check whether it is in dataFields keyset
+            if (isSkipField()) {
+                if (dataFields.keySet().contains(pos))  {
+                    counterMandatoryFields = setDataFieldValue(camelContext, model, line, pos, counterMandatoryFields, data, dataField);
+                }
+            } else {
+                counterMandatoryFields = setDataFieldValue(camelContext, model, line, pos, counterMandatoryFields, data, dataField);
             }
+            
+            ++pos;
 
-            if (dataField.required()) {
-                // Increment counter of mandatory fields
-                ++counterMandatoryFields;
+        }
 
-                // Check if content of the field is empty
-                // This is not possible for mandatory fields
-                if (data.equals("")) {
-                    throw new IllegalArgumentException("The mandatory field defined at the position " + pos + " is empty for the line: " + line);
-                }
-            }
+        LOG.debug("Counter mandatory fields: {}", counterMandatoryFields);
 
-            // Get Field to be setted
-            Field field = annotatedFields.get(pos);
-            field.setAccessible(true);
+        if (counterMandatoryFields < numberMandatoryFields) {
+            throw new IllegalArgumentException("Some mandatory fields are missing, line: " + line);
+        }
 
-            if (LOG.isDebugEnabled()) {
-                LOG.debug("Pos: {}, Data: {}, Field type: {}", new Object[]{pos, data, field.getType()});
+        if (pos < totalFields) {
+            setDefaultValuesForFields(model);
+        }
+
+    }
+
+    private int setDataFieldValue(CamelContext camelContext, Map<String, Object> model, int line, int pos, int counterMandatoryFields, String data, DataField dataField) throws Exception {
+        ObjectHelper.notNull(dataField, "No position " + pos + " defined for the field: " + data + ", line: " + line);
+
+        if (dataField.trim()) {
+            data = data.trim();
+        }
+
+        if (dataField.required()) {
+            // Increment counter of mandatory fields
+            ++counterMandatoryFields;
+
+            // Check if content of the field is empty
+            // This is not possible for mandatory fields
+            if (data.equals("")) {
+                throw new IllegalArgumentException("The mandatory field defined at the position " + pos + " is empty for the line: " + line);
             }
+        }
 
-            // Create format object to format the field
-            FormattingOptions formattingOptions = ConverterUtils.convert(dataField,
-                    field.getType(),
-                    field.getAnnotation(BindyConverter.class),
-                    getLocale());
-            Format<?> format = formatFactory.getFormat(formattingOptions);
+        // Get Field to be setted
+        Field field = annotatedFields.get(pos);
+        field.setAccessible(true);
 
-            // field object to be set
-            Object modelField = model.get(field.getDeclaringClass().getName());
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Pos: {}, Data: {}, Field type: {}", new Object[]{pos, data, field.getType()});
+        }
 
-            // format the data received
-            Object value = null;
+        // Create format object to format the field
+        FormattingOptions formattingOptions = ConverterUtils.convert(dataField,
+                field.getType(),
+                field.getAnnotation(BindyConverter.class),
+                getLocale());
+        Format<?> format = formatFactory.getFormat(formattingOptions);
 
-            if (!data.equals("")) {
-                try {
-                    if (quoting && quote != null && (data.contains("\\" + quote) || data.contains(quote)) && quotingEscaped) {
-                        value = format.parse(data.replaceAll("\\\\" + quote,  "\\" + quote));
-                    } else {
-                        value = format.parse(data);
-                    }
-                } catch (FormatException ie) {
-                    throw new IllegalArgumentException(ie.getMessage() + ", position: " + pos + ", line: " + line, ie);
-                } catch (Exception e) {
-                    throw new IllegalArgumentException("Parsing error detected for field defined at the position: " + pos + ", line: " + line, e);
-                }
-            } else {
-                if (!dataField.defaultValue().isEmpty()) {
-                    value = format.parse(dataField.defaultValue());
+        // field object to be set
+        Object modelField = model.get(field.getDeclaringClass().getName());
+
+        // format the data received
+        Object value = null;
+
+        if (!data.equals("")) {
+            try {
+                if (quoting && quote != null && (data.contains("\\" + quote) || data.contains(quote)) && quotingEscaped) {
+                    value = format.parse(data.replaceAll("\\\\" + quote, "\\" + quote));
                 } else {
-                    value = getDefaultValueForPrimitive(field.getType());
+                    value = format.parse(data);
                 }
+            } catch (FormatException ie) {
+                throw new IllegalArgumentException(ie.getMessage() + ", position: " + pos + ", line: " + line, ie);
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Parsing error detected for field defined at the position: " + pos + ", line: " + line, e);
             }
-            
-            if (value != null && !dataField.method().isEmpty()) {
-                Class<?> clazz;
-                if (dataField.method().contains(".")) {
-                    clazz = camelContext.getClassResolver().resolveMandatoryClass(dataField.method().substring(0, dataField.method().lastIndexOf(".")));
-                } else {
-                    clazz = field.getType();
-                }
-                
-                String methodName = dataField.method().substring(dataField.method().lastIndexOf(".") + 1,
-                                                                   dataField.method().length());
-                
-                Method m = ReflectionHelper.findMethod(clazz, methodName, field.getType());
-                if (m != null) {
-                    // this method must be static and return type
-                    // must be the same as the datafield and 
-                    // must receive only the datafield value 
-                    // as the method argument
-                    value = ObjectHelper.invokeMethod(m, null, value);
-                } else {
-                    // fallback to method without parameter, that is on the value itself
-                    m = ReflectionHelper.findMethod(clazz, methodName);
-                    value = ObjectHelper.invokeMethod(m, value);
-                }
+        } else {
+            if (!dataField.defaultValue().isEmpty()) {
+                value = format.parse(dataField.defaultValue());
+            } else {
+                value = getDefaultValueForPrimitive(field.getType());
             }
-
-            field.set(modelField, value);
-
-            ++pos;
-
         }
 
-        LOG.debug("Counter mandatory fields: {}", counterMandatoryFields);
+        if (value != null && !dataField.method().isEmpty()) {
+            Class<?> clazz;
+            if (dataField.method().contains(".")) {
+                clazz = camelContext.getClassResolver().resolveMandatoryClass(dataField.method().substring(0, dataField.method().lastIndexOf(".")));
+            } else {
+                clazz = field.getType();
+            }
 
-        if (counterMandatoryFields < numberMandatoryFields) {
-            throw new IllegalArgumentException("Some mandatory fields are missing, line: " + line);
-        }
+            String methodName = dataField.method().substring(dataField.method().lastIndexOf(".") + 1,
+                    dataField.method().length());
 
-        if (pos < totalFields) {
-            setDefaultValuesForFields(model);
+            Method m = ReflectionHelper.findMethod(clazz, methodName, field.getType());
+            if (m != null) {
+                // this method must be static and return type
+                // must be the same as the datafield and 
+                // must receive only the datafield value 
+                // as the method argument
+                value = ObjectHelper.invokeMethod(m, null, value);
+            } else {
+                // fallback to method without parameter, that is on the value itself
+                m = ReflectionHelper.findMethod(clazz, methodName);
+                value = ObjectHelper.invokeMethod(m, value);
+            }
         }
 
+        field.set(modelField, value);
+        return counterMandatoryFields;
     }
 
     @Override
@@ -719,4 +740,13 @@ public class BindyCsvFactory extends BindyAbstractFactory implements BindyFactor
     public boolean isEndWithLineBreak() {
         return endWithLineBreak;
     }
+
+    /**
+     * Indicate if DataField can be ignored
+     * 
+     * @return boolean
+     */
+    public boolean isSkipField() {
+        return this.isSkipField;
+    }
 }
diff --git a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/csv/BindyCsvDataFormat.java b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/csv/BindyCsvDataFormat.java
index 00ea809..d076198 100755
--- a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/csv/BindyCsvDataFormat.java
+++ b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/csv/BindyCsvDataFormat.java
@@ -47,11 +47,21 @@ import org.slf4j.LoggerFactory;
 public class BindyCsvDataFormat extends BindyAbstractDataFormat {
     private static final Logger LOG = LoggerFactory.getLogger(BindyCsvDataFormat.class);
 
+    /**
+     * If isSkipField = true, a CSV file doesn't need to declare all the fields, otherwise, all the fields are mandatory.
+     */
+    private boolean isSkipField;
+
     public BindyCsvDataFormat() {
     }
 
     public BindyCsvDataFormat(Class<?> type) {
+       this(type, false);
+    }
+    
+    public BindyCsvDataFormat(Class<?> type, boolean isSkipField) {
         super(type);
+        this.isSkipField = isSkipField;
     }
 
     @Override
@@ -310,8 +320,12 @@ public class BindyCsvDataFormat extends BindyAbstractDataFormat {
 
     @Override
     protected BindyAbstractFactory createModelFactory(FormatFactory formatFactory) throws Exception {
-        BindyCsvFactory bindyCsvFactory = new BindyCsvFactory(getClassType());
+        BindyCsvFactory bindyCsvFactory = new BindyCsvFactory(getClassType(), isSkipField());
         bindyCsvFactory.setFormatFactory(formatFactory);
         return bindyCsvFactory;
     }
+
+    private boolean isSkipField() {
+        return this.isSkipField;
+    }
 }
diff --git a/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/csv/BindyCsvSkipFieldTest.java b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/csv/BindyCsvSkipFieldTest.java
new file mode 100644
index 0000000..f9907b5
--- /dev/null
+++ b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/csv/BindyCsvSkipFieldTest.java
@@ -0,0 +1,170 @@
+/**
+ * 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.bindy.csv;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
+import org.apache.camel.dataformat.bindy.annotation.DataField;
+import org.junit.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
+
+
+@ContextConfiguration
+public class BindyCsvSkipFieldTest  extends AbstractJUnit4SpringContextTests {
+
+    private static final String URI_MOCK_RESULT = "mock:result";
+    private static final String URI_DIRECT_START = "direct:start";
+    
+    private static String input = "VOA,12 abc street,Skip Street,Melbourne,VIC,3000,Australia,Skip dummy1,end of record";
+
+    @Produce(uri = URI_DIRECT_START)
+    private ProducerTemplate template;
+
+    @EndpointInject(uri = URI_MOCK_RESULT)
+    private MockEndpoint result;
+
+    private String expected;
+    
+    @Test
+    @DirtiesContext
+    public void testUnMarshalAndMarshal() throws Exception {
+        
+        template.sendBody(input);
+        result.expectedMessageCount(1);
+        result.assertIsSatisfied();
+    }
+
+    public static class ContextConfig extends RouteBuilder {
+        BindyCsvDataFormat camelDataFormat = new BindyCsvDataFormat(CsvSkipField.class, true);
+
+        public void configure() {
+            from(URI_DIRECT_START).unmarshal(camelDataFormat)
+                    .process(new Processor() {
+                        @Override
+                        public void process(Exchange exchange) throws Exception {
+                            CsvSkipField csvSkipField = (CsvSkipField) exchange.getIn().getBody();
+                            assert  csvSkipField.getAttention().equals("VOA");
+                            assert  csvSkipField.getAddressLine1().equals("12 abc street");
+                            assert  csvSkipField.getCity().equals("Melbourne");
+                            assert  csvSkipField.getState().equals("VIC");
+                            assert  csvSkipField.getZip().equals("3000");
+                            assert  csvSkipField.getCountry() .equals("Australia");
+                            assert  csvSkipField.getDummy2().equals("end of record");
+                        }
+                    })
+                    
+                    .marshal(camelDataFormat)
+                    .convertBodyTo(String.class)
+                    .to(URI_MOCK_RESULT);
+        }
+
+    }
+    
+    @CsvRecord(separator = ",")
+    public static class CsvSkipField {
+        @DataField(pos = 1)
+        private String attention;
+        
+        @DataField(pos = 2)
+        private String addressLine1;
+        
+        @DataField(pos = 4)
+        private String city;
+        
+        @DataField(pos = 5)
+        private String state;
+        
+        @DataField(pos = 6)
+        private String zip;
+        
+        @DataField(pos = 7)
+        private String country;
+        
+        @DataField(pos = 9)
+        private String dummy2;
+
+        public String getAttention() {
+            return attention;
+        }
+
+        public void setAttention(String attention) {
+            this.attention = attention;
+        }
+
+        public String getAddressLine1() {
+            return addressLine1;
+        }
+
+        public void setAddressLine1(String addressLine1) {
+            this.addressLine1 = addressLine1;
+        }
+
+        public String getCity() {
+            return city;
+        }
+
+        public void setCity(String city) {
+            this.city = city;
+        }
+
+        public String getState() {
+            return state;
+        }
+
+        public void setState(String state) {
+            this.state = state;
+        }
+
+        public String getZip() {
+            return zip;
+        }
+
+        public void setZip(String zip) {
+            this.zip = zip;
+        }
+
+        public String getCountry() {
+            return country;
+        }
+
+        public void setCountry(String country) {
+            this.country = country;
+        }
+
+        public String getDummy2() {
+            return dummy2;
+        }
+
+        public void setDummy2(String dummy2) {
+            this.dummy2 = dummy2;
+        }
+
+        @Override
+        public String toString() {
+            return "Record [attention=" + getAttention() + ", addressLine1=" + getAddressLine1() + ", " + "city=" + getCity() + ", state=" + getState() + ", zip=" + getZip() + ", country="
+                    + getCountry() + ", dummy2=" + getDummy2() + "]";
+        }
+    }
+}
diff --git a/components/camel-bindy/src/test/resources/org/apache/camel/dataformat/bindy/csv/BindyCsvSkipFieldTest-context.xml b/components/camel-bindy/src/test/resources/org/apache/camel/dataformat/bindy/csv/BindyCsvSkipFieldTest-context.xml
new file mode 100644
index 0000000..c1c6ebd
--- /dev/null
+++ b/components/camel-bindy/src/test/resources/org/apache/camel/dataformat/bindy/csv/BindyCsvSkipFieldTest-context.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="
+     http://www.springframework.org/schema/beans
+     http://www.springframework.org/schema/beans/spring-beans.xsd
+     http://camel.apache.org/schema/spring
+     http://camel.apache.org/schema/spring/camel-spring.xsd">
+     
+	<camelContext xmlns="http://camel.apache.org/schema/spring">
+		<routeBuilder ref="myBuilder" /> 
+	</camelContext>
+	
+	<bean id="myBuilder" class="org.apache.camel.dataformat.bindy.csv.BindyCsvSkipFieldTest$ContextConfig"/>
+	
+</beans>
\ No newline at end of file

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