All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gdv.xport.util.GdvXmlFormatter Maven / Gradle / Ivy

Go to download

gdv-xport-lib ist die Java-Bibliothek fuer den Umgang mit dem GDV-Format. Sie erleichtert den Export und Export dieses Datenformats.

The newest version!
/*
 * Copyright (c) 2021 by Oli B.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * (c)reated 27.03.2021 by Oli B. ([email protected])
 */

package gdv.xport.util;

import gdv.xport.config.Config;
import gdv.xport.config.ConfigException;
import gdv.xport.feld.*;
import gdv.xport.satz.Satz;
import gdv.xport.satz.Teildatensatz;
import javanet.staxutils.IndentingXMLStreamWriter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.*;

/**
 * Diese Klasse orientiert an sich an der GDV-XML-Beschreibung fuer das
 * Ausgabeformat. Es kann u.a. dazu benutzt werden, um aus einem Datensatz
 * mit Enum-Beschreibung die entspechende XML-Beschreibung zu bekommen.
 * 

* Mit v6 soll die Beschreibung eigener Datensaetze mittels Enums durch * XML-basierte Beschreibungen abgeloest werden. Fuer dieses Ziel ist * diese Klasse ein Baustein dazu. *

* * @author oliver ([email protected]) * @since 5.0 (27.03.2021) */ public final class GdvXmlFormatter extends AbstractFormatter { private static final Logger LOG = LogManager.getLogger(GdvXmlFormatter.class); private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance(); private static final String DEFAULT_INFO = "(c)reated by gdv-xport at " + LocalDate.now(); private XMLStreamWriter xmlStreamWriter; private final Map felder = new HashMap<>(); private final String stand; /** * Default-Konstruktor. */ public GdvXmlFormatter() { this(System.out); } /** * Der Konstruktor fuer die normale Arbeit. * * @param writer the writer */ public GdvXmlFormatter(final Writer writer) { this(writer, DEFAULT_INFO); } /** * Der Konstruktor fuer die normale Arbeit. * Als Info erscheint dabei der Zeitpunkt der Generierung (analog zum * {@link XmlFormatter}, wo es als Kommentar ans Ende geschrieben wird). * * @param writer the writer * @param stand Info, die nach dem XML-Header steht */ public GdvXmlFormatter(final Writer writer, final String stand) { super(writer); this.stand = stand; this.xmlStreamWriter = createXMLStreamWriter(writer, this.stand); } /** * Der Konstruktor fuer einen {@link OutputStream}. * * @param ostream z.B. System.out * @param stand Datum, ab dem erzeugte XML-Beschreibung gilt (Format TT.MM.JJJJ) z.B. * "01.07.2018".
* Dieser Wert erscheint in Analogie zur GDV-XML-Beschreibung am Beginn der * XML-Beschreibung in einem Tag: * <info><stand>...</stand></info> */ public GdvXmlFormatter(final OutputStream ostream, final String stand) { this(ostream, Config.getInstance().withProperty("gdv.export.xml.stand", stand)); } /** * Der Konstruktor fuer einen {@link OutputStream}. * Als Info erscheint dabei der Zeitpunkt der Generierung (analog zum * {@link XmlFormatter}, wo es als Kommentar ans Ende geschrieben wird). * * @param ostream z.B. System.out */ public GdvXmlFormatter(final OutputStream ostream) { this(ostream, DEFAULT_INFO); } /** * Der Konstruktor fuer einen {@link OutputStream} zusammen mit einem * {@link Config}-Objekt fuer weitere Einstellungen. * * @param ostream z.B. System.out * @param config mit weiteren Einstellungen. So kann man ueber die Property * "gdv.export.xml.stand" z.B. den Stand fuer die erzeugte * XML-Beschreibung mitgeben. * Dieser Wert erscheint in Analogie zur GDV-XML-Beschreibung * am Beginn der XML-Beschreibung in einem Tag: * <info><stand>...</stand></info> * @since 5.3 */ public GdvXmlFormatter(final OutputStream ostream, final Config config) { super(new OutputStreamWriter(ostream, Config.DEFAULT_ENCODING), config); this.stand = config.getProperty("gdv.export.xml.stand", DEFAULT_INFO); this.xmlStreamWriter = createXMLStreamWriter(ostream, this.stand); } private static XMLStreamWriter writeHead(XMLStreamWriter writer, String gueltigAbDatum) throws XMLStreamException { XMLStreamWriter indentingWriter = toIndentingStreamWriter(writer); indentingWriter.writeStartDocument(StandardCharsets.ISO_8859_1.toString(), "1.0"); indentingWriter.writeStartElement("service"); writeInfo(gueltigAbDatum, indentingWriter); indentingWriter.writeStartElement("satzarten"); return indentingWriter; } private static void writeInfo(String gueltigAbDatum, XMLStreamWriter indentingWriter) throws XMLStreamException { // Das folgende Info-Tag orientiert an sich an der GDV-XML-Beschreibung indentingWriter.writeStartElement("info"); indentingWriter.writeStartElement("stand"); indentingWriter.writeCharacters(gueltigAbDatum); indentingWriter.writeEndElement(); indentingWriter.writeEndElement(); } @Override public void setWriter(Writer writer) { super.setWriter(writer); this.xmlStreamWriter = createXMLStreamWriter(writer, this.stand); } @Override public void setWriter(OutputStream ostream) { super.setWriter(ostream); this.xmlStreamWriter = createXMLStreamWriter(ostream, this.stand); } /** * Hierueber werden noch die Felder-Definitionen und der Abspann * rausgeschrieben, ehe die Writer-Resource geschlossen wird. * * @throws IOException falls was schief geht */ @Override public void close() throws IOException { try { xmlStreamWriter.writeEndElement(); writeFelder(); xmlStreamWriter.writeEndElement(); xmlStreamWriter.writeEndDocument(); xmlStreamWriter.flush(); xmlStreamWriter.close(); } catch (XMLStreamException ex) { throw new IOException("cannot close " + xmlStreamWriter, ex); } finally { super.close(); } } /** * Ausgabe eines Datensatzes als XML. * * @param satz der auszugebende (Daten-)Satz */ @Override public void write(final Satz satz) throws IOException { try { writeComment(satz.toShortString()); xmlStreamWriter.writeStartElement("satzart"); writeKennzeichnung(satz); writeElement("version", satz.getSatzversion().getInhalt()); write(satz.getTeildatensaetze()); xmlStreamWriter.writeEndElement(); } catch (XMLStreamException ex) { throw new IOException("cannot format " + satz, ex); } } private void writeKennzeichnung(Satz satz) throws XMLStreamException { xmlStreamWriter.writeStartElement("kennzeichnung"); writeReferenz(satz.getSatzartFeld()); writeSparte(satz); writeSatznummer(satz); xmlStreamWriter.writeEndElement(); } private void write(List teildatensaetze) throws XMLStreamException { for (Teildatensatz tds : teildatensaetze) { writeComment(tds.toShortString()); xmlStreamWriter.writeEmptyElement("satzanfang"); xmlStreamWriter.writeAttribute("teilsatz", tds.getSatznummer().getInhalt()); for (Feld feld : getFelderOhneLuecken(tds)) { writeComment(feld.toShortString()); writeReferenz(feld); felder.putIfAbsent(toFeldReferenzId(feld), feld); } xmlStreamWriter.writeEmptyElement("satzende"); xmlStreamWriter.flush(); } } private List getFelderOhneLuecken(Teildatensatz tds) { List felder = new ArrayList<>(); int adresse = 1; for (Feld feld : tds.getFelder()) { if (adresse < feld.getByteAdresse()) { felder.add(new AlphaNumFeld(Bezeichner.of("Leerstellen"), feld.getByteAdresse() - adresse, ByteAdresse.of(adresse))); LOG.info("An Adresse {} wurde {} mit Leerstellen aufgefuellt.", adresse, tds.toShortString()); } felder.add(feld); adresse = feld.getEndAdresse() + 1; } return felder; } private void writeReferenz(Feld feld) throws XMLStreamException { Bezeichner bezeichner = feld.getBezeichner(); xmlStreamWriter.writeStartElement("feldreferenz"); xmlStreamWriter.writeAttribute("referenz", toFeldReferenzId(feld)); writeElement("name", bezeichner.getName()); writeElement("technischerName", bezeichner.getTechnischerName()); if (feld.hasValue()) { writeElement("auspraegung", feld.getInhalt().trim()); } if (feld instanceof AlphaNumFeld) { writeAlignment((AlphaNumFeld) feld); } else if (feld instanceof Datum) { writeElement("bemerkung", ((Datum) feld).getFormat()); } xmlStreamWriter.writeEndElement(); } private void writeAlignment(AlphaNumFeld feld) throws XMLStreamException { if (feld.getAusrichtung() == Align.RIGHT) { writeElement("bemerkung", "rechtsbuendig"); } } private String toFeldReferenzId(Feld feld) { return String.format("%03d-%03d-%s-%s", feld.getByteAdresse(), feld.getEndAdresse(), feld.getBezeichner().getTechnischerName(), Datentyp.asString(feld)); } private void writeSparte(Satz satz) throws XMLStreamException { if (satz.getSatzTyp().hasSparte()) { Feld sparteFeld = new Feld(Bezeichner.SPARTE, ByteAdresse.of(11), satz.getSatzTyp().getSparteMitArt(), Align.LEFT); writeReferenz(sparteFeld); } } private void writeSatznummer(Satz satz) throws XMLStreamException { SatzTyp satzTyp = satz.getSatzTyp(); if (satzTyp.hasGdvSatzartNummer()) { Feld satznummer = new Feld(Bezeichner.SATZNUMMER, ByteAdresse.of(256), Integer.toString(satzTyp.getGdvSatzartNummer()), Align.LEFT); writeReferenz(satznummer); } } private void writeFelder() throws XMLStreamException { TreeMap sorted = new TreeMap<>(felder); Set> mappings = sorted.entrySet(); xmlStreamWriter.writeStartElement("felder"); for (Map.Entry entry : mappings) { write(entry.getValue()); } xmlStreamWriter.writeEndElement(); } private void write(Feld feld) throws XMLStreamException { xmlStreamWriter.writeStartElement("feld"); xmlStreamWriter.writeAttribute("referenz", toFeldReferenzId(feld)); writeElement("name", feld.getBezeichner().getName()); writeElement("bytes", Integer.toString(feld.getAnzahlBytes())); writeElement("datentyp", Datentyp.asString(feld)); if (feld instanceof NumFeld) { writeNachkommastellen((NumFeld) feld); } xmlStreamWriter.writeEndElement(); } private void writeNachkommastellen(NumFeld feld) throws XMLStreamException { if (feld.getNachkommastellen() > 0) { writeElement("nachkommastellen", Integer.toString(feld.getNachkommastellen())); } } private void writeElement(String tag, String value) throws XMLStreamException { xmlStreamWriter.writeStartElement(tag); xmlStreamWriter.writeCharacters(value); xmlStreamWriter.writeEndElement(); } private void writeComment(String comment) throws XMLStreamException { xmlStreamWriter.writeComment(" " + comment.trim() + " "); } private static XMLStreamWriter createXMLStreamWriter(OutputStream textWriter, String gdvSatzVersion) { try { XMLStreamWriter out = XML_OUTPUT_FACTORY.createXMLStreamWriter(textWriter, Config.DEFAULT_ENCODING.name()); return writeHead(out, gdvSatzVersion); } catch (XMLStreamException ex) { throw new IllegalArgumentException("can't create XmlStreamWriter with " + textWriter, ex); } catch (FactoryConfigurationError ex) { throw new ConfigException("XML problems", ex); } } private static XMLStreamWriter createXMLStreamWriter(Writer textWriter, String gdvSatzVersion) { try { XMLStreamWriter out = XML_OUTPUT_FACTORY.createXMLStreamWriter(textWriter); return writeHead(out, gdvSatzVersion); } catch (XMLStreamException ex) { throw new IllegalArgumentException("can't create XmlStreamWriter with " + textWriter, ex); } catch (FactoryConfigurationError ex) { throw new ConfigException("XML problems", ex); } } //https://stackoverflow.com/questions/10105187/java-indentingxmlstreamwriter-alternative private static IndentingXMLStreamWriter toIndentingStreamWriter(XMLStreamWriter out) { IndentingXMLStreamWriter indentWriter = new IndentingXMLStreamWriter(out); indentWriter.setIndent("\t"); return indentWriter; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy