/* FichothequeLib_Tools - Copyright (c) 2006-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.tools.extraction.dom;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import net.fichotheque.Fichotheque;
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.ExtractionDef;
import net.fichotheque.extraction.def.FicheFilter;
import net.fichotheque.extraction.def.MotcleFilter;
import net.fichotheque.extraction.def.TagNameInfo;
import net.fichotheque.extraction.def.ThesaurusExtractDef;
import net.fichotheque.extraction.filterunit.FilterUnit;
import net.fichotheque.include.ExtendedIncludeKey;
import net.fichotheque.selection.DocumentCondition;
import net.fichotheque.selection.DocumentQuery;
import net.fichotheque.selection.FicheCondition;
import net.fichotheque.selection.FicheQuery;
import net.fichotheque.selection.IllustrationCondition;
import net.fichotheque.selection.IllustrationQuery;
import net.fichotheque.selection.MotcleCondition;
import net.fichotheque.selection.MotcleQuery;
import net.fichotheque.tools.extraction.builders.AddendaExtractDefBuilder;
import net.fichotheque.tools.extraction.builders.AlbumExtractDefBuilder;
import net.fichotheque.tools.extraction.builders.CorpusExtractDefBuilder;
import net.fichotheque.tools.extraction.builders.DocumentFilterBuilder;
import net.fichotheque.tools.extraction.builders.ExtractionBuilderUtils;
import net.fichotheque.tools.extraction.builders.ExtractionDefBuilder;
import net.fichotheque.tools.extraction.builders.FicheFilterBuilder;
import net.fichotheque.tools.extraction.builders.FilterParametersBuilder;
import net.fichotheque.tools.extraction.builders.GroupClauseBuilder;
import net.fichotheque.tools.extraction.builders.IllustrationFilterBuilder;
import net.fichotheque.tools.extraction.builders.MotcleFilterBuilder;
import net.fichotheque.tools.extraction.builders.ThesaurusExtractDefBuilder;
import net.fichotheque.tools.selection.SelectionDOMUtils;
import net.fichotheque.utils.ExtractionUtils;
import net.fichotheque.utils.FichothequeUtils;
import net.fichotheque.utils.FilterUnits;
import net.fichotheque.utils.SelectionUtils;
import net.fichotheque.xml.extraction.ExtractionXMLUtils;
import net.mapeadores.util.instruction.Argument;
import net.mapeadores.util.instruction.Instruction;
import net.mapeadores.util.instruction.InstructionErrorHandler;
import net.mapeadores.util.instruction.InstructionParser;
import net.mapeadores.util.instruction.InstructionUtils;
import net.mapeadores.util.text.StringUtils;
import net.mapeadores.util.xml.DOMUtils;
import net.mapeadores.util.xml.DocumentFragmentHolder;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;


/**
 *
 * @author Vincent Calame
 */
public class ExtractionDOMReader {

    private final Fichotheque fichotheque;

    public ExtractionDOMReader(Fichotheque fichotheque) {
        this.fichotheque = fichotheque;
    }

    public ExtractionDef readExtraction(Element element, DocumentFragmentHolder includeCatalog) {
        NodeList includeList = element.getElementsByTagName("include");
        int includeCount = includeList.getLength();
        if (includeCount > 0) {
            Element[] includeArray = new Element[includeCount];
            for (int i = 0; i < includeCount; i++) {
                includeArray[i] = (Element) includeList.item(i);
            }
            for (int i = 0; i < includeCount; i++) {
                Element includeElement = includeArray[i];
                if (includeElement.getElementsByTagName("include").getLength() > 0) {
                    //@todo Warning
                    continue;
                }
                String fragmentKey = includeElement.getAttribute("fragment-key");
                if (fragmentKey.length() > 0) {
                    DocumentFragment documentFragment = includeCatalog.getFragment(fragmentKey);
                    if (documentFragment != null) {
                        DocumentFragment importDocumentFragment = (DocumentFragment) includeElement.getOwnerDocument().importNode(documentFragment, true);
                        Node parentNode = includeElement.getParentNode();
                        parentNode.replaceChild(importDocumentFragment, includeElement);
                    } else {
                        //@todo Warning
                    }
                }
            }
        }
        ExtractionDefBuilder extractionDefBuilder = ExtractionDefBuilder.init()
                .setTagNameInfo(ExtractionDOMUtils.getTagNameInfo(element));
        DOMUtils.readChildren(element, new RootConsumer(extractionDefBuilder));
        return extractionDefBuilder.toExtractionDef();
    }

    private AddendaExtractDef readAddendaExtractDef(Element element) {
        String name = ExtractionDOMUtils.getExtractName(element);
        TagNameInfo tagNameInfo = ExtractionDOMUtils.getTagNameInfo(element);
        DocumentFilterBuilder documentFilterBuilder = new DocumentFilterBuilder();
        List<DocumentCondition.Entry> entryList = new ArrayList<DocumentCondition.Entry>();
        DOMUtils.readChildren(element, new AddendaExtractConsumer(entryList, documentFilterBuilder));
        if (entryList.isEmpty()) {
            String addendaAttribute = element.getAttribute("addenda");
            if (addendaAttribute.length() > 0) {
                DocumentQuery attributeQuery = ExtractionDOMUtils.getDocumentQuery(addendaAttribute);
                if (attributeQuery != null) {
                    entryList.add(SelectionUtils.toDocumentConditionEntry(attributeQuery));
                }
            }
        }
        return AddendaExtractDefBuilder.init(documentFilterBuilder.toDocumentFilter())
                .setName(name)
                .setTagNameInfo(tagNameInfo)
                .setEntryList(entryList)
                .toAddendaExtractDef();
    }

    private AlbumExtractDef readAlbumExtractDef(Element element) {
        String name = ExtractionDOMUtils.getExtractName(element);
        TagNameInfo tagNameInfo = ExtractionDOMUtils.getTagNameInfo(element);
        IllustrationFilterBuilder illustrationFilterBuilder = new IllustrationFilterBuilder();
        List<IllustrationCondition.Entry> entryList = new ArrayList<IllustrationCondition.Entry>();
        DOMUtils.readChildren(element, new AlbumExtractConsumer(entryList, illustrationFilterBuilder));
        if (entryList.isEmpty()) {
            String albumAttribute = element.getAttribute("album");
            if (albumAttribute.length() > 0) {
                IllustrationQuery attributeQuery = ExtractionDOMUtils.getIllustrationQuery(albumAttribute);
                if (attributeQuery != null) {
                    entryList.add(SelectionUtils.toIllustrationConditionEntry(attributeQuery));
                }
            }
        }
        return AlbumExtractDefBuilder.init(illustrationFilterBuilder.toIllustrationFilter())
                .setName(name)
                .setTagNameInfo(tagNameInfo)
                .setEntryList(entryList)
                .toAlbumExtractDef();
    }

    private CorpusExtractDef readCorpusExtractDef(Element element) {
        CorpusExtractDefBuilder builder = CorpusExtractDefBuilder.init()
                .setName(ExtractionDOMUtils.getExtractName(element))
                .setTagNameInfo(ExtractionDOMUtils.getTagNameInfo(element));
        String sourceAttribute = element.getAttribute("source");
        if (sourceAttribute.equals("master")) {
            builder.setBoolean(CorpusExtractDef.MASTER_BOOLEAN, true);
        }
        String axisAttribute = element.getAttribute("axis");
        if (axisAttribute.equals("descendant")) {
            builder.setBoolean(CorpusExtractDef.DESCENDANTAXIS_BOOLEAN, true);
        }
        List<FicheCondition.Entry> entryList = new ArrayList<FicheCondition.Entry>();
        CorpusExtractConsumer consumer = new CorpusExtractConsumer(builder, entryList);
        DOMUtils.readChildren(element, consumer);
        if (entryList.isEmpty()) {
            String corpusAttribute = element.getAttribute("corpus");
            if (corpusAttribute.length() > 0) {
                FicheQuery attributeQuery = ExtractionDOMUtils.getFicheQuery(corpusAttribute);
                if (attributeQuery != null) {
                    entryList.add(SelectionUtils.toFicheConditionEntry(attributeQuery));
                }
            }
        }
        builder.setEntryList(entryList);
        if (!consumer.isGroupClauseDone()) {
            String sortAttribute = element.getAttribute("sort");
            if (sortAttribute.length() > 0) {
                builder.setClause(CorpusExtractDef.GROUP_CLAUSE, GroupClauseBuilder.getGroupClause(sortAttribute, true));
            }
        }
        if (!consumer.isFicheFilterDone()) {
            builder.setFicheFilter(ExtractionXMLUtils.TITLE_FICHEFILTER);
        }
        return builder.toCorpusExtractDef();
    }

    private ThesaurusExtractDef readThesaurusExtractDef(Element element) {
        ThesaurusExtractDefBuilder builder = ThesaurusExtractDefBuilder.init()
                .setName(ExtractionDOMUtils.getExtractName(element))
                .setTagNameInfo(ExtractionDOMUtils.getTagNameInfo(element));
        String sourceAttribute = element.getAttribute("source");
        if (sourceAttribute.equals("master")) {
            builder.setBoolean(ThesaurusExtractDef.MASTER_BOOLEAN, true);
        }
        List<MotcleCondition.Entry> entryList = new ArrayList<MotcleCondition.Entry>();
        ThesaurusExtractConsumer thesaurusExtractConsumer = new ThesaurusExtractConsumer(builder, entryList);
        DOMUtils.readChildren(element, thesaurusExtractConsumer);
        if (entryList.isEmpty()) {
            String thesaurusAttribute = element.getAttribute("thesaurus");
            if (thesaurusAttribute.length() > 0) {
                MotcleQuery attributeQuery = ExtractionDOMUtils.getMotcleQuery(thesaurusAttribute);
                if (attributeQuery != null) {
                    entryList.add(SelectionUtils.toMotcleConditionEntry(attributeQuery));
                }
            }
        }
        if (!thesaurusExtractConsumer.isMotcleFilterDone()) {
            builder.setMotcleFilter(ExtractionXMLUtils.NONE_MOTCLEFILTER);
        }
        builder.setEntryList(entryList);
        return builder.toThesaurusExtractDef();
    }


    private FicheFilter readFicheFilter(Element element) {
        FicheFilterBuilder builder = FicheFilterBuilder.init()
                .setTagNameInfo(ExtractionDOMUtils.getTagNameInfo(element));
        List<FilterUnit> list = new ArrayList<FilterUnit>();
        readFicheFilter(element, new FilterUnitConsumer(list));
        for (FilterUnit filterUnit : list) {
            builder.add(filterUnit);
        }
        return builder.toFicheFilter();
    }

    private Set<SubsetKey> readCorpusKeySet(Element element) {
        Set<SubsetKey> corpusKeySet = FichothequeUtils.EMPTY_SUBSETKEYSET;
        String corpusAttribute = element.getAttribute("corpus");
        if (corpusAttribute.length() > 0) {
            corpusKeySet = FichothequeUtils.toSubsetKeySet(SubsetKey.CATEGORY_CORPUS, corpusAttribute);
        }
        return corpusKeySet;
    }

    private void readMotcleFilter(MotcleFilterBuilder motcleFilterBuilder, Element element) {
        TagNameInfo tagNameInfo = ExtractionDOMUtils.getTagNameInfo(element);
        motcleFilterBuilder.setTagNameInfo(tagNameInfo);
        if (StringUtils.isTrue(element.getAttribute("recursive"))) {
            motcleFilterBuilder.setRecursive();
        }
        DOMUtils.readChildren(element, new MotcleFilterConsumer(motcleFilterBuilder));
    }

    private void readFicheFilter(Element parentElement, FilterUnitConsumer filterUnitConsumer) {
        NodeList nodeList = parentElement.getChildNodes();
        int length = nodeList.getLength();
        for (int i = 0; i < length; i++) {
            Node node = nodeList.item(i);
            switch (node.getNodeType()) {
                case Node.ELEMENT_NODE:
                    filterUnitConsumer.accept((Element) node);
                    break;
                case Node.TEXT_NODE:
                case Node.CDATA_SECTION_NODE:
                    String text = ((Text) node).getData();
                    if (text != null) {
                        filterUnitConsumer.parse(text);
                    }
            }
        }
    }

    private static FilterParameters readFilterParameters(Element element) {
        FilterParametersBuilder filterParametersBuilder = new FilterParametersBuilder();
        String paramsString = element.getAttribute("params");
        if (paramsString.length() > 0) {
            paramsString = paramsString.replace("libtype", "labeltype");
            parseInstruction(filterParametersBuilder, paramsString, null);
        }
        DOMUtils.readChildren(element, new FilterParametersConsumer(filterParametersBuilder));
        return filterParametersBuilder.toFilterParameters();
    }


    private class RootConsumer implements Consumer<Element> {

        private final ExtractionDefBuilder extractionDefBuilder;
        private boolean dynamicFicheDone = false;
        private boolean dynamicMotcleDone = false;

        private RootConsumer(ExtractionDefBuilder extractionDefBuilder) {
            this.extractionDefBuilder = extractionDefBuilder;
        }

        @Override
        public void accept(Element element) {
            String tagname = element.getTagName();
            switch (tagname) {
                case "static":
                    extractionDefBuilder.setStaticTagNameInfo(ExtractionDOMUtils.getTagNameInfo(element));
                    DOMUtils.readChildren(element, new StaticConsumer(extractionDefBuilder));
                    break;
                case "extraitcorpus":
                case "fiches":
                    if (!dynamicFicheDone) {
                        extractionDefBuilder.setDynamicCorpusExtractDef(readCorpusExtractDef(element));
                        dynamicFicheDone = true;
                    }
                    break;
                case "extraitthesaurus":
                case "motcles":
                    if (!dynamicMotcleDone) {
                        extractionDefBuilder.setDynamicThesaurusExtractDef(readThesaurusExtractDef(element));
                        dynamicMotcleDone = true;
                    }
                    break;
                case "fiche":
                    if (!dynamicFicheDone) {
                        FicheFilter ficheFilter = readFicheFilter(element);
                        extractionDefBuilder.setDynamicCorpusExtractDef(CorpusExtractDefBuilder.init(ficheFilter).setTagNameInfo(TagNameInfo.NULL).toCorpusExtractDef());
                        dynamicFicheDone = true;
                    }
                    break;

            }
        }

    }


    private class StaticConsumer implements Consumer<Element> {

        private final ExtractionDefBuilder extractionDefBuilder;

        private StaticConsumer(ExtractionDefBuilder extractionDefBuilder) {
            this.extractionDefBuilder = extractionDefBuilder;
        }

        @Override
        public void accept(Element element) {
            String tagname = element.getTagName();
            switch (tagname) {
                case "extraitcorpus":
                case "fiches":
                    extractionDefBuilder.addStatic(readCorpusExtractDef(element));
                    break;
                case "extraitthesaurus":
                case "motcles":
                    extractionDefBuilder.addStatic(readThesaurusExtractDef(element));
                    break;
            }
        }

    }


    private class AddendaExtractConsumer implements Consumer<Element> {

        private final DocumentFilterBuilder documentFilterBuilder;
        private final List<DocumentCondition.Entry> entryList;

        private AddendaExtractConsumer(List<DocumentCondition.Entry> entryList, DocumentFilterBuilder documentFilterBuilder) {
            this.entryList = entryList;
            this.documentFilterBuilder = documentFilterBuilder;
        }

        @Override
        public void accept(Element element) {
            String tagname = element.getTagName();
            switch (tagname) {
                case "document-query":
                case "document-select":
                    entryList.add(SelectionDOMUtils.getDocumentConditionEntry(element));
                    break;
                case "document":
                    TagNameInfo tagNameInfo = ExtractionDOMUtils.getTagNameInfo(element);
                    documentFilterBuilder.setTagNameInfo(tagNameInfo);
                    break;
            }
        }

    }


    private class AlbumExtractConsumer implements Consumer<Element> {

        private final List<IllustrationCondition.Entry> entryList;
        private final IllustrationFilterBuilder illustrationFilterBuilder;

