Package org.apache.camel.dataformat.bindy

Source Code of org.apache.camel.dataformat.bindy.BindyCsvFactory

/**
* 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.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.jar.JarException;

import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
import org.apache.camel.dataformat.bindy.annotation.DataField;
import org.apache.camel.dataformat.bindy.annotation.Link;
import org.apache.camel.dataformat.bindy.annotation.Section;
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 CSV record, bind data from a record to the POJOs, export data of POJOs
* to a CSV record and format data into String, Date, Double, ... according to
* the format/pattern defined
*/
public class BindyCsvFactory extends BindyAbstractFactory implements BindyFactory {

    private static final transient Log LOG = LogFactory.getLog(BindyCsvFactory.class);

    private Map<Integer, DataField> dataFields = new LinkedHashMap<Integer, DataField>();
    private Map<Integer, Field> annotedFields = new LinkedHashMap<Integer, Field>();
    private Map<String, Integer> sections = new HashMap<String, Integer>();
    private int numberOptionalFields;
    private int numberMandatoryFields;
    private int totalFields;

    private String separator;
    private boolean skipFirstLine;
    private boolean messageOrdered;

    public BindyCsvFactory(PackageScanClassResolver resolver, String... packageNames) throws Exception {
        super(resolver, packageNames);

        // initialize specific parameters of the csv model
        initCsvModel();
    }

    /**
     * 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 and retrieve the
     * separator of the CSV record
     *
     * @throws Exception
     */
    public void initCsvModel() throws Exception {

        // Find annotated Datafields declared in the Model classes
        initAnnotedFields();

        // initialize Csv parameter(s)
        // separator and skip first line from @CSVrecord annotation
        initCsvRecordParameters();
    }

    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);
           
           
        }
    }

    public void bind(List<String> tokens, Map<String, Object> model) throws Exception {

        int pos = 0;
        int counterMandatoryFields = 0;
        for (String data : tokens) {
       
            // Get DataField from model
            DataField dataField = dataFields.get(pos);
            ObjectHelper.notNull(dataField, "No position " + pos + " defined for the field : " + data);
           
            if (dataField.required()) {
                // Increment counter of mandatory fields
                ++counterMandatoryFields;

                // Check if content of the field is empty
                // This is not possible for mandatory fields
                if (data.equals("")) {
                    throw new IllegalArgumentException("The mandatory field defined at the position " + pos
                                                       + " is empty !");
                }
            }
           
            // Get Field to be setted
            Field field = annotedFields.get(pos);
            field.setAccessible(true);
           
            if (LOG.isDebugEnabled()) {
                LOG.debug("Pos : " + pos + ", Data : " + data + ", Field type : " + field.getType());
            }
           
            Format<?> format;
           
            // Get pattern defined for the field
            String 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 (!data.equals("")) {
                value = format.parse(data);
            } 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) !!");
        }

        if (counterMandatoryFields < numberMandatoryFields) {
            throw new IllegalArgumentException("Some mandatory fields are missing !!");
        }
       
    }

    public String unbind(Map<String, Object> model) throws Exception {

        StringBuilder builder = new StringBuilder();

        Map<Integer, DataField> dataFieldsSorted = new TreeMap<Integer, DataField>(dataFields);
        Iterator<Integer> it = dataFieldsSorted.keySet().iterator();
       
        // Map containing the OUT position of the field
        // The key is double and is created using the position of the field and
        // location of the class in the message (using section)
        Map<Integer, String> positions = new TreeMap<Integer, String>();

        // Check if separator exists
        ObjectHelper.notNull(this.separator, "The separator has not been instantiated or property not defined in the @CsvRecord annotation");
       
        char separator = Converter.getCharDelimitor(this.getSeparator());

        if (LOG.isDebugEnabled()) {
            LOG.debug("Separator converted : '0x" + Integer.toHexString(separator) + "', from : "
                    + this.getSeparator());
        }

        while (it.hasNext()) {

            DataField dataField = dataFieldsSorted.get(it.next());

            // Retrieve the field
            Field field = annotedFields.get(dataField.pos());
            // Change accessibility to allow to read protected/private fields
            field.setAccessible(true);

            // 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 from model
            Object modelField = model.get(field.getDeclaringClass().getName());
           
            if (modelField != null) {
                // Get field value
                Object value = field.get(modelField);
                String strValue = null;

                if (this.isMessageOrdered()) {

                    // Generate a key using the number of the section
                    // and the position of the field
                    Integer key1 = sections.get(modelField.getClass().getName());
                    Integer key2 = dataField.position();
                    Integer keyGenerated = generateKey(key1, key2);
                   
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Key generated : " + String.valueOf(keyGenerated) + ", for section : " + key1);
                    }                   
                   
                    // Get field value
                    //Object value = field.get(modelField);
                   
                    if (value != null) {
                        // Format field value
                        strValue = format.format(value);
                    }
                   
                    // Add the content to the TreeMap according to the
                    // position defined
                    positions.put(keyGenerated, strValue);

                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Positions size : " + positions.size());
                    }
                       
                } else {
                    // Get field value
                    //Object value = field.get(modelField);
                    //String strValue = null;

                    // Add value to the list if not null
                    if (value != null) {

                        // Format field value
                        strValue = format.format(value);
                       
                    }
                   
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Data : " + value + ", value : " + strValue);
                    }
                   
                    builder.append(strValue);

                    if (it.hasNext()) {
                        builder.append(separator);
                    }
                }
            }
        }
       
        // Iterate through the list to generate
        // the message according to the order/position
        if (this.isMessageOrdered()) {

            Iterator<Integer> posit = positions.keySet().iterator();
           
            while (posit.hasNext()) {
                String value = positions.get(posit.next());
               
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Value added at the position (" + posit + ") : " + value + separator);
                }
               
                builder.append(value);
                if (it.hasNext()) {
                    builder.append(separator);
                }
            }
        }
       
        return builder.toString();
    }

    /**
     * Find the separator used to delimit the CSV fields
     */
    public String getSeparator() {
        return separator;
    }

    /**
     * Find the separator used to delimit the CSV fields
     */
    public boolean getSkipFirstLine() {
        return skipFirstLine;
    }

    /**
     * Flag indicating if the message must be ordered
     *
     * @return boolean
     */
    public boolean isMessageOrdered() {
        return messageOrdered;
    }

    /**
     *
     * Get paramaters defined in @Csvrecord annotation
     *
     */
    private void initCsvRecordParameters() {
        if (separator == null) {
            for (Class<?> cl : models) {
                // Get annotation @CsvRecord from the class
                CsvRecord record = cl.getAnnotation(CsvRecord.class);

                // Get annotation @Section from the class
                Section section = cl.getAnnotation(Section.class);

                if (record != null) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Csv record : " + record.toString());
                    }

                    // Get skipFirstLine parameter
                    skipFirstLine = record.skipFirstLine();
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Skip First Line parameter of the CSV : " + skipFirstLine);
                    }

                    // Get Separator parameter
                    ObjectHelper.notNull(record.separator(),
                            "No separator has been defined in the @Record annotation !");
                    separator = record.separator();
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Separator defined for the CSV : " + separator);
                    }

                    // Get carriage return parameter
                    crlf = record.crlf();
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Carriage return defined for the CSV : " + crlf);
                    }
                }

                if (section != null) {
                    // Test if section number is not null
                    ObjectHelper.notNull(section.number(), "No number has been defined for the section !");

                    // Get section number and add it to the sections
                    sections.put(cl.getName(), section.number());
                }
            }
        }
    }
}
TOP

Related Classes of org.apache.camel.dataformat.bindy.BindyCsvFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.