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

gdv.xport.satz.Satz 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 09.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.enums.TeildatensatzEnum;
import gdv.xport.satz.feld.common.Kopffelder1bis7;
import gdv.xport.util.SatzTyp;
import gdv.xport.util.SimpleConstraintViolation;
import net.sf.oval.ConstraintViolation;
import net.sf.oval.Validator;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.*;
import java.lang.reflect.Constructor;
import java.util.*;

import static gdv.xport.feld.Bezeichner.SATZART;
import static patterntesting.runtime.NullConstants.NULL_STRING;

/**
 * Die Satz-Klasse ist die oberste Klasse, von der alle weiteren Saetze
 * abgeleitet sind.
 *
 * @author oliver
 */
public abstract class Satz implements Cloneable {

	private static final Logger LOG = LogManager.getLogger(Satz.class);

	private final NumFeld satzart = new NumFeld((SATZART), 4, 1);
	private Teildatensatz[] teildatensatz = new Teildatensatz[0];

  /**
   * Zum Abspeichern der Satznummer einer 0220er-GdvSatzart der Sparte 010
   * (Leben). Die Namen dieser Satzarten bestehen bestehen aus 4 Teilen:
   * .... Beispiel:
   * "0220.010.13.6" (siehe Online-Version bei gdv-online.de). Wird benoetigt,
   * um 0220er-Satzarten bei Leben zu unterscheiden wg. Bezugsrechte,
   * Auszahlungen, zukünftige Summenänderungen und Wertungssummen. Nicht
   * verwechseln mit Satznummer eines Teildatensatzes!
   */
  private String gdvSatzartNummer = "";
  /**
   * Zum Abspeichern des Namens einer Gdv-Satzart gemaess Online-Version bei
   * gdv-online.de
   */
  private String gdvSatzartName = "";
    private final AlphaNumFeld satzVersion = new AlphaNumFeld(3, 1);