        private AlbumExtractConsumer(List<IllustrationCondition.Entry> entryList, IllustrationFilterBuilder illustrationFilterBuilder) {
            this.entryList = entryList;
            this.illustrationFilterBuilder = illustrationFilterBuilder;
        }

        @Override
        public void accept(Element element) {
            String tagname = element.getTagName();
            switch (tagname) {
                case "illustration-query":
                case "illustration-select":
                    entryList.add(SelectionDOMUtils.getIllustrationConditionEntry(element));
                    break;
                case "illustration":
                    TagNameInfo tagNameInfo = ExtractionDOMUtils.getTagNameInfo(element);
                    illustrationFilterBuilder.setTagNameInfo(tagNameInfo);
                    break;
            }
        }

    }


    private class FilterUnitConsumer implements Consumer<Element> {

        private final List<FilterUnit> list;

        private FilterUnitConsumer(List<FilterUnit> list) {
            this.list = list;
        }

        private void parse(String text) {
            String[] tokens = StringUtils.getTechnicalTokens(text, true);
            for (String token : tokens) {
                parseToken(token);
            }
        }

        private void parseToken(String token) {
            if (token.equals("chrono")) {
                list.add(FilterUnits.CHRONO_FILTERUNIT);
                return;
            }
            if (token.startsWith("phrase_")) {
                String phraseName = token.substring("phrase_".length());
                if (!phraseName.isEmpty()) {
                    list.add(FilterUnits.phrase(phraseName));
                }
            }
            if (token.startsWith("data_")) {
                String dataName = token.substring("data_".length());
                if (!dataName.isEmpty()) {
                    list.add(FilterUnits.data(dataName, ExtractionUtils.EMPTY_FILTERPARAMETERS));
                }
            }
            try {
                FieldKey fieldKey = FieldKey.parse(token);
                list.add(FilterUnits.fieldKey(fieldKey, ExtractionUtils.EMPTY_FILTERPARAMETERS));
            } catch (ParseException pe) {
                try {
                    ExtendedIncludeKey extendedIncludeKey = ExtendedIncludeKey.parse(token);
                    list.add(ExtractionBuilderUtils.getDefaultFilterUnit(extendedIncludeKey, fichotheque));
                } catch (ParseException pe2) {

                }
            }
        }

        @Override
        public void accept(Element element) {
            FilterParameters filterParameters = readFilterParameters(element);
            String tagname = element.getTagName();
            switch (tagname) {
                case "head":
                case "entete":
                    list.add(FilterUnits.HEAD_FILTERUNIT);
                    break;
                case "body":
                case "corpsdefiche":
                    list.add(FilterUnits.BODY_FILTERUNIT);
                    break;
                case "title":
                case "titre":
                    list.add(FilterUnits.fieldKey(FieldKey.TITLE, filterParameters));
                    break;
                case "subtitle":
                case "soustitre":
                    list.add(FilterUnits.fieldKey(FieldKey.SUBTITLE, filterParameters));
                    break;
                case "owners":
                case "redacteurs":
                    list.add(FilterUnits.fieldKey(FieldKey.OWNERS, filterParameters));
                    break;
                case "lang":
                    list.add(FilterUnits.fieldKey(FieldKey.LANG, filterParameters));
                    break;
                case "prop":
                case "propriete": {
                    String fieldName = getFieldName(element);
                    if (fieldName.length() > 0) {
                        addFieldNameFilter(FieldKey.PROP_CATEGORY, fieldName, filterParameters);
                    }
                    break;
                }
                case "info":
                case "information": {
                    String fieldName = getFieldName(element);
                    if (fieldName.length() > 0) {
                        addFieldNameFilter(FieldKey.INFO_CATEGORY, fieldName, filterParameters);
                    }
                    break;
                }
                case "section": {
                    String fieldName = getFieldName(element);
                    if (fieldName.length() > 0) {
                        addFieldNameFilter(FieldKey.SECTION_CATEGORY, fieldName, filterParameters);
                    }
                    break;
                }
                case "lot": {
                    TagNameInfo tagNameInfo = ExtractionDOMUtils.getTagNameInfo(element);
                    List<FilterUnit> lotList = new ArrayList<>();
                    readFicheFilter(element, new FilterUnitConsumer(lotList));
                    if (!lotList.isEmpty()) {
                        list.add(FilterUnits.lot(tagNameInfo, lotList));
                    }
                    break;
                }
                case "chrono":
                    list.add(FilterUnits.CHRONO_FILTERUNIT);
                    break;
                case "intitule":
                    list.add(FilterUnits.FICHEPHRASE_FILTERUNIT);
                    break;
                case "phrase":
                    String phraseName = element.getAttribute("name");
                    if (!phraseName.isEmpty()) {
                        list.add(FilterUnits.phrase(phraseName));
                    }
                    break;
                case "extraitaddenda":
                case "documents":
                    AddendaExtractDef addendaExtractDef = readAddendaExtractDef(element);
                    list.add(FilterUnits.addendaExtract(addendaExtractDef, filterParameters));
                    break;
                case "extraitcorpus":
                case "fiches":
                    CorpusExtractDef corpusExtractDef = readCorpusExtractDef(element);
                    list.add(FilterUnits.corpusExtract(corpusExtractDef, filterParameters));
                    break;
                case "extraitthesaurus":
                case "motcles":
                    ThesaurusExtractDef thesaurusExtractDef = readThesaurusExtractDef(element);
                    list.add(FilterUnits.thesaurusExtract(thesaurusExtractDef, filterParameters));
                    break;
                case "extraitalbum":
                case "illustrations":
                    AlbumExtractDef albumExtractDef = readAlbumExtractDef(element);
                    list.add(FilterUnits.albumExtract(albumExtractDef, filterParameters));
                    break;
                case "fiche":
                    list.add(FilterUnits.ficheParentage(readFicheFilter(element), filterParameters, readCorpusKeySet(element)));
                    break;
                case "motcle": {
                    MotcleFilterBuilder filterBuilder = new MotcleFilterBuilder(MotcleFilter.DEFAULT_TYPE);
                    readMotcleFilter(filterBuilder, element);
                    list.add(FilterUnits.masterMotcle(filterBuilder.toMotcleFilter(), filterParameters));
                    break;
                }
                case "data": {
                    String dataName = element.getAttribute("name");
                    if (!dataName.isEmpty()) {
                        list.add(FilterUnits.data(dataName, filterParameters));
                    }
                    break;
                }
            }
        }

        private String getFieldName(Element element) {
            String fieldName = element.getAttribute("name");
            if (fieldName.isEmpty()) {
                fieldName = element.getAttribute("type");
            }
            return fieldName;
        }

        private void addFieldNameFilter(short category, String fieldName, FilterParameters filterParameters) {
            if (fieldName.endsWith("*")) {
                int length = fieldName.length();
                if (length == 1) {
                    return;
                }
                String prefix = fieldName.substring(0, length - 1);
                list.add(FilterUnits.fieldNamePrefix(category, prefix, filterParameters));
            } else {
                try {
                    FieldKey fieldKey = FieldKey.parse(category, fieldName);
                    list.add(FilterUnits.fieldKey(fieldKey, filterParameters));
                } catch (java.text.ParseException pe) {
                }
            }
        }

    }


    private class CorpusExtractConsumer implements Consumer<Element> {

        private final CorpusExtractDefBuilder builder;
        private final List<FicheCondition.Entry> entryList;
        private boolean groupClauseDone;
        private boolean ficheFilterDone;

        private CorpusExtractConsumer(CorpusExtractDefBuilder builder, List<FicheCondition.Entry> entryList) {
            this.builder = builder;
            this.entryList = entryList;
        }

        @Override
        public void accept(Element element) {
            String tagname = element.getTagName();
            switch (tagname) {
                case "fiche-query":
                case "fiche-select":
                    entryList.add(SelectionDOMUtils.getFicheConditionEntry(fichotheque, element));
                    break;
                case "group":
                    builder.setClause(CorpusExtractDef.GROUP_CLAUSE, ExtractionDOMUtils.readGroupClause(element));
                    groupClauseDone = true;
                    break;
                case "title":
                case "intitule":
                    builder.setClause(CorpusExtractDef.TITLE_CLAUSE, ExtractionDOMUtils.readTitleClause(element));
                    break;
                case "pack":
                    builder.setClause(CorpusExtractDef.PACK_CLAUSE, ExtractionDOMUtils.readPackClause(element));
                    break;
                case "fiche":
                    if (!ficheFilterDone) {
                        ficheFilterDone = true;
                        builder.setFicheFilter(readFicheFilter(element));
                    }
                    break;
            }
        }

        public boolean isGroupClauseDone() {
            return groupClauseDone;
        }

        public boolean isFicheFilterDone() {
            return ficheFilterDone;
        }

    }


    private class ThesaurusExtractConsumer implements Consumer<Element> {

        private final ThesaurusExtractDefBuilder builder;
        private final List<MotcleCondition.Entry> entryList;
        private boolean motcleFilterDone;

        private ThesaurusExtractConsumer(ThesaurusExtractDefBuilder builder, List<MotcleCondition.Entry> entryList) {
            this.builder = builder;
            this.entryList = entryList;
        }

        @Override
        public void accept(Element element) {
            String tagname = element.getTagName();
            switch (tagname) {
                case "motcle-query":
                case "motcle-select":
                    entryList.add(SelectionDOMUtils.getMotcleConditionEntry(fichotheque, element));
                    break;
                case "motcle":
                    if (!motcleFilterDone) {
                        MotcleFilterBuilder motcleFilterBuilder = new MotcleFilterBuilder(MotcleFilter.DEFAULT_TYPE);
                        readMotcleFilter(motcleFilterBuilder, element);
                        builder.setMotcleFilter(motcleFilterBuilder.toMotcleFilter());
                        motcleFilterDone = true;
                    }
                    break;
                case "title":
                case "intitule":
                    builder.setBoolean(ThesaurusExtractDef.WITHTHESAURUSTITLE_BOOLEAN, true);
                    break;
            }
        }

        public boolean isMotcleFilterDone() {
            return motcleFilterDone;
        }

    }


    private class MotcleFilterConsumer implements Consumer<Element> {

        private final MotcleFilterBuilder motcleFilterBuilder;

        private MotcleFilterConsumer(MotcleFilterBuilder motcleFilterBuilder) {
            this.motcleFilterBuilder = motcleFilterBuilder;
        }

        @Override
        public void accept(Element element) {
            String tagname = element.getTagName();
            switch (tagname) {
                case "icon":
                    motcleFilterBuilder.setWithIcon(true);
                    break;
                case "labels":
                case "lib":
                    motcleFilterBuilder.setWithLabels(true);
                    break;
                case "level":
                    motcleFilterBuilder.setWithLevel(true);
                    break;
                case "intitule":
                    motcleFilterBuilder.setWithFicheStylePhrase(true);
                    break;
                case "phrase":
                    if (element.getAttribute("name").equals(FichothequeConstants.FICHESTYLE_PHRASE)) {
                        motcleFilterBuilder.setWithFicheStylePhrase(true);
                    }
                    break;
                case "next":
                    MotcleFilterBuilder nextFilterBuilder = new MotcleFilterBuilder(MotcleFilter.NEXT_TYPE);
                    readMotcleFilter(nextFilterBuilder, element);
                    motcleFilterBuilder.setNextFilter(nextFilterBuilder.toMotcleFilter());
                    break;
                case "previous":
                    MotcleFilterBuilder previousFilterBuilder = new MotcleFilterBuilder(MotcleFilter.PREVIOUS_TYPE);
                    readMotcleFilter(previousFilterBuilder, element);
                    motcleFilterBuilder.setPreviousFilter(previousFilterBuilder.toMotcleFilter());
                    break;
                case "motcle":
                    MotcleFilterBuilder childrenFilterBuilder = new MotcleFilterBuilder(MotcleFilter.DEFAULT_TYPE);
                    readMotcleFilter(childrenFilterBuilder, element);
                    motcleFilterBuilder.setChildrenFilter(childrenFilterBuilder.toMotcleFilter());
                    break;
                case "parent":
                    MotcleFilterBuilder parentFilterBuilder = new MotcleFilterBuilder(MotcleFilter.PARENT_TYPE);
                    readMotcleFilter(parentFilterBuilder, element);
                    motcleFilterBuilder.setParentFilter(parentFilterBuilder.toMotcleFilter());
                    break;
                case "fiche":
                    motcleFilterBuilder.add(FilterUnits.ficheParentage(readFicheFilter(element), readFilterParameters(element), readCorpusKeySet(element)));
                    break;
                case "extraitcorpus":
                case "fiches":
                    CorpusExtractDef corpusExtractDef = readCorpusExtractDef(element);
                    motcleFilterBuilder.add(FilterUnits.corpusExtract(corpusExtractDef, readFilterParameters(element)));
                    break;
            }
        }


    }


    private static class FilterParametersConsumer implements Consumer<Element> {

        private final FilterParametersBuilder filterParametersBuilder;

        private FilterParametersConsumer(FilterParametersBuilder filterParametersBuilder) {
            this.filterParametersBuilder = filterParametersBuilder;
        }

        @Override
        public void accept(Element element) {
            String tagName = element.getTagName();
            switch (tagName) {
                case "attr": {
                    String name = element.getAttribute("key");
                    if (!name.isEmpty()) {
                        DOMUtils.readChildren(element, new ValConsumer(filterParametersBuilder, name));
                    }
                    break;
                }
                case "hide-empty": {
                    filterParametersBuilder.appendValue(ExtractionConstants.HIDE_PARAM, ExtractionConstants.EMPTY_HIDE);
                    break;
                }
                case "hide": {
                    String hideValue = DOMUtils.readSimpleElement(element);
                    if (!hideValue.isEmpty()) {
                        filterParametersBuilder.appendValue(ExtractionConstants.HIDE_PARAM, hideValue);
                    }
                    break;
                }
                case "groups": {
                    String groupsValue = DOMUtils.readSimpleElement(element);
                    if (!groupsValue.isEmpty()) {
                        filterParametersBuilder.appendValue(ExtractionConstants.GROUPS_PARAM, groupsValue);
                    }
                    break;
                }
                case "cell": {
                    String order = element.getAttribute("order");
                    if (!order.isEmpty()) {
                        try {
                            int val = Integer.parseInt(order);
                            filterParametersBuilder.appendValue(ExtractionConstants.CELL_ORDER_PARAM, String.valueOf(val));
                        } catch (NumberFormatException nfe) {

                        }
                    }
                    String format = element.getAttribute("format");
                    if (!format.isEmpty()) {
                        filterParametersBuilder.appendValue(ExtractionConstants.CELL_FORMAT_PARAM, format);
                    }
                    break;
                }
                case "param": {
                    String paramName = element.getAttribute("name");
                    String paramValue = DOMUtils.readSimpleElement(element);
                    if ((!paramName.isEmpty()) && (!paramValue.isEmpty())) {
                        filterParametersBuilder.appendValue(paramName, paramValue);
                    }
                    break;
                }
            }
        }

    }


    private static class ValConsumer implements Consumer<Element> {

        private final FilterParametersBuilder filterParametersBuilder;
        private final String name;

        private ValConsumer(FilterParametersBuilder attributeBuilder, String name) {
            this.filterParametersBuilder = attributeBuilder;
            this.name = name;
        }

        @Override
        public void accept(Element element) {
            String tagName = element.getTagName();
            switch (tagName) {
                case "val": {
                    String value = DOMUtils.readSimpleElement(element);
                    if (!value.isEmpty()) {
                        filterParametersBuilder.appendValue(name, value);
                    }
                }
            }
        }

    }

    private static void parseInstruction(FilterParametersBuilder attributeBuffer, String s, InstructionErrorHandler errorHandler) {
        if (errorHandler == null) {
            errorHandler = InstructionUtils.DEFAULT_ERROR_HANDLER;
        }
        Instruction instruction = InstructionParser.parse(s, errorHandler);
        if (instruction == null) {
            return;
        }
        for (Argument argument : instruction) {
            attributeBuffer.appendValue(argument.getKey(), argument.getValue());
        }
    }

}
