com.nridge.core.base.io.xml.DataTableXML.java Source code

Java tutorial

Introduction

Here is the source code for com.nridge.core.base.io.xml.DataTableXML.java

Source

/*
 * NorthRidge Software, LLC - Copyright (c) 2019.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.nridge.core.base.io.xml;

import com.nridge.core.base.field.Field;
import com.nridge.core.base.field.FieldRow;
import com.nridge.core.base.field.data.DataBag;
import com.nridge.core.base.field.data.DataField;
import com.nridge.core.base.field.data.DataTable;

import com.nridge.core.base.std.StrUtl;
import com.nridge.core.base.std.XMLUtl;
import com.nridge.core.base.io.IO;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Map;

/**
 * The DataTableXML provides a collection of methods that can generate/load
 * an XML representation of a {@link DataTable} object.
 *
 * @author Al Cole
 * @since 1.0
 */
public class DataTableXML implements DOMInterface {
    private int mContextTotal;
    private int mContextStart;
    private int mContextLimit;
    private DataTable mDataTable;
    private boolean mSaveFieldsWithoutValues;

    public DataTableXML() {
        mDataTable = new DataTable();
    }

    public DataTableXML(DataTable aDataTable) {
        mDataTable = aDataTable;
    }

    public DataTableXML(DataTable aDataTable, int aStart, int aLimit, int aTotal) {
        mContextStart = aStart;
        mContextLimit = aLimit;
        mContextTotal = aTotal;
        mDataTable = aDataTable;
    }

    public DataTable getTable() {
        return mDataTable;
    }

    public void setContext(int aStart, int aCount, int aSize) {
        mContextTotal = aSize;
        mContextStart = aStart;
        mContextLimit = aCount;
    }

    public int getContextTotal() {
        return mContextTotal;
    }

    public int getContextStart() {
        return mContextStart;
    }

    public int getContextLimit() {
        return mContextLimit;
    }

    /**
     * Assigning this to <i>true</i> will ensure all fields are written
     * (regardless of them having values assigned).  Use this method
     * to generate schema files.
     *
     * @param aFlag <i>true</i> or <i>false</i>
     */
    public void setSaveFieldsWithoutValues(boolean aFlag) {
        mSaveFieldsWithoutValues = aFlag;
    }

    /**
     * Saves the previous assigned bag/table (e.g. via constructor or set method)
     * to the print writer stream wrapped in a tag name specified in the parameter.
     *
     * @param aPW            PrintWriter stream instance.
     * @param aTagName       Tag name.
     * @param anIndentAmount Indentation count.
     * @throws java.io.IOException I/O related exception.
     */
    @Override
    public void save(PrintWriter aPW, String aTagName, int anIndentAmount) throws IOException {
        String cellValue;
        DataField dataField;
        int columnCount, rowCount;

        rowCount = mDataTable.rowCount();
        columnCount = mDataTable.columnCount();
        String tagName = StringUtils.remove(aTagName, StrUtl.CHAR_SPACE);
        IOXML.indentLine(aPW, anIndentAmount);
        aPW.printf("<%s", tagName);
        IOXML.writeAttrNameValue(aPW, "type", IO.extractType(mDataTable.getClass().getName()));
        IOXML.writeAttrNameValue(aPW, "name", mDataTable.getName());
        IOXML.writeAttrNameValue(aPW, "dimensions", String.format("%d cols x %d rows", columnCount, rowCount));
        IOXML.writeAttrNameValue(aPW, "version", IO.DATATABLE_XML_FORMAT_VERSION);
        for (Map.Entry<String, String> featureEntry : mDataTable.getFeatures().entrySet())
            IOXML.writeAttrNameValue(aPW, featureEntry.getKey(), featureEntry.getValue());
        aPW.printf(">%n");

        if ((mContextTotal != 0) || (mContextLimit != 0)) {
            IOXML.indentLine(aPW, anIndentAmount + 2);
            aPW.printf("<Context");
            IOXML.writeAttrNameValue(aPW, "start", mContextStart);
            IOXML.writeAttrNameValue(aPW, "limit", mContextLimit);
            IOXML.writeAttrNameValue(aPW, "total", mContextTotal);
            aPW.printf("/>%n");
        }
        DataBag dataBag = new DataBag(mDataTable.getColumnBag());
        if (mSaveFieldsWithoutValues)
            dataBag.setAssignedFlagAll(true);
        DataBagXML dataBagXML = new DataBagXML(dataBag);
        dataBagXML.save(aPW, "Columns", anIndentAmount + 2);
        if (rowCount > 0) {
            IOXML.indentLine(aPW, anIndentAmount + 2);
            aPW.printf("<Rows");
            IOXML.writeAttrNameValue(aPW, "count", rowCount);
            aPW.printf(">%n");
            for (int row = 0; row < rowCount; row++) {
                IOXML.indentLine(aPW, anIndentAmount + 3);
                aPW.printf("<Row>%n");
                IOXML.indentLine(aPW, anIndentAmount + 4);
                for (int col = 0; col < columnCount; col++) {
                    dataField = mDataTable.getFieldByRowCol(row, col);
                    cellValue = dataField.collapse();
                    if (StringUtils.isEmpty(cellValue))
                        aPW.printf("<C/>");
                    else
                        aPW.printf("<C>%s</C>", StringEscapeUtils.escapeXml10(cellValue));
                }
                aPW.printf("%n");
                IOXML.indentLine(aPW, anIndentAmount + 3);
                aPW.printf("</Row>%n");
            }
            IOXML.indentLine(aPW, anIndentAmount + 2);
            aPW.printf("</Rows>%n");
        }
        IOXML.indentLine(aPW, anIndentAmount);
        aPW.printf("</%s>%n", tagName);
    }

    /**
     * Saves the previous assigned bag/table (e.g. via constructor or set method)
     * to the print writer stream specified as a parameter.
     *
     * @param aPW PrintWriter stream instance.
     * @throws java.io.IOException I/O related exception.
     */
    @Override
    public void save(PrintWriter aPW) throws IOException {
        save(aPW, "DataTable", 0);
    }

    /**
     * Saves the previous assigned bag/table (e.g. via constructor or set method)
     * to the path/file name specified as a parameter.
     *
     * @param aPathFileName Absolute file name.
     * @throws java.io.IOException I/O related exception.
     */
    @Override
    public void save(String aPathFileName) throws IOException {
        try (PrintWriter printWriter = new PrintWriter(aPathFileName, StrUtl.CHARSET_UTF_8)) {
            save(printWriter);
        }
    }

    private void loadRow(Element anElement) throws IOException {
        Node nodeItem;
        String mvDelimiter;
        DataField dataField;
        String nodeName, nodeValue;

        int columnOffset = 0;
        FieldRow fieldRow = mDataTable.newRow();
        int columnCount = mDataTable.columnCount();

        NodeList nodeList = anElement.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            nodeItem = nodeList.item(i);

            if (nodeItem.getNodeType() != Node.ELEMENT_NODE)
                continue;

            nodeName = nodeItem.getNodeName();
            if (nodeName.equalsIgnoreCase("C")) {
                nodeValue = XMLUtl.getNodeStrValue(nodeItem);
                if ((StringUtils.isNotEmpty(nodeValue)) && (columnOffset < columnCount)) {
                    dataField = mDataTable.getColumn(columnOffset);
                    if (dataField.isMultiValue()) {
                        mvDelimiter = dataField.getFeature(Field.FEATURE_MV_DELIMITER);
                        if (StringUtils.isNotEmpty(mvDelimiter))
                            fieldRow.setValues(columnOffset, StrUtl.expandToList(nodeValue, mvDelimiter.charAt(0)));
                        else
                            fieldRow.setValues(columnOffset, StrUtl.expandToList(nodeValue, StrUtl.CHAR_PIPE));
                    } else
                        fieldRow.setValue(columnOffset, nodeValue);
                }
                columnOffset++;
            }
        }
        mDataTable.addRow(fieldRow);
    }

    private void loadRows(Element anElement) throws IOException {
        Node nodeItem;
        String nodeName;
        Element nodeElement;

        NodeList nodeList = anElement.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            nodeItem = nodeList.item(i);

            if (nodeItem.getNodeType() != Node.ELEMENT_NODE)
                continue;

            nodeName = nodeItem.getNodeName();
            if (nodeName.equalsIgnoreCase("Row")) {
                nodeElement = (Element) nodeItem;
                loadRow(nodeElement);
            }
        }
    }

    /**
     * Parses an XML DOM element and loads it into a bag/table.
     *
     * @param anElement DOM element.
     * @throws java.io.IOException I/O related exception.
     */
    @Override
    public void load(Element anElement) throws IOException {
        Node nodeItem;
        Attr nodeAttr;
        Element nodeElement;
        String nodeName, nodeValue, attrValue;

        attrValue = anElement.getAttribute("name");
        if (StringUtils.isNotEmpty(attrValue))
            mDataTable.setName(attrValue);

        NamedNodeMap namedNodeMap = anElement.getAttributes();
        int attrCount = namedNodeMap.getLength();
        for (int attrOffset = 0; attrOffset < attrCount; attrOffset++) {
            nodeAttr = (Attr) namedNodeMap.item(attrOffset);
            nodeName = nodeAttr.getNodeName();
            nodeValue = nodeAttr.getNodeValue();

            if (StringUtils.isNotEmpty(nodeValue)) {
                if (!StringUtils.equalsIgnoreCase(nodeName, "name"))
                    mDataTable.addFeature(nodeName, nodeValue);
            }
        }

        NodeList nodeList = anElement.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            nodeItem = nodeList.item(i);

            if (nodeItem.getNodeType() != Node.ELEMENT_NODE)
                continue;

            nodeName = nodeItem.getNodeName();
            if (nodeName.equalsIgnoreCase("Context")) {
                nodeElement = (Element) nodeItem;
                attrValue = nodeElement.getAttribute("start");
                if (StringUtils.isNumeric(attrValue))
                    mContextStart = Integer.parseInt(attrValue);
                attrValue = nodeElement.getAttribute("limit");
                if (StringUtils.isNumeric(attrValue))
                    mContextLimit = Integer.parseInt(attrValue);
                attrValue = nodeElement.getAttribute("total");
                if (StringUtils.isNumeric(attrValue))
                    mContextTotal = Integer.parseInt(attrValue);
            } else if (nodeName.equalsIgnoreCase("Columns")) {
                nodeElement = (Element) nodeItem;
                DataBagXML dataBagXML = new DataBagXML();
                dataBagXML.load(nodeElement);
                DataBag dataBag = dataBagXML.getBag();
                dataBag.setName(mDataTable.getName());
                mDataTable = new DataTable(dataBag);
            } else if (nodeName.equalsIgnoreCase("Rows")) {
                nodeElement = (Element) nodeItem;
                loadRows(nodeElement);
            }
        }
    }

    /**
     * Parses an XML DOM element and loads it into a bag/table.
     *
     * @param anIS Input stream.
     * @throws java.io.IOException                            I/O related exception.
     * @throws javax.xml.parsers.ParserConfigurationException XML parser related exception.
     * @throws org.xml.sax.SAXException                       XML parser related exception.
     */
    @Override
    public void load(InputStream anIS)
            throws ParserConfigurationException, IOException, SAXException, TransformerException {
        DocumentBuilderFactory docBldFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = docBldFactory.newDocumentBuilder();
        InputSource inputSource = new InputSource(anIS);
        Document xmlDocument = docBuilder.parse(inputSource);
        xmlDocument.getDocumentElement().normalize();

        load(xmlDocument.getDocumentElement());
    }

    /**
     * Parses an XML file identified by the path/file name parameter
     * and loads it into a bag/table.
     *
     * @param aPathFileName Absolute file name.
     * @throws java.io.IOException                            I/O related exception.
     * @throws javax.xml.parsers.ParserConfigurationException XML parser related exception.
     * @throws org.xml.sax.SAXException                       XML parser related exception.
     */
    @Override
    public void load(String aPathFileName) throws IOException, ParserConfigurationException, SAXException {
        File xmlFile = new File(aPathFileName);
        if (!xmlFile.exists())
            throw new IOException(aPathFileName + ": Does not exist.");

        DocumentBuilderFactory docBldFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = docBldFactory.newDocumentBuilder();
        Document xmlDocument = docBuilder.parse(new File(aPathFileName));
        xmlDocument.getDocumentElement().normalize();

        load(xmlDocument.getDocumentElement());
    }
}