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

gdv.xport.satz.Datensatz 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.

There is a newer version: 7.2.2
Show newest version
/*
 * Copyright (c) 2009-2020 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 12.10.2009 by Oli B. ([email protected])
 */

package gdv.xport.satz;

import com.fasterxml.jackson.annotation.JsonIgnore;
import gdv.xport.config.Config;
import gdv.xport.feld.*;
import gdv.xport.io.ImportException;
import gdv.xport.io.PushbackLineNumberReader;
import gdv.xport.satz.feld.common.Kopffelder1bis7;
import gdv.xport.satz.feld.common.TeildatensatzNummer;
import gdv.xport.satz.feld.common.WagnisartLeben;
import gdv.xport.util.SatzTyp;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.io.PushbackReader;

import static gdv.xport.feld.Bezeichner.*;

/**
 * Datensatz ist von {@link Satz} abgeleitet, enthaelt aber zusaetzlich noch
 * die Sparte.
 *
 * @author oliver
 * @since 12.10.2009
 */
public class Datensatz extends Satz {

	private static final Logger LOG = LogManager.getLogger(Datensatz.class);
	/** 3 Zeichen, Byte 11 - 13. */
  	private final NumFeld sparte = new NumFeld(Kopffelder1bis7.SPARTE);
	/** 1 Zeichen, Byte 59. */
	private final AlphaNumFeld wagnisart = new AlphaNumFeld((WAGNISART), 1, 59);

	/**
	 * Default-Konstruktor (wird zur Registrierung bei der {@link gdv.xport.util.SatzFactory}
	 * benoetigt).
	 * 

* Anm.: In {@link gdv.xport.util.SatzRegistry#getSatz(SatzTyp)} wird der * Default-Constructor per Reflection aufgerufen. Daher kann er nicht * einfach entfernt werden. *

* * @since 0.6 */ public Datensatz() { this(SatzTyp.of(0)); } /** * Instantiiert einen neuen Datensatz mit 1 Teildatensatz.
* Der Teildatensatz besteht nur aus 8 Feldern:
*
    *
  • Satzart
  • *
  • VU_NUMMER
  • *
  • BUENDELUNGSKENNZEICHEN
  • *
  • SPARTE
  • *
  • VERSICHEURUNGSSCHEINNUMMER
  • *
  • FOLGENUMMER
  • *
  • VERMITTLER
  • *
  • SATZNUMMER
  • *
*

* Anm.: Dieser Constructor wird noch von * {@link gdv.xport.util.SatzFactory#register(Class, int)} * verwendet. *

* * @param satzart z.B. 100 */ public Datensatz(final int satzart) { this(SatzTyp.of(satzart)); } /** * Instantiiert einen neuen Datensatz mit 1 Teildatensatz.
* Der Teildatensatz besteht nur aus 8 oder 9 Feldern: *
    *
  • Satzart
  • *
  • VU_NUMMER
  • *
  • BUENDELUNGSKENNZEICHEN
  • *
  • SPARTE
  • *
  • VERSICHEURUNGSSCHEINNUMMER
  • *
  • FOLGENUMMER
  • *
  • VERMITTLER
  • *
  • ART ("0220.580.X") oder WAGNISART (bei "0220.010.X")
  • *
  • SATZNUMMER
  • *
* Das Feld 4 (Sparte) im Teildatensatz wird nur bei den vordefinierten GDV-Spartensaetzen belegt. * * @param satzTyp z.B. "0210.040" (Vertragsspezifischer Teil, Haftpflicht) * @since 5.0 */ public Datensatz(SatzTyp satzTyp) { this(satzTyp, Config.getInstance()); } protected Datensatz(SatzTyp satzTyp, Config cfg) { this(satzTyp, 1, cfg); } /** * Instantiiert einen neuen Datensatz. * Die Teildatensaetze bestehen nur aus 8 oder 9 Feldern: *
    *
  • Satzart
  • *
  • VU_NUMMER
  • *
  • BUENDELUNGSKENNZEICHEN
  • *
  • SPARTE
  • *
  • VERSICHEURUNGSSCHEINNUMMER
  • *
  • FOLGENUMMER
  • *
  • VERMITTLER
  • *
  • ART ("0220.580.X") oder WAGNISART (bei "0220.010.X")
  • *
  • SATZNUMMER
  • *
* Das Feld 4 (Sparte) im Teildatensatz wird nur bei vordefinierten Spartensaetzen belegt. * * @param satzTyp z.B. "0100" (Adressteil) oder "0220.110" (Glas) * @param n Anzahl der Teildatensaetze * @since 5.0 */ public Datensatz(final SatzTyp satzTyp, final int n) { this(satzTyp, n, Config.getInstance()); } protected Datensatz(final SatzTyp satzTyp, final int n, final Config cfg) { super(satzTyp, n, cfg); this.init(satzTyp); this.setUpTeildatensaetze(); } /** * Dies ist der Copy-Constructor, mit dem man einen bestehenden Datensatz * kopieren kann. * * @param other der originale Datensatz */ public Datensatz(final Datensatz other) { super(other, other.cloneTeildatensaetze()); this.sparte.setInhalt(other.sparte.getInhalt()); this.wagnisart.setInhalt(other.wagnisart.getInhalt()); } /** * Kann von Unterklassen verwendet werden, um die Teildatensaetze * aufzusetzen. */ protected void setUpTeildatensaetze() { for (Teildatensatz tds : this.getTeildatensaetze()) { setUpTeildatensatz(tds); } } /** * Hiermit kann ein einzelner Teildatensatz aufgesetzt werden. *

* Wenn alle Datensaetze nur noch ueber Enums (Soplets) initialisiert * werden, duerfte die Inialisierung hier hinfaellig sein. *

* * @param tds der (leere) Teildatensatz * @since 0.4 */ protected void setUpTeildatensatz(final Teildatensatz tds) { setUpTeildatensatz(tds, this.sparte); } protected static void setUpTeildatensatz(final Teildatensatz tds, final NumFeld sparte) { if (!tds.hasFeld(Kopffelder1bis7.VU_NUMMER.getBezeichner()) && !tds.getFeldInhalt(Kopffelder1bis7.SATZART.getBezeichner()) .equals("9999")) { setUp(tds, Kopffelder1bis7.VU_NUMMER.getBezeichner(), Config.getInstance().getVUNr()); setUp(tds, Kopffelder1bis7.BUENDELUNGSKENNZEICHEN.getBezeichner(), new AlphaNumFeld(Kopffelder1bis7.BUENDELUNGSKENNZEICHEN)); setUp(tds, Kopffelder1bis7.SPARTE.getBezeichner(), sparte); setUp(tds, Kopffelder1bis7.VERSICHERUNGSSCHEINNUMMER.getBezeichner(), new AlphaNumFeld(Kopffelder1bis7.VERSICHERUNGSSCHEINNUMMER)); setUp(tds, Kopffelder1bis7.FOLGENUMMER.getBezeichner(), new NumFeld(Kopffelder1bis7.FOLGENUMMER)); setUp(tds, Kopffelder1bis7.VERMITTLER.getBezeichner(), new AlphaNumFeld(Kopffelder1bis7.VERMITTLER)); /** * @Oli: wenn dieser Teildatensatz via "Datensatz(final SatzTyp satzTyp, final int n)" erzeugt * wurde, handelte es sich bisher um einen "TeildatensatzEnum". Er beinhaltete immer ein * Feld "Satznummer". Mit der Aenderung in "Satz.createTeildatensaetze(final int n)" * gibt es aus "Datensatz(final SatzTyp satzTyp, final int n)" nur noch einen * "Teildatensatz". Also muss das Feld "Satznummer" wieder ergaenzt werden, falls nicht * vorhanden. **/ if (!tds.hasFeld(Bezeichner.SATZNUMMER)) { try { setUp(tds, Bezeichner.SATZNUMMER, new Satznummer(tds.getSatznummer())); } catch (IllegalArgumentException e) { /** * @Oli: "Teildatensatz.add(final Feld feld)" wirft diese Exception, wenn es beim * Einfuegen eines Feldes zur Ueberschneidung kommt. Das geschieht hier besonders * dann, wenn ein Teildatensatz per definitionem keine Satznummer hat (z.B. 0220.110 * oder 0210.030)! */ LOG.debug("Teildatensatz {} hat kein Platz fuer Satznummer", tds.toLongString()); } } LOG.trace(tds + " is set up."); } else if (tds.hasFeld(Kopffelder1bis7.SPARTE)) { tds.getFeld(Kopffelder1bis7.SPARTE.getBezeichner()).setInhalt(sparte.getInhalt()); } } private static void setUp(final Teildatensatz tds, final Bezeichner bezeichner, final Feld value) { if (!tds.hasFeld(bezeichner)) { LOG.trace("{} initialized with value {}.", tds, value); tds.add(value); } } /** * Kann von Unterklassen verwendet werden, um fehlende Felder in den * Teildatensaetze zu vervollstaendigen. Kann aber seit 1.0 nicht mehr * ueberschrieben werden, da diese Methode vom Konstruktor waehrend der * Objekt-Kreierung benoetigt wird. * * @since 0.6 */ protected final void completeTeildatensaetze() { for (Teildatensatz tds : this.getTeildatensaetze()) { setUpTeildatensatz(tds); } } /* * (non-Javadoc) * @see gdv.xport.satz.Satz#addFiller() */ @Override public void addFiller() { for (Teildatensatz tds : this.getTeildatensaetze()) { tds.add(new AlphaNumFeld((LEERSTELLEN), 213, 43)); } } /** * Dient dazu, um mit den Informationen des Satztyps Sparte und andere * Felder vorzubelegen. * * @param satztyp SatzTyp, z.B. "0220.010.13.1" * @since 5.1 */ public void init(SatzTyp satztyp) { if (satztyp.hasSparte()) { setSparte(satztyp.getSparte()); } if (satztyp.hasWagnisart()) { initWagnisart(satztyp.getWagnisart()); } if (satztyp.hasBausparenArt()) { initBausparenart(satztyp.getBausparenArt()); } } private void initWagnisart(int art) { if (!hasWagnisart()) { add(new AlphaNumFeld(WAGNISART, 1, 60)); } setFeld(WAGNISART, Integer.toString(art).substring(0, 1)); } private void initBausparenart(int art) { if (!hasBausparenArt()) { add(new AlphaNumFeld(ART1, 1, 44)); } setFeld(ART1, art); } /** * Setzt die Sparte. * * @param x z.B. 70 (Rechtsschutz) */ public void setSparte(final int x) { this.sparte.setInhalt(x); for (Teildatensatz tds : getTeildatensaetze()) { if (tds.getFelder().size() > 3) { tds.getFeld(4).setInhalt(x); } } } /** * Setzt die Sparte. Der uebergebene String kann dabei auch die Art der * Sparte enthalten. * * @param x z.B. "580.01" fuer Sparte 580, Art 01 */ public void setSparte(final String x) { String[] parts = x.split("\\."); this.setSparte(Integer.parseInt(parts[0])); if ((parts.length > 1) && getGdvSatzartName().isEmpty()) { this.setGdvSatzartName(String.format("%04d.%s", getSatzart(), x)); } } /** * Gets the sparte. * * @return die Sparte als int */ @Override public int getSparte() { return this.sparte.toInt(); } /** * Manche Satzarten wie Bausparen haben eine Element fuer die Untersparte, * im Feld Wagnisart oder Art abgespeichert. Dies ist z.B. fuer Satz * 220.580.1 (Bausparen) der Fall. * * @return 0 oder Untersparte / Art */ @JsonIgnore public int getArt() { return this.getSatzTyp().getArt(); } /** * Wenn der Datensatz ein Element fuer eine Untersparte hat, wird 'true' * zurueckgegeben. Dies ist z.B. fuer Satz 220.580.1 (Bausparen) der Fall. * * @return true, falls der Datensatz eine Untersparte hat. */ public boolean hasArt() { return this.getSatzTyp().hasArt(); } /** * Ueberprueft, ob der Datensatz ueberhaupt eine Sparte gesetzt hat. * * @return true, if successful * @since 0.6 */ @Override public boolean hasSparte() { /* * @Oli: die Abfrage auf Existenz von "sparte" ist noetig, damit es beim Debugging nicht in * "Satz.toString()" zur RuntimeException kommen kann, solange das Datensatz-Objekt noch * nicht fertig ist. */ return this.sparte != null && !this.sparte.isEmpty() && this.getSparte() > 0; } /** * Gets the sparte feld. * * @return die Sparte als Feld */ public NumFeld getSparteFeld() { return this.sparte; } /** * Wenn der Datensatz ein Element fuer eine Untersparte hat, wird 'true' * zurueckgegeben. Dies ist z.B. fuer Satz 220.580.1 (Bausparen) der Fall. * * @return true, falls der Datensatz eine Untersparte hat. */ public boolean hasSatzartNummer() { return !this.getGdvSatzartNummer().isEmpty(); } /** * Sets the vu nummer. * * @param s VU-Nummer (max. 5 Stellen) */ public void setVuNummer(final String s) { setFeld(VU_NUMMER, s); } /** * Gets the vu nummer. * * @return die VU-Nummer */ @JsonIgnore public String getVuNummer() { return this.getFeld(VU_NUMMER).getInhalt().trim(); } /** * Nicht jeder Datensatz hat eine VU-Nummer. So kommt sie in Satzart * 0291.550 nicht vor. * * @return true, if VU-Nummer vorhanden ist * @since 5.2 */ @JsonIgnore public boolean hasVuNummer() { return hasFeld(VU_NUMMER); } /** * Sets the versicherungsschein nummer. * * @param nr die Versicherungsschein-Nummer * @since 0.3 */ public void setVersicherungsscheinNummer(final String nr) { this.setFeld(Kopffelder1bis7.VERSICHERUNGSSCHEINNUMMER.getBezeichner(), nr); } /** * Gets the versicherungsschein nummer. * * @return die Versicherungsschein-Nummer * @since 0.3 */ @JsonIgnore public String getVersicherungsscheinNummer() { return this.getFeld(Kopffelder1bis7.VERSICHERUNGSSCHEINNUMMER.getBezeichner()).getInhalt().trim(); } /** * Da nicht alle Satzarten die Satznummer am Ende des Satzes haben, kann * man dies ueber diese Methode korrigieren. *

* TODO: wird ab v7 nicht mehr unterstuetzt *

* * @param satznummer das neue Feld fuer die Satznummer * @since 3.2 * @deprecated ab 5.1 nicht mehr noetig, da {@link Teildatensatz#getSatznummer()} * jetzt die tatsaechliche Satznummer liefert */ @Deprecated public void setSatznummer(Zeichen satznummer) { remove(Bezeichner.SATZNUMMER); for (Teildatensatz tds : getTeildatensaetze()) { tds.setSatznummer(satznummer); } } /** * Hiermit kann die Folgenummer gesetzt werden. * * @param nr man sollte hier bei 1 anfangen mit zaehlen * @since 0.3 */ public void setFolgenummer(final int nr) { this.setFeld(Kopffelder1bis7.FOLGENUMMER.getBezeichner(), nr); } /** * Gets the folgenummer. * * @return die Folgenummer * @since 0.3 */ public int getFolgenummer() { NumFeld folgenummer = (NumFeld) this.getFeld(Kopffelder1bis7.FOLGENUMMER.getBezeichner()); return folgenummer.toInt(); } /** * Liest 14 Bytes, um die Sparte zu bestimmen und stellt die Bytes * anschliessend wieder zurueck in den Reader. * * @param reader muss mind. einen Pushback-Puffer von 14 Zeichen * bereitstellen * @return Sparte * @throws IOException falls was schief gegangen ist */ public static int readSparte(final PushbackReader reader) throws IOException { char[] cbuf = new char[14]; if (reader.read(cbuf) == -1) { throw new IOException("can't read 14 bytes (" + new String(cbuf) + ") from " + reader); } reader.unread(cbuf); String intro = new String(cbuf); try { return Integer.parseInt(intro.substring(10, 13)); } catch (NumberFormatException ex) { throw new ImportException("cannot read sparte from first 14 bytes (\"" + intro + "\")"); } } /** * Liest 49 Bytes, um die Folge-Nr. in Satzart 220, Sparte 20 (Kranken) zu bestimmen und stellt die Bytes * anschliessend wieder zurueck in den Reader. * * @param reader muss mind. einen Pushback-Puffer von 14 Zeichen * bereitstellen * @return Folge-Nr * @throws IOException falls was schief gegangen ist */ public static int readKrankenFolgeNr(final PushbackLineNumberReader reader) throws IOException { int satzart = readSatzart(reader); if (satzart != 220) { throw new IllegalArgumentException("can't read Kranken Folge-Nr., wrong satzart " + satzart +", must be 220"); } int sparte = readSparte(reader); if (sparte != 20) { throw new IllegalArgumentException("can't read Kranken Folge-Nr., wrong sparte " + sparte + ", must be 20"); } char[] cbuf = new char[49]; if (reader.read(cbuf) == -1) { throw new IOException("can't read 49 bytes (" + new String(cbuf) + ") from " + reader); } reader.unread(cbuf); String first10Fields = new String(cbuf); try { return Integer.parseInt(first10Fields.substring(47, 48)); } catch (NumberFormatException ex) { return -1; } } /** * Liest 45 Bytes, um die Bauspar-Art in Satzart 220, Sparte 580 (Bausparen) * zu bestimmen und stellt die Bytes anschliessend wieder zurueck in den * Reader. * * @param reader muss mind. einen Pushback-Puffer von 14 Zeichen bereitstellen * @return Folge-Nr * @throws IOException falls was schief gegangen ist */ public static int readBausparenArt(final PushbackLineNumberReader reader) throws IOException { int satzart = readSatzart(reader); if (satzart != 220) { throw new IllegalArgumentException("can't read Bauspar-Art, wrong satzart " + satzart +", must be 220"); } int sparte = readSparte(reader); if (sparte != 580) { throw new IllegalArgumentException("can't read Bauspar-Art, wrong sparte " + sparte + ", must be 580"); } char[] cbuf = new char[45]; if (reader.read(cbuf) == -1) { throw new IOException("can't read 45 bytes (" + new String(cbuf) + ") from " + reader); } reader.unread(cbuf); String first10Fields = new String(cbuf); try { return Integer.parseInt(first10Fields.substring(43, 44)); } catch (NumberFormatException ex) { return -1; } } /** * Liest 1 Byte, um die Wagnisart zu bestimmen und stellt das Byte * anschliessend wieder zurueck in den Reader. * * @param reader muss mind. einen Pushback-Puffer von 1 Zeichen bereitstellen * @return Wagnisart * @throws IOException falls was schief gegangen ist */ public static WagnisartLeben readWagnisart(final PushbackReader reader) throws IOException { char[] cbuf = new char[60]; if (reader.read(cbuf) == -1) { throw new IOException("can't read 1 bytes (" + new String(cbuf) + ") from " + reader); } reader.unread(cbuf); String wagnisart = new String(cbuf).substring(59, 60); if (wagnisart.trim().length() == 0) { return WagnisartLeben.NULL; } else { try { return WagnisartLeben.isIn(Integer.parseInt(wagnisart)); } catch (NumberFormatException e) { LOG.warn("Not allowed value for wagnisart found. Type Number is required but was \"" + wagnisart + "\"."); return WagnisartLeben.NULL; } } } /** * Prüfe ob die kommende Zeile ein Teildatensatz der letzten ist. Dazu * werden (normalerweise) die ersten 7 Felder abgeglichen. Leider fuehrt * dieses Verfahren nicht immer zum Erfolg, sodass wir uns in bestimmten * Situationen doch den ganzen naechsten Teildatensatz anschauen muessen. * * @param reader ein Reader * @param lastFeld1To7 Feld1..7 als Char-Array (42 Zeichen) der letzten Zeile * oder {@code null} für ersten Teildatensatz * @return {@code true}, falls ein Teildatensatz, {@code false} falls nicht, * d.h. neuer Datensatz. * @throws IOException bei I/O-Fehlern * @see Satz#matchesNextTeildatensatz(PushbackLineNumberReader, char[], Character) * @since 0.5.1 */ @Override protected boolean matchesNextTeildatensatz(final PushbackLineNumberReader reader, char[] lastFeld1To7, Character satznummer) throws IOException { if (super.matchesNextTeildatensatz(reader, lastFeld1To7, satznummer)) { if (lastFeld1To7 == null) { //erster Teildatensatz hat noch keine lastFeld... //return matchesFirstTeildatensatz(reader); return true; } else { // TODO: ugly aber ich sehe bisher noch keinen eleganten Weg in der aktuellen Struktur ohne umfangreiche Refaktorings. char[] newLine = new char[256]; int res = reader.read(newLine); if (res < 256) { return false;// EOF } reader.unread(newLine); // wir vergleichen teilweise die ersten 7 Felder (42 Zeichen) auf // Gleichheit....wenn ein Unterschied -> neuer Datensatz, for (int i = 0; i < 4; i++) { if (lastFeld1To7[i] != newLine[i]) return false; } for (int i = 10; i < 13; i++) { if (lastFeld1To7[i] != newLine[i]) return false; } for (int i = 30; i < 42; i++) { if (lastFeld1To7[i] != newLine[i]) return false; } return matchesLastFeld(satznummer, reader); } } return false; } private static boolean matchesLastFeld(Character satznummer, PushbackLineNumberReader reader) throws IOException { // Das letzte Feld wird darauf verglichen, dass es groesser als das // vorherige ist, falls Teildatensaetze uebersprungen werden char newSatznummer = Satznummer.readSatznummer(reader).toChar(); return !(Character.isDigit(newSatznummer) && Character.isDigit(satznummer) && newSatznummer <= satznummer); } /** * Read teildatensatz nummer. *

* TODO: wird mit v7 entfernt *

* * @param reader the reader * @return the teildatensatz nummer * @throws IOException Signals that an I/O exception has occurred. * @deprecated bitte {@link Satznummer#readSatznummer(PushbackLineNumberReader)} verwenden */ @Deprecated public static TeildatensatzNummer readTeildatensatzNummer(final PushbackReader reader) throws IOException { Satznummer satznr = Satznummer.readSatznummer(new PushbackLineNumberReader(reader)); return TeildatensatzNummer.of(satznr.toInt()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy