DataComfortXMLReader.java

package nl.b3p.brmo.loader.util;

import java.util.ArrayList;
import java.util.List;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Source;
import org.geotools.gml.stream.XmlStreamGeometryReader;
import org.javasimon.SimonManager;
import org.javasimon.Split;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.MultiPolygon;
import org.locationtech.jts.geom.Polygon;

/**
 * @author Boy de Wit
 */
public class DataComfortXMLReader {

  private static final int LEVEL_ROOT = 0;
  private static final int LEVEL_DATA = 1;
  private static final int LEVEL_COMFORT = 2;
  private static final int LEVEL_TABLE = 3;
  private static final int LEVEL_DELETE = 4;
  private final XMLInputFactory xif = XMLInputFactory.newInstance();

  public DataComfortXMLReader() {
    xif.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); // Coalesce characters
    xif.setProperty(
        XMLInputFactory.SUPPORT_DTD,
        Boolean.FALSE); // No XML entity expansions or external entities
  }

  public List<TableData> readDataXML(Source dataXML) throws Exception {
    Split split = SimonManager.getStopwatch("b3p.util.datacomfortxmlreader").start();

    XMLStreamReader xer = xif.createXMLStreamReader(dataXML);
    XmlStreamGeometryReader geometryReader = new XmlStreamGeometryReader(xer);

    int level = LEVEL_ROOT;
    List<TableData> list = new ArrayList<>();
    TableData data = null;
    TableRow row = null;
    boolean inComfortData = false;
    boolean inDeleteData = false;

    root:
    while (xer.hasNext()) {
      xer.nextTag();
      String tag = xer.getLocalName();
      switch (level) {
        case LEVEL_ROOT:
          if ("data".equals(tag)) {
            level = LEVEL_DATA;
          }
          break;

        case LEVEL_DATA:
          if (xer.isEndElement()) {
            break root;
          }

          if ("comfort".equals(tag)) {
            String comfortSearchTable = xer.getAttributeValue(null, "search-table");
            String comfortSearchColumn = xer.getAttributeValue(null, "search-column");
            String comfortSearchValue = xer.getAttributeValue(null, "search-value");
            String snapshotDate = xer.getAttributeValue(null, "snapshot-date");
            data =
                new TableData(
                    comfortSearchTable, comfortSearchColumn, comfortSearchValue, snapshotDate);
            level = LEVEL_COMFORT;
            inComfortData = true;
            inDeleteData = false;
          } else if ("delete".equals(tag)) {
            data = new TableData();
            level = LEVEL_DELETE;
            inComfortData = false;
            inDeleteData = true;
          } else {
            row = new TableRow();
            row.setTable(tag);
            row.setIgnoreDuplicates("yes".equals(xer.getAttributeValue(null, "ignore-duplicates")));
            String beginDatumColumn = xer.getAttributeValue(null, "column-dat-beg-geldh");
            String eindeDatumColumn = xer.getAttributeValue(null, "column-datum-einde-geldh");
            row.setColumnDatumBeginGeldigheid(beginDatumColumn);
            row.setColumnDatumEindeGeldigheid(eindeDatumColumn);
            data = new TableData(row);
            level = LEVEL_TABLE;
            inComfortData = false;
            inDeleteData = false;
          }
          list.add(data);
          break;

        case LEVEL_COMFORT:
          if (xer.isEndElement() && "comfort".equals(tag)) {
            level = LEVEL_DATA;
          } else {
            row = new TableRow();
            row.setTable(tag);

            data.addRow(row);

            level = LEVEL_TABLE;
          }
          break;
        case LEVEL_DELETE:
          if (xer.isEndElement() && "delete".equals(tag)) {
            level = LEVEL_DATA;
          } else {
            row = new TableRow();
            row.setTable(tag);
            data.addRow(row);
            level = LEVEL_TABLE;
          }
          break;
        case LEVEL_TABLE:
          if (xer.isEndElement()) {
            if (inComfortData) {
              level = LEVEL_COMFORT;
            } else if (inDeleteData) {
              level = LEVEL_DELETE;
            } else {
              level = LEVEL_DATA;
            }
          } else {
            data = list.get(list.size() - 1);
            row.getColumns().add(tag);
            String alleenArchief = xer.getAttributeValue(null, "alleen-archief");
            if ("true".equals(alleenArchief)) {
              row.setAlleenArchiefColumn(tag);
            }
            // Detecteer XML elementen of text
            xer.next();
            // Skip whitespace before a possible element
            while (xer.isWhiteSpace()) {
              xer.next();
            }
            if (xer.isStartElement()) {
              Split split2 =
                  SimonManager.getStopwatch("b3p.util.datacomfortxmlreader.parsegml").start();
              Geometry geom = geometryReader.readGeometry();
              // TODO/HACK force polygon to multi-polygon
              if (geom instanceof Polygon) {
                geom = new MultiPolygon(new Polygon[] {(Polygon) geom}, geom.getFactory());
              }
              // Note: this linearizes curves, we could use a WKTWriter2 instead!
              row.getValues().add(geom.toString());
              split2.stop();
              // After parsing geometry, move to end of enclosing tag
              while (!(tag.equals(xer.getLocalName()) && xer.isEndElement())) {
                xer.nextTag();
              }
            } else if (xer.isCharacters()) {
              StringBuilder t = new StringBuilder();
              do {
                t.append(xer.getText());
                xer.next();
              } while (xer.isCharacters());
              row.getValues().add(t.toString());
            } else {
              assert (xer.isEndElement());
              row.getValues().add(null);
            }
          }
          break;
      }
    }
    split.stop();
    return list;
  }
}