You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by an...@apache.org on 2016/06/28 20:00:52 UTC

camel git commit: CAMEL-9476: added possibility to ignore too short lines for fixed layout

Repository: camel
Updated Branches:
  refs/heads/master 05f13c00a -> 0e72f475f


CAMEL-9476: added possibility to ignore too short lines for fixed layout


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/0e72f475
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/0e72f475
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/0e72f475

Branch: refs/heads/master
Commit: 0e72f475f80a9699fe49004c431e1ce4e360c35d
Parents: 05f13c0
Author: Arno Noordover <an...@users.noreply.github.com>
Authored: Tue Jun 28 22:00:39 2016 +0200
Committer: Arno Noordover <an...@users.noreply.github.com>
Committed: Tue Jun 28 22:00:39 2016 +0200

----------------------------------------------------------------------
 .../bindy/BindyFixedLengthFactory.java          | 61 +++++++++++-
 .../bindy/annotation/FixedLengthRecord.java     |  4 +
 .../bindy/fixed/BindyFixedLengthDataFormat.java | 73 +++++++++------
 .../fixed/BindyPaddingAndTrimmingTest.java      | 97 ++++++++++++++++++++
 ...impleFixedLengthUnmarshallTrimFieldTest.java |  6 +-
 5 files changed, 209 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/0e72f475/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java
----------------------------------------------------------------------
diff --git a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java
index c517852..302a2f7 100644
--- a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java
+++ b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java
@@ -64,6 +64,7 @@ public class BindyFixedLengthFactory extends BindyAbstractFactory implements Bin
     private char paddingChar;
     private int recordLength;
     private boolean ignoreTrailingChars;
+    private boolean ignoreMissingChars;
 
     private Class<?> header;
     private Class<?> footer;
@@ -201,7 +202,15 @@ public class BindyFixedLengthFactory extends BindyAbstractFactory implements Bin
             }
 
             if (length > 0) {
-                token = record.substring(offset - 1, offset + length - 1);
+                if (record.length() < offset) {
+                    token = "";
+                } else {
+                    int endIndex = offset + length - 1;
+                    if (endIndex > record.length()) {
+                        endIndex = record.length();
+                    }
+                    token = record.substring(offset - 1, endIndex);
+                }
                 offset += length;
             } else if (!delimiter.equals("")) {
                 String tempToken = record.substring(offset - 1, record.length());
@@ -214,7 +223,8 @@ public class BindyFixedLengthFactory extends BindyAbstractFactory implements Bin
             }
 
             if (dataField.trim()) {
-                token = token.trim();
+                token = trim(token, dataField, paddingChar);
+                //token = token.trim();
             }
 
             // Check mandatory field
@@ -252,7 +262,10 @@ public class BindyFixedLengthFactory extends BindyAbstractFactory implements Bin
             // format the data received
             Object value = null;
 
-            if (!token.equals("")) {
+            if ("".equals(token)) {
+                token = dataField.defaultValue();
+            }
+            if (!"".equals(token)) {
                 try {
                     value = format.parse(token);
                 } catch (FormatException ie) {
@@ -287,6 +300,38 @@ public class BindyFixedLengthFactory extends BindyAbstractFactory implements Bin
 
     }
 
+    private String trim(String token, DataField dataField, char paddingChar) {
+        char myPaddingChar = dataField.paddingChar();
+        if (dataField.paddingChar() == 0) {
+            myPaddingChar = paddingChar;
+        }
+        if ("R".equals(dataField.align())) {
+            return leftTrim(token, myPaddingChar);
+        } else {
+            return rightTrim(token, myPaddingChar);
+        }
+    }
+
+    private String rightTrim(String token, char myPaddingChar) {
+        StringBuilder sb = new StringBuilder(token);
+
+        while (sb.length() > 0 && myPaddingChar == sb.charAt(sb.length() - 1)) {
+            sb.deleteCharAt(sb.length() - 1);
+        }
+
+        return sb.toString();
+    }
+
+    private String leftTrim(String token, char myPaddingChar) {
+        StringBuilder sb = new StringBuilder(token);
+
+        while (sb.length() > 0 && myPaddingChar == (sb.charAt(0))) {
+            sb.deleteCharAt(0);
+        }
+
+        return sb.toString();
+    }
+
     @Override
     public String unbind(Map<String, Object> model) throws Exception {
 
@@ -516,6 +561,9 @@ public class BindyFixedLengthFactory extends BindyAbstractFactory implements Bin
                 // Get flag for ignore trailing characters
                 ignoreTrailingChars = record.ignoreTrailingChars();
                 LOG.debug("Ignore trailing chars: {}", ignoreTrailingChars);
+
+                ignoreMissingChars = record.ignoreMissingChars();
+                LOG.debug("Enable ignore missing chars: {}", ignoreMissingChars);
             }
         }
 
@@ -613,4 +661,11 @@ public class BindyFixedLengthFactory extends BindyAbstractFactory implements Bin
         return this.ignoreTrailingChars;
     }
 
