/* 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.dom;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.fiche.AmountItem;
import net.fichotheque.corpus.fiche.AttConsumer;
import net.fichotheque.corpus.fiche.Cdatadiv;
import net.fichotheque.corpus.fiche.Code;
import net.fichotheque.corpus.fiche.CountryItem;
import net.fichotheque.corpus.fiche.DateItem;
import net.fichotheque.corpus.fiche.Div;
import net.fichotheque.corpus.fiche.EmailItem;
import net.fichotheque.corpus.fiche.Fiche;
import net.fichotheque.corpus.fiche.FicheBlock;
import net.fichotheque.corpus.fiche.FicheBlocks;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.FicheItems;
import net.fichotheque.corpus.fiche.GeopointItem;
import net.fichotheque.corpus.fiche.H;
import net.fichotheque.corpus.fiche.ImageItem;
import net.fichotheque.corpus.fiche.Insert;
import net.fichotheque.corpus.fiche.Item;
import net.fichotheque.corpus.fiche.LanguageItem;
import net.fichotheque.corpus.fiche.Li;
import net.fichotheque.corpus.fiche.LinkItem;
import net.fichotheque.corpus.fiche.Ln;
import net.fichotheque.corpus.fiche.NumberItem;
import net.fichotheque.corpus.fiche.P;
import net.fichotheque.corpus.fiche.ParaItem;
import net.fichotheque.corpus.fiche.PersonItem;
import net.fichotheque.corpus.fiche.S;
import net.fichotheque.corpus.fiche.Table;
import net.fichotheque.corpus.fiche.Td;
import net.fichotheque.corpus.fiche.TextContentBuilder;
import net.fichotheque.corpus.fiche.Tr;
import net.fichotheque.corpus.fiche.Ul;
import net.fichotheque.corpus.fiche.ZoneBlock;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.utils.FicheUtils;
import net.fichotheque.utils.FichothequeUtils;
import net.mapeadores.util.html.HtmlCleaner;
import net.mapeadores.util.localisation.Country;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.models.EmailCore;
import net.mapeadores.util.models.EmailCoreUtils;
import net.mapeadores.util.models.PersonCore;
import net.mapeadores.util.models.PersonCoreUtils;
import net.mapeadores.util.money.ExtendedCurrency;
import net.mapeadores.util.primitives.Decimal;
import net.mapeadores.util.primitives.DegreDecimal;
import net.mapeadores.util.primitives.FuzzyDate;
import net.mapeadores.util.text.StringUtils;
import net.mapeadores.util.xml.DOMUtils;
import net.mapeadores.util.xml.XMLUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;


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

    private final HtmlCleaner htmlCleaner;
    private final RootConsumer rootConsumer = new RootConsumer();
    private final HeadConsumer headConsumer = new HeadConsumer();
    private final BodyConsumer bodyConsumer = new BodyConsumer();
    private Fiche currentFiche;
    private boolean append = true;


    public FicheDOMReader(HtmlCleaner htmlCleaner) {
        this.htmlCleaner = htmlCleaner;
    }

    public void setAppend(boolean append) {
        this.append = append;
    }

    public Fiche readFiche(Element element) {
        Fiche fiche = new Fiche();
        try {
            Lang lang = Lang.parse(element.getAttribute("xml:lang"));
            fiche.setLang(lang);
        } catch (ParseException pe) {
        }
        this.currentFiche = fiche;
        DOMUtils.readChildren(element, rootConsumer);
        return fiche;
    }


    private class RootConsumer implements Consumer<Element> {

        private RootConsumer() {

        }

        @Override
        public void accept(Element element) {
            switch (element.getTagName()) {
                case "head":
                case "entete":
                case "source":
                    DOMUtils.readChildren(element, headConsumer);
                    break;
                case "body":
                case "corpsdefiche":
                    DOMUtils.readChildren(element, bodyConsumer);
                    break;
            }
        }

    }


    private class HeadConsumer implements Consumer<Element> {

        private HeadConsumer() {

        }

        @Override
        public void accept(Element element) {
            readHeadElement(element);
        }

        private boolean readHeadElement(Element element) {
            switch (element.getTagName()) {
                case "title":
                case "titre":
                    currentFiche.setTitle(XMLUtils.getData(element));
                    return true;
                case "subtitle":
                case "soustitre":
                    currentFiche.setSubtitle(readPara(element));
                    return true;
                case "prop":
                case "propriete":
                    readProp(currentFiche, element);
                    return true;
                case "info":
                case "information":
                    readInfo(currentFiche, element);
                    return true;
                case "owners":
                case "redacteurs":
                    readOwners(currentFiche, element);
                    return true;
            }
            return false;
        }

    }


    private class BodyConsumer implements Consumer<Element> {

        private BodyConsumer() {

        }

        @Override
        public void accept(Element element) {
            readBodyElement(element);
        }

        private boolean readBodyElement(Element element) {
            String tagname = element.getTagName();
            if (tagname.equals("texte")) {
                FieldKey fieldKey = FieldKey.build(FieldKey.SECTION_CATEGORY, "texte");
                List<FicheBlock> list = readFicheBlockList(element);
                FicheBlocks ficheBlocks = FicheUtils.toFicheBlocks(list);
                if (append) {
                    currentFiche.appendSection(fieldKey, ficheBlocks);
                } else {
                    currentFiche.setSection(fieldKey, ficheBlocks);
                }
                return true;
            } else if ((tagname.equals("section")) || (tagname.equals("annexe"))) {
                readSection(currentFiche, element);
                return true;
            }
            return false;
        }

    }

    public void readProp(Fiche fiche, Element element) {
        String fieldName = element.getAttribute("name");
        if (fieldName.length() == 0) {
            fieldName = element.getAttribute("type");
        }
        if (fieldName.length() == 0) {
            return;
        }
        FieldKey fieldKey;
        try {
            fieldKey = FieldKey.parse(FieldKey.PROP_CATEGORY, fieldName);
        } catch (ParseException pe) {
            return;
        }
        FicheItem ficheItem = null;
        NodeList liste = element.getChildNodes();
        for (int j = 0; j < liste.getLength(); j++) {
            if (liste.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element el = (Element) liste.item(j);
                ficheItem = readFicheItem(el);
                break;
            }
        }
        fiche.setProp(fieldKey, ficheItem);
    }

    public void readInfo(Fiche fiche, Element element) {
        String fieldName = element.getAttribute("name");
        if (fieldName.length() == 0) {
            fieldName = element.getAttribute("type");
        }
        if (fieldName.length() == 0) {
            return;
        }
        FieldKey fieldKey;
        try {
            fieldKey = FieldKey.parse(FieldKey.INFO_CATEGORY, fieldName);
        } catch (ParseException pe) {
            return;
        }
        FicheItems ficheItems = readFicheItems(element);
        if (append) {
            fiche.appendInfo(fieldKey, ficheItems);
        } else {
            fiche.setInfo(fieldKey, ficheItems);
        }
    }

    public void readOwners(Fiche fiche, Element element) {
        FicheItems ficheItems = readFicheItems(element);
        if (append) {
            fiche.appendOwners(ficheItems);
        } else {
            fiche.setOwners(ficheItems);
        }
    }

    public FicheItem readFicheItem(Element element) {
        switch (element.getTagName()) {
            case "item":
                String value = XMLUtils.getData(element);
                if (value.length() == 0) {
                    return null;
                } else {
                    return new Item(value);
                }
            case "person":
            case "personne":
                return readPerson(element);
            case "language":
            case "langue":
                return readLanguage(element);
            case "country":
            case "pays":
                return readCountry(element);
            case "date":
            case "datation":
                return readDate(element);
            case "link":
                return readLink(element);
            case "email":
            case "courriel":
                return readEmail(element);
            case "amount":
            case "montant":
                return readAmount(element);
            case "number":
            case "nombre":
                return readNumber(element);
            case "geopoint":
                return readGeopoint(element);
            case "para":
                return readPara(element);
            case "image":
                return readImage(element);
            default:
                return null;
        }
    }

    public PersonItem readPerson(Element element) {
        String idString = element.getAttribute("id");
        if (idString.length() == 0) {
            idString = element.getAttribute("idsph");
        }
        String spherestring = element.getAttribute("sphere");
        if ((spherestring.length() != 0) && (idString.length() != 0)) {
            try {
                SubsetKey sphereKey = SubsetKey.parse(SubsetKey.CATEGORY_SPHERE, spherestring);
                int id = Integer.parseInt(idString);
                if (id > 0) {
                    return new PersonItem(FichothequeUtils.toGlobalId(sphereKey, id));
                }
            } catch (ParseException | NumberFormatException e) {
                return null;
            }
        }
        String surname = "";
        String forename = "";
        String nonlatin = "";
        boolean surnameFirst = false;
        String organism = "";
        NodeList nodeList = element.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            if (nodeList.item(i).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(i);
                String tagname = child.getTagName();
                if ((tagname.equals("surname")) || (tagname.equals("nom"))) {
                    surname = XMLUtils.getData(child);
                    String surnameFirstString = child.getAttribute("surname-first");
                    if (surnameFirstString.length() == 0) {
                        surnameFirstString = child.getAttribute("avant");
                    }
                    if (surnameFirstString.equals("true")) {
                        surnameFirst = true;
                    }
                } else if ((tagname.equals("forename")) || (tagname.equals("prenom"))) {
                    forename = XMLUtils.getData(child);
                } else if ((tagname.equals("nonlatin")) || (tagname.equals("original"))) {
                    nonlatin = XMLUtils.getData(child);
                } else if ((tagname.equals("organism")) || (tagname.equals("organisme"))) {
                    organism = XMLUtils.getData(child);
                }
            }
        }
        PersonCore personCore = PersonCoreUtils.toPersonCore(surname, forename, nonlatin, surnameFirst);
        return new PersonItem(personCore, organism);
    }

    public LanguageItem readLanguage(Element element) {
        String langString = element.getAttribute("lang");
        try {
            Lang lang = Lang.parse(langString);
            return new LanguageItem(lang);
        } catch (ParseException mcle) {
            return null;
        }
    }

    public CountryItem readCountry(Element element) {
        try {
            Country country = Country.parse(element.getAttribute("country"));
            return new CountryItem(country);
        } catch (ParseException pe) {
            return null;
        }
    }

    public NumberItem readNumber(Element element) {
        String valString = element.getAttribute("val");
        try {
            Decimal decimal = StringUtils.parseStrictDecimal(valString);
            return new NumberItem(decimal);
        } catch (NumberFormatException nfe) {
            return null;
        }
    }

    public GeopointItem readGeopoint(Element element) {
        String latitudeString = element.getAttribute("lat");
        String longitudeString = element.getAttribute("lon");
        if (longitudeString.length() == 0) {
            longitudeString = element.getAttribute("long"); //Ancienne version avant 0.9.8.7-beta07
        }
        try {
            Decimal latitude = StringUtils.parseStrictDecimal(latitudeString);
            Decimal longitude = StringUtils.parseStrictDecimal(longitudeString);
            return new GeopointItem(DegreDecimal.newInstance(latitude), DegreDecimal.newInstance(longitude));
        } catch (NumberFormatException nfe) {
            return null;
        }
    }

    public ParaItem readPara(Element element) {
        ParaItem.Builder builder = new ParaItem.Builder();
        readContentList(builder, element);
        return builder.toParaItem();
    }

    public AmountItem readAmount(Element element) {
        String valString = element.getAttribute("val");
        String curString = element.getAttribute("cur");
        try {
            Decimal decimal = StringUtils.parseStrictDecimal(valString);
            ExtendedCurrency currency = ExtendedCurrency.parse(curString);
            return new AmountItem(decimal, currency);
        } catch (NumberFormatException | ParseException e) {
            return null;
        }
    }

    public DateItem readDate(Element element) {
        String type = element.getAttribute("type");
        String yearString = element.getAttribute("a");
        if (yearString.isEmpty()) {
            yearString = element.getAttribute("y");
        }
        String halfyearString = element.getAttribute("s");
        String quarterString = element.getAttribute("t");
        if (quarterString.isEmpty()) {
            quarterString = element.getAttribute("q");
        }
        String monthString = element.getAttribute("m");
        String dayString = element.getAttribute("j");
        if (dayString.isEmpty()) {
            dayString = element.getAttribute("d");
        }
        FuzzyDate date;
        try {
            int year = Integer.parseInt(yearString);
            switch (type) {
                case "s":
                    int halfyear = Integer.parseInt(halfyearString);
                    date = FuzzyDate.fromHalfYear(year, halfyear);
                    break;
                case "t":
                case "q":
                    int quarter = Integer.parseInt(quarterString);
                    date = FuzzyDate.fromQuarter(year, quarter);
                    break;
                case "m":
                    int month = Integer.parseInt(monthString);
                    date = FuzzyDate.fromMonth(year, month);
                    break;
                case "j":
                case "d":
                    int daymonth = Integer.parseInt(monthString);
                    int day = Integer.parseInt(dayString);
                    date = FuzzyDate.fromDay(year, daymonth, day);
                    break;
                default:
                    date = FuzzyDate.fromYear(year);
            }
        } catch (IllegalArgumentException e) {
            return null;
        }
        return new DateItem(date);
    }

    public LinkItem readLink(Element element) {
        String href = element.getAttribute("href");
        String title = "";
        String comment = "";
        NodeList liste = element.getChildNodes();
        for (int i = 0; i < liste.getLength(); i++) {
            if (liste.item(i).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) liste.item(i);
                String tagname = child.getTagName();
                if (tagname.equals("title")) {
                    title = XMLUtils.getData(child);
                } else if (tagname.equals("comment")) {
                    comment = XMLUtils.getData(child);
                }
            }
        }
        return new LinkItem(href, title, comment);
    }

    public ImageItem readImage(Element element) {
        String src = element.getAttribute("src");
        String alt = "";
        String title = "";
        NodeList liste = element.getChildNodes();
        for (int i = 0; i < liste.getLength(); i++) {
            if (liste.item(i).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) liste.item(i);
                String tagname = child.getTagName();
                if (tagname.equals("alt")) {
                    alt = XMLUtils.getData(child);
                } else if (tagname.equals("title")) {
                    title = XMLUtils.getData(child);
                }
            }
        }
        return new ImageItem(src, alt, title);
    }

    public EmailItem readEmail(Element element) {
        String addrSpec = element.getAttribute("addr-spec");
        String realName = element.getAttribute("real-name");
        if (realName.length() == 0) {
            String strg = XMLUtils.getData(element);
            if (strg.length() > 0) {
                try {
                    EmailCore c2 = EmailCoreUtils.parse(strg);
                    realName = c2.getRealName();
                } catch (ParseException pe) {
                }
            }
        }
        try {
            EmailCore emailCore = EmailCoreUtils.parse(addrSpec, realName);
            return new EmailItem(emailCore);
        } catch (ParseException pe) {
            return null;
        }
    }

    public void readSection(Fiche fiche, Element element) {
        String fieldName = element.getAttribute("name");
        if (fieldName.length() == 0) {
            fieldName = element.getAttribute("type");
        }
        FieldKey fieldKey;
        try {
            fieldKey = FieldKey.parse(FieldKey.SECTION_CATEGORY, fieldName);
        } catch (ParseException pe) {
            return;
        }
        List<FicheBlock> list = readFicheBlockList(element);
        FicheBlocks ficheBlocks = FicheUtils.toFicheBlocks(list);
        if (append) {
            fiche.appendSection(fieldKey, ficheBlocks);
        } else {
            fiche.setSection(fieldKey, ficheBlocks);
        }
    }

    public FicheBlock readFicheBlock(Element element) {
        switch (element.getTagName()) {
            case "p":
                return readP(element);
            case "ul":
                return readUl(element);
            case "h":
                return readH(element);
            case "code":
                return readCode(element);
            case "table":
                return readTable(element);
            case "insert":
                return readInsert(element);
            case "div":
                return readDiv(element);
            case "cdatadiv":
                return readCdatadiv(element);
            default:
                return null;
        }
    }

    public P readP(Element element) {
        short type = P.typeToShort(element.getAttribute("type"));
        P p = new P(type);
        p.setSource(element.getAttribute("source"));
        readAtts(element, p);
        readContentList(p, element);
        return p;
    }

    public H readH(Element element) {
        int level = 1;
        try {
            level = Integer.parseInt(element.getAttribute("level"));
        } catch (NumberFormatException nfe) {
        }
        H h = new H(level);
        readAtts(element, h);
        readContentList(h, element);
        return h;
    }

    public Ul readUl(Element element) {
        Ul ul = null;
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("li")) {
                    Li li = readLi(child);
                    if (li != null) {
                        if (ul == null) {
                            ul = new Ul(li);
                        } else {
                            ul.add(li);
                        }
                    }
                }
            }
        }
        if (ul != null) {
            readAtts(element, ul);
        }
        return ul;
    }

    public Li readLi(Element element) {
        List<FicheBlock> list = readFicheBlockList(element);
        int size = list.size();
        if (size == 0) {
            return null;
        }
        Li li;
        FicheBlock firstFicheBlock = list.get(0);
        if (firstFicheBlock instanceof P) {
            li = new Li((P) firstFicheBlock);
        } else {
            li = new Li(new P());
            if (li.isValidFicheBlock(firstFicheBlock)) {
                li.add(firstFicheBlock);
            }
        }
        readAtts(element, li);
        for (int i = 1; i < size; i++) {
            FicheBlock ficheBlock = list.get(i);
            if (li.isValidFicheBlock(ficheBlock)) {
                li.add(ficheBlock);
            }
        }
        return li;
    }

    public Code readCode(Element element) {
        short type = Code.typeToShort(element.getAttribute("type"));
        Code code = new Code(type);
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("ln")) {
                    code.add(readLn(child));
                }
            }
        }
        readZoneBlock(code, element);
        return code;
    }

    public Table readTable(Element element) {
        Table table = new Table();
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("tr")) {
                    table.add(readTr(child));
                } else if (tagname.equals("caption")) {
                    String caption = XMLUtils.getData(child);
                    table.getLegendeBuilder().addText(caption);
                }
            }
        }
        readZoneBlock(table, element);
        return table;
    }

    public Tr readTr(Element element) {
        Tr tr = new Tr();
        readAtts(element, tr);
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("td")) {
                    tr.add(readTd(child));
                }
            }
        }
        return tr;
    }

    public Ln readLn(Element element) {
        int indentation = 0;
        try {
            indentation = Integer.parseInt(element.getAttribute("indent"));
            if (indentation < 0) {
                indentation = 0;
            }
        } catch (NumberFormatException nfe) {
        }
        Ln ln = new Ln(XMLUtils.getData(element, false), indentation);
        readAtts(element, ln);
        return ln;
    }

    public Td readTd(Element element) {
        String typeString = element.getAttribute("type");
        Td td = new Td(Td.typeToShort(typeString));
        readAtts(element, td);
        readContentList(td, element);
        return td;
    }

    public Insert readInsert(Element element) {
        Insert insert;
        try {
            short type = Insert.typeToShort(element.getAttribute("type"));
            insert = new Insert(type);
        } catch (IllegalArgumentException iae) {
            return null;
        }
        insert.setSrc(element.getAttribute("src"));
        insert.setRef(element.getAttribute("ref"));
        String creditString = element.getAttribute("credit");
        if (creditString.length() > 0) {
            insert.getCreditBuilder().addText(creditString);
        }
        insert.setPosition(Insert.positionToString(element.getAttribute("position")));
        try {
            int width = Integer.parseInt(element.getAttribute("width"));
            insert.setWidth(width);
        } catch (NumberFormatException nfe) {
        }
        try {
            int height = Integer.parseInt(element.getAttribute("height"));
            insert.setHeight(height);
        } catch (NumberFormatException nfe) {
        }
        String albumName = element.getAttribute("album");
        if (albumName.length() > 0) {
            try {
                SubsetKey albumKey = SubsetKey.parse(SubsetKey.CATEGORY_ALBUM, albumName);
                String idString = element.getAttribute("id");
                if (idString.length() == 0) {
                    idString = element.getAttribute("idalbum");
                }
                if (idString.length() > 0) {
                    try {
                        int illustrationId = Integer.parseInt(idString);
                        String albumDimName = element.getAttribute("albumdim");
                        insert.setSubsetItem(albumKey, illustrationId, albumDimName);
                    } catch (NumberFormatException nfe) {
                    }
                }
            } catch (ParseException pe) {
            }
        } else {
            String addendaName = element.getAttribute("addenda");
            if (addendaName.length() > 0) {
                try {
                    SubsetKey addendaKey = SubsetKey.parse(SubsetKey.CATEGORY_ADDENDA, addendaName);
                    String idString = element.getAttribute("id");
                    if (idString.length() > 0) {
                        try {
                            int documentId = Integer.parseInt(idString);
                            insert.setSubsetItem(addendaKey, documentId, "");
                        } catch (NumberFormatException nfe) {
                        }
                    }
                } catch (ParseException pe) {
                }
            }
        }
        readZoneBlock(insert, element);
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("alt")) {
                    readContentList(insert.getAltBuilder(), child);
                } else if (tagname.equals("credit")) {
                    readContentList(insert.getCreditBuilder(), child);
                }
            }
        }
        return insert;
    }

    public Div readDiv(Element element) {
        Div div = new Div();
        String langString = element.getAttribute("xml:lang");
        if (langString.length() > 0) {
            try {
                Lang lang = Lang.parse(langString);
                div.setLang(lang);
            } catch (ParseException pe) {
            }
        }
        readZoneBlock(div, element);
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("fbl")) {
                    List<FicheBlock> list = readFicheBlockList(child);
                    for (FicheBlock ficheBlock : list) {
                        div.add(ficheBlock);
                    }
                }
            }
        }
        return div;
    }

    public Cdatadiv readCdatadiv(Element element) {
        Cdatadiv cdatadiv = new Cdatadiv();
        readZoneBlock(cdatadiv, element);
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("cdata")) {
                    String cdata = XMLUtils.getRawData(child);
                    cdatadiv.setCdata(htmlCleaner.cleanHtml(cdata));
                    break;
                }
            }
        }
        return cdatadiv;
    }

    private void readZoneBlock(ZoneBlock zoneBlock, Element element) {
        readAtts(element, zoneBlock);
        NodeList liste = element.getChildNodes();
        for (int j = 0; j < liste.getLength(); j++) {
            if (liste.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) liste.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("numero")) {
                    readContentList(zoneBlock.getNumeroBuilder(), child);
                } else if (tagname.equals("legende")) {
                    readContentList(zoneBlock.getLegendeBuilder(), child);
                }
            }
        }
    }

    public S readS(Element element) {
        short type;
        try {
            type = S.typeToShort(element.getAttribute("type"));
        } catch (IllegalArgumentException iae) {
            return null;
        }
        S s = new S(type);
        s.setValue(XMLUtils.getData(element, false));
        String ref = element.getAttribute("ref");
        if (type == S.IMAGE) {
            String src = element.getAttribute("src");
            if (src.length() > 0) {
                s.setRef(src);
                if (ref.length() > 0) {
                    s.putAtt("href", ref);
                }
            } else {
                s.setRef(ref);
            }
        } else {
            s.setRef(ref);
        }
        readSAtts(element, s);
        return s;
    }

    private void readContentList(TextContentBuilder pb, Element element) {
        NodeList list = element.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            short type = list.item(i).getNodeType();
            if (type == Node.TEXT_NODE) {
                String data = ((Text) list.item(i)).getData();
                data = XMLUtils.cleanString(data, false);
                if (data.length() > 0) {
                    pb.addText(data);
                }
            } else if (type == Node.ELEMENT_NODE) {
                Element child = (Element) list.item(i);
                if (child.getTagName().equals("s")) {
                    S span = readS(child);
                    if (span != null) {
                        pb.addS(span);
                    } else {
                        String data = XMLUtils.getData(child, false);
                        if (data.length() > 0) {
                            pb.addText(data);
                        }
                    }
                }
            }
        }
    }

    private FicheItems readFicheItems(Element element) {
        List<FicheItem> list = new ArrayList<FicheItem>();
        NodeList liste = element.getChildNodes();
        for (int j = 0; j < liste.getLength(); j++) {
            if (liste.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) liste.item(j);
                FicheItem ficheItem = readFicheItem(child);
                if (ficheItem != null) {
                    try {
                        list.add(ficheItem);
                    } catch (IllegalArgumentException iae) {
                    }
                }
            }
        }
        return FicheUtils.toFicheItems(list);
    }

    public List<FicheBlock> readFicheBlockList(Element element) {
        List<FicheBlock> list = new ArrayList<FicheBlock>();
        NodeList liste = element.getChildNodes();
        for (int j = 0; j < liste.getLength(); j++) {
            if (liste.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) liste.item(j);
                FicheBlock ficheBlock = readFicheBlock(child);
                if (ficheBlock != null) {
                    list.add(ficheBlock);
                }
            }
        }
        return list;
    }

    private void readAtts(Element element, AttConsumer attConsumer) {
        NamedNodeMap nodeMap = element.getAttributes();
        if (nodeMap != null) {
            int length = nodeMap.getLength();
            for (int i = 0; i < length; i++) {
                Attr attr = (Attr) nodeMap.item(i);
                String name = attr.getName();
                if ((name.startsWith("att-")) && (name.length() > 4)) {
                    attConsumer.putAtt(name.substring(4), attr.getValue());
                } else if (name.equals("class")) {
                    attConsumer.putAtt("class", attr.getValue());
                } else if (name.equals("htmlid")) {
                    attConsumer.putAtt("id", attr.getValue());
                } else if (name.equals("rowspan")) {
                    attConsumer.putAtt("rowspan", attr.getValue());
                } else if (name.equals("colspan")) {
                    attConsumer.putAtt("colspan", attr.getValue());
                }
            }
        }
    }

    private void readSAtts(Element element, AttConsumer attConsumer) {
        StringBuilder classBuf = new StringBuilder();
        NamedNodeMap nodeMap = element.getAttributes();
        if (nodeMap != null) {
            int length = nodeMap.getLength();
            for (int i = 0; i < length; i++) {
                Attr attr = (Attr) nodeMap.item(i);
                String name = attr.getName();
                if ((name.startsWith("att-")) && (name.length() > 4)) {
                    attConsumer.putAtt(name.substring(4), attr.getValue());
                } else if (name.equals("class")) {
                    if (classBuf.length() > 0) {
                        classBuf.append(' ');
                    }
                    classBuf.append(attr.getValue());
                } else if (name.equals("subtype")) {
                    if (classBuf.length() > 0) {
                        classBuf.append(' ');
                    }
                    classBuf.append(attr.getValue());
                } else if (name.equals("htmlid")) {
                    attConsumer.putAtt("id", attr.getValue());
                } else if (name.equals("title")) {
                    attConsumer.putAtt("title", attr.getValue());
                } else if (name.equals("lang")) {
                    attConsumer.putAtt("hreflang", attr.getValue());
                }
            }
        }
        if (classBuf.length() > 0) {
            attConsumer.putAtt("class", classBuf.toString());
        }
    }

}
