/* FichothequeLib_Xml - Copyright (c) 2013-2025 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.fichotheque.xml.defs;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.fichotheque.FichothequeConstants;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.extraction.ExtractionConstants;
import net.fichotheque.extraction.FilterParameters;
import net.fichotheque.extraction.def.AddendaExtractDef;
import net.fichotheque.extraction.def.AlbumExtractDef;
import net.fichotheque.extraction.def.CorpusExtractDef;
import net.fichotheque.extraction.def.DocumentFilter;
import net.fichotheque.extraction.def.ExtractionDef;
import net.fichotheque.extraction.def.FicheFilter;
import net.fichotheque.extraction.def.GroupClause;
import net.fichotheque.extraction.def.GroupParams;
import net.fichotheque.extraction.def.IllustrationFilter;
import net.fichotheque.extraction.def.MotcleFilter;
import net.fichotheque.extraction.def.PackClause;
import net.fichotheque.extraction.def.TagNameInfo;
import net.fichotheque.extraction.def.ThesaurusExtractDef;
import net.fichotheque.extraction.def.TitleClause;
import net.fichotheque.extraction.filterunit.AddendaExtractFilterUnit;
import net.fichotheque.extraction.filterunit.AlbumExtractFilterUnit;
import net.fichotheque.extraction.filterunit.ChronoFilterUnit;
import net.fichotheque.extraction.filterunit.CorpusExtractFilterUnit;
import net.fichotheque.extraction.filterunit.DataFilterUnit;
import net.fichotheque.extraction.filterunit.DefaultIncludeUnit;
import net.fichotheque.extraction.filterunit.FicheParentageFilterUnit;
import net.fichotheque.extraction.filterunit.FieldKeyFilterUnit;
import net.fichotheque.extraction.filterunit.FieldNamePrefixFilterUnit;
import net.fichotheque.extraction.filterunit.FilterUnit;
import net.fichotheque.extraction.filterunit.LotFilterUnit;
import net.fichotheque.extraction.filterunit.MasterMotcleFilterUnit;
import net.fichotheque.extraction.filterunit.PhraseFilterUnit;
import net.fichotheque.extraction.filterunit.ThesaurusExtractFilterUnit;
import net.fichotheque.selection.DocumentCondition;
import net.fichotheque.selection.FicheCondition;
import net.fichotheque.selection.IllustrationCondition;
import net.fichotheque.selection.MotcleCondition;
import net.fichotheque.selection.SubsetCondition;
import net.fichotheque.utils.ExtractionUtils;
import net.fichotheque.utils.FichothequeXMLUtils;
import net.mapeadores.util.exceptions.SwitchException;
import net.mapeadores.util.text.StringUtils;
import net.mapeadores.util.xml.AppendableXMLPart;
import net.mapeadores.util.xml.AppendableXMLWriter;
import net.fichotheque.extraction.filterunit.HeadFilterUnit;
import net.fichotheque.extraction.filterunit.BodyFilterUnit;


/**
 *
 * @author Vincent Calame
 */
public class ExtractionDefXMLPart extends AppendableXMLPart {

    private final boolean compactStyle;

    public ExtractionDefXMLPart(AppendableXMLWriter xmlWriter, boolean compactStyle) {
        super(xmlWriter);
        this.compactStyle = compactStyle;
    }

    public ExtractionDefXMLPart addExtractionDef(ExtractionDef extractionDef) throws IOException {
        startOpenTag("extraction");
        addTagName(extractionDef.getTagNameInfo());
        endOpenTag();
        addDynamicCorpusExtract(extractionDef.getDynamicCorpusExtractDef());
        addDynamicThesaurusExtract(extractionDef.getDynamicThesaurusExtractDef());
        List<CorpusExtractDef> corpusExtractDefList = extractionDef.getStaticCorpusExtractDefList();
        List<ThesaurusExtractDef> thesaurusExtractDefList = extractionDef.getStaticThesaurusExtractDefList();
        if ((!corpusExtractDefList.isEmpty()) || (!thesaurusExtractDefList.isEmpty())) {
            startOpenTag("static");
            addTagName(extractionDef.getStaticTagNameInfo());
            endOpenTag();
            for (CorpusExtractDef corpusExtractDef : corpusExtractDefList) {
                addCorpusExtract(corpusExtractDef, ExtractionUtils.EMPTY_FILTERPARAMETERS);
            }
            for (ThesaurusExtractDef thesaurusExtractDef : thesaurusExtractDefList) {
                addThesaurusExtract(thesaurusExtractDef, ExtractionUtils.EMPTY_FILTERPARAMETERS);
            }
            closeTag("static");
        }
        closeTag("extraction");
        return this;
    }

