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