+    /**
+     * Flag indicating whether too short lines are ignored
+     */
+    public boolean isIgnoreMissingChars() {
+        return ignoreMissingChars;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/0e72f475/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java
----------------------------------------------------------------------
diff --git a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java
index eca6fd1..ba1e25b 100644
--- a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java
+++ b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java
@@ -81,4 +81,8 @@ public @interface FixedLengthRecord {
      */
     boolean ignoreTrailingChars() default false;
 
+    /**
+     * Indicates whether too short lines will be ignored
+     */
+    boolean ignoreMissingChars() default false;
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/0e72f475/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedLengthDataFormat.java
----------------------------------------------------------------------
diff --git a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedLengthDataFormat.java b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedLengthDataFormat.java
index 22efd73..23fce52 100644
--- a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedLengthDataFormat.java
+++ b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedLengthDataFormat.java
@@ -45,15 +45,15 @@ import org.slf4j.LoggerFactory;
  * {@link DataFormat}) using Bindy to marshal to and from Fixed Length
  */
 public class BindyFixedLengthDataFormat extends BindyAbstractDataFormat {
-    
+
     public static final String CAMEL_BINDY_FIXED_LENGTH_HEADER = "CamelBindyFixedLengthHeader";
     public static final String CAMEL_BINDY_FIXED_LENGTH_FOOTER = "CamelBindyFixedLengthFooter";
 
     private static final Logger LOG = LoggerFactory.getLogger(BindyFixedLengthDataFormat.class);
-    
+
     private BindyFixedLengthFactory headerFactory;
     private BindyFixedLengthFactory footerFactory;
-    
+
     public BindyFixedLengthDataFormat() {
     }
 
@@ -65,7 +65,7 @@ public class BindyFixedLengthDataFormat extends BindyAbstractDataFormat {
     public String getDataFormatName() {
         return "bindy-fixed";
     }
-    
+
     @SuppressWarnings("unchecked")
     public void marshal(Exchange exchange, Object body, OutputStream outputStream) throws Exception {
         BindyFixedLengthFactory factory = (BindyFixedLengthFactory) getFactory();
@@ -92,13 +92,13 @@ public class BindyFixedLengthDataFormat extends BindyAbstractDataFormat {
             // cast to the expected type
             models = (List<Map<String, Object>>) body;
         }
-        
+
         // add the header if it is in the exchange header
         Map<String, Object> headerRow = (Map<String, Object>) exchange.getIn().getHeader(CAMEL_BINDY_FIXED_LENGTH_HEADER);
         if (headerRow != null) {
             models.add(0, headerRow);
         }
-        
+
         // add the footer if it is in the exchange header
         Map<String, Object> footerRow = (Map<String, Object>) exchange.getIn().getHeader(CAMEL_BINDY_FIXED_LENGTH_FOOTER);
         if (footerRow != null) {
@@ -109,18 +109,18 @@ public class BindyFixedLengthDataFormat extends BindyAbstractDataFormat {
         for (Map<String, Object> model : models) {
             row++;
             String result = null;
-            
+
             if (row == 1 && headerFactory != null) {
                 // marshal the first row as a header if the models match
                 Set<String> modelClassNames = model.keySet();
                 // only use the header factory if the row is the header
                 if (headerFactory.supportsModel(modelClassNames)) {
-                    if (factory.skipHeader())  {
+                    if (factory.skipHeader()) {
                         LOG.info("Skipping marshal of header row; 'skipHeader=true'");
                         continue;
                     } else {
                         result = headerFactory.unbind(model);
-                    }    
+                    }
                 }
             } else if (row == models.size() && footerFactory != null) {
                 // marshal the last row as a footer if the models match
@@ -135,12 +135,12 @@ public class BindyFixedLengthDataFormat extends BindyAbstractDataFormat {
                     }
                 }
             }
-            
+
             if (result == null) {
                 // marshal as a normal / default row
                 result = factory.unbind(model);
             }
-            
+
             byte[] bytes = exchange.getContext().getTypeConverter().convertTo(byte[].class, exchange, result);
             outputStream.write(bytes);
 
@@ -178,7 +178,7 @@ public class BindyFixedLengthDataFormat extends BindyAbstractDataFormat {
     public Object unmarshal(Exchange exchange, InputStream inputStream) throws Exception {
         BindyFixedLengthFactory factory = (BindyFixedLengthFactory) getFactory();
         ObjectHelper.notNull(factory, "not instantiated");
-        
+
         // List of Pojos
         List<Map<String, Object>> models = new ArrayList<Map<String, Object>>();
 
@@ -196,10 +196,10 @@ public class BindyFixedLengthDataFormat extends BindyAbstractDataFormat {
 
             // Parse the header if it exists
             if (scanner.hasNextLine() && factory.hasHeader()) {
-                
+
                 // Read the line (should not trim as its fixed length)
                 String line = getNextNonEmptyLine(scanner, count);
-                
+
                 if (!factory.skipHeader()) {
                     Map<String, Object> headerObjMap = createModel(headerFactory, line, count.intValue());
                     exchange.getOut().setHeader(CAMEL_BINDY_FIXED_LENGTH_HEADER, headerObjMap);
@@ -215,7 +215,7 @@ public class BindyFixedLengthDataFormat extends BindyAbstractDataFormat {
 
             // Parse the main file content
             while (thisLine != null && nextLine != null) {
-                
+
                 model = createModel(factory, thisLine, count.intValue());
 
                 // Add objects graph to the list
@@ -224,7 +224,7 @@ public class BindyFixedLengthDataFormat extends BindyAbstractDataFormat {
                 thisLine = nextLine;
                 nextLine = getNextNonEmptyLine(scanner, count);
             }
-            
+
             // this line should be the last non-empty line from the file
             // optionally parse the line as a footer
             if (thisLine != null) {
@@ -260,7 +260,7 @@ public class BindyFixedLengthDataFormat extends BindyAbstractDataFormat {
             count.incrementAndGet();
             line = scanner.nextLine();
         }
-        
+
         if (ObjectHelper.isEmpty(line)) {
             return null;
         } else {
@@ -269,47 +269,68 @@ public class BindyFixedLengthDataFormat extends BindyAbstractDataFormat {
     }
 
     protected Map<String, Object> createModel(BindyFixedLengthFactory factory, String line, int count) throws Exception {
+        String myLine = line;
+
         // Check if the record length corresponds to the parameter
         // provided in the @FixedLengthRecord
         if (factory.recordLength() > 0) {
-            if ((line.length() < factory.recordLength()) || (line.length() > factory.recordLength())) {
-                throw new java.lang.IllegalArgumentException("Size of the record: " + line.length() 
+            if (isPaddingNeededAndEnable(factory, myLine)) {
+                //myLine = rightPad(myLine, factory.recordLength());
+            }
+            if (isTrimmingNeededAndEnabled(factory, myLine)) {
+                myLine = myLine.substring(0, factory.recordLength());
+            }
+            if ((myLine.length() < factory.recordLength()
+                    && !factory.isIgnoreMissingChars()) || (myLine.length() > factory.recordLength())) {
+                throw new java.lang.IllegalArgumentException("Size of the record: " + myLine.length()
                         + " is not equal to the value provided in the model: " + factory.recordLength());
             }
         }
 
         // Create POJO where Fixed data will be stored
         Map<String, Object> model = factory.factory();
-        
+
         // Bind data from Fixed record with model classes
-        factory.bind(line, model, count);
+        factory.bind(myLine, model, count);
 
         // Link objects together
         factory.link(model);
-        
+
         LOG.debug("Graph of objects created: {}", model);
         return model;
     }
 
+    private boolean isTrimmingNeededAndEnabled(BindyFixedLengthFactory factory, String myLine) {
+        return factory.isIgnoreTrailingChars() && myLine.length() > factory.recordLength();
+    }
+
+    private String rightPad(String myLine, int length) {
+        return String.format("%1$-" + length + "s", myLine);
+    }
+
+    private boolean isPaddingNeededAndEnable(BindyFixedLengthFactory factory, String myLine) {
+        return myLine.length() < factory.recordLength() && factory.isIgnoreMissingChars();
+    }
+
     @Override
     protected BindyAbstractFactory createModelFactory(FormatFactory formatFactory) throws Exception {
 
         BindyFixedLengthFactory factory = new BindyFixedLengthFactory(getClassType());
         factory.setFormatFactory(formatFactory);
-        
+
         // Optionally initialize the header factory... using header model classes
         if (factory.hasHeader()) {
             this.headerFactory = new BindyFixedLengthFactory(factory.header());
             this.headerFactory.setFormatFactory(formatFactory);
         }
-        
+
         // Optionally initialize the footer factory... using footer model classes
         if (factory.hasFooter()) {
             this.footerFactory = new BindyFixedLengthFactory(factory.footer());
             this.footerFactory.setFormatFactory(formatFactory);
         }
-        
+
         return factory;
     }
-    
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/0e72f475/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/BindyPaddingAndTrimmingTest.java
----------------------------------------------------------------------
diff --git a/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/BindyPaddingAndTrimmingTest.java b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/BindyPaddingAndTrimmingTest.java
new file mode 100644
index 0000000..9e3e174
--- /dev/null
+++ b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/BindyPaddingAndTrimmingTest.java
@@ -0,0 +1,97 @@
+/**
+ * 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.fixed;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.dataformat.bindy.annotation.DataField;
+import org.apache.camel.dataformat.bindy.annotation.FixedLengthRecord;
+import org.apache.camel.model.dataformat.BindyType;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.hamcrest.core.Is;
+import org.junit.Test;
+
+import static org.hamcrest.core.IsNull.nullValue;
+
+public class BindyPaddingAndTrimmingTest extends CamelTestSupport {
+
+    private static final String URI_DIRECT_UNMARSHAL = "direct:unmarshall";
+    private static final String URI_MOCK_UNMARSHAL_RESULT = "mock:unmarshal_result";
+
+    @EndpointInject(uri = URI_MOCK_UNMARSHAL_RESULT)
+    private MockEndpoint unmarhsalResult;
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from(URI_DIRECT_UNMARSHAL)
+                        .unmarshal().bindy(BindyType.Fixed, MyBindyModel.class)
+                        .to(URI_MOCK_UNMARSHAL_RESULT);
+            }
+        };
+    }
+
+    @Test
+    public void testUnmarshal() throws Exception {
+        unmarhsalResult.expectedMessageCount(1);
+        template.sendBody(URI_DIRECT_UNMARSHAL, "foo  \r\n");
+
+        unmarhsalResult.assertIsSatisfied();
+        MyBindyModel myBindyModel = unmarhsalResult.getReceivedExchanges().get(0).getIn().getBody(MyBindyModel.class);
+        assertEquals("foo  ", myBindyModel.foo);
+        assertThat(myBindyModel.bar, Is.is(nullValue()));
+    }
+
+    @Test
+    public void testUnmarshalTooLong() throws Exception {
+        unmarhsalResult.expectedMessageCount(1);
+        template.sendBody(URI_DIRECT_UNMARSHAL, "foo  bar    \r\n");
+
+        unmarhsalResult.assertIsSatisfied();
+        MyBindyModel myBindyModel = unmarhsalResult.getReceivedExchanges().get(0).getIn().getBody(MyBindyModel.class);
+        assertEquals("foo  ", myBindyModel.foo);
+
+    }
+
+    @FixedLengthRecord(length = 10, ignoreMissingChars = true, ignoreTrailingChars = true)
+    public static class MyBindyModel {
+        @DataField(pos = 0, length = 5)
+        String foo;
+
+        @DataField(pos = 5, length = 5)
+        String bar;
+
+        public String getFoo() {
+            return foo;
+        }
+
+        public void setFoo(String foo) {
+            this.foo = foo;
+        }
+
+        public String getBar() {
+            return bar;
+        }
+
+        public void setBar(String bar) {
+            this.bar = bar;
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/0e72f475/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/unmarshall/simple/trimfield/BindySimpleFixedLengthUnmarshallTrimFieldTest.java
----------------------------------------------------------------------
diff --git a/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/unmarshall/simple/trimfield/BindySimpleFixedLengthUnmarshallTrimFieldTest.java b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/unmarshall/simple/trimfield/BindySimpleFixedLengthUnmarshallTrimFieldTest.java
index e26dcae..452d951 100644
--- a/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/unmarshall/simple/trimfield/BindySimpleFixedLengthUnmarshallTrimFieldTest.java
+++ b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/unmarshall/simple/trimfield/BindySimpleFixedLengthUnmarshallTrimFieldTest.java
@@ -51,7 +51,7 @@ public class BindySimpleFixedLengthUnmarshallTrimFieldTest extends AbstractJUnit
     @DirtiesContext
     public void testUnMarshallMessage() throws Exception {
 
-        expected = "10A9  PaulineM    ISINXD12345678BUYShare000002500.45USD01-08-2009  Hello   ";
+        expected = "10A9  PaulineM    ISINXD12345678BUYShare000002500.45USD01-08-2009  Hello###";
 
         template.sendBody(expected);
 
@@ -65,7 +65,7 @@ public class BindySimpleFixedLengthUnmarshallTrimFieldTest extends AbstractJUnit
         // the field is not trimmed
         Assert.assertEquals("Pauline", order.getFirstName());
         Assert.assertEquals("M    ", order.getLastName()); // no trim
-        Assert.assertEquals("Hello", order.getComment());
+        Assert.assertEquals("  Hello", order.getComment());
     }
 
     public static class ContextConfig extends RouteBuilder {
@@ -113,7 +113,7 @@ public class BindySimpleFixedLengthUnmarshallTrimFieldTest extends AbstractJUnit
         @DataField(pos = 56, length = 10, pattern = "dd-MM-yyyy")
         private Date orderDate;
 
-        @DataField(pos = 66, length = 10, trim = true)
+        @DataField(pos = 66, length = 10, trim = true, align = "L", paddingChar = '#')
         private String comment;
 
         public int getOrderNr() {