    private void addDynamicCorpusExtract(CorpusExtractDef corpusExtractDef) throws IOException {
        if (corpusExtractDef == null) {
            return;
        }
        if (canIgnore(corpusExtractDef)) {
            addFicheFilter(corpusExtractDef.getFicheFilter());
        } else {
            addCorpusExtract(corpusExtractDef, ExtractionUtils.EMPTY_FILTERPARAMETERS);
        }
    }

    private void addCorpusExtract(CorpusExtractFilterUnit filterUnit) throws IOException {
        addCorpusExtract(filterUnit.getCorpusExtractDef(), filterUnit);
    }

    private void addCorpusExtract(CorpusExtractDef corpusExtractDef, FilterParameters filterParameters) throws IOException {
        String tagName = "fiches";
        List<FicheCondition.Entry> entryList = corpusExtractDef.getConditionEntryList();
        String onlyCorpusAttribute = getOnlyCorpusAttribute(entryList);
        startOpenTag(tagName);
        addAttribute("name", corpusExtractDef.getName());
        if (onlyCorpusAttribute != null) {
            addAttribute("corpus", onlyCorpusAttribute);
        }
        addTagName(corpusExtractDef.getTagNameInfo());
        if (corpusExtractDef.isMaster()) {
            addAttribute("source", "master");
        }
        if (corpusExtractDef.isDescendantAxis()) {
            addAttribute("axis", "descendant");
        }
        endOpenTag();
        if (onlyCorpusAttribute == null) {
            for (FicheCondition.Entry entry : corpusExtractDef.getConditionEntryList()) {
                openTag("fiche-query");
                FichothequeXMLUtils.writeFicheQuery(this, entry, true);
                closeTag("fiche-query");
            }
        }
        addGroupClause(corpusExtractDef.getGroupClause());
        addPackClause(corpusExtractDef.getPackClause());
        addTitleClause(corpusExtractDef.getTitleClause());
        addParameters(filterParameters);
        addFicheFilter(corpusExtractDef.getFicheFilter());
        closeTag(tagName);
    }

    private void addFicheFilter(FicheFilter ficheFilter) throws IOException {
        startOpenTag("fiche");
        addTagName(ficheFilter.getTagNameInfo());
        List<FilterUnit> filterUnitList = ficheFilter.getFilterUnitList();
        if (filterUnitList.isEmpty()) {
            closeEmptyTag();
        } else {
            endOpenTag();
            for (FilterUnit filterUnit : filterUnitList) {
                addFilterUnit(filterUnit);
            }
            closeTag("fiche");
        }
    }

    private void addDynamicThesaurusExtract(ThesaurusExtractDef thesaurusExtractDef) throws IOException {
        if (thesaurusExtractDef == null) {
            return;
        }
        addThesaurusExtract(thesaurusExtractDef, ExtractionUtils.EMPTY_FILTERPARAMETERS);
    }

    private void addThesaurusExtract(ThesaurusExtractFilterUnit filterUnit) throws IOException {
        addThesaurusExtract(filterUnit.getThesaurusExtractDef(), filterUnit);
    }

    private void addThesaurusExtract(ThesaurusExtractDef thesaurusExtractDef, FilterParameters filterParameters) throws IOException {
        String tagName = "motcles";
        startOpenTag(tagName);
        addAttribute("name", thesaurusExtractDef.getName());
        addTagName(thesaurusExtractDef.getTagNameInfo());
        if (thesaurusExtractDef.isMaster()) {
            addAttribute("source", "master");
        }
        endOpenTag();
        for (MotcleCondition.Entry entry : thesaurusExtractDef.getConditionEntryList()) {
            openTag("motcle-query");
            FichothequeXMLUtils.writeMotcleQuery(this, entry, true);
            closeTag("motcle-query");
        }
        addParameters(filterParameters);
        addMotcleFilter(thesaurusExtractDef.getMotcleFilter());
        closeTag(tagName);
    }

    private void addAddendaExtract(AddendaExtractFilterUnit filterUnit) throws IOException {
        AddendaExtractDef addendaExtractDef = filterUnit.getAddendaExtractDef();
        String tagName = "documents";
        startOpenTag(tagName);
        addAttribute("name", addendaExtractDef.getName());
        addTagName(addendaExtractDef.getTagNameInfo());
        endOpenTag();
        for (DocumentCondition.Entry entry : addendaExtractDef.getConditionEntryList()) {
            openTag("document-query");
            FichothequeXMLUtils.writeDocumentQuery(this, entry.getDocumentQuery(), entry.getJunctionCondition());
            closeTag("document-query");
        }
        ExtractionDefXMLPart.this.addParameters(filterUnit);
        DocumentFilter documentFilter = addendaExtractDef.getDocumentFilter();
        startOpenTag("document");
        addTagName(documentFilter.getTagNameInfo());
        closeEmptyTag();
        closeTag(tagName);
    }

    private void addAlbumExtract(AlbumExtractFilterUnit filterUnit) throws IOException {
        AlbumExtractDef albumExtractDef = filterUnit.getAlbumExtractDef();
        String tagName = "illustrations";
        startOpenTag(tagName);
        addAttribute("name", albumExtractDef.getName());
        addTagName(albumExtractDef.getTagNameInfo());
        endOpenTag();
        for (IllustrationCondition.Entry entry : albumExtractDef.getConditionEntryList()) {
            openTag("illustration-query");
            FichothequeXMLUtils.writeIllustrationQuery(this, entry.getIllustrationQuery(), entry.getJunctionCondition());
            closeTag("illustration-query");
        }
        ExtractionDefXMLPart.this.addParameters(filterUnit);
        IllustrationFilter illustrationFilter = albumExtractDef.getIllustrationFilter();
        startOpenTag("illustration");
        addTagName(illustrationFilter.getTagNameInfo());
        closeEmptyTag();
        closeTag(tagName);
    }

    private void addGroupClause(GroupClause groupClause) throws IOException {
        if (groupClause == null) {
            return;
        }
        startOpenTag("group");
        addAttribute("type", groupClause.getGroupType());
        GroupParams groupParams = groupClause.getGroupParams();
        if (groupParams != null) {
            addAttribute("params", groupParams.toString());
        }
        addAttribute("sort", groupClause.getSortOrder());
        addTagName(groupClause.getTagNameInfo());
        GroupClause subGroupClause = groupClause.getSubGroupClause();
        if (subGroupClause != null) {
            endOpenTag();
            addGroupClause(subGroupClause);
            closeTag("group");
        } else {
            closeEmptyTag();
        }
    }

    private void addPackClause(PackClause packClause) throws IOException {
        if (packClause == null) {
            return;
        }
        startOpenTag("pack");
        addAttribute("size", String.valueOf(packClause.getPackSize()));
        int modulo = packClause.getModulo();
        if (modulo > 0) {
            addAttribute("size", String.valueOf(modulo));
        }
        addTagName(packClause.getTagNameInfo());
        closeEmptyTag();
    }

    private void addTitleClause(TitleClause titleClause) throws IOException {
        if (titleClause == null) {
            return;
        }
        if (titleClause.withTitle()) {
            addEmptyElement("title");
        }
    }

    private void addFilterUnit(FilterUnit filterUnit) throws IOException {
        if (filterUnit instanceof HeadFilterUnit) {
            addEmptyElement("entete");
        } else if (filterUnit instanceof BodyFilterUnit) {
            addEmptyElement("corpsdefiche");
        } else if (filterUnit instanceof FieldKeyFilterUnit) {
            addFieldKey((FieldKeyFilterUnit) filterUnit);
        } else if (filterUnit instanceof FieldNamePrefixFilterUnit) {
            addFieldNamePrefix((FieldNamePrefixFilterUnit) filterUnit);
        } else if (filterUnit instanceof LotFilterUnit) {
            addLot((LotFilterUnit) filterUnit);
        } else if (filterUnit instanceof ChronoFilterUnit) {
            if (compactStyle) {
                appendCompact("chrono");
            } else {
                addEmptyElement("chrono");
            }
        } else if (filterUnit instanceof PhraseFilterUnit) {
            if (compactStyle) {
                appendCompact("phrase_");
                addText(((PhraseFilterUnit) filterUnit).getName());
            } else {
                startOpenTag("phrase");
                addAttribute("name", ((PhraseFilterUnit) filterUnit).getName());
                closeEmptyTag();
            }
        } else if (filterUnit instanceof AddendaExtractFilterUnit) {
            if (!insertDefaultIncludeUnit(filterUnit)) {
                addAddendaExtract((AddendaExtractFilterUnit) filterUnit);
            }
        } else if (filterUnit instanceof CorpusExtractFilterUnit) {
            if (!insertDefaultIncludeUnit(filterUnit)) {
                addCorpusExtract((CorpusExtractFilterUnit) filterUnit);
            }
        } else if (filterUnit instanceof ThesaurusExtractFilterUnit) {
            if (!insertDefaultIncludeUnit(filterUnit)) {
                addThesaurusExtract((ThesaurusExtractFilterUnit) filterUnit);
            }
        } else if (filterUnit instanceof AlbumExtractFilterUnit) {
            if (!insertDefaultIncludeUnit(filterUnit)) {
                addAlbumExtract((AlbumExtractFilterUnit) filterUnit);
            }
        } else if (filterUnit instanceof FicheParentageFilterUnit) {
            addFicheParentage((FicheParentageFilterUnit) filterUnit);
        } else if (filterUnit instanceof MasterMotcleFilterUnit) {
            addMasterMotcle((MasterMotcleFilterUnit) filterUnit);
        } else if (filterUnit instanceof DataFilterUnit) {
            addData((DataFilterUnit) filterUnit);
        }
    }

    private boolean insertDefaultIncludeUnit(FilterUnit filterUnit) throws IOException {
        if (!compactStyle) {
            return false;
        }
        if (!(filterUnit instanceof DefaultIncludeUnit)) {
            return false;
        }
        appendIndent();
        append(((DefaultIncludeUnit) filterUnit).getExtendedIncludeKey().getKeyString());
        return true;
    }

    private void addTagName(TagNameInfo tagNameInfo) throws IOException {
        switch (tagNameInfo.getType()) {
            case TagNameInfo.CUSTOM_TYPE:
                addAttribute("tag-name", tagNameInfo.getCustomTagName());
                break;
            case TagNameInfo.NULL_TYPE:
                addAttribute("tag-name", "_null");
                break;
        }
    }

    private void addFicheParentage(FicheParentageFilterUnit filterUnit) throws IOException {
        FicheFilter ficheFilter = filterUnit.getFicheFilter();
        startOpenTag("fiche");
        List<SubsetKey> subsetKeyList = filterUnit.getSubsetKeyList();
        if (!subsetKeyList.isEmpty()) {
            StringBuilder buf = new StringBuilder();
            for (SubsetKey subsetKey : subsetKeyList) {
                if (buf.length() > 0) {
                    buf.append(',');
                }
                buf.append(subsetKey.getSubsetName());
            }
            addAttribute("corpus", buf.toString());
        }
        addTagName(ficheFilter.getTagNameInfo());
        List<FilterUnit> filterUnitList = ficheFilter.getFilterUnitList();
        if ((filterUnitList.isEmpty()) && (filterUnit.isEmpty())) {
            closeEmptyTag();
        } else {
            endOpenTag();
            ExtractionDefXMLPart.this.addParameters(filterUnit);
            for (FilterUnit childFilterUnit : filterUnitList) {
                addFilterUnit(childFilterUnit);
            }
            closeTag("fiche");
        }
    }

    private void addMasterMotcle(MasterMotcleFilterUnit filterUnit) throws IOException {
        addMotcleFilter(filterUnit.getMotcleFilter(), filterUnit);
    }

    private void addMotcleFilter(MotcleFilter motcleFilter) throws IOException {
        addMotcleFilter(motcleFilter, ExtractionUtils.EMPTY_FILTERPARAMETERS);
    }

    private void addMotcleFilter(MotcleFilter motcleFilter, FilterParameters filterParameters) throws IOException {
        String tagName = getTagName(motcleFilter);
        startOpenTag(tagName);
        if (isRecursive(motcleFilter)) {
            addAttribute("recursive", "1");
        }
        addTagName(motcleFilter.getTagNameInfo());
        if ((motcleFilter.isNoneFiltering()) && (filterParameters.isEmpty())) {
            closeEmptyTag();
            return;
        }
        endOpenTag();
        addParameters(filterParameters);
        if (motcleFilter.withIcon()) {
            addEmptyElement("icon");
        }
        if (motcleFilter.withLabels()) {
            addEmptyElement("labels");
        }
        if (motcleFilter.withLevel()) {
            addEmptyElement("level");
        }
        if (motcleFilter.withFicheStylePhrase()) {
            startOpenTag("phrase");
            addAttribute("name", FichothequeConstants.FICHESTYLE_PHRASE);
            closeEmptyTag();
        }
        MotcleFilter parentFilter = motcleFilter.getParentFilter();
        if (parentFilter != null) {
            if (!parentFilter.equals(motcleFilter)) {
                addMotcleFilter(parentFilter);
            }
        }
        MotcleFilter nextFilter = motcleFilter.getNextFilter();
        if (nextFilter != null) {
            addMotcleFilter(nextFilter);
        }
        MotcleFilter previousFilter = motcleFilter.getPreviousFilter();
        if (previousFilter != null) {
            addMotcleFilter(previousFilter);
        }
        MotcleFilter childrenFilter = motcleFilter.getChildrenFilter();
        if (childrenFilter != null) {
            if (!childrenFilter.equals(motcleFilter)) {
                addMotcleFilter(childrenFilter);
            }
        }
        for (FilterUnit filterUnit : motcleFilter.getFilterUnitList()) {
            addFilterUnit(filterUnit);
        }
        closeTag(tagName);
    }

    private boolean isRecursive(MotcleFilter motcleFilter) {
        boolean isRecursive = false;
        switch (motcleFilter.getType()) {
            case MotcleFilter.DEFAULT_TYPE: {
                MotcleFilter childrenFilter = motcleFilter.getChildrenFilter();
                if (childrenFilter != null) {
                    if (!childrenFilter.equals(motcleFilter)) {
                        isRecursive = true;
                    }
                }
                break;
            }
            case MotcleFilter.PARENT_TYPE: {
                MotcleFilter parentFilter = motcleFilter.getParentFilter();
                if (parentFilter != null) {
                    if (!parentFilter.equals(motcleFilter)) {
                        isRecursive = true;
                    }
                }
                break;
            }
        }
        return isRecursive;
    }

    private String getTagName(MotcleFilter motcleFilter) {
        switch (motcleFilter.getType()) {
            case MotcleFilter.NEXT_TYPE:
                return "next";
            case MotcleFilter.PARENT_TYPE:
                return "parent";
            case MotcleFilter.PREVIOUS_TYPE:
                return "previous";
            default:
                return "motcle";
        }
    }

    private void addData(DataFilterUnit dataFilterUnit) throws IOException {
        if ((compactStyle) && (dataFilterUnit.isEmpty())) {
            appendCompact("data_" + dataFilterUnit.getName());
            return;
        }
        startOpenTag("data");
        addAttribute("name", dataFilterUnit.getName());
        if (dataFilterUnit.isEmpty()) {
            closeEmptyTag();
        } else {
            endOpenTag();
            ExtractionDefXMLPart.this.addParameters(dataFilterUnit);
            closeTag("data");
        }
    }

    private void addFieldKey(FieldKeyFilterUnit filterUnit) throws IOException {
        FieldKey fieldKey = filterUnit.getFieldKey();
        if ((compactStyle) && (filterUnit.isEmpty())) {
            appendCompact(fieldKey.getKeyString());
            return;
        }
        switch (fieldKey.getCategory()) {
            case FieldKey.PROP_CATEGORY:
                startOpenTag(FieldKey.PROP_CATEGORY_STRING);
                addAttribute("name", fieldKey.getFieldName());
                break;
            case FieldKey.INFO_CATEGORY:
                startOpenTag(FieldKey.INFO_CATEGORY_STRING);
                addAttribute("name", fieldKey.getFieldName());
                break;
            case FieldKey.SECTION_CATEGORY:
                startOpenTag(FieldKey.SECTION_CATEGORY_STRING);
                addAttribute("name", fieldKey.getFieldName());
                break;
            case FieldKey.SPECIAL_CATEGORY:
                startOpenTag(fieldKey.getKeyString());
                break;
            default:
                throw new SwitchException("Unknown category = " + fieldKey.getCategory());
        }
        if (filterUnit.isEmpty()) {
            closeEmptyTag();
        } else {
            endOpenTag();
            ExtractionDefXMLPart.this.addParameters(filterUnit);
            switch (fieldKey.getCategory()) {
                case FieldKey.PROP_CATEGORY:
                    closeTag(FieldKey.PROP_CATEGORY_STRING);
                    break;
                case FieldKey.INFO_CATEGORY:
                    closeTag(FieldKey.INFO_CATEGORY_STRING);
                    break;
                case FieldKey.SECTION_CATEGORY:
                    closeTag(FieldKey.SECTION_CATEGORY_STRING);
                    break;
                case FieldKey.SPECIAL_CATEGORY:
                    closeTag(fieldKey.getKeyString());
                    break;
                default:
                    throw new SwitchException("Unknown category = " + fieldKey.getCategory());
            }
        }
    }

    private void addFieldNamePrefix(FieldNamePrefixFilterUnit filterUnit) throws IOException {
        short category = filterUnit.getCategory();
        String tagName = FieldKey.categoryToString(category);
        startOpenTag(tagName);
        addAttribute("name", filterUnit.getPrefix() + "*");
        if (filterUnit.isEmpty()) {
            closeEmptyTag();
        } else {
            endOpenTag();
            ExtractionDefXMLPart.this.addParameters(filterUnit);
            closeTag(tagName);
        }
    }

    private void addLot(LotFilterUnit lotFilterUnit) throws IOException {
        startOpenTag("lot");
        addTagName(lotFilterUnit.getTagNameInfo());
        endOpenTag();
        for (FilterUnit filterUnit : lotFilterUnit.getFilterUnitList()) {
            addFilterUnit(filterUnit);
        }
        closeTag("lot");
    }

    private void appendCompact(String text) throws IOException {
        appendIndent();
        append(text);
    }

    private boolean canIgnore(CorpusExtractDef corpusExtractDef) {
        if (corpusExtractDef.getTagNameInfo().getType() != TagNameInfo.NULL_TYPE) {
            return false;
        }
        if (corpusExtractDef.getName() != null) {
            return false;
        }
        if (corpusExtractDef.hasClauseObjects()) {
            return false;
        }
        if (corpusExtractDef.hasBooleanParameters()) {
            return false;
        }
        if (!corpusExtractDef.getConditionEntryList().isEmpty()) {
            return false;
        }
        return true;
    }

    private String getOnlyCorpusAttribute(List<FicheCondition.Entry> entryList) {
        if (entryList.size() != 1) {
            return null;
        }
        FicheCondition.Entry entry = entryList.get(0);
        if (entry.getJunctionCondition() != null) {
            return null;
        }
        if (entry.includeSatellites()) {
            return null;
        }
        if (!entry.getFicheQuery().isOnlyCorpusCondition()) {
            return null;
        }
        SubsetCondition corpusCondition = entry.getFicheQuery().getCorpusCondition();
        if ((corpusCondition.isWithCurrent()) || (corpusCondition.isExclude())) {
            return null;
        }
        StringBuilder buf = new StringBuilder();
        for (SubsetKey corpusKey : corpusCondition.getSubsetKeySet()) {
            if (buf.length() > 0) {
                buf.append(",");
            }
            buf.append(corpusKey.getSubsetName());
        }
        return buf.toString();
    }

    private void addParameters(FilterParameters filterParameters) throws IOException {
        Map<String, String> cellParamMap = new HashMap<String, String>();
        for (String name : filterParameters.getParameterNameSet()) {
            if (name.startsWith(ExtractionConstants.CELL_PARAMPREFIX)) {
                String cellSubparam = name.substring(ExtractionConstants.CELL_PARAMPREFIX.length());
                if (StringUtils.isTechnicalName(cellSubparam, false)) {
                    cellParamMap.put(cellSubparam, filterParameters.getFirstValue(name));
                    continue;
                }
            }
            switch (name) {
                case ExtractionConstants.HIDE_PARAM:
                    addSimpleElement("hide", filterParameters.getFirstValue(name));
                    break;
                case ExtractionConstants.GROUPS_PARAM:
                    for (String value : filterParameters.getParameter(name)) {
                        addSimpleElement("groups", value);
                    }
                    break;
                default:
                    for (String value : filterParameters.getParameter(name)) {
                        startOpenTag("param");
                        addAttribute("name", name);
                        endOpenTag();
                        addText(value);
                        closeTag("param", false);
                    }
            }
        }
        if (!cellParamMap.isEmpty()) {
            startOpenTag("cell");
            for (Map.Entry<String, String> entry : cellParamMap.entrySet()) {
                addAttribute(entry.getKey(), entry.getValue());
            }
            closeEmptyTag();
        }
    }

    public static ExtractionDefXMLPart init(AppendableXMLWriter xmlWriter, boolean compactStyle) {
        return new ExtractionDefXMLPart(xmlWriter, compactStyle);
    }

}