	/**
	 * Instantiates a new satz.
	 * 

* TODO: Wird mit v6 entfernt. *

* * @param art Satzart * @param n Anzahl der Teildatensaetze * @deprecated bitte {@link Satz#Satz(SatzTyp, int)} verwenden */ @Deprecated public Satz(final NumFeld art, final int n) { this(SatzTyp.of(art.getInhalt()), n); } /** * Instantiates a new satz. *

* TODO: Wird mit v6 entfernt. *

* * @param content die Satzart * @param n Anzahl * @deprecated bitte {@link Satz#Satz(SatzTyp, int)} verwenden */ @Deprecated public Satz(final String content, final int n) { this(SatzTyp.of(content), n); } /** * The Constructor. *

* TODO: Wird mit v6 entfernt. *

* * @param art z.B. 100 (f. Adressteil) * @param n Anzahl der Teildatensaetze * @deprecated bitte {@link Satz#Satz(SatzTyp, int)} verwenden */ @Deprecated public Satz(final int art, final int n) { this(SatzTyp.of(art), n); } /** * Mit diesem Konstruktor wird ein Satz fuer die entsprechende Satzart * mit n Teildatensaetzen angelegt. * * @param art z.B. Satzart 0100 (f. Adressteil) * @param n Anzahl der Teildatensaetze * @since 5.0 */ public Satz(final SatzTyp art, final int n) { this.satzart.setInhalt(art.getSatzart()); this.createTeildatensaetze(n); } /** * The Constructor. * * @param satz z.B. Satzart 0100 (f. Adressteil) * @param n Anzahl der Teildatensaetze */ public Satz(final Satz satz, final int n) { this.satzart.setInhalt(satz.getSatzart()); this.gdvSatzartName = satz.getGdvSatzartName(); this.gdvSatzartNummer = satz.getGdvSatzartNummer(); this.setSatzversion(satz.getSatzversion().getInhalt()); this.createTeildatensaetze(n); } /** * Instanziiert einen neuen Satz. *

* TODO: Wird mit v6 entfernt. *

* * @param art z.B. 100 (f. Adressteil) * @param tdsList Liste mit den Teildatensaetzen * @deprecated bitte {@link Satz#Satz(SatzTyp, List)} verwenden */ @Deprecated public Satz(final int art, final List tdsList) { this(SatzTyp.of(art), tdsList); } /** * Instanziiert einen neuen Satz. * * @param art Satzart, z.B. 100 (f. Adressteil) * @param tdsList Liste mit den Teildatensaetzen * @since 5.0 */ public Satz(final SatzTyp art, final List tdsList) { this.satzart.setInhalt(art.getSatzart()); this.createTeildatensaetze(tdsList); } /** * Instanziiert einen neuen Satz. * * @param satz z.B. 100 (f. Adressteil) * @param tdsList Liste mit den Teildatensaetzen */ protected Satz(final Satz satz, final List tdsList) { this.satzart.setInhalt(satz.getSatzart()); this.satzVersion.setInhalt(satz.getSatzversion().getInhalt()); this.gdvSatzartName = satz.getGdvSatzartName(); this.gdvSatzartNummer = satz.getGdvSatzartNummer(); this.createTeildatensaetze(tdsList); } protected void createTeildatensaetze(final int n) { teildatensatz = new Teildatensatz[n]; for (int i = 0; i < n; i++) { teildatensatz[i] = new TeildatensatzEnum(satzart, i + 1); } } protected void createTeildatensaetze(final List tdsList) { teildatensatz = new Teildatensatz[tdsList.size()]; for (int i = 0; i < tdsList.size(); i++) { teildatensatz[i] = new TeildatensatzEnum(tdsList.get(i)); } } /** * Liefert alle Teildatensaetze zurueck. Aus Performance-Gruenden wird * keine Kopie zurueckgegeben. Sollte eine Kopie gewuenscht sein, kann * man auf {@link #cloneTeildatensaetze()} zurueckgreifen. * * @return Teildatensaetze * @since 0.2 */ public final List getTeildatensaetze() { return Arrays.asList(this.teildatensatz); } /** * Hier wird eine Kopie aller Teildatensaetze zurueckgegeben. * * @return Liste mit Teildatensaetzen * @since 1.0 */ protected final List cloneTeildatensaetze() { List cloned = new ArrayList<>(this.teildatensatz.length); for (Teildatensatz tds : this.teildatensatz) { cloned.add(new Teildatensatz(tds)); } return cloned; } /** * Liefert die Anzahl der Teildatensaetze. * * @return Anzahl der Teildatensaetze. * @since 0.6 */ public final int getNumberOfTeildatensaetze() { return teildatensatz.length; } /** * Liefert den n-ten Teildatensatz aus der Liste der Teildatensätze zurueck. *

* Achtung n ist nicht immer identisch mit der Satznummer des * Teildatensatzes (siehe z.B. Satzart 0221.140). * Dazu besser {@link #getTeildatensatzBySatzNr(int)} verwenden. *

* * @param n Nummer (Index n-1 in der Liste der Teildatensätze) des * Teildatensatzes (beginnend mit 1) * @return the teildatensatz * @since 0.2 */ public final Teildatensatz getTeildatensatz(final int n) { return this.teildatensatz[n - 1]; } /** * Liefert den Teildatensatz mit der wirklichen Satznummer n zurueck. * * @param n Satznummer des Teildatensatzes * @return the teildatensatz */ public final Teildatensatz getTeildatensatzBySatzNr(final int n) { for (Teildatensatz tds : this.teildatensatz) { if (Integer.parseInt(tds.getSatznummer().getInhalt()) == n) return tds; } throw new IllegalArgumentException("Satznummer " + n + " nicht vorhanden."); } /** * Hiermit koennen Unterklassen alle Teildatensaetze wieder entfernen (wird * z.B. vom Satz 0220.030 benoetigt). * * @since 0.4 */ public final void removeAllTeildatensaetze() { this.teildatensatz = new Teildatensatz[0]; } /** * Entfernt den gewuenschten Teildatensatz. Ein neuer Teildatensatz kann * ueber add() hinzugefuegt werden. * * @param n der gewuenschte Teildatensatz (beginnend bei 1) * @see #add(Teildatensatz) * @since 0.4 */ public final void removeTeildatensatz(final int n) { if ((n < 1) || (n > this.teildatensatz.length)) { throw new IllegalArgumentException(n + " liegt nicht zwischen 1 und " + this.teildatensatz.length); } this.teildatensatz = ArrayUtils.remove(this.teildatensatz, n - 1); } /** * Und hierueber kann ein Teildatensatz hinzugefuegt werden. * * @param tds der neue (gefuellte) Teildatensatz * @since 0.4 */ public final void add(final Teildatensatz tds) { this.teildatensatz = ArrayUtils.add(this.teildatensatz, tds); } /** * Fuegt das uebergebene Feld zur Liste der Datenfelder hinzu. * * @param feld das Feld */ public void add(final Feld feld) { this.add(feld, 1); } /** * Fuegt das uebergebene Feld zur Liste der Datenfelder hinzu. * * @param feld the feld * @param teildatensatzNr the teildatensatz nr */ public void add(final Feld feld, final int teildatensatzNr) { if (feld.getByteAdresse() > 256) { throw new IllegalArgumentException(feld + " ueberschreitet Teildatensatz-Grenze"); } if ((teildatensatzNr < 1) || (teildatensatzNr > this.teildatensatz.length)) { throw new IllegalArgumentException("Teildatensatz-Nr. " + teildatensatzNr + " fuer " + feld + " liegt nicht zwischen 1 und " + teildatensatz.length); } this.teildatensatz[teildatensatzNr - 1].add(feld); } /** * Fuellt fuer alle leeren Stellen ein entsprechendes Fuellfeld auf. */ public void addFiller() { throw new UnsupportedOperationException("not yet implemented"); } /** * Falls ein Feld zuviel gesetzt wurde, kann es mit 'remove" wieder entfernt * werden. * * @param name Name des Feldes */ public void remove(final String name) { this.remove(new Bezeichner(name)); } /** * Falls ein Feld zuviel gesetzt wurde, kann es mit 'remove" wieder entfernt * werden. * * @param bezeichner der Feld-Beezeichner * @since 1.0 */ public void remove(final Bezeichner bezeichner) { for (Teildatensatz tds : this.teildatensatz) { tds.remove(bezeichner); } } /** * Setzt das angegebene Feld in allen Teildatensaetzen, in denen es gefunden * wird. Normalerweise braeuchten wir eigentlich nur die erste Fundstelle * setzen, da die anderen Teildatensaetze (hoffentlich) auf die gleiche * Referenz verweisen - aber sicher ist sicher. Falls das Feld nicht * gefunden wird, wird eine IllegalArgumentException geworfen. * * @param name Name des Felds (Bezeichnung) * @param value the value */ public void set(final String name, final String value) { this.set(new Bezeichner(name), value); } /** * Setzt den Inhalt des gewuenschten Feldes. * * @param name Name des Felds (Bezeichnung) * @param value neuer Inhalt */ public void set(final Bezeichner name, final Integer value) { this.set(name, Integer.toString(value)); } /** * Setzt das angegebene Feld in allen Teildatensaetzen, in denen es gefunden * wird. Normalerweise braeuchten wir eigentlich nur die erste Fundstelle * setzen, da die anderen Teildatensaetze (hoffentlich) auf die gleiche * Referenz verweisen - aber sicher ist sicher. Falls das Feld nicht * gefunden wird, wird eine IllegalArgumentException geworfen. * * @param name Name des Felds (Bezeichnung) * @param value the value * @since 2.0 */ public void set(final Bezeichner name, final String value) { boolean found = false; for (Teildatensatz tds : teildatensatz) { if (tds.hasFeld(name)) { Feld x = tds.getFeld(name); x.setInhalt(value); if (x.isInvalid()) { throw new IllegalArgumentException(String.format("ungueltiger Wert '%s' fuer %s", value, x)); } found = true; } } if (!found) { throw new IllegalArgumentException("Feld \"" + name + "\" not found"); } } /** * Setzt den Inhalt des gewuenschten Feldes. * * @param feldX das gewuenschte Feld-Element * @param value neuer Inhalt */ public final void set(final Enum feldX, final String value) { Bezeichner name = Feld.getAsBezeichner(feldX); this.set(name, value); } /** * Setzt den Inhalt des gewuenschten Feldes. * * @param feldX das gewuenschte Feld-Element * @param value neuer Inhalt * @since 0.9 (oboehm, 1-Apr-2013) */ public final void set(final Enum feldX, final Integer value) { this.set(feldX, Integer.toString(value)); } /** * Setzt den Inhalt des gewuenschten Feldes. * * @param feldX das gewuenschte Feld-Element * @param value neuer Inhalt * @since 0.9 (oboehm, 1-Apr-2013) */ public final void set(final Enum feldX, final Character value) { this.set(feldX, Character.toString(value)); } /** * Setzt die Satzartnummer einer Satzart. Nicht verwechseln mit Satznummer! * * @param x z.B. "6" fuer Satzart 0220, Sparte 010, Wagnisart 2, Bezugsrechte */ protected void setGdvSatzartNummer(final String x) { this.gdvSatzartNummer = x; } /** * Gets die Satzartnummer. Nicht verwechseln mit Satznummer! *

* Manche Satzarten wie Leben haben ein Element fuer die Satznummer, im Feld * Satzartnummer gespeichert. Dies ist z.B. fuer Satz 0220.010.13.6 * (Bezugsrechte) der Fall. * * @return die Satzartnummer als String */ public String getGdvSatzartNummer() { return this.gdvSatzartNummer; } /** * @return Name der GDV-Satzart gemaess Online-Version bei gdv-online.de */ public String getGdvSatzartName() { return this.gdvSatzartName; } /** * Setzen des Namens einer Gdv-Satzart. *

* Der string wird mit dem Trennzeichen '.' an den bisherigen * Inhalt angehaengt. *

* * @param string Satzart-Name */ protected void setGdvSatzartName(String string) { StringBuilder buf = new StringBuilder(); if (this.gdvSatzartName.isEmpty()) { buf.append(string); } else { buf.append(this.gdvSatzartName).append(".").append(string); } this.gdvSatzartName = buf.toString(); } /** * Setzt die Version des Satzes * * @param version die Satzversion */ public final void setSatzversion(final String version) { this.satzVersion.setInhalt(version); } /** * Liefert die Satzversion * * @return die Satzversion */ public final AlphaNumFeld getSatzversion() { return this.satzVersion; } /** * Liefert den Inhalt des gewuenschten Feldes. * * @param name gesuchtes Feld * @return Inhalt des gefundenden Felds (NULL_STRING, falls 'name' nicht * gefunden wurde) */ public final String get(final String name) { return get(new Bezeichner(name)); } /** * Liefert den Inhalt des gewuenschten Feldes. * * @param bezeichner gesuchtes Field * @return Inhalt des gefundenden Felds (NULL_STRING, falls 'name' nicht * gefunden wurde) * @since 2.0 */ public String get(final Bezeichner bezeichner) { Feld f = getFeld(bezeichner); if (f == Feld.NULL_FELD) { return NULL_STRING; } else { return f.getInhalt(); } } /** * Liefert den Inhalt des gewuenschten Feldes. * * @param feldX das gewuenschte Feld-Element * @return Inhalt des gefundenden Felds */ public final String get(final Enum feldX) { Bezeichner name = Feld.getAsBezeichner(feldX); return this.get(name); } /** * Liefert das gewuenschte Feld. Allerdings wird nur der Name des Feldes * benutzt, um das Feld zu bestimmen. Dazu werden auch die Konstanten in * {@link gdv.xport.feld.Bezeichner} verwendet. *

* TODO: Wird in v6 entfernt werden. *

* * @param feld gewuenschtes Feld-Element * @return das gesuchte Feld * @throws IllegalArgumentException falls es das Feld nicht gibt * @deprecated inzwischen durch {@link #getFeld(Bezeichner)} abgeloest */ @Deprecated public Feld getFeld(final Enum feld) throws IllegalArgumentException { for (int i = 0; i < teildatensatz.length; i++) { if (teildatensatz[i].hasFeld(feld)) { return teildatensatz[i].getFeld(feld); } } throw new IllegalArgumentException("Feld \"" + feld + "\" nicht in " + this.toShortString() + " vorhanden!"); } /** * Liefert das gewuenschte Feld oder {@link Feld#NULL_FELD}, wenn nicht * vorhanden. Allerdings wird nur der Name des Feldes benutzt, um das Feld zu * bestimmen. Dazu werden auch die Konstanten in * {@link gdv.xport.feld.Bezeichner} verwendet. *

* TODO: Wird in v6 entfernt. *

* * @param feld gewuenschtes Feld-Element * @return das gesuchte Feld * @deprecated durch {@link #getFeldSafe(Bezeichner)} abgeloest */ @Deprecated public Feld getFeldSafe(final Enum feld) { try { return getFeld(feld); } catch (IllegalArgumentException ex) { return Feld.NULL_FELD; } } /** * Liefert das gewuenschte Feld falls vorhanden oder null. *

* TODO: Wird mit v6 entfernt. *

* * @param name gewuenschter Bezeichner des Feldes * @return das gesuchte Feld * @deprecated bitte {@link #hasFeld(Bezeichner)} verwenden */ @Deprecated public Feld containsFeld(final String name) { Bezeichner bezeichner = Bezeichner.of(name); if (hasFeld(bezeichner)) { return getFeld(bezeichner); } else { return null; } } /** * Liefert das gewuenschte Feld. * * @param name gewuenschter Bezeichner des Feldes * @return das gesuchte Feld * @throws IllegalArgumentException falls es das Feld nicht gibt */ public Feld getFeld(final String name) throws IllegalArgumentException { return this.getFeld(Bezeichner.of(name)); } /** * Liefert das gewuenschte Feld oder {@link Feld#NULL_FELD}, wenn nicht * vorhanden. * * @param name gewuenschter Bezeichner des Feldes * @return das gesuchte Feld */ public Feld getFeldSafe(final String name) { return this.getFeldSafe(Bezeichner.of(name)); } /** * Fraegt ab, ob das entsprechende Feld vorhanden ist. * * @param bezeichner gewuenschter Bezeichner des Feldes * @return true / false */ public boolean hasFeld(final Bezeichner bezeichner) { for (Teildatensatz tds : teildatensatz) { if (tds.hasFeld(bezeichner)) { return true; } } return false; } /** * Liefert das gewuenschte Feld. * * @param bezeichner gewuenschter Bezeichner des Feldes * @return das gesuchte Feld * @throws IllegalArgumentException falls es das Feld nicht gibt */ public Feld getFeld(final Bezeichner bezeichner) throws IllegalArgumentException { for (Teildatensatz tds : teildatensatz) { for (Feld f : tds.getFelder()) { if (bezeichner.equals(f.getBezeichner())) { return f; } } } return findFeld(bezeichner); } private Feld findFeld(final Bezeichner bezeichner) throws IllegalArgumentException { for (Teildatensatz tds : teildatensatz) { if (tds.hasFeld(bezeichner)) { return tds.getFeld(bezeichner); } } throw new IllegalArgumentException("Feld \"" + bezeichner + "\" nicht in " + this.toShortString() + " vorhanden!"); } /** * Liefert das gewuenschte Feld im gewuenschten Typ. * Falls als Typ {@link BetragMitVorzeichen} gewuenscht wird, wird das * Feld mit dem angegebenen Bezeichner und das benachbarte Vorzeichenfeld * zusammengefasst und als Einheit zurueckgegeben. * * @param Unterklasse von Feld * @param bezeichner gewuenschter Bezeichner des Feldes * @param clazz Feld-Typ * @return das gesuchte Feld * @throws IllegalArgumentException falls es das Feld nicht gibt * @since 5.0 */ public T getFeld(final Bezeichner bezeichner, final Class clazz) { if (clazz.equals(BetragMitVorzeichen.class)) { return (T) getBetragMitVorzeichen(bezeichner); } Feld feld = getFeld(bezeichner); if (clazz.isAssignableFrom(feld.getClass())) { return (T) feld; } else { try { Constructor ctor = clazz.getConstructor(Feld.class); return ctor.newInstance(feld); } catch (ReflectiveOperationException ex) { throw new IllegalArgumentException("cannot instantiate " + clazz, ex); } } } private BetragMitVorzeichen getBetragMitVorzeichen(final Bezeichner bezeichner) { Betrag betrag = getFeld(bezeichner, Betrag.class); Feld vorzeichen = getVorzeichenOf(bezeichner); BetragMitVorzeichen bmv = new BetragMitVorzeichen(Bezeichner.of(bezeichner.getName() + " mit Vorzeichen"), betrag.getAnzahlBytes()+1, betrag.getByteAdresse()); bmv.setInhalt(betrag.getInhalt() + vorzeichen.getInhalt()); return bmv; } private Feld getVorzeichenOf(final Bezeichner bezeichner) { for (int n = 1; n <= getNumberOfTeildatensaetze(); n++) { Teildatensatz tds = getTeildatensatz(n); if (tds.hasFeld(bezeichner)) { Feld beforeVorzeichen = tds.getFeld(bezeichner); return tds.getFeld(ByteAdresse.of(beforeVorzeichen.getEndAdresse()+1)); } } throw new IllegalArgumentException(bezeichner + " does not exist"); } /** * Liefert das gewuenschte Feld oder {@link Feld#NULL_FELD}, wenn nicht * vorhanden. *

* TODO: wird mit v6 abgeloest *

* * @param bezeichner gewuenschter Bezeichner des Feldes * @return das gesuchte Feld * @deprecated bitte {@link #hasFeld(Bezeichner)} und {@link #getFeld(Bezeichner)} verwenden */ @Deprecated public Feld getFeldSafe(final Bezeichner bezeichner) { try { return getFeld(bezeichner); } catch (IllegalArgumentException ex) { return Feld.NULL_FELD; } } /** * Liefert den Inhalt des gewuenschten Feldes. * * @param bezeichner gewuenschter Bezeichner des Feldes * @return Inhalt des Feldes (getrimmt, d.h. ohne Leerzeichen am Ende) * @throws IllegalArgumentException falls es das Feld nicht gibt * @since 2.0 */ public final String getFeldInhalt(final Bezeichner bezeichner) throws IllegalArgumentException { return this.getFeld(bezeichner).getInhalt().trim(); } /** * Liefert das gewuenschte Feld. * * @param bezeichner gewuenschter Bezeichner des Feldes * @param nr Nummer des Teildatensatzes (1, 2, ...) * @return NULL_FELD, falls das angegebene Feld nicht gefunden wird * @throws IllegalArgumentException falls es das Feld nicht gibt * @since 2.0 */ public final Feld getFeld(final Bezeichner bezeichner, final int nr) throws IllegalArgumentException { return getFeld(bezeichner.getName(), nr); } /** * Liefert das gewuenschte Feld. * * @param name gewuenschter Bezeichner des Feldes * @param nr Nummer des Teildatensatzes (1, 2, ...) * @return NULL_FELD, falls das angegebene Feld nicht gefunden wird * @throws IllegalArgumentException falls es das Feld nicht gibt * @since 0.2 */ public final Feld getFeld(final String name, final int nr) throws IllegalArgumentException { assert (0 < nr) && (nr <= teildatensatz.length) : nr + " liegt ausserhalb des Bereichs"; return teildatensatz[nr - 1].getFeld(name); } /** * Liefert den Inhalt des gewuenschten Feldes. * * @param name gewuenschter Bezeichner des Feldes * @param nr Nummer des Teildatensatzes (1, 2, ...) * @return Inhalt des Feldes (getrimmt, d.h. ohne Leerzeichen am Ende) * @throws IllegalArgumentException falls es das Feld nicht gibt * @since 0.3 */ public final String getFeldInhalt(final String name, final int nr) throws IllegalArgumentException { return this.getFeld(name, nr).getInhalt().trim(); } /** * Liefert die Satzart. * * @return the satzart */ public final NumFeld getSatzartFeld() { return this.satzart; } /** * Liefert die Satzart zurueck. * * @return die Satzart als int * @since 0.3 */ public final int getSatzart() { return this.satzart.toInt(); } /** * Liefert den Satz-Typ zurueck. Der Satz-Typ ist eine Zusammenfassung aus * Satzart und Sparte. * * @return den Satz-Typ * @since 1.0 */ @JsonIgnore public SatzTyp getSatzTyp() { if (StringUtils.isNotEmpty(this.gdvSatzartName)) { return SatzTyp.of(this.gdvSatzartName); } else if (this.hasSparte()) { if (this.hasWagnisart() && this.getWagnisart().matches("\\d")) { return SatzTyp.of(this.getSatzart(), this.getSparte(), Integer.parseInt(this.getWagnisart())); } else if (this.hasKrankenFolgeNr() && this.getKrankenFolgeNr().matches("\\d")) { return SatzTyp.of(this.getSatzart(), this.getSparte(), Integer.parseInt(this.getKrankenFolgeNr())); } else if (this.hasBausparenArt() && this.getBausparenArt().matches("\\d")) { return SatzTyp.of(this.getSatzart(), this.getSparte(), Integer.parseInt(this.getBausparenArt())); } else { return new SatzTyp(this.getSatzart(), this.getSparte()); } } else { return new SatzTyp(this.getSatzart()); } } /** * Schaut nach einem Feld "SPARTE" und liefert true zurueck, falls es * existiert. * * @return true, falls Sparten-Feld vorhanden ist * @since 0.9 */ public boolean hasSparte() { return hasFeld(Bezeichner.of(Kopffelder1bis7.SPARTE.getBezeichnung())); } /** * Schaut nach einem Feld "WAGNISART" und liefert true zurueck, falls es * existiert. * * @return true, falls Wagnisart-Feld vorhanden ist * @since 1.0 */ public boolean hasWagnisart() { return this.hasFeld((Bezeichner.WAGNISART)); } /** * Schaut nach dem 10. Feld in Satzart 220, Sparte 20 (Kranken) und liefert * true zurueck, falls es existiert. * * @return true, falls das Feld existiert * @since 18.04.2018 */ public boolean hasKrankenFolgeNr() { return this.getSatzart() == 220 && this.getSparte() == 20 && (this.hasFeld(Bezeichner.FOLGE_NR_ZUR_LAUFENDEN_PERSONEN_NR_UNTER_NR_LAUFENDE_NR_TARIF) || this.hasFeld(Bezeichner.FOLGE_NR_ZUR_LAUFENDEN_PERSONEN_NR_UNTER_NR_BZW_LAUFENDEN_NR_TARIF)); } /** /** * Schaut nach dem 9. Feld in Satzart 220, Sparte 580 (Bausparen) und liefert true zurueck, falls * es existiert. * * @return true, falls das Feld existiert * @since 30.06.2021 */ public boolean hasBausparenArt() { return this.getSatzart() == 220 && this.getSparte() == 580 && (this.hasFeld(Bezeichner.ART_580)); } /** * Liefert den Inhalt des Sparten-Felds. Vorher sollte allerdings mittels * {@link #hasSparte()} geprueft werden, ob der Satz ein Sparten-Feld * besitzt. * * @return die Sparte * @since 0.9 */ @JsonIgnore public int getSparte() { NumFeld sparte = (NumFeld) this.getFeld(Kopffelder1bis7.SPARTE.getBezeichner()); return sparte.toInt(); } /** * Liefert den Inhalt des Wagnisart-Felds. Vorher sollte allerdings mittels * {@link #hasWagnisart()} geprueft werden, ob der Satz ein Wagnisart-Feld * besitzt. *

* Anmerkung: Vor 1.0 war diese Methode noch in der Datensatz-Klasse * beheimatet. *

* * @return die Wagnisart */ @JsonIgnore public final String getWagnisart() { Feld wagnisart = this.getFeld(Bezeichner.WAGNISART); return wagnisart.getInhalt(); } /** * Liefert den Inhalt des 10. Feldes in Satzart 220, Sparte 20 (Kranken). Vorhersollte allerdings * mittels {@link #hasKrankenFolgeNr()} geprueft werden, ob der Satz eine KrankenfolgeNr-Feld * besitzt. *

* * @return die KrankenFolgeNr */ @JsonIgnore public final String getKrankenFolgeNr() { String inhalt = NULL_STRING; if (this.hasFeld(Bezeichner.FOLGE_NR_ZUR_LAUFENDEN_PERSONEN_NR_UNTER_NR_LAUFENDE_NR_TARIF)) inhalt = this.get(Bezeichner.FOLGE_NR_ZUR_LAUFENDEN_PERSONEN_NR_UNTER_NR_LAUFENDE_NR_TARIF); if (inhalt == NULL_STRING) inhalt = this.get(Bezeichner.FOLGE_NR_ZUR_LAUFENDEN_PERSONEN_NR_UNTER_NR_BZW_LAUFENDEN_NR_TARIF); return inhalt; } /** * Liefert den Inhalt des 9. Feldes in Satzart 0220, Sparte 580 (Bausparen). Vorher sollte * allerdings mittels {@link #hasBausparenArt()} geprueft werden, ob der Satz ein * Bausparenart-Feld besitzt. *

* * @return die Bausparenart */ @JsonIgnore public final String getBausparenArt() { Feld art = this.getFeld(Bezeichner.ART_580); return art.getInhalt(); } /** * Exportiert den Satz. * * @param writer the writer * @throws IOException Signals that an I/O exception has occurred. */ public void export(final Writer writer) throws IOException { for (Teildatensatz tds : teildatensatz) { tds.export(writer); } } /** * Exportiert den Satz. * * @param file Datei * @throws IOException Signals that an I/O exception has occurred. */ public void export(final File file) throws IOException { try (Writer writer = new FileWriter(file)) { this.export(writer); } } /** * Export. * * @param writer the writer * @param eod das End-of-Datensatz- oder Trennzeichen (z.B. linefeed) * @throws IOException Signals that an I/O exception has occurred. */ public void export(final Writer writer, final String eod) throws IOException { for (Teildatensatz tds : teildatensatz) { tds.export(writer, eod); } } /** * @param ostream z.B. System.out * @throws IOException falls mal was schief geht * @since 0.3 */ public void export(final OutputStream ostream) throws IOException { Writer writer = new OutputStreamWriter(ostream); export(writer); writer.flush(); ostream.flush(); } /** * Eigentlich wollte ich ja diese Methode "import" nennen, aber das kollidiert * leider mit dem Schluesselwort "import" in Java. Inzwischen beruecksichtigt * diese Import-Methode auch zusaetzlich eingestreute Newlines ("\n") oder/und * Wagenruecklaeufe ("\r"). *

* Vor der Behebung von Issue #8 ist man davon ausgegangen, dass die * Teildatensaetze hintereinander kommen und dass es keine Luecken gibt. Dies * ist aber nicht der Fall. Jetzt koennen die Teildatensaetze in beliebiger * Reihenfolge kommen. Nicht importierte Teildatensaetze werden am Ende * aussortiert. * * @param s String zum Importieren * @throws IOException Signals that an I/O exception has occurred. */ public void importFrom(final String s) throws IOException { int satzlength = getSatzlength(s); SortedSet importedTeilsatzIndexes = new TreeSet<>(); for (int i = 0; i < teildatensatz.length; i++) { String input = s.substring(i * satzlength); if (input.trim().isEmpty()) { LOG.info("mehr Daten fuer Satz " + this.getSatzart() + " erwartet, aber nur " + i + " Teildatensaetze vorgefunden"); removeUnusedTeildatensaetze(importedTeilsatzIndexes); break; } int satznummer = readSatznummer(input.toCharArray()) - '0'; int teildatensatzIndex = getTeildatensatzIndex(i, satznummer); teildatensatz[teildatensatzIndex].importFrom(input); importedTeilsatzIndexes.add(teildatensatzIndex); } } private int getTeildatensatzIndex(int index, int satznummer) { if (satznummer < 1) { return index; } for (int i = 0; i < teildatensatz.length; i++) { if (teildatensatz[i].getSatznummer().toInt() == satznummer) { return i; } } return index; } private void removeUnusedTeildatensaetze(SortedSet usedIndexes) { Teildatensatz[] usedTeildatensaetze = new Teildatensatz[usedIndexes.size()]; int i = 0; for (int teilsatzIndex : usedIndexes) { usedTeildatensaetze[i] = teildatensatz[teilsatzIndex]; i++; } this.teildatensatz = usedTeildatensaetze; } /** * Importiert einen Satz von der angegebenen Datei. * * @param file die Import-Datei * @throws IOException Signals that an I/O exception has occurred. */ public void importFrom(final File file) throws IOException { try (Reader reader = new FileReader(file)) { this.importFrom(reader); } } /** * Ermittelt die Satzlaenge. Je nachdem, ob das Zeilenende aus keinem, einem * oder zwei Zeichen besteht, wird 256, 257 oder 258 zurueckgegeben. * * @param s der komplette Satz * @return 256, 257 oder 258 * @since 0.4 */ protected final int getSatzlength(final String s) { int satzlength = 256; try { char c256 = s.charAt(256); if ((c256 == '\n') || (c256 == '\r')) { satzlength = 257; } if (s.length() > satzlength) { char c257 = s.charAt(257); if ((c257 == '\n') || (c257 == '\r')) { satzlength = 258; } } } catch (StringIndexOutOfBoundsException e) { LOG.trace("end of string \"" + s + "\" reached", e); } return satzlength; } /** * Import from. * * @param istream the istream * @throws IOException Signals that an I/O exception has occurred. */ public final void importFrom(final InputStream istream) throws IOException { importFrom(new InputStreamReader(istream, Config.DEFAULT_ENCODING)); } /** * Import from. * * @param reader the reader * @throws IOException Signals that an I/O exception has occurred. */ public final void importFrom(final Reader reader) throws IOException { PushbackLineNumberReader lnr = new PushbackLineNumberReader(reader, 256); try { importFrom(lnr); } catch (IOException ioe) { throw new ImportException(lnr, "read error", ioe); } catch (NumberFormatException nfe) { throw new ImportException(lnr, "number expected", nfe); } } /** * Der hier verwendete PushbackReader wird benoetigt, damit die gelesene * Satzart und Sparte wieder zurueckgesetllt werden kann. Seit 0.5.1 ist * diese Methode nicht mehr final, da manche Satzarten wohl Eigenarten haben * koennen (wie z.B. fehlende Sparten-Eintraege). * * @param reader the reader * @throws IOException Signals that an I/O exception has occurred. */ public void importFrom(final PushbackLineNumberReader reader) throws IOException { char[] cbuf = new char[257 * teildatensatz.length]; char[] feld1to7 = null; Character satznummer = null; for (int i = 0; i < teildatensatz.length; i++) { reader.skipNewline(); if (!matchesNextTeildatensatz(reader, feld1to7, satznummer)) { LOG.info((teildatensatz.length - i) + " more Teildatensaetze expected for " + this + ", but Satzart or Sparte or Wagnisart or TeildatensatzNummer has changed"); break; } satznummer = readSatznummer(reader); importFrom(reader, cbuf, i * 257); cbuf[i * 257 + 256] = '\n'; feld1to7 = Arrays.copyOfRange(cbuf, i*257, i*257 + 42); } importFrom(new String(cbuf)); } /** * Prueft, ob die kommende Zeile noch zu dem aktuellen Datensatz gehoert. * D.h. es wird geprueft, ob es ein weiterer Teildatensatz oder ein neuer * Datensatz ist. *

* Unterklassen (wie Datensatz) sind dafuer verantwortlich, dass auch noch * die Sparte/... ueberprueft wird, ob sie noch richtig ist oder ob da schon der * naechste Satz beginnt. Hier (fuer den allgemeinen Fall) wird nur die * Satzart ueberprueft. *

* * @param reader den Reader * @param lastFeld1To7 die Felder 1 .. 7 aus dem letzten Datensatz * @param satznummer die Satznummer * @return true wenn wenigstens die Satzart uebereinstimmt (nur für Vor/Nachsatz anwendbar) * @throws IOException bei I/O-Fehlern * @since 0.5.1 */ protected boolean matchesNextTeildatensatz(final PushbackLineNumberReader reader, char[] lastFeld1To7, Character satznummer) throws IOException { try { int art = readSatzart(reader); return art == this.getSatzart(); } catch (EOFException ex) { LOG.info("No next teildatensatz found ({}).", ex.getLocalizedMessage()); LOG.debug("Details:", ex); return false; } } private static void importFrom(final Reader reader, final char[] cbuf, final int i) throws IOException { if (reader.read(cbuf, i, 256) == -1) { throw new EOFException("can't read 256 bytes from " + reader); } } /** * Liest 4 Bytes, um die Satzart zu bestimmen und stellt die Bytes * anschliessend wieder zurueck in den Reader. * * @param reader the reader * @return Satzart * @throws IOException Signals that an I/O exception has occurred. */ public static int readSatzart(final PushbackLineNumberReader reader) throws IOException { reader.skipWhitespace(); char[] cbuf = new char[4]; importFrom(reader, cbuf); reader.unread(cbuf); return Integer.parseInt(new String(cbuf).trim()); } private static void importFrom(final Reader reader, final char[] cbuf) throws IOException { if (reader.read(cbuf) == -1) { String s = new String(cbuf).trim(); throw new EOFException("can't read " + cbuf.length + " bytes from " + reader + ", only \"" + s + "\" ("+ s.length() + " bytes)"); } } /** * Aus Performance-Gruenden stuetzt sich diese Methode nicht auf die * validate()-Methode ab. * * @return true/false */ public boolean isValid() { if (!this.satzart.isValid() || (this.getSatzart() < 1)) { return false; } if (this.teildatensatz != null) { for (int i = 0; i < teildatensatz.length; i++) { if (!teildatensatz[i].isValid()) { LOG.info("Teildatensatz " + (i + 1) + " is invalid"); return false; } } } return true; } /** * Validiert die einzelnen Teildatensaetze. * * @return Liste mit Constraint-Verletzungen */ public List validate() { Validator validator = new Validator(); List violations = validator.validate(this); if (!this.satzart.isValid() || (this.getSatzart() < 1)) { ConstraintViolation cv = new SimpleConstraintViolation("invalid Satzart " + this.satzart.getInhalt(), this, this.satzart); violations.add(cv); } if (this.teildatensatz != null) { for (Teildatensatz tds : teildatensatz) { violations.addAll(tds.validate()); } } return violations; } /* * (non-Javadoc) * @see java.lang.Object#toString() */ @Override public final String toString() { try { return this.toShortString() + " (" + StringUtils.abbreviate(this.toLongString(), 47) + ")"; } catch (RuntimeException shouldNeverHappen) { LOG.error("shit happens in toString()", shouldNeverHappen); return super.toString(); } } /** * To short string. * * @return the string */ public String toShortString() { return "Satzart " + this.getSatzTyp(); } /** * To long string. * * @return the string */ public String toLongString() { StringWriter swriter = new StringWriter(); try { this.export(swriter); } catch (IOException canthappen) { LOG.warn(canthappen + " ignored", canthappen); swriter.write(canthappen.getLocalizedMessage()); } return swriter.toString(); } /** * Zwei Saetze sind gleich, wenn sie die gleichen Daten besitzen. Die * Idee dabei ist, dass wir die beiden Saetze exportieren und dann das * Resultat vergleichen. * * @param obj der andere Satz * @return true, wenn beide Saetze gleich sind * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(final Object obj) { if (!(obj instanceof Satz)) { return false; } Satz other = (Satz) obj; return this.toLongString().equals(other.toLongString()); } /* * (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return this.getSatzart(); // TODO: intelligentere Implementierung mit weniger Kollisionen } /** * Liefert die Felder aller Teildatensaetze zurueck. * * @return the felder * @since 1.2 */ public Collection getFelder() { List felder = new ArrayList<>(); for (Teildatensatz tds : this.getTeildatensaetze()) { for (Feld feld : tds.getFelder()) { if (!contains(feld.getBezeichner(), felder)) { felder.add(feld); } } } return felder; } private static boolean contains(Bezeichner bezeichner, List felder) { for (Feld feld : felder) { if (bezeichner.equals(feld.getBezeichner())) { return true; } } return false; } /** * Liest die Satznummer. * * @param reader den Reader * @return Teildatensatz-Nummer * @throws IOException bei Lesefehler */ public static Character readSatznummer(final PushbackReader reader) throws IOException { char[] cbuf = new char[256]; if (reader.read(cbuf) == -1) { throw new EOFException("can't read 1 bytes (" + new String(cbuf) + ") from " + reader); } reader.unread(cbuf); return readSatznummer(cbuf); } /** * Liest die Satznummer. * * @param cbuf der eingelesene Satz in char array * @return Teildatensatz -Nummer */ public static char readSatznummer(char[] cbuf) { if (cbuf.length < 256) { return 0; } String satz = new String(cbuf); String satzartString = satz.substring(0, 4) .trim(); int satzart = isNumber(satzartString) ? Integer.parseInt(satzartString) : -1; String sparteString = satz.substring(10, 13) .trim(); int sparte = isNumber(sparteString) ? Integer.parseInt(sparteString) : -1; int satznummerIndex = 255; switch (satzart) { case 210: satznummerIndex = getSatznummerIndexOfSatz210(sparte); break; case 211: switch (sparte) { case 0: case 80: case 170: case 190: satznummerIndex = 42; break; default: break; } break; case 220: case 221: satznummerIndex = getSatznummerIndexOf(satz, sparte); break; case 250: case 251: satznummerIndex = 50; break; case 270: case 280: case 291: case 292: case 293: case 294: case 295: satznummerIndex = 42; break; case 410: case 420: case 450: satznummerIndex = 50; break; case 550: satznummerIndex = 65; break; default: break; } return satz.charAt(satznummerIndex); } private static int getSatznummerIndexOfSatz210(int sparte) { switch (sparte) { case 0: case 80: case 170: case 190: case 550: case 560: case 570: case 580: return 42; case 130: return 250; default: return 255; } } private static int getSatznummerIndexOf(String satz, int sparte) { switch (sparte) { case 0: return 46; case 30: if ((satz.charAt(48) == '2' && satz.charAt(255) == 'X') || (satz.charAt( 48) == '1' || satz.charAt(48) == '4')) { return 48; } else if (Character.isDigit(satz.charAt(255)) && satz.charAt(255) != '0' && satz.charAt(255) != '2') { return 249; } else if (satz.charAt(42) == '3') { return 42; } else { return 59; } case 40: case 140: return 50; case 70: return 52; case 80: case 190: return 48; case 170: return 49; case 550: case 560: case 570: case 580: return 42; default: return 255; } } public static boolean isNumber(String string) { return string.matches("-?\\d+"); } /** * Legt eine Kopie des Satzes an. * * @return Kopie * @throws CloneNotSupportedException sollte nicht auftreten * @see Cloneable */ @SuppressWarnings("squid:S2975") @Override public Object clone() throws CloneNotSupportedException { Satz cloned = (Satz) super.clone(); cloned.teildatensatz = new Teildatensatz[teildatensatz.length]; for (int i = 0; i < teildatensatz.length; i++) { cloned.teildatensatz[i] = new Teildatensatz(teildatensatz[i]); } return cloned; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy