package net.sourceforge.rtf.template;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import net.sourceforge.rtf.IRTFDocumentTransformer;
import net.sourceforge.rtf.context.RTFContextFieldsReader;
import net.sourceforge.rtf.context.fields.RTFContextField;
import net.sourceforge.rtf.context.fields.RTFContextFields;
import net.sourceforge.rtf.document.RTFAnnotation;
import net.sourceforge.rtf.document.RTFBookmark;
import net.sourceforge.rtf.document.RTFDocument;
import net.sourceforge.rtf.document.RTFElement;
import net.sourceforge.rtf.document.RTFField;
import net.sourceforge.rtf.document.RTFPage;
import net.sourceforge.rtf.document.RTFRow;
import net.sourceforge.rtf.document.RTFStartBookmark;
import net.sourceforge.rtf.document.RTFUserProperty;
import net.sourceforge.rtf.document.transformer.config.DigesterTransformerConfig;
import net.sourceforge.rtf.document.transformer.config.TransformerConfig;
import net.sourceforge.rtf.document.transformer.fields.DefaultRTFHyperlinkTransform;
import net.sourceforge.rtf.document.transformer.fields.DefaultRTFMergeFieldTransform;
import net.sourceforge.rtf.document.transformer.fields.DefaultRTFSimpleTransform;
import net.sourceforge.rtf.document.transformer.fields.IRTFFieldNameTransform;
import net.sourceforge.rtf.document.transformer.fields.IRTFFieldTransform;
import net.sourceforge.rtf.util.StringUtils;

public abstract class AbstractRTFDocumentTransformer implements
        IRTFDocumentTransformer, IRTFFieldNameTransform {

    private static TransformerConfig defaultTransformerConfig;
    static {
        try {
            defaultTransformerConfig = DigesterTransformerConfig
                    .getTransformerConfig();
        } catch (Exception e) {
        }
    }

    protected static final int RTF_ELEMENT_DOCUMENT = 0;

    protected static final int RTF_ELEMENT_STRINGBUFFER = 1;

    protected static final int RTF_ELEMENT_ROW = 2;

    protected static final int RTF_ELEMENT_FIELD = 3;

    protected static final int RTF_ELEMENT_BOOKMARK = 4;

    protected static final int RTF_ELEMENT_PAGE = 5;

    protected static final int RTF_ELEMENT_USERPROPERTY = 6;

    protected static final int RTF_ELEMENT_ANNOTATION = 7;

    private TransformerConfig transformerConfig;

    /**
     * This value allow to group by content when ther eis PageBreak in order to
     * group by content.
     */
    protected int groupByPerPageBreak = -1;

    /**
     * When Row is after START_LOOP, Row must not generate #foreach This list
     * contains row which not generate #foreach
     */
    protected List rowMustNotGenerateForeachList = null;

    /**
     * Map of START/END LOOP Bookmark
     */
    protected Map bookmarkStartMap = null;

    protected Map bookmarkEndMap = null;

    protected Map bookmarkBkmEndMap = null;

    protected Map pageBeforeEndBookmarkMap = null;

    protected Map startBookmarksNotEnded = null;

    protected RTFBookmark lastEndLoopRTFBookmark = null;

    /**
     * This list contains start LOOP Bookmark
     */
    protected Vector startBookmarksList = null;

    /**
     * Map of IF/ELSE/ENDIF Bookmark
     */
    protected Map bookmarkIfMap = null;

    protected Map bookmarkEndIfMap = null;

    protected Map bookmarkElseMap = null;

    protected Map bookmarkBkmIfMap = null;

    /**
     * 
     */
    protected Map rtfTransformMap = null; // Map contains
    
    
    // true if context has circular references and false otherwise
    private boolean circularReferences;

    // IRTFMergefieldTransform,
    // IRTFHyperlinkTransform

    public AbstractRTFDocumentTransformer() {
        // START/END LOOP
        this.bookmarkStartMap = new HashMap();
        this.bookmarkEndMap = new HashMap();
        this.bookmarkBkmEndMap = new HashMap();
        this.pageBeforeEndBookmarkMap = new HashMap();
        this.startBookmarksList = new Vector();
        this.startBookmarksNotEnded = new HashMap();
        // IF/ELSE/ENDIF bookmark
        this.bookmarkIfMap = new HashMap();
        this.bookmarkEndIfMap = new HashMap();
        this.bookmarkElseMap = new HashMap();
        this.bookmarkBkmIfMap = new HashMap();
    }

    public TransformerConfig getTransformerConfig() {
        if (transformerConfig == null)
            return defaultTransformerConfig;
        return transformerConfig;
    }

    public void setTransformerConfig(TransformerConfig transformerConfig) {
        this.transformerConfig = transformerConfig;
    }

    public RTFDocument transform(RTFDocument document, IContext context)
            throws IOException {
        RTFContextFields contextFields = (context == null ? null
                : getContextFields(context, isCircularReferences()));
        return transform(document, contextFields);
    }

    public RTFDocument transform(RTFDocument document,
            RTFContextFields contextFields) throws IOException {
        Map contextFieldsMap = (contextFields == null ? new HashMap()
                : contextFields.getMergeFieldsMap());
        RTFDocument documentTransformed = new RTFDocument(document);
        transform(documentTransformed, documentTransformed.getElementList(),
                contextFieldsMap);
        // Replace element Bookmark end by #end, if Bookmark start were replace
        // by #foreach
        for (Iterator iter = bookmarkEndMap.keySet().iterator(); iter.hasNext();) {
            String endBookmarkName = (String) iter.next();
            int index = getTransformerConfig()
                    .getBookmarkIndex(endBookmarkName);
            String startBookmarkName = getTransformerConfig()
                    .getBookmarkStartLoopName(index);
            RTFBookmark startBookmark = (RTFBookmark) bookmarkStartMap
                    .get(startBookmarkName);
            if (startBookmark != null) {
                // Start bookmark exist
                RTFBookmark endBookmark = (RTFBookmark) bookmarkEndMap
                        .get(endBookmarkName);
                String newContent = getMacroEndForEach(); // "#end";
                RTFPage page = (RTFPage) pageBeforeEndBookmarkMap
                        .get(endBookmarkName);
                if (page != null) {
                    String content = null;
                    if (groupByPerPageBreak > -1) {
                        // Content must be group by
                        // There is a page break defined into RTF and group by
                        // per page is defined
                        content = " "
                                + getMacroPageBreak(startBookmark
                                        .getItemNameList(), page
                                        .getRTFFirstContentOfElement(),
                                        groupByPerPageBreak) + " ";
                    } else {
                        // There is a page break defined into RTF
                        // and group by per page is not defined
                        content = " "
                                + getMacroPageBreak(startBookmark
                                        .getItemNameList(), page
                                        .getRTFFirstContentOfElement()) + " ";
                    }
                    page.replaceElement(content);
                } else {
                    if (groupByPerPageBreak > -1) {
                        if (lastEndLoopRTFBookmark != null
                                && lastEndLoopRTFBookmark.equals(endBookmark)) {
                            newContent = getMacroPageBreak(startBookmark
                                    .getItemNameList(), "\\page",
                                    groupByPerPageBreak)
                                    + newContent;
                        }
                    }
                }
                endBookmark.replaceElement(newContent);

                // Replace BKMEND by ""
                RTFBookmark startBookmarkBKMEND = (RTFBookmark) bookmarkBkmEndMap
                        .get(startBookmarkName);
                startBookmarkBKMEND.replaceElement("");
                RTFBookmark endBookmarkBKMEND = (RTFBookmark) bookmarkBkmEndMap
                        .get(endBookmarkName);
                endBookmarkBKMEND.replaceElement("");
            }
        }
        // Replace element ENDIF Bookmark end by #end
        for (Iterator iter = bookmarkEndIfMap.keySet().iterator(); iter
                .hasNext();) {
            String endIfBookmarkName = (String) iter.next();
            int index = getTransformerConfig().getBookmarkIndex(
                    endIfBookmarkName);
            String ifBookmarkName = getTransformerConfig().getBookmarkIfName(
                    index);
            RTFBookmark ifBookmark = (RTFBookmark) bookmarkIfMap
                    .get(ifBookmarkName);
            if (ifBookmark != null) {
                RTFBookmark endIfBookmark = (RTFBookmark) bookmarkEndIfMap
                        .get(endIfBookmarkName);
                endIfBookmark.replaceElement(getMacroEndIf());
                RTFBookmark endIfBookmarkBKMEND = (RTFBookmark) bookmarkBkmIfMap
                        .get(endIfBookmarkName);
                endIfBookmarkBKMEND.replaceElement("");
            }
        }
        // Replace element ELSE Bookmark end by #else
        for (Iterator iter = bookmarkElseMap.keySet().iterator(); iter
                .hasNext();) {
            String elseBookmarkName = (String) iter.next();
            int index = getTransformerConfig().getBookmarkIndex(
                    elseBookmarkName);
            String ifBookmarkName = getTransformerConfig().getBookmarkIfName(
                    index);
            RTFBookmark ifBookmark = (RTFBookmark) bookmarkIfMap
                    .get(ifBookmarkName);
            if (ifBookmark != null) {
                RTFBookmark elseBookmark = (RTFBookmark) bookmarkElseMap
                        .get(elseBookmarkName);
                elseBookmark.replaceElement(getMacroElse());
            }
        }
        return documentTransformed;

    }

    protected void transform(RTFElement parentElement, Vector elementList,
            Map contextFieldsMap) throws IOException {
        RTFPage lastPage = null;
        for (Iterator iter = elementList.iterator(); iter.hasNext();) {
            Object element = (Object) iter.next();
            int elementType = getType(element);
            switch (elementType) {
            case RTF_ELEMENT_STRINGBUFFER:
                StringBuffer sringBuffer = (StringBuffer) element;
                if (sringBuffer.length() == 2) {
                    // There id a BUG with velocity
                    // org.apache.velocity.exception.ParseErrorException:
                    // Encountered "<EOF>" at line...
                    // when there is \r\n (after a #end macro for instance)
                    // To resolve this problem, \n must be remove
                    int index1 = sringBuffer.indexOf("\n");
                    int index2 = sringBuffer.indexOf("\r");
                    if (index1 != -1 && index2 != -1) {
                        sringBuffer.deleteCharAt(index1);
                    }
                }

                break;

            case RTF_ELEMENT_BOOKMARK:
                // RTF element Bookmark is found.
                // ONLY BKMSTART Bookmark are used.
                RTFBookmark currentBookmark = (RTFBookmark) element;
                String bookMarkName = currentBookmark.getName().toUpperCase();
                if (isBookmarkStartLoop(bookMarkName)) {
                    // Bookmark is start loop
                    if (currentBookmark instanceof RTFStartBookmark) {
                        startBookmarksList.addElement(currentBookmark);
                        lastPage = null;
                    } else {
                        // Bookmark BMKMEND => must be replace by "" at the end
                        bookmarkBkmEndMap.put(bookMarkName, currentBookmark);
                    }
                } else {
                    if (isBookmarkEndLoop(bookMarkName)) {
                        // Bookmark is end loop
                        if (currentBookmark instanceof RTFStartBookmark) {
                            bookmarkEndMap.put(bookMarkName, currentBookmark);
                            pageBeforeEndBookmarkMap
                                    .put(bookMarkName, lastPage);
                            // Store last RTF Bookmark End Loop
                            lastEndLoopRTFBookmark = currentBookmark;
                        } else {
                            // Bookmark BMKMEND => must be replace by "" at the
                            // end
                            bookmarkBkmEndMap
                                    .put(bookMarkName, currentBookmark);
                        }
                        // Current bookmark is END_LOOP_i Remove START_LOOP_i of
                        // the
                        // startBookmarksNotEnded Map
                        Collection bookmarksNotEnded = startBookmarksNotEnded
                                .values();
                        for (Iterator iterator = bookmarksNotEnded.iterator(); iterator
                                .hasNext();) {
                            RTFStartBookmark startBookmarkNoEnded = (RTFStartBookmark) iterator
                                    .next();
                            String startBookmarkNameNoEnded = startBookmarkNoEnded
                                    .getName();
                            int index = getTransformerConfig()
                                    .getBookmarkIndex(startBookmarkNameNoEnded);
                            String endBookmarkName = getTransformerConfig()
                                    .getBookmarkEndLoopName(index);
                            if (endBookmarkName.equals(currentBookmark
                                    .getName())) {
                                bookmarksNotEnded.remove(startBookmarkNoEnded);
                                break;
                            }
                        }

                    } else {
                        if (isBookmarkIf(bookMarkName)) {
                            // Bookmark is if condition
                            if (currentBookmark instanceof RTFStartBookmark)
                                bookmarkIfMap
                                        .put(bookMarkName, currentBookmark);
                            else
                                // Bookmark BMKMEND => must be replace by "" at
                                // the end
                                bookmarkBkmIfMap.put(bookMarkName,
                                        currentBookmark);
                        } else {
                            if (isBookmarkEndIf(bookMarkName)) {
                                // Bookmark is endif condition
                                if (currentBookmark instanceof RTFStartBookmark)
                                    bookmarkEndIfMap.put(bookMarkName,
                                            currentBookmark);
                                else
                                    // Bookmark BMKMEND => must be replace by ""
                                    // at
                                    // the end
                                    bookmarkBkmIfMap.put(bookMarkName,
                                            currentBookmark);
                            }
                            else {
                                if (isBookmarkElse(bookMarkName)) {
                                    // Bookmark is else condition
                                    if (currentBookmark instanceof RTFStartBookmark)
                                        bookmarkElseMap.put(bookMarkName,
                                                currentBookmark);
                                    else
                                        // Bookmark BMKMEND => must be replace by ""
                                        // at
                                        // the end
                                        bookmarkBkmIfMap.put(bookMarkName,
                                                currentBookmark);
                                }
                            }
                        }
                    }
                }

                break;

            case RTF_ELEMENT_FIELD:
                RTFField field = (RTFField) element;
                String fieldName = field.getName();
                if (fieldName != null) {
                    int fieldType = field.getType();
                    RTFContextField rtfField = (RTFContextField) contextFieldsMap
                            .get(fieldName);
                    if (rtfField != null) {
                        // RTF field is into the context
                        String newFieldName = "";
                        String fieldNameWithListInfo = rtfField.getListInfo();
                        boolean fieldIsList = isList(rtfField);
                        boolean fieldIsImage = isImage(rtfField);
                        switch (fieldType) {
                        case RTFField.FIELDTYPE_HYPERLINK:
                            /*
                             * HYPERLINK field code RTF example : /* {\field
                             * {\*\fldinst {\insrsid9655237 HYPERLINK
                             * "mailto:$developers.Email" } {\insrsid9655237
                             * {\*\datafield
                             * 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b320000006d00610069006c0074006f003a00240064006500760065006c006f0070006500720073002e0045006d00610069006c000000 } } }
                             * {\fldrslt {\cs16\
                             * ul\cf2\insrsid9655237\charrsid9655237
                             * mailto:$developers.Email } } }
                             */
                            IRTFFieldTransform hyperlinkTransform = getRTFFieldTransform(
                                    fieldName, fieldType, fieldIsImage);
                            hyperlinkTransform.transform(field, fieldIsList,
                                    this);
                            break;
                        case RTFField.FIELDTYPE_MERGEFIELD:
                            /*
                             * MERGEFIELD code RTF example : {\field\flddirty
                             * {\*\fldinst
                             * {\b\cf17\lang1036\langfe2057\langnp1036\insrsid331776\charrsid5667574
                             * MERGEFIELD $personne.Name \\* MERGEFORMAT } }
                             * {\fldrslt
                             * {\b\cf17\lang1024\langfe1024\noproof\langnp1036\insrsid331776\charrsid5667574
                             * \'ab$personne.Name\'bb } } }
                             */
                            IRTFFieldTransform fieldTransform = getRTFFieldTransform(
                                    fieldName, fieldType, fieldIsImage);
                            fieldTransform.transform(field, fieldIsList, this);
                            break;
                        }

                        // Test if field is list
                        if (fieldIsList) {
                            boolean loopIsManagedWithBookmark = false;
                            // Mergefield is list, it must be placed :
                            // * between 2 Bookmark (START_LOOP and END_LOOP) or
                            // * inside Row
                            int bookmarkSize = startBookmarksList.size();
                            if (bookmarkSize > 0) {
                                // MergeField is found and Bookmark was started
                                // construct #foreach with name of field
                                RTFBookmark startBookmark = (RTFBookmark) startBookmarksList
                                        .elementAt(bookmarkSize - 1);
                                startBookmark.replaceElement(getForeach(
                                        fieldName, fieldNameWithListInfo));
                                startBookmark.setItemNameList(getItemListName(
                                        fieldName, fieldNameWithListInfo));
                                startBookmarksList.removeElement(startBookmark);
                                bookmarkStartMap.put(startBookmark.getName(),
                                        startBookmark);
                                startBookmarksNotEnded.put(startBookmark
                                        .getItemNameList(), startBookmark);
                                loopIsManagedWithBookmark = true;
                            } else {
                                // Tet si if exist A START_LOOP_i which is not
                                // ended and which have the name list
                                // waited
                                String itemListName = getItemListName(
                                        fieldName, fieldNameWithListInfo);
                                loopIsManagedWithBookmark = (startBookmarksNotEnded
                                        .get(itemListName) != null);
                            }

                            if (loopIsManagedWithBookmark) {

                                // Test if rtfParent is row => to indicate that
                                // row must not generate #foreach
                                if (getType(parentElement) == RTF_ELEMENT_ROW) {
                                    RTFRow row = (RTFRow) parentElement;
                                    if (rowMustNotGenerateForeachList == null) {
                                        rowMustNotGenerateForeachList = new ArrayList();
                                    }
                                    rowMustNotGenerateForeachList.add(row);
                                }
                            } else {
                                // Test if rtfParent is row
                                if (getType(parentElement) == RTF_ELEMENT_ROW) {
                                    RTFRow row = (RTFRow) parentElement;
                                    if (row.getFirstRTFString() == null) {
                                        if (rowMustNotGenerateForeachList == null
                                                || !rowMustNotGenerateForeachList
                                                        .contains(row)) {
                                            row.addFirstRTFString(getForeach(
                                                    fieldName,
                                                    fieldNameWithListInfo));
                                            row
                                                    .addLastRTFString(getMacroEndForEach()); // #end
                                        }
                                    }
                                }
                            }

                            // Add item_ after $
                            String fieldContent = field
                                    .getRTFContentOfSimpleElement();
                            if (fieldName.startsWith("$"))
                                fieldName = fieldName.substring(1);
                            newFieldName = getObjectValueList(fieldName,
                                    fieldNameWithListInfo, true);
                            if (newFieldName.startsWith("$"))
                                newFieldName = newFieldName.substring(1);

                            fieldContent = StringUtils.sub(fieldContent,
                                    fieldName, newFieldName);
                            field.replaceElement(fieldContent);
                        }
                    } else {
                        if (fieldType == RTFField.FIELDTYPE_REF) {
                            String condition = field.getCondition();
                            if (condition != null) {
                                // Condition is defined
                                // Field is REF (IF condition)
                                // Get Bookmark with name field
                                RTFBookmark ifBookmark = (RTFBookmark) bookmarkIfMap
                                        .get(fieldName);
                                if (ifBookmark != null) {
                                    // Bookmark IF exists. Replace this RTF code
                                    // with the condition
                                    // comming from field
                                    condition = getMacroIf(condition);
                                    ifBookmark.replaceElement(condition);
                                    // Remove content of the field
                                    field.replaceElement("");
                                    // Replace BKMEND by ""
                                    RTFBookmark ifBookmarkBKMEND = (RTFBookmark) bookmarkBkmIfMap
                                            .get(fieldName);
                                    ifBookmarkBKMEND.replaceElement("");                                    
                                }
                            }
                        }
                    }
                }
                break;

            case RTF_ELEMENT_ROW:
                RTFRow row = (RTFRow) element;
                transform(row, row.getElementList(), contextFieldsMap);
                row.flush();
                break;

            case RTF_ELEMENT_PAGE:
                lastPage = (RTFPage) element;
                break;
            case RTF_ELEMENT_USERPROPERTY:
                RTFUserProperty userProperty = (RTFUserProperty) element;
                String userPropertyName = userProperty.getName();
                if (userPropertyName != null
                        && userPropertyName.startsWith("$")) {
                    RTFContextField rtfUserProperty = (RTFContextField) contextFieldsMap
                            .get(userPropertyName);
                    boolean userPropertyIsList = isList(rtfUserProperty);
                    if (userPropertyIsList) {
                        String userPropertyNameWithListInfo = rtfUserProperty
                                .getListInfo();
                        // User property is list
                        // Add item_ after $
                        // Manage \propname
                        String userPropertyContent = userProperty
                                .getRTFContentOfSimpleElement();
                        if (userPropertyName.startsWith("$"))
                            userPropertyName = userPropertyName.substring(1);
                        String newUserpropertyName = getObjectValueList(
                                userPropertyName, userPropertyNameWithListInfo,
                                true);
                        if (newUserpropertyName.startsWith("$"))
                            newUserpropertyName = newUserpropertyName
                                    .substring(1);

                        userPropertyContent = userPropertyContent.replaceAll(
                                userPropertyName, newUserpropertyName);
                        // Add # foreach and send with #end
                        userPropertyContent = getForeach(userPropertyName,
                                userPropertyNameWithListInfo)
                                + userPropertyContent + getMacroEndForEach();// "#end";

                        // Manage \staticval
                        String userPropertyValue = userProperty.getValue();
                        if (userPropertyValue.startsWith("$"))
                            userPropertyValue = userPropertyValue.substring(1);
                        String newUserPropertyValue = getObjectValueList(
                                userPropertyValue,
                                userPropertyNameWithListInfo, true);
                        if (newUserPropertyValue.startsWith("$"))
                            newUserPropertyValue = newUserPropertyValue
                                    .substring(1);
                        userPropertyContent = userPropertyContent.replaceAll(
                                userPropertyValue, newUserPropertyValue);
                        userProperty.replaceElement(userPropertyContent);
                    }
                }
                String userPropertyContent = userProperty
                        .getRTFContentOfSimpleElement();
                userProperty.replaceElement(userPropertyContent);

                break;

            /*
             * case RTF_ELEMENT_ANNOTATION: // TODO : manage start/end loop and
             * condition with Comments RTFAnnotation annotation =
             * (RTFAnnotation) element;
             * 
             * break;
             */
            }
        }
    }

    /**
     * Return type of object <element>
     * 
     * @param element
     * @return
     * @throws IOException
     */
    private int getType(Object element) throws IOException {
        if (element instanceof StringBuffer)
            return RTF_ELEMENT_STRINGBUFFER;
        if (element instanceof RTFRow)
            return RTF_ELEMENT_ROW;
        if (element instanceof RTFField)
            return RTF_ELEMENT_FIELD;
        if (element instanceof RTFBookmark)
            return RTF_ELEMENT_BOOKMARK;
        if (element instanceof RTFDocument)
            return RTF_ELEMENT_DOCUMENT;
        if (element instanceof RTFPage)
            return RTF_ELEMENT_PAGE;
        if (element instanceof RTFUserProperty)
            return RTF_ELEMENT_USERPROPERTY;
        if (element instanceof RTFAnnotation)
            return RTF_ELEMENT_ANNOTATION;
        throw new IOException(
                "Error getType into RTFVelocityTransformer : current element must be StringBuffer, RTFRow, RTFBookmark, RTFField, RTFPage or RTFUserProperty. ");
    }

    private boolean isList(RTFContextField rtfField) {
        if (rtfField != null) {
            return rtfField.isList();
        }
        return false;
    }

    private boolean isImage(RTFContextField rtfField) {
        if (rtfField != null) {
            return rtfField.isImage();
        }
        return false;
    }

    // ///////////// PATTERN
    public boolean isBookmarkStartLoop(String bookmarkName) {
        return getTransformerConfig().isBookmarkStartLoop(bookmarkName);
    }

    public boolean isBookmarkEndLoop(String bookmarkName) {
        return getTransformerConfig().isBookmarkEndLoop(bookmarkName);
    }

    public boolean isBookmarkIf(String bookmarkName) {
        return getTransformerConfig().isBookmarkIf(bookmarkName);
    }

    public boolean isBookmarkEndIf(String bookmarkName) {
        return getTransformerConfig().isBookmarkEndIf(bookmarkName);
    }

    public boolean isBookmarkElse(String bookmarkName) {
        return getTransformerConfig().isBookmarkElse(bookmarkName);
    }

    private IRTFFieldTransform getRTFFieldTransform(String fieldName,
            int fieldType, boolean isFieldImage) {
        IRTFFieldTransform t = null;
        if (rtfTransformMap != null) {
            t = (IRTFFieldTransform) rtfTransformMap.get(fieldName);
        }
        if (t == null) {
            switch (fieldType) {
            case RTFField.FIELDTYPE_MERGEFIELD:
                if (isFieldImage)
                    t = new DefaultRTFSimpleTransform();
                else
                    t = new DefaultRTFMergeFieldTransform();
                break;
            case RTFField.FIELDTYPE_HYPERLINK:
                t = new DefaultRTFHyperlinkTransform();
                break;
            }
        }
        return t;
    }

    // /////////Velocity/Freemarker,... Context
    public RTFContextFields getContextFields(IContext context, boolean hasCircularReferences) {
        RTFContextFieldsReader loader = new RTFContextFieldsReader();
        loader.readContext(context, hasCircularReferences);
        return loader.getContextFields();
    }

    public void setGroupByPerPageBreak(int groupByPerPageBreak) {
        this.groupByPerPageBreak = groupByPerPageBreak;
    }

    protected String getItemListName(String fieldName,
            String fieldNameWithListInfo) {
        String objectName = fieldName;
        String objectNameList = fieldName;

        if (fieldNameWithListInfo != null && fieldNameWithListInfo.length() > 0) {
            if (fieldNameWithListInfo.startsWith("$"))
                fieldNameWithListInfo = fieldNameWithListInfo.substring(1);
            int indexLastEndedArray = fieldNameWithListInfo.lastIndexOf("]");
            if (indexLastEndedArray > 0) {
                // Test if fieldName has several list (
                if (fieldNameWithListInfo.indexOf("]") != indexLastEndedArray) {
                    // There is several list
                    // eg : $workspace.[Developers].[Roles].Name => must be
                    // transformed into
                    // $item_workspace_Developers.Roles
                    String s = fieldNameWithListInfo.substring(0,
                            indexLastEndedArray + 1);
                    String[] splitUntilLastArray = StringUtils.split(s, ".");
                    if (splitUntilLastArray != null
                            && splitUntilLastArray.length > 0) {
                        // Loop for splitUntilLastArray
                        String currentObjectName = "";
                        String velocityNameList = "";
                        int nbList = 0;
                        boolean afterSecondList = false;
                        for (int i = splitUntilLastArray.length - 1; i >= 0; i--) {
                            currentObjectName = splitUntilLastArray[i];
                            if (currentObjectName.indexOf("[") != -1) {
                                // current object is list
                                currentObjectName = currentObjectName
                                        .replaceAll("\\[", "");
                                currentObjectName = currentObjectName
                                        .replaceAll("\\]", "");
                                nbList++;
                            }
                            if (i == 0) {
                                currentObjectName = "item_" + currentObjectName;
                            }

                            if (afterSecondList) {
                                velocityNameList = currentObjectName + "_"
                                        + velocityNameList;
                            } else {
                                if (velocityNameList.length() > 0)
                                    velocityNameList = currentObjectName
                                            + "."
                                            + formatSubFieldName(velocityNameList);
                                else
                                    velocityNameList = currentObjectName;
                            }

                            if (nbList > 1) {
                                afterSecondList = true;
                            }
                        }

                        return velocityNameList;
                    }
                } else {
                    // eg : $workspace.[Developers].Email => must be transformed
                    // to : $workspace.Developers
                    String s = fieldNameWithListInfo.substring(0,
                            indexLastEndedArray);
                    s = s.replaceAll("\\[", "");
                    return s;
                }
            }
        }

        // OLD Version (if listInfo is not filled)
        String[] splitFieldName = StringUtils.split(fieldName, ".");
        int characterSize = splitFieldName.length - 1;
        if (characterSize > 0) {
            objectName = splitFieldName[characterSize - 1];
            objectNameList = objectName;
            if (characterSize > 1) {
                objectNameList = "item_" + splitFieldName[characterSize - 2]
                        + "." + splitFieldName[characterSize - 1];
            }
        }
        return objectNameList;
    }

    /**
     * Return string for end for each
     * 
     * @return
     */
    protected abstract String getMacroEndForEach();

    protected abstract String getForeach(String fieldName,
            String fieldNameWithListInfo);

    protected abstract String getObjectValueList(String fieldName,
            String fieldNameWithListInfo, boolean withGetter);

    protected abstract String getObjectValueList(String fieldName,
            boolean withGetter);

    protected String formatSubFieldName(String fieldName) {
        return fieldName;
    }

    public String getTransformedFieldName(boolean fieldIsList, String fieldName) {
        return fieldName;
    }

    protected abstract String getMacroPageBreak(String itemNameList,
            String rtfPageBreakContent);

    protected abstract String getMacroPageBreak(String itemNameList,
            String rtfPageBreakContent, int groupByPerPageBreak);

    protected abstract String getMacroIf(String condition);

    protected abstract String getMacroEndIf();

    protected abstract String getMacroElse();

    public boolean isCircularReferences() {
        return circularReferences;
    }

    public void setCircularReferences(boolean circularReferences) {
        this.circularReferences = circularReferences;
    }

    public int getGroupByPerPageBreak() {
        return groupByPerPageBreak;
    }
    
}
