Author: cmoulliard
Date: Thu Jun 17 15:19:51 2010
New Revision: 955632

URL: http://svn.apache.org/viewvc?rev=955632&view=rev
Log:
CAMEL-2731: Implement unmarshall / add new annotations @FixedLengthRecord and 
modify @DataField

Added:
    
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java
    
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java
    
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/fixed/
    
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedLengthDataFormat.java
    
camel/trunk/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/
    
camel/trunk/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/BindySimpleFixedLengthUnmarshallTest.java
    
camel/trunk/components/camel-bindy/src/test/resources/org/apache/camel/dataformat/bindy/fixed/
    
camel/trunk/components/camel-bindy/src/test/resources/org/apache/camel/dataformat/bindy/fixed/BindySimpleFixedLengthUnmarshallTest-context.xml
Modified:
    
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyAbstractFactory.java
    
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java
    
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/util/AnnotationModelLoader.java

Modified: 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyAbstractFactory.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyAbstractFactory.java?rev=955632&r1=955631&r2=955632&view=diff
==============================================================================
--- 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyAbstractFactory.java
 (original)
+++ 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyAbstractFactory.java
 Thu Jun 17 15:19:51 2010
@@ -81,7 +81,7 @@ public abstract class BindyAbstractFacto
     public abstract void initAnnotedFields() throws Exception;
 
     public abstract void bind(List<String> data, Map<String, Object> model, 
int line) throws Exception;
-
+    
     public abstract String unbind(Map<String, Object> model) throws Exception;
 
     /**

Added: 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java?rev=955632&view=auto
==============================================================================
--- 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java
 (added)
+++ 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/BindyFixedLengthFactory.java
 Thu Jun 17 15:19:51 2010
@@ -0,0 +1,522 @@
+/**
+ * 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;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
+import org.apache.camel.dataformat.bindy.annotation.DataField;
+import org.apache.camel.dataformat.bindy.annotation.FixedLengthRecord;
+import org.apache.camel.dataformat.bindy.annotation.Link;
+import org.apache.camel.dataformat.bindy.annotation.OneToMany;
+import org.apache.camel.dataformat.bindy.annotation.Section;
+import org.apache.camel.dataformat.bindy.format.FormatException;
+import org.apache.camel.dataformat.bindy.util.Converter;
+import org.apache.camel.spi.PackageScanClassResolver;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * The BindyCsvFactory is the class who allows to : Generate a model associated
+ * to a fixed length record, bind data from a record to the POJOs, export data 
of POJOs
+ * to a fixed length record and format data into String, Date, Double, ... 
according to
+ * the format/pattern defined
+ */
+public class BindyFixedLengthFactory extends BindyAbstractFactory implements 
BindyFactory {
+
+    private static final transient Log LOG = 
LogFactory.getLog(BindyFixedLengthFactory.class);
+
+    boolean isOneToMany;
+
+    private Map<Integer, DataField> dataFields = new LinkedHashMap<Integer, 
DataField>();
+    private Map<Integer, Field> annotedFields = new LinkedHashMap<Integer, 
Field>();
+
+    private Map<Integer, List> results;
+
+    private int numberOptionalFields;
+    private int numberMandatoryFields;
+    private int totalFields;
+
+    private boolean hasHeader;
+    private boolean hasFooter;
+    private char paddingChar;
+    private int recordLength;
+
+    public BindyFixedLengthFactory(PackageScanClassResolver resolver, 
String... packageNames) throws Exception {
+        super(resolver, packageNames);
+
+        // initialize specific parameters of the fixed length model
+        initFixedLengthModel();
+    }
+
+    /**
+     * method uses to initialize the model representing the classes who will
+     * bind the data. This process will scan for classes according to the
+     * package name provided, check the annotated classes and fields
+     * 
+     * @throws Exception
+     */
+    public void initFixedLengthModel() throws Exception {
+
+        // Find annotated fields declared in the Model classes
+        initAnnotedFields();
+
+        // initialize Fixed length parameter(s)
+        // from @FixedLengthrecord annotation
+        initFixedLengthRecordParameters();
+    }
+
+    public void initAnnotedFields() {
+
+        for (Class<?> cl : models) {
+
+            List<Field> linkFields = new ArrayList<Field>();
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Class retrieved : " + cl.getName());
+            }
+
+            for (Field field : cl.getDeclaredFields()) {
+                DataField dataField = field.getAnnotation(DataField.class);
+                if (dataField != null) {
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Position defined in the class : " + 
cl.getName() + ", position : " + dataField.pos() + ", Field : " + 
dataField.toString());
+                    }
+
+                    if (dataField.required()) {
+                        ++numberMandatoryFields;
+                    } else {
+                        ++numberOptionalFields;
+                    }
+
+                    dataFields.put(dataField.pos(), dataField);
+                    annotedFields.put(dataField.pos(), field);
+                }
+
+                Link linkField = field.getAnnotation(Link.class);
+
+                if (linkField != null) {
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Class linked  : " + cl.getName() + ", 
Field" + field.toString());
+                    }
+                    linkFields.add(field);
+                }
+
+            }
+
+            if (!linkFields.isEmpty()) {
+                annotedLinkFields.put(cl.getName(), linkFields);
+            }
+
+            totalFields = numberMandatoryFields + numberOptionalFields;
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Number of optional fields : " + 
numberOptionalFields);
+                LOG.debug("Number of mandatory fields : " + 
numberMandatoryFields);
+                LOG.debug("Total : " + totalFields);
+            }
+
+        }
+    }
+    
+    // Will not be used in the case of a Fixed Length record
+    // as we provide the content of the record and 
+    // we don't split it as this is the case for a CSV record
+       @Override
+       public void bind(List<String> data, Map<String, Object> model, int line)
+                       throws Exception {
+               // TODO Auto-generated method stub
+               
+       }
+
+    public void bind(String record, Map<String, Object> model, int line) 
throws Exception {
+
+        int pos = 1;
+        int counterMandatoryFields = 0;
+        DataField dataField;
+        StringBuilder result = new StringBuilder();
+        String token;
+        int offset;
+        int length;
+        Field field;
+        String pattern;
+
+        // Iterate through the list of positions
+        // defined in the @DataFieldf
+        // and grab the data from the line
+        Collection c = dataFields.values();
+        Iterator itr = c.iterator();
+
+        while(itr.hasNext()) {
+               dataField = (DataField) itr.next();
+            offset = dataField.pos();
+            length = dataField.length();
+            
+            ObjectHelper.notNull(offset, "Position/offset is not defined for  
the  field " + dataField.toString());
+            ObjectHelper.notNull(offset, "Length is not defined for the  field 
" + dataField.toString());
+            
+            if (offset-1 <= -1 ) {
+               throw new IllegalArgumentException("Offset / Position of the 
field " + dataField.toString() + " cannot be negative !");
+            }
+            
+               token = record.substring(offset-1, offset+length-1);
+               
+               // Check mandatory field
+            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 (token.equals("")) {
+                    throw new IllegalArgumentException("The mandatory field 
defined at the position " + pos + " is empty for the line : " + line);
+                }
+            }
+            
+            // Get Field to be setted
+            field = annotedFields.get(offset);
+            field.setAccessible(true);
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Pos/Offset : " + offset + ", Data : " + token + ", 
Field type : " + field.getType());
+            }
+            
+            Format<?> format;
+
+            // Get pattern defined for the field
+            pattern = dataField.pattern();
+
+            // Create format object to format the field
+            format = FormatFactory.getFormat(field.getType(), pattern, 
dataField.precision());
+
+            // field object to be set
+            Object modelField = model.get(field.getDeclaringClass().getName());
+
+            // format the data received
+            Object value = null;
+
+            if (!token.equals("")) {
+                try {
+                    value = format.parse(token);
+                } catch (FormatException ie) {
+                    throw new IllegalArgumentException(ie.getMessage() + ", 
position : " + offset + ", line : " + line, ie);
+                } catch (Exception e) {
+                    throw new IllegalArgumentException("Parsing error detected 
for field defined at the position/offset : " + offset + ", line : " + line, e);
+                }
+            } else {
+                value = getDefaultValueForPrimitive(field.getType());
+            }
+
+            field.set(modelField, value);
+
+            ++pos;
+               
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Counter mandatory fields : " + counterMandatoryFields);
+        }
+
+        if (pos < totalFields) {
+            throw new IllegalArgumentException("Some fields are missing 
(optional or mandatory), line : " + line);
+        }
+
+        if (counterMandatoryFields < numberMandatoryFields) {
+            throw new IllegalArgumentException("Some mandatory fields are 
missing, line : " + line);
+        }  
+        
+   }
+
+    public String unbind(Map<String, Object> model) throws Exception {
+
+        StringBuilder buffer = new StringBuilder();
+
+        return buffer.toString();
+
+    }
+
+    private List<List> product(Map<Integer, List> values) {
+
+        TreeMap<Integer, List> sortValues = new TreeMap<Integer, List>(values);
+
+        List<List> product = new ArrayList<List>();
+        Map<Integer, Integer> index = new HashMap<Integer, Integer>();
+
+        boolean cont = true;
+        int idx = 0;
+        int idxSize;
+
+        do {
+
+            idxSize = 0;
+            List v = new ArrayList();
+
+            for (int ii = 1; ii <= sortValues.lastKey(); ii++) {
+
+                List l = values.get(ii);
+
+                if (l == null) {
+                    v.add("");
+                    ++idxSize;
+                    continue;
+                }
+
+                if (l.size() >= idx + 1) {
+                    v.add(l.get(idx));
+                    index.put(ii, idx);
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Value : " + l.get(idx) + ", pos : " + ii + 
", at :" + idx);
+                    }
+
+                } else {
+                    v.add(l.get(0));
+                    index.put(ii, 0);
+                    ++idxSize;
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Value : " + l.get(0) + ", pos : " + ii + ", 
at index : " + 0);
+                    }
+                }
+
+            }
+
+            if (idxSize != sortValues.lastKey()) {
+                product.add(v);
+            }
+            ++idx;
+
+        } while (idxSize != sortValues.lastKey());
+
+        return product;
+    }
+    
+    /**
+    private void generateCsvPositionMap(Class clazz, Object obj) throws 
Exception {
+
+        String result = "";
+
+        for (Field field : clazz.getDeclaredFields()) {
+
+            field.setAccessible(true);
+
+            DataField datafield = field.getAnnotation(DataField.class);
+
+            if (datafield != null) {
+
+                if (obj != null) {
+
+                    // Retrieve the format, pattern and precision associated to
+                    // the type
+                    Class type = field.getType();
+                    String pattern = datafield.pattern();
+                    int precision = datafield.precision();
+
+                    // Create format
+                    Format format = FormatFactory.getFormat(type, pattern, 
precision);
+
+                    // Get field value
+                    Object value = field.get(obj);
+
+                    result = formatString(format, value);
+
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Value to be formatted : " + value + ", 
position : " + datafield.pos() + ", and its formated value : " + result);
+                    }
+
+                } else {
+                    result = "";
+                }
+
+                Integer key;
+
+                if (isMessageOrdered()) {
+
+                    // Generate a key using the number of the section
+                    // and the position of the field
+                    Integer key1 = sections.get(obj.getClass().getName());
+                    Integer key2 = datafield.position();
+                    Integer keyGenerated = generateKey(key1, key2);
+
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Key generated : " + 
String.valueOf(keyGenerated) + ", for section : " + key1);
+                    }
+
+                    key = keyGenerated;
+
+                } else {
+
+                    key = datafield.pos();
+                }
+
+                if (!results.containsKey(key)) {
+
+                    List list = new LinkedList();
+                    list.add(result);
+                    results.put(key, list);
+
+                } else {
+
+                    List list = (LinkedList)results.get(key);
+                    list.add(result);
+                }
+
+            }
+
+            OneToMany oneToMany = field.getAnnotation(OneToMany.class);
+            if (oneToMany != null) {
+
+                // Set global variable
+                // Will be used during generation of CSV
+                isOneToMany = true;
+
+                ArrayList list = (ArrayList)field.get(obj);
+
+                if (list != null) {
+
+                    Iterator it = list.iterator();
+
+                    while (it.hasNext()) {
+
+                        Object target = it.next();
+                        generateCsvPositionMap(target.getClass(), target);
+
+                    }
+
+                } else {
+
+                    // Call this function to add empty value
+                    // in the table
+                    generateCsvPositionMap(field.getClass(), null);
+                }
+
+            }
+        }
+
+    }
+    **/
+    
+    private String formatString(Format format, Object value) throws Exception {
+
+        String strValue = "";
+
+        if (value != null) {
+
+            // Format field value
+            try {
+                strValue = format.format(value);
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Formatting error detected 
for the value : " + value, e);
+            }
+
+        }
+
+        return strValue;
+
+    }
+
+    /**
+     * Get parameters defined in @FixedLengthRecord annotation
+     */
+    private void initFixedLengthRecordParameters() {
+
+            for (Class<?> cl : models) {
+
+                // Get annotation @FixedLengthRecord from the class
+               FixedLengthRecord record = 
cl.getAnnotation(FixedLengthRecord.class);
+
+                if (record != null) {
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Fixed length record : " + 
record.toString());
+                    }
+
+                    // Get carriage return parameter
+                    crlf = record.crlf();
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Carriage return defined for the CSV : " + 
crlf);
+                    }
+
+                    // Get hasHeader parameter
+                    hasHeader = record.hasHeader();
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Has Header :  " + hasHeader);
+                    }
+                    
+                    // Get hasFooter parameter
+                    hasFooter = record.hasFooter();
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Has Footer :  " + hasFooter);
+                    }
+                    
+                    // Get padding character
+                    paddingChar = record.paddingChar();
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Padding char :  " + paddingChar);
+                    }                    
+                    
+                    // Get length of the record
+                    recordLength = record.length();
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Length of the record :  " + recordLength);
+                    } 
+                    
+
+                }
+        }
+    }
+
+    /**
+     * Flag indicating if we have a header
+     * 
+     * @return boolean
+     */
+    public boolean hasHeader() {
+        return hasHeader;
+    }
+    
+    /**
+     * Flag indicating if we have a footer
+     * 
+     * @return boolean
+     */
+    public boolean hasFooter() {
+        return hasFooter;
+    }
+    
+    /**
+     * Padding char used to fill the field
+     * 
+     * @return char
+     */
+    public char paddingchar() {
+       return paddingChar;
+    }
+    
+    public int recordLength() {
+       return recordLength;
+    }
+
+}

Modified: 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java?rev=955632&r1=955631&r2=955632&view=diff
==============================================================================
--- 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java
 (original)
+++ 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/DataField.java
 Thu Jun 17 15:19:51 2010
@@ -66,12 +66,18 @@ public @interface DataField {
     String pattern() default "";
 
     /**
-     * length of the data block (useful for the fixedlength record) (optional 
in
-     * this version)
+     * length of the data block (useful for the fixedlength record) 
      * 
      * @return int
      */
     int length() default 0;
+    
+    /**
+     * align the text to the RIGHT or to LEFT part 
+     * 
+     * @return String
+     */
+    String align() default "R";
 
     /**
      * precision of the BigDecimal number to be created

Added: 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java?rev=955632&view=auto
==============================================================================
--- 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java
 (added)
+++ 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/annotation/FixedLengthRecord.java
 Thu Jun 17 15:19:51 2010
@@ -0,0 +1,63 @@
+/**
+ * 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.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This annotation represents the root class of the model. When a 
+ * fixed-length record must be described in the model we will use this
+ * annotation to split the data during the unmarshal process.
+ */
+...@documented
+...@retention(RetentionPolicy.RUNTIME)
+public @interface FixedLengthRecord {
+
+    /**
+     * Name describing the record (optional)
+     * 
+     * @return String
+     */
+    String name() default "";
+
+    /**
+     * Character to be used to add a carriage return after each record
+     * (optional) Three values can be used : WINDOWS, UNIX or MAC
+     * 
+     * @return String
+     */
+    String crlf() default "WINDOWS";
+    
+    /**
+     * The char to pad with.
+     * @return the char to pad with if the record is set to a fixed length;
+     */
+    char paddingChar() default ' ';
+    
+    /**
+     * The fixed length of the record. It means that the record will always be 
that long padded with {#paddingChar()}'s
+     * @return the length of the record.
+     */
+    int length() default 0;
+
+    boolean hasHeader() default false;
+    
+    boolean hasFooter() default false;
+
+}

Added: 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedLengthDataFormat.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedLengthDataFormat.java?rev=955632&view=auto
==============================================================================
--- 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedLengthDataFormat.java
 (added)
+++ 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/fixed/BindyFixedLengthDataFormat.java
 Thu Jun 17 15:19:51 2010
@@ -0,0 +1,192 @@
+/**
+ * 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 java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.dataformat.bindy.BindyCsvFactory;
+import org.apache.camel.dataformat.bindy.BindyFixedLengthFactory;
+import org.apache.camel.dataformat.bindy.util.Converter;
+import org.apache.camel.spi.DataFormat;
+import org.apache.camel.spi.PackageScanClassResolver;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A <a href="http://camel.apache.org/data-format.html";>data format</a> (
+ * {...@link DataFormat}) using Bindy to marshal to and from Fixed Length
+ */
+public class BindyFixedLengthDataFormat implements DataFormat {
+    private static final transient Log LOG = 
LogFactory.getLog(BindyFixedLengthDataFormat.class);
+
+    private String[] packages;
+    private BindyFixedLengthFactory modelFactory;
+
+    public BindyFixedLengthDataFormat() {
+    }
+
+    public BindyFixedLengthDataFormat(String... packages) {
+        this.packages = packages;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void marshal(Exchange exchange, Object body, OutputStream 
outputStream) throws Exception {
+
+       BindyFixedLengthFactory factory = 
getFactory(exchange.getContext().getPackageScanClassResolver());
+        ObjectHelper.notNull(factory, "not instantiated");
+
+        // Get CRLF
+        byte[] bytesCRLF = 
Converter.getByteReturn(factory.getCarriageReturn());
+
+        List<Map<String, Object>> models;
+
+        // the body is not a prepared list so help a bit here and create one 
for us
+        if (exchange.getContext().getTypeConverter().convertTo(List.class, 
body) == null) {
+            models = new ArrayList<Map<String, Object>>();
+            Iterator it = ObjectHelper.createIterator(body);
+            while (it.hasNext()) {
+                Object model = it.next();
+                String name = model.getClass().getName();
+                Map<String, Object> row = new HashMap<String, Object>();
+                row.put(name, body);
+                models.add(row);
+            }
+        } else {
+            // cast to the expected type
+            models = (List<Map<String, Object>>) body;
+        }
+
+        for (Map<String, Object> model : models) {
+
+            String result = factory.unbind(model);
+
+            byte[] bytes = 
exchange.getContext().getTypeConverter().convertTo(byte[].class, exchange, 
result);
+            outputStream.write(bytes);
+
+            // Add a carriage return
+            outputStream.write(bytesCRLF);
+        }
+    }
+
+    public Object unmarshal(Exchange exchange, InputStream inputStream) throws 
Exception {
+       BindyFixedLengthFactory factory = 
getFactory(exchange.getContext().getPackageScanClassResolver());
+        ObjectHelper.notNull(factory, "not instantiated");
+
+        // List of Pojos
+        List<Map<String, Object>> models = new ArrayList<Map<String, 
Object>>();
+
+        // Pojos of the model
+        Map<String, Object> model;
+
+        InputStreamReader in = new InputStreamReader(inputStream);
+
+        // Scanner is used to read big file
+        Scanner scanner = new Scanner(in);
+
+        int count = 0;
+
+        try {
+
+            // TODO Test if we have a Header
+               // TODO Test if we have a Footer (containing by example 
checksum)
+
+            while (scanner.hasNextLine()) {
+
+                // Read the line
+                String line = scanner.nextLine().trim();
+
+                if (ObjectHelper.isEmpty(line)) {
+                    // skip if line is empty
+                    continue;
+                }
+
+                // Increment counter
+                count++;
+                
+                // Check if the record length corresponds to the parameter
+                // provided in the @FixedLengthRecord
+                if (line.length() != factory.recordLength()-1) {
+                       throw new java.lang.IllegalArgumentException("Size of 
the record : " + line.length() + " is not equal to the value provided in the 
model : " + factory.recordLength() + " !");
+                }
+
+                // Create POJO where Fixed data will be stored
+                model = factory.factory();
+                
+                // Bind data from Fixed record with model classes
+                factory.bind(line, model, count);
+
+                // Link objects together
+                factory.link(model);
+
+                // Add objects graph to the list
+                models.add(model);
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Graph of objects created : " + model);
+                }
+
+            }
+
+            // Test if models list is empty or not
+            // If this is the case (correspond to an empty stream, ...)
+            if (models.size() == 0) {
+                throw new java.lang.IllegalArgumentException("No records have 
been defined in the message !");
+            } else {
+                return models;
+            }
+
+        } finally {
+            scanner.close();
+            ObjectHelper.close(in, "in", LOG);
+        }
+
+    }
+
+    /**
+     * Method used to create the singleton of the BindyCsvFactory
+     */
+    public BindyFixedLengthFactory getFactory(PackageScanClassResolver 
resolver) throws Exception {
+        if (modelFactory == null) {
+            modelFactory = new BindyFixedLengthFactory(resolver, packages);
+        }
+        return modelFactory;
+    }
+
+    public void setModelFactory(BindyFixedLengthFactory modelFactory) {
+        this.modelFactory = modelFactory;
+    }
+
+    public String[] getPackages() {
+        return packages;
+    }
+
+    public void setPackages(String[] packages) {
+        this.packages = packages;
+    }
+
+}

Modified: 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/util/AnnotationModelLoader.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/util/AnnotationModelLoader.java?rev=955632&r1=955631&r2=955632&view=diff
==============================================================================
--- 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/util/AnnotationModelLoader.java
 (original)
+++ 
camel/trunk/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/util/AnnotationModelLoader.java
 Thu Jun 17 15:19:51 2010
@@ -21,6 +21,7 @@ import java.util.LinkedHashSet;
 import java.util.Set;
 
 import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
+import org.apache.camel.dataformat.bindy.annotation.FixedLengthRecord;
 import org.apache.camel.dataformat.bindy.annotation.Link;
 import org.apache.camel.dataformat.bindy.annotation.Message;
 import org.apache.camel.dataformat.bindy.annotation.Section;
@@ -42,6 +43,7 @@ public class AnnotationModelLoader {
         annotations.add(Link.class);
         annotations.add(Message.class);
         annotations.add(Section.class);
+        annotations.add(FixedLengthRecord.class);
     }
 
     public Set<Class<?>> loadModels(String... packageNames) throws Exception {

Added: 
camel/trunk/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/BindySimpleFixedLengthUnmarshallTest.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/BindySimpleFixedLengthUnmarshallTest.java?rev=955632&view=auto
==============================================================================
--- 
camel/trunk/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/BindySimpleFixedLengthUnmarshallTest.java
 (added)
+++ 
camel/trunk/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/fixed/BindySimpleFixedLengthUnmarshallTest.java
 Thu Jun 17 15:19:51 2010
@@ -0,0 +1,215 @@
+/**
+ * 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 java.math.BigDecimal;
+import java.util.Date;
+
+import org.apache.camel.EndpointInject;
+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.apache.camel.dataformat.bindy.annotation.FixedLengthRecord;
+import org.apache.camel.test.junit4.TestSupport;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import 
org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
+
+import static org.junit.Assert.assertEquals;
+
+...@contextconfiguration
+public class BindySimpleFixedLengthUnmarshallTest extends 
AbstractJUnit4SpringContextTests {
+
+    private static final transient Log LOG = 
LogFactory.getLog(BindySimpleFixedLengthUnmarshallTest.class);
+
+    private static final String URI_MOCK_RESULT = "mock:result";
+    private static final String URI_MOCK_ERROR = "mock:error";
+    private static final String URI_DIRECT_START = "direct:start";
+
+    @Produce(uri = URI_DIRECT_START)
+    private ProducerTemplate template;
+
+    @EndpointInject(uri = URI_MOCK_RESULT)
+    private MockEndpoint result;
+
+    @EndpointInject(uri = URI_MOCK_ERROR)
+    private MockEndpoint error;
+
+    private String expected;
+
+    @Test
+    @DirtiesContext
+    public void testUnMarshallMessage() throws Exception {
+
+       expected = "10A9PaulineMISINXD12345678BUYShare2500.45USD08-01-2009";
+
+        template.sendBody(expected);
+
+        result.expectedMessageCount(1);
+        result.assertIsSatisfied();
+    }
+
+    public static class ContextConfig extends RouteBuilder {
+        BindyFixedLengthDataFormat camelDataFormat = new 
BindyFixedLengthDataFormat("org.apache.camel.dataformat.bindy.fixed");
+
+        public void configure() {
+            from(URI_DIRECT_START)
+            .unmarshal(camelDataFormat)
+            .to(URI_MOCK_RESULT);
+        }
+
+    }
+    
+    @FixedLengthRecord(length=55, paddingChar=' ')
+    public static class Order {
+
+        @DataField(pos = 1, length=2)
+        private int orderNr;
+
+        @DataField(pos = 3, length=2)
+        private String clientNr;
+
+        @DataField(pos = 5, length=7)
+        private String firstName;
+
+        @DataField(pos = 12, length=1)
+        private String lastName;
+
+        @DataField(pos = 13, length=4)
+        private String instrumentCode;
+
+        @DataField(pos = 17, length=10)
+        private String instrumentNumber;
+
+        @DataField(pos = 27, length=3)
+        private String orderType;
+
+        @DataField(pos = 30, length=5)
+        private String instrumentType;
+
+        @DataField(pos = 35, precision = 2, length=7)
+        private BigDecimal amount;
+
+        @DataField(pos = 42, length=3)
+        private String currency;
+
+        @DataField(pos = 45, length=10, pattern = "dd-MM-yyyy")
+        private Date orderDate;
+
+        public int getOrderNr() {
+            return orderNr;
+        }
+
+        public void setOrderNr(int orderNr) {
+            this.orderNr = orderNr;
+        }
+
+        public String getClientNr() {
+            return clientNr;
+        }
+
+        public void setClientNr(String clientNr) {
+            this.clientNr = clientNr;
+        }
+
+        public String getFirstName() {
+            return firstName;
+        }
+
+        public void setFirstName(String firstName) {
+            this.firstName = firstName;
+        }
+
+        public String getLastName() {
+            return lastName;
+        }
+
+        public void setLastName(String lastName) {
+            this.lastName = lastName;
+        }
+
+        public String getInstrumentCode() {
+            return instrumentCode;
+        }
+
+        public void setInstrumentCode(String instrumentCode) {
+            this.instrumentCode = instrumentCode;
+        }
+
+        public String getInstrumentNumber() {
+            return instrumentNumber;
+        }
+
+        public void setInstrumentNumber(String instrumentNumber) {
+            this.instrumentNumber = instrumentNumber;
+        }
+
+        public String getOrderType() {
+            return orderType;
+        }
+
+        public void setOrderType(String orderType) {
+            this.orderType = orderType;
+        }
+
+        public String getInstrumentType() {
+            return instrumentType;
+        }
+
+        public void setInstrumentType(String instrumentType) {
+            this.instrumentType = instrumentType;
+        }
+
+        public BigDecimal getAmount() {
+            return amount;
+        }
+
+        public void setAmount(BigDecimal amount) {
+            this.amount = amount;
+        }
+
+        public String getCurrency() {
+            return currency;
+        }
+
+        public void setCurrency(String currency) {
+            this.currency = currency;
+        }
+
+        public Date getOrderDate() {
+            return orderDate;
+        }
+
+        public void setOrderDate(Date orderDate) {
+            this.orderDate = orderDate;
+        }
+
+        @Override
+        public String toString() {
+            return "Model : " + Order.class.getName() + " : " + this.orderNr + 
", " + this.orderType + ", " + String.valueOf(this.amount) + ", " + 
this.instrumentCode + ", "
+                   + this.instrumentNumber + ", " + this.instrumentType + ", " 
+ this.currency + ", " + this.clientNr + ", " + this.firstName + ", " + 
this.lastName + ", "
+                   + String.valueOf(this.orderDate);
+        }
+    }
+
+}

Added: 
camel/trunk/components/camel-bindy/src/test/resources/org/apache/camel/dataformat/bindy/fixed/BindySimpleFixedLengthUnmarshallTest-context.xml
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-bindy/src/test/resources/org/apache/camel/dataformat/bindy/fixed/BindySimpleFixedLengthUnmarshallTest-context.xml?rev=955632&view=auto
==============================================================================
--- 
camel/trunk/components/camel-bindy/src/test/resources/org/apache/camel/dataformat/bindy/fixed/BindySimpleFixedLengthUnmarshallTest-context.xml
 (added)
+++ 
camel/trunk/components/camel-bindy/src/test/resources/org/apache/camel/dataformat/bindy/fixed/BindySimpleFixedLengthUnmarshallTest-context.xml
 Thu Jun 17 15:19:51 2010
@@ -0,0 +1,32 @@
+<?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-2.5.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.fixed.BindySimpleFixedLengthUnmarshallTest$ContextConfig"/>
+       
+</beans>
\ No newline at end of file


Reply via email to