<commit_before>package net.ceos.project.poi.annotated.core;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jaexcel.framework.JAEX.configuration.Configuration;

import net.ceos.project.poi.annotated.annotation.XlsConfiguration;
import net.ceos.project.poi.annotated.annotation.XlsDecorator;
import net.ceos.project.poi.annotated.annotation.XlsDecorators;
import net.ceos.project.poi.annotated.annotation.XlsElement;
import net.ceos.project.poi.annotated.annotation.XlsFreeElement;
import net.ceos.project.poi.annotated.annotation.XlsNestedHeader;
import net.ceos.project.poi.annotated.annotation.XlsSheet;
import net.ceos.project.poi.annotated.definition.ExceptionMessage;
import net.ceos.project.poi.annotated.definition.ExtensionFileType;
import net.ceos.project.poi.annotated.definition.PropagationType;
import net.ceos.project.poi.annotated.definition.TitleOrientationType;
import net.ceos.project.poi.annotated.exception.ConfigurationException;
import net.ceos.project.poi.annotated.exception.ConverterException;
import net.ceos.project.poi.annotated.exception.CustomizedRulesException;
import net.ceos.project.poi.annotated.exception.ElementException;
import net.ceos.project.poi.annotated.exception.SheetException;

public class Engine implements IEngine {

	/**
	 * @deprecated
	 */
	@Deprecated
	Configuration configuration;

	/**
	 * Get the runtime class of the object passed as parameter.
	 * 
	 * @param object
	 *            the object
	 * @return the runtime class
	 * @throws ElementException
	 */
	private Class<?> initializeRuntimeClass(final Object object) throws ElementException {
		Class<?> oC = null;
		try {
			/* instance object class */
			oC = object.getClass();
		} catch (Exception e) {
			throw new ElementException(ExceptionMessage.ElementException_NullObject.getMessage(), e);
		}
		return oC;
	}

	/**
	 * Initialize the configuration to apply at the Excel.
	 * 
	 * @param oC
	 *            the {@link Class<?>}
	 * @return
	 * @throws ConfigurationException
	 */
	private void initializeConfigurationData(final XConfigCriteria configCriteria, final Class<?> oC)
			throws ConfigurationException {
		/* Process @XlsConfiguration */
		if (oC.isAnnotationPresent(XlsConfiguration.class)) {
			XlsConfiguration xlsAnnotation = (XlsConfiguration) oC.getAnnotation(XlsConfiguration.class);
			initializeXlsConfiguration(configCriteria, xlsAnnotation);
		} else {
			throw new ConfigurationException(
					ExceptionMessage.ConfigurationException_XlsConfigurationMissing.getMessage());
		}
		/* Process @XlsSheet */
		if (oC.isAnnotationPresent(XlsSheet.class)) {
			XlsSheet xlsAnnotation = (XlsSheet) oC.getAnnotation(XlsSheet.class);
			initializeXlsSheet(configCriteria, xlsAnnotation);
		} else {
			throw new ConfigurationException(ExceptionMessage.ConfigurationException_XlsSheetMissing.getMessage());
		}
	}

	/**
	 * Add the main xls configuration.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param annotation
	 *            the {@link XlsConfiguration}
	 * @return
	 */
	private void initializeXlsConfiguration(final XConfigCriteria configCriteria, final XlsConfiguration annotation) {
		configCriteria.setFileName(annotation.nameFile());
		configCriteria.setCompleteFileName(annotation.nameFile() + annotation.extensionFile().getExtension());
		configCriteria.setExtension(annotation.extensionFile());
	}

	/**
	 * Add the sheet configuration.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param annotation
	 *            the {@link XlsSheet}
	 * @return
	 */
	private void initializeXlsSheet(final XConfigCriteria configCriteria, final XlsSheet annotation) {
		configCriteria.setTitleSheet(annotation.title());
		configCriteria.setPropagation(annotation.propagation());
		configCriteria.setCascadeLevel(annotation.cascadeLevel());
		configCriteria.setStartRow(annotation.startRow());
		configCriteria.setStartCell(annotation.startCell());
		configCriteria.setFreezePane(annotation.freezePane());
		configCriteria.setGroupElement(annotation.groupElement());

	}

	/**
	 * initialize style cell via annotation {@link XlsDecorators}
	 * 
	 * @param objectClass
	 *            the object class
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @throws ConfigurationException
	 */
	private void initializeCellStyleViaAnnotation(final Class<?> objectClass, final XConfigCriteria configCriteria)
			throws ConfigurationException {
		if (objectClass.isAnnotationPresent(XlsDecorators.class)) {
			XlsDecorators xlsDecorators = (XlsDecorators) objectClass.getAnnotation(XlsDecorators.class);
			for (XlsDecorator decorator : xlsDecorators.values()) {
				if (configCriteria.getStylesMap().containsKey(decorator.decoratorName())) {
					throw new ConfigurationException(
							ExceptionMessage.ConfigurationException_CellStyleDuplicated.getMessage());
				}
				configCriteria.getStylesMap().put(decorator.decoratorName(),
						CellStyleHandler.initializeCellStyleByXlsDecorator(configCriteria.getWorkbook(), decorator));
			}
		}
	}

	/**
	 * Initialize Workbook.
	 * 
	 * @param type
	 *            the {@link ExtensionFileType} of workbook
	 * @return the {@link Workbook}.
	 */
	private Workbook initializeWorkbook(final ExtensionFileType type) {
		if (type != null && ExtensionFileType.XLS.getExtension().equals(type.getExtension())) {
			return new HSSFWorkbook();
		} else {
			return new XSSFWorkbook();
		}
	}

	/**
	 * Initialize Workbook from FileInputStream.
	 * 
	 * @param inputStream
	 *            the file input stream
	 * @param type
	 *            the {@link ExtensionFileType} of workbook
	 * @return the {@link Workbook} created
	 * @throws IOException
	 */
	private Workbook initializeWorkbook(final FileInputStream inputStream, final ExtensionFileType type)
			throws IOException {
		if (type != null && ExtensionFileType.XLS.getExtension().equals(type.getExtension())) {
			return new HSSFWorkbook(inputStream);
		} else {
			return new XSSFWorkbook(inputStream);
		}
	}

	/**
	 * Initialize Workbook from byte[].
	 * 
	 * @param byteArray
	 *            the array of bytes
	 * @param type
	 *            the {@link ExtensionFileType} of workbook
	 * @return the {@link Workbook} created
	 * @throws IOException
	 */
	private Workbook initializeWorkbook(final byte[] byteArray, final ExtensionFileType type) throws IOException {
		if (type != null && ExtensionFileType.XLS.getExtension().equals(type.getExtension())) {
			return new HSSFWorkbook(new ByteArrayInputStream(byteArray));
		} else {
			return new XSSFWorkbook(new ByteArrayInputStream(byteArray));
		}
	}

	/**
	 * Initialize Sheet.
	 * 
	 * @param wb
	 *            the {@link Workbook} to use
	 * @param sheetName
	 *            the name of the sheet
	 * @return the {@link Sheet} created
	 * @throws SheetException
	 */
	private Sheet initializeSheet(final Workbook wb, final String sheetName) throws SheetException {
		Sheet s = null;
		try {
			s = wb.createSheet(sheetName);
		} catch (Exception e) {
			throw new SheetException(ExceptionMessage.SheetException_CreationSheet.getMessage(), e);
		}
		return s;
	}

	/**
	 * Validate if the nested header configuration is valid.
	 * 
	 * @param isPH
	 *            true if propagation is HORIZONTAL otherwise false to
	 *            propagation VERTICAL
	 * @param annotation
	 *            the {@link XlsNestedHeader} annotation
	 * @throws ConfigurationException
	 */
	private void isValidNestedHeaderConfiguration(final boolean isPH, final XlsNestedHeader annotation)
			throws ConfigurationException {

		if (isPH && annotation.startX() == annotation.endX()) {
			throw new ConfigurationException(ExceptionMessage.ConfigurationException_Conflict.getMessage());

		} else if (!isPH && annotation.startY() == annotation.endY()) {
			throw new ConfigurationException(ExceptionMessage.ConfigurationException_Conflict.getMessage());
		}
	}

	/**
	 * Apply merge region if necessary.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param r
	 *            the {@link Row}
	 * @param idxR
	 *            the position of the row
	 * @param idxC
	 *            the position of the cell
	 * @param isPH
	 *            true if propagation horizontally, false if propagation
	 *            vertically
	 * @throws ConfigurationException
	 */
	private void applyMergeRegion(final XConfigCriteria configCriteria, Row r, final int idxR, final int idxC,
			final boolean isPH) throws ConfigurationException {
		/* Process @XlsNestedHeader */
		if (configCriteria.getField().isAnnotationPresent(XlsNestedHeader.class)) {
			XlsNestedHeader annotation = (XlsNestedHeader) configCriteria.getField()
					.getAnnotation(XlsNestedHeader.class);
			/* if row null is necessary to create it */
			Row row = r;
			if (row == null) {
				row = initializeRow(configCriteria.getSheet(), idxR);
			}

			/* validation of configuration */
			isValidNestedHeaderConfiguration(isPH, annotation);

			/* prepare position rows / cells */
			int startRow;
			int endRow;
			int startCell;
			int endCell;
			if (isPH) {
				startRow = endRow = idxR;
				startCell = idxC + annotation.startX();
				endCell = idxC + annotation.endX();
			} else {
				startRow = idxR + annotation.startY();
				endRow = idxR + annotation.endY();
				startCell = endCell = idxC;
			}

			/* initialize master header cell */
			CellStyleHandler.initializeHeaderCell(configCriteria.getStylesMap(), row, startCell, annotation.title());

			/* merge region of the master header cell */
			configCriteria.getSheet().addMergedRegion(new CellRangeAddress(startRow, endRow, startCell, endCell));

		}
	}

	/**
	 * Initialize an row.
	 * 
	 * @param s
	 *            the {@link Sheet} to add the row
	 * @param idxR
	 *            position of the new row
	 * @return the {@link Row} created
	 */
	private Row initializeRow(final Sheet s, final int idxR) {
		return s.createRow(idxR);
	}

	/**
	 * Initialize the cell position based at the title orientation.
	 * 
	 * @param positionCell
	 *            the cell position defined at the element
	 * @param orientation
	 *            the {@link TitleOrientationType} of the element
	 * @return the cell position
	 */
	private int initializeHeaderCellPosition(final int positionCell, final TitleOrientationType orientation) {
		int idxCell = positionCell - 1;
		if (TitleOrientationType.LEFT == orientation) {
			idxCell -= 1;
		} else if (TitleOrientationType.RIGHT == orientation) {
			idxCell += 1;
		}
		return idxCell;
	}

	/**
	 * initialize the row position based at the title orientation.
	 * 
	 * @param positionRow
	 *            the row position defined at the element
	 * @param orientation
	 *            the {@link TitleOrientationType} of the element
	 * @return the row position
	 */
	private int initializeHeaderRowPosition(final int positionRow, final TitleOrientationType orientation) {
		int idxRow = positionRow;
		if (TitleOrientationType.TOP == orientation) {
			idxRow -= 1;
		} else if (TitleOrientationType.BOTTOM == orientation) {
			idxRow += 1;
		}
		return idxRow;
	}

	private void initializeCellByField(final XConfigCriteria configCriteria, final XlsFreeElement xlsAnnotation,
			final Object o, final Field field, final int idxC, final int cL)
					throws ElementException, ConverterException, CustomizedRulesException {

		/* validate cascade level */
		if (cL <= configCriteria.getCascadeLevel().getCode()) {

			/* make the field accessible to recover the value */
			field.setAccessible(true);

			Class<?> fT = field.getType();

			if (configCriteria.getSheet().getRow(xlsAnnotation.row()) != null) {
				configCriteria.setRow(configCriteria.getSheet().getRow(xlsAnnotation.row()));
			} else {
				configCriteria.setRow(configCriteria.getSheet().createRow(xlsAnnotation.row()));
			}
			configCriteria.setField(field);

			// initialize Element
			configCriteria.setElement(XlsElementFactory.build(xlsAnnotation));

			boolean isAppliedObject = toExcel(configCriteria, o, fT, idxC);

			if (!isAppliedObject && !fT.isPrimitive()) {
				throw new ElementException(ExceptionMessage.ElementException_ComplexObject.getMessage());
			}
		}
	}

	/**
	 * Initialize an cell according the type of field and the PropagationType is
	 * PROPAGATION_HORIZONTAL.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param o
	 *            the object
	 * @param idxR
	 *            the position of the row
	 * @param idxC
	 *            the position of the cell
	 * @param cL
	 *            the cascade level
	 * @return in case of the object return the number of cell created,
	 *         otherwise 0
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws CustomizedRulesException
	 * @throws ElementException
	 * @throws ConfigurationException
	 * @throws InvocationTargetException
	 * @throws ConverterException
	 * @throws Exception
	 */
	private int initializeCellByFieldHorizontal(final XConfigCriteria configCriteria, final Object o, final int idxR,
			final int idxC, final int cL)
					throws IllegalAccessException, InstantiationException, InvocationTargetException,
					ConfigurationException, ElementException, CustomizedRulesException, ConverterException {

		int counter = 0;

		/* validate cascade level */
		if (cL <= configCriteria.getCascadeLevel().getCode()) {
			/* make the field accessible to recover the value */
			configCriteria.getField().setAccessible(true);

			Class<?> fT = configCriteria.getField().getType();

			boolean isAppliedObject = toExcel(configCriteria, o, fT, idxC);

			if (!isAppliedObject && !fT.isPrimitive()) {
				Object nO = configCriteria.getField().get(o);
				/* manage null objects */
				if (nO == null) {
					nO = fT.newInstance();
				}
				Class<?> oC = nO.getClass();

				counter = marshalAsPropagationHorizontal(configCriteria, nO, oC, idxR, idxC - 1, cL + 1);
			}
		}
		return counter;
	}

	/**
	 * Initialize an cell according the type of field and the PropagationType is
	 * PROPAGATION_VERTICAL.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param o
	 *            the object
	 * @param r
	 *            the content row
	 * @param idxR
	 *            the position of the row
	 * @param idxC
	 *            the position of the cell
	 * @param cL
	 *            the cascade level
	 * @return
	 * @throws ConverterException
	 * @throws ElementException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws ConfigurationException
	 * @throws CustomizedRulesException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	private int initializeCellByFieldVertical(final XConfigCriteria configCriteria, final Object o, final Row r,
			final int idxR, final int idxC, int cL)
					throws ElementException, ConverterException, IllegalAccessException, InstantiationException,
					NoSuchMethodException, InvocationTargetException, CustomizedRulesException, ConfigurationException {

		int counter = 0;

		/* validate cascade level */
		if (cL <= configCriteria.getCascadeLevel().getCode()) {
			/* make the field accessible to recover the value */
			configCriteria.getField().setAccessible(true);

			Class<?> fT = configCriteria.getField().getType();

			configCriteria.setRow(r);

			boolean isAppliedObject = toExcel(configCriteria, o, fT, idxC);

			if (!isAppliedObject && !fT.isPrimitive()) {
				Object nO = configCriteria.getField().get(o);
				/* manage null objects */
				if (nO == null) {
					nO = fT.newInstance();
				}
				Class<?> oC = nO.getClass();

				counter = marshalAsPropagationVertical(configCriteria, nO, oC, idxR - 1, idxC - 1, cL + 1);
			}
		}
		return counter;
	}

	/**
	 * Apply the base object to cell.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param o
	 *            the object
	 * @param fT
	 *            the field type
	 * @param idxC
	 *            the position of the cell
	 * @return
	 * @throws ElementException
	 * @throws ConverterException
	 * @throws CustomizedRulesException
	 * @throws Exception
	 */
	private boolean toExcel(final XConfigCriteria configCriteria, final Object o, final Class<?> fT, final int idxC)
			throws ElementException, ConverterException, CustomizedRulesException {
		/* flag which define if the cell was updated or not */
		boolean isUpdated;
		/* initialize cell */
		Cell cell;

		/*
		 * check if the cell to be applied the element is empty otherwise one
		 * exception will be launched
		 */
		if (configCriteria.getRow().getCell(idxC) != null) {
			throw new ElementException(ExceptionMessage.ElementException_OverwriteCell.getMessage());
		}

		switch (fT.getName()) {
		case CellHandler.OBJECT_DATE:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.dateWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_LOCALDATE:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.localDateWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_LOCALDATETIME:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.localDateTimeWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_STRING:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.stringWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_SHORT:
			/* falls through */
		case CellHandler.PRIMITIVE_SHORT:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.shortWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_INTEGER:
			/* falls through */
		case CellHandler.PRIMITIVE_INTEGER:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.integerWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_LONG:
			/* falls through */
		case CellHandler.PRIMITIVE_LONG:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.longWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_DOUBLE:
			/* falls through */
		case CellHandler.PRIMITIVE_DOUBLE:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.doubleWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_BIGDECIMAL:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.bigDecimalWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_FLOAT:
			/* falls through */
		case CellHandler.PRIMITIVE_FLOAT:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.floatWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_BOOLEAN:
			/* falls through */
		case CellHandler.PRIMITIVE_BOOLEAN:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.booleanWriter(configCriteria, o, cell);
			break;

		default:
			isUpdated = false;
			break;
		}

		if (!isUpdated && fT.isEnum()) {
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.enumWriter(configCriteria, o, cell);
		}

		return isUpdated;
	}

	/**
	 * Apply the base object from cell.
	 * 
	 * @param o
	 *            the object
	 * @param fT
	 *            the field type
	 * @param f
	 *            the field
	 * @param c
	 *            the cell
	 * @param xlsAnnotation
	 *            the {@link XlsElement} annotation
	 * @return
	 * 
	 * @throws IllegalAccessException
	 * @throws ConverterException
	 */
	private boolean toObject(final Object o, final Class<?> fT, final Field f, final Cell c,
			final XlsElement xlsAnnotation) throws IllegalAccessException, ConverterException {
		/* flag which define if the cell was updated or not */
		boolean isUpdated;

		f.setAccessible(true);

		switch (fT.getName()) {
		case CellHandler.OBJECT_DATE:
			CellHandler.dateReader(o, f, c, xlsAnnotation);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_LOCALDATE:
			CellHandler.localDateReader(o, f, c, xlsAnnotation);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_LOCALDATETIME:
			CellHandler.localDateTimeReader(o, f, c, xlsAnnotation);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_STRING:
			CellHandler.stringReader(o, f, c);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_SHORT:
			/* falls through */
		case CellHandler.PRIMITIVE_SHORT:
			CellHandler.shortReader(o, f, c);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_INTEGER:
			/* falls through */
		case CellHandler.PRIMITIVE_INTEGER:
			CellHandler.integerReader(o, f, c);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_LONG:
			/* falls through */
		case CellHandler.PRIMITIVE_LONG:
			CellHandler.longReader(o, f, c);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_DOUBLE:
			/* falls through */
		case CellHandler.PRIMITIVE_DOUBLE:
			CellHandler.doubleReader(o, f, c, xlsAnnotation);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_BIGDECIMAL:
			CellHandler.bigDecimalReader(o, f, c, xlsAnnotation);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_FLOAT:
			/* falls through */
		case CellHandler.PRIMITIVE_FLOAT:
			CellHandler.floatReader(o, f, c);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_BOOLEAN:
			/* falls through */
		case CellHandler.PRIMITIVE_BOOLEAN:
			CellHandler.booleanReader(o, f, c, xlsAnnotation);
			isUpdated = true;
			break;

		default:
			isUpdated = false;
			break;
		}

		if (!isUpdated && fT.isEnum()) {
			CellHandler.enumReader(o, fT, f, c);
			isUpdated = true;
		}

		return isUpdated;
	}

	/**
	 * Convert the object to file with the PropagationType as
	 * PROPAGATION_HORIZONTAL.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param o
	 *            the object
	 * @param oC
	 *            the object class
	 * @param idxR
	 *            the position of the row
	 * @param idxC
	 *            the position of the cell
	 * @param cL
	 *            the cascade level
	 * @return
	 * @throws ConfigurationException
	 * @throws ElementException
	 * @throws CustomizedRulesException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws ConverterException
	 * @throws InstantiationException
	 * 
	 */
	private int marshalAsPropagationHorizontal(final XConfigCriteria configCriteria, final Object o, final Class<?> oC,
			final int idxR, final int idxC, final int cL)
					throws ConfigurationException, ElementException, CustomizedRulesException, IllegalAccessException,
					InvocationTargetException, InstantiationException, ConverterException {
		/* counter related to the number of fields (if new object) */
		int counter = -1;
		int indexCell = idxC;

		/* get declared fields */
		List<Field> fL = Arrays.asList(oC.getDeclaredFields());
		for (Field f : fL) {
			/* update field at ConfigCriteria */
			configCriteria.setField(f);

			/* process each field from the object */
			if (configCriteria.getRowHeader() != null) {
				/* calculate index of the cell */
				int tmpIdxRow = idxR - 3;
				/* apply merge region */
				applyMergeRegion(configCriteria, null, tmpIdxRow, indexCell, true);
			}
			/* Process @XlsElement */
			if (f.isAnnotationPresent(XlsElement.class)) {
				XlsElement xlsAnnotation = (XlsElement) f.getAnnotation(XlsElement.class);

				/* validate the position of the element */
				if (xlsAnnotation.position() < 1) {
					throw new ElementException(ExceptionMessage.ElementException_InvalidPosition.getMessage());
				}

				/* validate the propagation type & formulas */
				if (xlsAnnotation.isFormula() && StringUtils.isNotBlank(xlsAnnotation.formula())
						&& xlsAnnotation.formula().contains("idy")) {
					throw new ElementException(ExceptionMessage.ConfigurationException_Conflict.getMessage());
				}

				/* update annotation at ConfigCriteria */
				configCriteria.setElement(xlsAnnotation);

				/* apply customized rules defined at the object */
				if (StringUtils.isNotBlank(xlsAnnotation.customizedRules())) {
					try {
						CellHandler.applyCustomizedRules(o, xlsAnnotation.customizedRules());
					} catch (NoSuchMethodException e) {
						throw new CustomizedRulesException(
								ExceptionMessage.CustomizedRulesException_NoSuchMethod.getMessage(), e);
					}
				}

				/*
				 * increment of the counter related to the number of fields (if
				 * new object)
				 */
				counter++;
				if (configCriteria.getRowHeader() != null) {
					/* header treatment */
					CellStyleHandler.initializeHeaderCell(configCriteria.getStylesMap(), configCriteria.getRowHeader(),
							indexCell + xlsAnnotation.position(), xlsAnnotation.title());
				}
				/* prepare the column width */
				if (configCriteria.getResizeActive() && xlsAnnotation.columnWidthInUnits() != 0) {
					configCriteria.getColumnWidthMap().put(indexCell + xlsAnnotation.position(),
							xlsAnnotation.columnWidthInUnits());
				}

				/* content treatment */
				indexCell += initializeCellByFieldHorizontal(configCriteria, o, idxR,
						indexCell + xlsAnnotation.position(), cL);
			}

			/* Process @XlsFreeElement */
			processXlsFreeElement(configCriteria, o, cL, f);
		}
		/* disable the resize */
		configCriteria.setResizeActive(false);

		return counter;
	}

	/**
	 * Convert the object to file with the PropagationType as
	 * PROPAGATION_VERTICAL.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param o
	 *            the object
	 * @param oC
	 *            the object class
	 * @param idxR
	 *            the position of the row
	 * @param idxC
	 *            the position of the cell
	 * @param cL
	 *            the cascade level
	 * @return
	 * @throws ElementException
	 * @throws CustomizedRulesException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws NoSuchMethodException
	 * @throws ConfigurationException
	 * @throws ConverterException
	 * @throws InstantiationException
	 * 
	 */
	private int marshalAsPropagationVertical(final XConfigCriteria configCriteria, final Object o, Class<?> oC,
			final int idxR, final int idxC, final int cL)
					throws ElementException, NoSuchMethodException, IllegalAccessException, InvocationTargetException,
					CustomizedRulesException, ConfigurationException, ConverterException, InstantiationException {
		/* counter related to the number of fields (if new object) */
		int counter = -1;
		int indexCell = idxC;
		int indexRow = idxR;
		/* backup base index of the cell */
		int baseIdxCell = indexCell;

		/* get declared fields */
		List<Field> fL = Arrays.asList(oC.getDeclaredFields());
		for (Field f : fL) {
			/* process each field from the object */
			configCriteria.setField(f);

			/* restart the index of the cell */
			indexCell = baseIdxCell;

			/* Process @XlsElement */
			if (f.isAnnotationPresent(XlsElement.class)) {
				XlsElement xlsAnnotation = (XlsElement) f.getAnnotation(XlsElement.class);

				/* validate the position of the element */
				if (xlsAnnotation.position() < 1) {
					throw new ElementException(ExceptionMessage.ElementException_InvalidPosition.getMessage());
				}

				/* validate the propagation type & formulas */
				if (xlsAnnotation.isFormula() && StringUtils.isNotBlank(xlsAnnotation.formula())
						&& xlsAnnotation.formula().contains("idx")) {
					throw new ElementException(ExceptionMessage.ConfigurationException_Conflict.getMessage());
				}

				/* update annotation at ConfigCriteria */
				configCriteria.setElement(xlsAnnotation);

				/* apply customized rules defined at the object */
				if (StringUtils.isNotBlank(xlsAnnotation.customizedRules())) {
					CellHandler.applyCustomizedRules(o, xlsAnnotation.customizedRules());
				}

				/*
				 * increment of the counter related to the number of fields (if
				 * new object)
				 */
				counter++;

				/* create the row */
				Row row = configCriteria.getSheet().getRow(indexRow + xlsAnnotation.position());
				if (row == null) {
					int tmpIdxCell = indexCell - 1;
					/* initialize row */
					row = initializeRow(configCriteria.getSheet(), indexRow + xlsAnnotation.position());

					/* apply merge region */
					applyMergeRegion(configCriteria, row, indexRow, tmpIdxCell, false);

					/* header treatment */
					CellStyleHandler.initializeHeaderCell(configCriteria.getStylesMap(), row, indexCell,
							xlsAnnotation.title());

				} /*
					 * else { row = configCriteria.getSheet().getRow(indexRow +
					 * xlsAnnotation.position()); }
					 */
				/* prepare the column width */
				if (configCriteria.getResizeActive() && xlsAnnotation.columnWidthInUnits() != 0) {
					configCriteria.getColumnWidthMap().put(indexCell + xlsAnnotation.position(),
							xlsAnnotation.columnWidthInUnits());
				}

				/* increment the cell position */
				indexCell++;
				/* content treatment */
				indexRow += initializeCellByFieldVertical(configCriteria, o, row, indexRow + xlsAnnotation.position(),
						indexCell, cL);
			}

			/* Process @XlsFreeElement */
			processXlsFreeElement(configCriteria, o, cL, f);
		}
		/* disable the resize */
		configCriteria.setResizeActive(false);

		return counter;
	}

	/**
	 * Process the annotation {@link XlsFreeElement}
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param o
	 *            the object
	 * @param oC
	 *            the object class
	 * @param f
	 *            the field
	 * @throws ElementException
	 * @throws ConverterException
	 * @throws CustomizedRulesException
	 */
	private void processXlsFreeElement(final XConfigCriteria configCriteria, final Object o, final int cL,
			final Field f) throws ElementException, ConverterException, CustomizedRulesException {

		if (f.isAnnotationPresent(XlsFreeElement.class)) {
			XlsFreeElement xlsAnnotation = (XlsFreeElement) f.getAnnotation(XlsFreeElement.class);

			/* validate the row/cell of the element */<commit_msg>Fix problem with @XlsFreeElement<commit_after>package net.ceos.project.poi.annotated.core;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jaexcel.framework.JAEX.configuration.Configuration;

import net.ceos.project.poi.annotated.annotation.XlsConfiguration;
import net.ceos.project.poi.annotated.annotation.XlsDecorator;
import net.ceos.project.poi.annotated.annotation.XlsDecorators;
import net.ceos.project.poi.annotated.annotation.XlsElement;
import net.ceos.project.poi.annotated.annotation.XlsFreeElement;
import net.ceos.project.poi.annotated.annotation.XlsNestedHeader;
import net.ceos.project.poi.annotated.annotation.XlsSheet;
import net.ceos.project.poi.annotated.definition.ExceptionMessage;
import net.ceos.project.poi.annotated.definition.ExtensionFileType;
import net.ceos.project.poi.annotated.definition.PropagationType;
import net.ceos.project.poi.annotated.definition.TitleOrientationType;
import net.ceos.project.poi.annotated.exception.ConfigurationException;
import net.ceos.project.poi.annotated.exception.ConverterException;
import net.ceos.project.poi.annotated.exception.CustomizedRulesException;
import net.ceos.project.poi.annotated.exception.ElementException;
import net.ceos.project.poi.annotated.exception.SheetException;

public class Engine implements IEngine {

	/**
	 * @deprecated
	 */
	@Deprecated
	Configuration configuration;

	/**
	 * Get the runtime class of the object passed as parameter.
	 * 
	 * @param object
	 *            the object
	 * @return the runtime class
	 * @throws ElementException
	 */
	private Class<?> initializeRuntimeClass(final Object object) throws ElementException {
		Class<?> oC = null;
		try {
			/* instance object class */
			oC = object.getClass();
		} catch (Exception e) {
			throw new ElementException(ExceptionMessage.ElementException_NullObject.getMessage(), e);
		}
		return oC;
	}

	/**
	 * Initialize the configuration to apply at the Excel.
	 * 
	 * @param oC
	 *            the {@link Class<?>}
	 * @return
	 * @throws ConfigurationException
	 */
	private void initializeConfigurationData(final XConfigCriteria configCriteria, final Class<?> oC)
			throws ConfigurationException {
		/* Process @XlsConfiguration */
		if (oC.isAnnotationPresent(XlsConfiguration.class)) {
			XlsConfiguration xlsAnnotation = (XlsConfiguration) oC.getAnnotation(XlsConfiguration.class);
			initializeXlsConfiguration(configCriteria, xlsAnnotation);
		} else {
			throw new ConfigurationException(
					ExceptionMessage.ConfigurationException_XlsConfigurationMissing.getMessage());
		}
		/* Process @XlsSheet */
		if (oC.isAnnotationPresent(XlsSheet.class)) {
			XlsSheet xlsAnnotation = (XlsSheet) oC.getAnnotation(XlsSheet.class);
			initializeXlsSheet(configCriteria, xlsAnnotation);
		} else {
			throw new ConfigurationException(ExceptionMessage.ConfigurationException_XlsSheetMissing.getMessage());
		}
	}

	/**
	 * Add the main xls configuration.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param annotation
	 *            the {@link XlsConfiguration}
	 * @return
	 */
	private void initializeXlsConfiguration(final XConfigCriteria configCriteria, final XlsConfiguration annotation) {
		configCriteria.setFileName(annotation.nameFile());
		configCriteria.setCompleteFileName(annotation.nameFile() + annotation.extensionFile().getExtension());
		configCriteria.setExtension(annotation.extensionFile());
	}

	/**
	 * Add the sheet configuration.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param annotation
	 *            the {@link XlsSheet}
	 * @return
	 */
	private void initializeXlsSheet(final XConfigCriteria configCriteria, final XlsSheet annotation) {
		configCriteria.setTitleSheet(annotation.title());
		configCriteria.setPropagation(annotation.propagation());
		configCriteria.setCascadeLevel(annotation.cascadeLevel());
		configCriteria.setStartRow(annotation.startRow());
		configCriteria.setStartCell(annotation.startCell());
		configCriteria.setFreezePane(annotation.freezePane());
		configCriteria.setGroupElement(annotation.groupElement());

	}

	/**
	 * initialize style cell via annotation {@link XlsDecorators}
	 * 
	 * @param objectClass
	 *            the object class
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @throws ConfigurationException
	 */
	private void initializeCellStyleViaAnnotation(final Class<?> objectClass, final XConfigCriteria configCriteria)
			throws ConfigurationException {
		if (objectClass.isAnnotationPresent(XlsDecorators.class)) {
			XlsDecorators xlsDecorators = (XlsDecorators) objectClass.getAnnotation(XlsDecorators.class);
			for (XlsDecorator decorator : xlsDecorators.values()) {
				if (configCriteria.getStylesMap().containsKey(decorator.decoratorName())) {
					throw new ConfigurationException(
							ExceptionMessage.ConfigurationException_CellStyleDuplicated.getMessage());
				}
				configCriteria.getStylesMap().put(decorator.decoratorName(),
						CellStyleHandler.initializeCellStyleByXlsDecorator(configCriteria.getWorkbook(), decorator));
			}
		}
	}

	/**
	 * Initialize Workbook.
	 * 
	 * @param type
	 *            the {@link ExtensionFileType} of workbook
	 * @return the {@link Workbook}.
	 */
	private Workbook initializeWorkbook(final ExtensionFileType type) {
		if (type != null && ExtensionFileType.XLS.getExtension().equals(type.getExtension())) {
			return new HSSFWorkbook();
		} else {
			return new XSSFWorkbook();
		}
	}

	/**
	 * Initialize Workbook from FileInputStream.
	 * 
	 * @param inputStream
	 *            the file input stream
	 * @param type
	 *            the {@link ExtensionFileType} of workbook
	 * @return the {@link Workbook} created
	 * @throws IOException
	 */
	private Workbook initializeWorkbook(final FileInputStream inputStream, final ExtensionFileType type)
			throws IOException {
		if (type != null && ExtensionFileType.XLS.getExtension().equals(type.getExtension())) {
			return new HSSFWorkbook(inputStream);
		} else {
			return new XSSFWorkbook(inputStream);
		}
	}

	/**
	 * Initialize Workbook from byte[].
	 * 
	 * @param byteArray
	 *            the array of bytes
	 * @param type
	 *            the {@link ExtensionFileType} of workbook
	 * @return the {@link Workbook} created
	 * @throws IOException
	 */
	private Workbook initializeWorkbook(final byte[] byteArray, final ExtensionFileType type) throws IOException {
		if (type != null && ExtensionFileType.XLS.getExtension().equals(type.getExtension())) {
			return new HSSFWorkbook(new ByteArrayInputStream(byteArray));
		} else {
			return new XSSFWorkbook(new ByteArrayInputStream(byteArray));
		}
	}

	/**
	 * Initialize Sheet.
	 * 
	 * @param wb
	 *            the {@link Workbook} to use
	 * @param sheetName
	 *            the name of the sheet
	 * @return the {@link Sheet} created
	 * @throws SheetException
	 */
	private Sheet initializeSheet(final Workbook wb, final String sheetName) throws SheetException {
		Sheet s = null;
		try {
			s = wb.createSheet(sheetName);
		} catch (Exception e) {
			throw new SheetException(ExceptionMessage.SheetException_CreationSheet.getMessage(), e);
		}
		return s;
	}

	/**
	 * Validate if the nested header configuration is valid.
	 * 
	 * @param isPH
	 *            true if propagation is HORIZONTAL otherwise false to
	 *            propagation VERTICAL
	 * @param annotation
	 *            the {@link XlsNestedHeader} annotation
	 * @throws ConfigurationException
	 */
	private void isValidNestedHeaderConfiguration(final boolean isPH, final XlsNestedHeader annotation)
			throws ConfigurationException {

		if (isPH && annotation.startX() == annotation.endX()) {
			throw new ConfigurationException(ExceptionMessage.ConfigurationException_Conflict.getMessage());

		} else if (!isPH && annotation.startY() == annotation.endY()) {
			throw new ConfigurationException(ExceptionMessage.ConfigurationException_Conflict.getMessage());
		}
	}

	/**
	 * Apply merge region if necessary.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param r
	 *            the {@link Row}
	 * @param idxR
	 *            the position of the row
	 * @param idxC
	 *            the position of the cell
	 * @param isPH
	 *            true if propagation horizontally, false if propagation
	 *            vertically
	 * @throws ConfigurationException
	 */
	private void applyMergeRegion(final XConfigCriteria configCriteria, Row r, final int idxR, final int idxC,
			final boolean isPH) throws ConfigurationException {
		/* Process @XlsNestedHeader */
		if (configCriteria.getField().isAnnotationPresent(XlsNestedHeader.class)) {
			XlsNestedHeader annotation = (XlsNestedHeader) configCriteria.getField()
					.getAnnotation(XlsNestedHeader.class);
			/* if row null is necessary to create it */
			Row row = r;
			if (row == null) {
				row = initializeRow(configCriteria.getSheet(), idxR);
			}

			/* validation of configuration */
			isValidNestedHeaderConfiguration(isPH, annotation);

			/* prepare position rows / cells */
			int startRow;
			int endRow;
			int startCell;
			int endCell;
			if (isPH) {
				startRow = endRow = idxR;
				startCell = idxC + annotation.startX();
				endCell = idxC + annotation.endX();
			} else {
				startRow = idxR + annotation.startY();
				endRow = idxR + annotation.endY();
				startCell = endCell = idxC;
			}

			/* initialize master header cell */
			CellStyleHandler.initializeHeaderCell(configCriteria.getStylesMap(), row, startCell, annotation.title());

			/* merge region of the master header cell */
			configCriteria.getSheet().addMergedRegion(new CellRangeAddress(startRow, endRow, startCell, endCell));

		}
	}

	/**
	 * Initialize an row.
	 * 
	 * @param s
	 *            the {@link Sheet} to add the row
	 * @param idxR
	 *            position of the new row
	 * @return the {@link Row} created
	 */
	private Row initializeRow(final Sheet s, final int idxR) {
		return s.createRow(idxR);
	}

	/**
	 * Initialize the cell position based at the title orientation.
	 * 
	 * @param positionCell
	 *            the cell position defined at the element
	 * @param orientation
	 *            the {@link TitleOrientationType} of the element
	 * @return the cell position
	 */
	private int initializeHeaderCellPosition(final int positionCell, final TitleOrientationType orientation) {
		int idxCell = positionCell - 1;
		if (TitleOrientationType.LEFT == orientation) {
			idxCell -= 1;
		} else if (TitleOrientationType.RIGHT == orientation) {
			idxCell += 1;
		}
		return idxCell;
	}

	/**
	 * initialize the row position based at the title orientation.
	 * 
	 * @param positionRow
	 *            the row position defined at the element
	 * @param orientation
	 *            the {@link TitleOrientationType} of the element
	 * @return the row position
	 */
	private int initializeHeaderRowPosition(final int positionRow, final TitleOrientationType orientation) {
		int idxRow = positionRow;
		if (TitleOrientationType.TOP == orientation) {
			idxRow -= 1;
		} else if (TitleOrientationType.BOTTOM == orientation) {
			idxRow += 1;
		}
		return idxRow;
	}

	private void initializeCellByField(final XConfigCriteria configCriteria, final XlsFreeElement xlsAnnotation,
			final Object o, final Field field, final int idxC, final int cL)
					throws ElementException, ConverterException, CustomizedRulesException {

		/* validate cascade level */
		if (cL <= configCriteria.getCascadeLevel().getCode()) {

			/* make the field accessible to recover the value */
			field.setAccessible(true);

			Class<?> fT = field.getType();

			if (configCriteria.getSheet().getRow(xlsAnnotation.row()) != null) {
				configCriteria.setRow(configCriteria.getSheet().getRow(xlsAnnotation.row()));
			} else {
				configCriteria.setRow(configCriteria.getSheet().createRow(xlsAnnotation.row()));
			}
			configCriteria.setField(field);

			// initialize Element
			configCriteria.setElement(XlsElementFactory.build(xlsAnnotation));

			boolean isAppliedObject = toExcel(configCriteria, o, fT, idxC);

			if (!isAppliedObject && !fT.isPrimitive()) {
				throw new ElementException(ExceptionMessage.ElementException_ComplexObject.getMessage());
			}
		}
	}

	/**
	 * Initialize an cell according the type of field and the PropagationType is
	 * PROPAGATION_HORIZONTAL.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param o
	 *            the object
	 * @param idxR
	 *            the position of the row
	 * @param idxC
	 *            the position of the cell
	 * @param cL
	 *            the cascade level
	 * @return in case of the object return the number of cell created,
	 *         otherwise 0
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws CustomizedRulesException
	 * @throws ElementException
	 * @throws ConfigurationException
	 * @throws InvocationTargetException
	 * @throws ConverterException
	 * @throws Exception
	 */
	private int initializeCellByFieldHorizontal(final XConfigCriteria configCriteria, final Object o, final int idxR,
			final int idxC, final int cL)
					throws IllegalAccessException, InstantiationException, InvocationTargetException,
					ConfigurationException, ElementException, CustomizedRulesException, ConverterException {

		int counter = 0;

		/* validate cascade level */
		if (cL <= configCriteria.getCascadeLevel().getCode()) {
			/* make the field accessible to recover the value */
			configCriteria.getField().setAccessible(true);

			Class<?> fT = configCriteria.getField().getType();

			boolean isAppliedObject = toExcel(configCriteria, o, fT, idxC);

			if (!isAppliedObject && !fT.isPrimitive()) {
				Object nO = configCriteria.getField().get(o);
				/* manage null objects */
				if (nO == null) {
					nO = fT.newInstance();
				}
				Class<?> oC = nO.getClass();

				counter = marshalAsPropagationHorizontal(configCriteria, nO, oC, idxR, idxC - 1, cL + 1);
			}
		}
		return counter;
	}

	/**
	 * Initialize an cell according the type of field and the PropagationType is
	 * PROPAGATION_VERTICAL.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param o
	 *            the object
	 * @param r
	 *            the content row
	 * @param idxR
	 *            the position of the row
	 * @param idxC
	 *            the position of the cell
	 * @param cL
	 *            the cascade level
	 * @return
	 * @throws ConverterException
	 * @throws ElementException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws ConfigurationException
	 * @throws CustomizedRulesException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	private int initializeCellByFieldVertical(final XConfigCriteria configCriteria, final Object o, final Row r,
			final int idxR, final int idxC, int cL)
					throws ElementException, ConverterException, IllegalAccessException, InstantiationException,
					NoSuchMethodException, InvocationTargetException, CustomizedRulesException, ConfigurationException {

		int counter = 0;

		/* validate cascade level */
		if (cL <= configCriteria.getCascadeLevel().getCode()) {
			/* make the field accessible to recover the value */
			configCriteria.getField().setAccessible(true);

			Class<?> fT = configCriteria.getField().getType();

			configCriteria.setRow(r);

			boolean isAppliedObject = toExcel(configCriteria, o, fT, idxC);

			if (!isAppliedObject && !fT.isPrimitive()) {
				Object nO = configCriteria.getField().get(o);
				/* manage null objects */
				if (nO == null) {
					nO = fT.newInstance();
				}
				Class<?> oC = nO.getClass();

				counter = marshalAsPropagationVertical(configCriteria, nO, oC, idxR - 1, idxC - 1, cL + 1);
			}
		}
		return counter;
	}

	/**
	 * Apply the base object to cell.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param o
	 *            the object
	 * @param fT
	 *            the field type
	 * @param idxC
	 *            the position of the cell
	 * @return
	 * @throws ElementException
	 * @throws ConverterException
	 * @throws CustomizedRulesException
	 * @throws Exception
	 */
	private boolean toExcel(final XConfigCriteria configCriteria, final Object o, final Class<?> fT, final int idxC)
			throws ElementException, ConverterException, CustomizedRulesException {
		/* flag which define if the cell was updated or not */
		boolean isUpdated;
		/* initialize cell */
		Cell cell;

		/*
		 * check if the cell to be applied the element is empty otherwise one
		 * exception will be launched
		 */
		if (configCriteria.getRow().getCell(idxC) != null) {
			throw new ElementException(ExceptionMessage.ElementException_OverwriteCell.getMessage());
		}

		switch (fT.getName()) {
		case CellHandler.OBJECT_DATE:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.dateWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_LOCALDATE:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.localDateWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_LOCALDATETIME:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.localDateTimeWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_STRING:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.stringWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_SHORT:
			/* falls through */
		case CellHandler.PRIMITIVE_SHORT:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.shortWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_INTEGER:
			/* falls through */
		case CellHandler.PRIMITIVE_INTEGER:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.integerWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_LONG:
			/* falls through */
		case CellHandler.PRIMITIVE_LONG:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.longWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_DOUBLE:
			/* falls through */
		case CellHandler.PRIMITIVE_DOUBLE:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.doubleWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_BIGDECIMAL:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.bigDecimalWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_FLOAT:
			/* falls through */
		case CellHandler.PRIMITIVE_FLOAT:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.floatWriter(configCriteria, o, cell);
			break;

		case CellHandler.OBJECT_BOOLEAN:
			/* falls through */
		case CellHandler.PRIMITIVE_BOOLEAN:
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.booleanWriter(configCriteria, o, cell);
			break;

		default:
			isUpdated = false;
			break;
		}

		if (!isUpdated && fT.isEnum()) {
			cell = configCriteria.getRow().createCell(idxC);
			isUpdated = CellHandler.enumWriter(configCriteria, o, cell);
		}

		return isUpdated;
	}

	/**
	 * Apply the base object from cell.
	 * 
	 * @param o
	 *            the object
	 * @param fT
	 *            the field type
	 * @param f
	 *            the field
	 * @param c
	 *            the cell
	 * @param xlsAnnotation
	 *            the {@link XlsElement} annotation
	 * @return
	 * 
	 * @throws IllegalAccessException
	 * @throws ConverterException
	 */
	private boolean toObject(final Object o, final Class<?> fT, final Field f, final Cell c,
			final XlsElement xlsAnnotation) throws IllegalAccessException, ConverterException {
		/* flag which define if the cell was updated or not */
		boolean isUpdated;

		f.setAccessible(true);

		switch (fT.getName()) {
		case CellHandler.OBJECT_DATE:
			CellHandler.dateReader(o, f, c, xlsAnnotation);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_LOCALDATE:
			CellHandler.localDateReader(o, f, c, xlsAnnotation);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_LOCALDATETIME:
			CellHandler.localDateTimeReader(o, f, c, xlsAnnotation);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_STRING:
			CellHandler.stringReader(o, f, c);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_SHORT:
			/* falls through */
		case CellHandler.PRIMITIVE_SHORT:
			CellHandler.shortReader(o, f, c);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_INTEGER:
			/* falls through */
		case CellHandler.PRIMITIVE_INTEGER:
			CellHandler.integerReader(o, f, c);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_LONG:
			/* falls through */
		case CellHandler.PRIMITIVE_LONG:
			CellHandler.longReader(o, f, c);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_DOUBLE:
			/* falls through */
		case CellHandler.PRIMITIVE_DOUBLE:
			CellHandler.doubleReader(o, f, c, xlsAnnotation);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_BIGDECIMAL:
			CellHandler.bigDecimalReader(o, f, c, xlsAnnotation);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_FLOAT:
			/* falls through */
		case CellHandler.PRIMITIVE_FLOAT:
			CellHandler.floatReader(o, f, c);
			isUpdated = true;
			break;

		case CellHandler.OBJECT_BOOLEAN:
			/* falls through */
		case CellHandler.PRIMITIVE_BOOLEAN:
			CellHandler.booleanReader(o, f, c, xlsAnnotation);
			isUpdated = true;
			break;

		default:
			isUpdated = false;
			break;
		}

		if (!isUpdated && fT.isEnum()) {
			CellHandler.enumReader(o, fT, f, c);
			isUpdated = true;
		}

		return isUpdated;
	}

	/**
	 * Convert the object to file with the PropagationType as
	 * PROPAGATION_HORIZONTAL.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param o
	 *            the object
	 * @param oC
	 *            the object class
	 * @param idxR
	 *            the position of the row
	 * @param idxC
	 *            the position of the cell
	 * @param cL
	 *            the cascade level
	 * @return
	 * @throws ConfigurationException
	 * @throws ElementException
	 * @throws CustomizedRulesException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws ConverterException
	 * @throws InstantiationException
	 * 
	 */
	private int marshalAsPropagationHorizontal(final XConfigCriteria configCriteria, final Object o, final Class<?> oC,
			final int idxR, final int idxC, final int cL)
					throws ConfigurationException, ElementException, CustomizedRulesException, IllegalAccessException,
					InvocationTargetException, InstantiationException, ConverterException {
		/* counter related to the number of fields (if new object) */
		int counter = -1;
		int indexCell = idxC;

		/* get declared fields */
		List<Field> fL = Arrays.asList(oC.getDeclaredFields());
		for (Field f : fL) {
			/* update field at ConfigCriteria */
			configCriteria.setField(f);

			/* process each field from the object */
			if (configCriteria.getRowHeader() != null) {
				/* calculate index of the cell */
				int tmpIdxRow = idxR - 3;
				/* apply merge region */
				applyMergeRegion(configCriteria, null, tmpIdxRow, indexCell, true);
			}
			/* Process @XlsElement */
			if (f.isAnnotationPresent(XlsElement.class)) {
				XlsElement xlsAnnotation = (XlsElement) f.getAnnotation(XlsElement.class);

				/* validate the position of the element */
				if (xlsAnnotation.position() < 1) {
					throw new ElementException(ExceptionMessage.ElementException_InvalidPosition.getMessage());
				}

				/* validate the propagation type & formulas */
				if (xlsAnnotation.isFormula() && StringUtils.isNotBlank(xlsAnnotation.formula())
						&& xlsAnnotation.formula().contains("idy")) {
					throw new ElementException(ExceptionMessage.ConfigurationException_Conflict.getMessage());
				}

				/* update annotation at ConfigCriteria */
				configCriteria.setElement(xlsAnnotation);

				/* apply customized rules defined at the object */
				if (StringUtils.isNotBlank(xlsAnnotation.customizedRules())) {
					try {
						CellHandler.applyCustomizedRules(o, xlsAnnotation.customizedRules());
					} catch (NoSuchMethodException e) {
						throw new CustomizedRulesException(
								ExceptionMessage.CustomizedRulesException_NoSuchMethod.getMessage(), e);
					}
				}

				/*
				 * increment of the counter related to the number of fields (if
				 * new object)
				 */
				counter++;
				if (configCriteria.getRowHeader() != null) {
					/* header treatment */
					CellStyleHandler.initializeHeaderCell(configCriteria.getStylesMap(), configCriteria.getRowHeader(),
							indexCell + xlsAnnotation.position(), xlsAnnotation.title());
				}
				/* prepare the column width */
				if (configCriteria.getResizeActive() && xlsAnnotation.columnWidthInUnits() != 0) {
					configCriteria.getColumnWidthMap().put(indexCell + xlsAnnotation.position(),
							xlsAnnotation.columnWidthInUnits());
				}

				/* content treatment */
				indexCell += initializeCellByFieldHorizontal(configCriteria, o, idxR,
						indexCell + xlsAnnotation.position(), cL);
			}
		}

		for (Field f : fL) {
			/* update field at ConfigCriteria */
			configCriteria.setField(f);
			
			/* Process @XlsFreeElement */
			processXlsFreeElement(configCriteria, o, cL, f);
		}
		
		/* disable the resize */
		configCriteria.setResizeActive(false);

		return counter;
	}

	/**
	 * Convert the object to file with the PropagationType as
	 * PROPAGATION_VERTICAL.
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param o
	 *            the object
	 * @param oC
	 *            the object class
	 * @param idxR
	 *            the position of the row
	 * @param idxC
	 *            the position of the cell
	 * @param cL
	 *            the cascade level
	 * @return
	 * @throws ElementException
	 * @throws CustomizedRulesException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws NoSuchMethodException
	 * @throws ConfigurationException
	 * @throws ConverterException
	 * @throws InstantiationException
	 * 
	 */
	private int marshalAsPropagationVertical(final XConfigCriteria configCriteria, final Object o, Class<?> oC,
			final int idxR, final int idxC, final int cL)
					throws ElementException, NoSuchMethodException, IllegalAccessException, InvocationTargetException,
					CustomizedRulesException, ConfigurationException, ConverterException, InstantiationException {
		/* counter related to the number of fields (if new object) */
		int counter = -1;
		int indexCell = idxC;
		int indexRow = idxR;
		/* backup base index of the cell */
		int baseIdxCell = indexCell;

		/* get declared fields */
		List<Field> fL = Arrays.asList(oC.getDeclaredFields());
		for (Field f : fL) {
			/* process each field from the object */
			configCriteria.setField(f);

			/* restart the index of the cell */
			indexCell = baseIdxCell;

			/* Process @XlsElement */
			if (f.isAnnotationPresent(XlsElement.class)) {
				XlsElement xlsAnnotation = (XlsElement) f.getAnnotation(XlsElement.class);

				/* validate the position of the element */
				if (xlsAnnotation.position() < 1) {
					throw new ElementException(ExceptionMessage.ElementException_InvalidPosition.getMessage());
				}

				/* validate the propagation type & formulas */
				if (xlsAnnotation.isFormula() && StringUtils.isNotBlank(xlsAnnotation.formula())
						&& xlsAnnotation.formula().contains("idx")) {
					throw new ElementException(ExceptionMessage.ConfigurationException_Conflict.getMessage());
				}

				/* update annotation at ConfigCriteria */
				configCriteria.setElement(xlsAnnotation);

				/* apply customized rules defined at the object */
				if (StringUtils.isNotBlank(xlsAnnotation.customizedRules())) {
					CellHandler.applyCustomizedRules(o, xlsAnnotation.customizedRules());
				}

				/*
				 * increment of the counter related to the number of fields (if
				 * new object)
				 */
				counter++;

				/* create the row */
				Row row = configCriteria.getSheet().getRow(indexRow + xlsAnnotation.position());
				if (row == null) {
					int tmpIdxCell = indexCell - 1;
					/* initialize row */
					row = initializeRow(configCriteria.getSheet(), indexRow + xlsAnnotation.position());

					/* apply merge region */
					applyMergeRegion(configCriteria, row, indexRow, tmpIdxCell, false);

					/* header treatment */
					CellStyleHandler.initializeHeaderCell(configCriteria.getStylesMap(), row, indexCell,
							xlsAnnotation.title());

				} /*
					 * else { row = configCriteria.getSheet().getRow(indexRow +
					 * xlsAnnotation.position()); }
					 */
				/* prepare the column width */
				if (configCriteria.getResizeActive() && xlsAnnotation.columnWidthInUnits() != 0) {
					configCriteria.getColumnWidthMap().put(indexCell + xlsAnnotation.position(),
							xlsAnnotation.columnWidthInUnits());
				}

				/* increment the cell position */
				indexCell++;
				/* content treatment */
				indexRow += initializeCellByFieldVertical(configCriteria, o, row, indexRow + xlsAnnotation.position(),
						indexCell, cL);
			}
		}

		for (Field f : fL) {
			/* update field at ConfigCriteria */
			configCriteria.setField(f);
			
			/* Process @XlsFreeElement */
			processXlsFreeElement(configCriteria, o, cL, f);
		}

		/* disable the resize */
		configCriteria.setResizeActive(false);

		return counter;
	}

	/**
	 * Process the annotation {@link XlsFreeElement}
	 * 
	 * @param configCriteria
	 *            the {@link XConfigCriteria}
	 * @param o
	 *            the object
	 * @param oC
	 *            the object class
	 * @param f
	 *            the field
	 * @throws ElementException
	 * @throws ConverterException
	 * @throws CustomizedRulesException
	 */
	private void processXlsFreeElement(final XConfigCriteria configCriteria, final Object o, final int cL,
			final Field f) throws ElementException, ConverterException, CustomizedRulesException {

		if (f.isAnnotationPresent(XlsFreeElement.class)) {
			XlsFreeElement xlsAnnotation = (XlsFreeElement) f.getAnnotation(XlsFreeElement.class);

			/* validate the row/cell of the element */
{'fraction_non_alphanumeric': 0.10046428280524558, 'fraction_numerical': 0.0007167874888001955, 'mean_word_length': 3.6085585585585584, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '982056', 'n_tokens_mistral': 19990, 'n_tokens_neox': 17053, 'n_words': 4407}