/*
* This file is part of jHaushalt.
* jHaushalt 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 2 of the License, or
* (at your option) any later version.
* jHaushalt 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 jHaushalt; if not, see <http://www.gnu.org/licenses/>.
* (C)opyright 2002-2010 Dr. Lars H. Hahn
*/
package haushalt.daten;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Logger;
/**
* Die Register dienen als Container für die Buchung. Beispiele
* für Register sind "Girokonto" oder "Bargeld".
*
* @author Dr. Lars H. Hahn
* @version 2.1.3/2006.06.21
*/
/*
* 2006.06.21 Verbesserung der Performanz beim Einsortieren
* durch die Annahme, dass meistens an das Ende des
* Registers eingefügt werden muss
* 2006.06.21 BugFix: Einsortieren war doch notwendig, da die
* Umbuchungen tw. schon vorzeitig ins Register
* geladen werden
* 2006.06.19 Verbesserung der Performanz durch Verzichten des
* "Einsortierens" der Buchungen beim Laden
* 2006.02.14 BugFix: Abfangen des Löschens der "letzten Zeile"
* 2006.02.10 Implementierung des Interface 'Comparable'
* 2006.01.31 Neusortieren einer Buchung jetzt unter Angabe
* der Buchung selbst
*/
public class Register implements Comparable<Register> {
private static final boolean DEBUG = false;
private static final Logger LOGGER = Logger.getLogger(Register.class.getName());
private String name;
private final ArrayList<AbstractBuchung> buchungen;
public Register(final String name) {
this.name = name;
this.buchungen = new ArrayList<AbstractBuchung>();
}
@Override
public String toString() {
return this.name;
}
public void setName(final String neuerName) {
this.name = neuerName;
}
/**
* Liefert die Anzahl der im Register gespeicherten Buchungen.
*
* @return Anzahl der Buchungen
*/
public int getAnzahlBuchungen() {
return this.buchungen.size();
}
/**
* Liefert die Buchung an der angegebenen Position.
*
* @param nr
* Position der gesuchten Buchung
* @return gesuchte Buchung
*/
public AbstractBuchung getBuchung(final int nr) {
if (nr == getAnzahlBuchungen()) {
return new StandardBuchung();
}
return this.buchungen.get(nr);
}
/**
* Wird benötigt um alte Buchungen zu löschen.
*
* @param nr
* Position bis zu der Buchungen gelöscht werden
*/
public void removeBisBuchung(final int nr) {
for (int i = 0; i <= nr; i++) {
this.buchungen.remove(0);
// mit jedem remove verändert sich der Index -> immer erstes Element
// löschen
}
}
/**
* Löscht eine Buchung.
*
* @param nr
*/
public void entferneBuchung(final int nr) {
if (nr == this.buchungen.size()) {
return; // wenn eine neue (Split-)Buchung eingefügt
}
// werden soll, kann keine alte Buchung gelöscht
// werden :-)
final AbstractBuchung buchung = this.buchungen.get(nr);
if (buchung.getClass() == Umbuchung.class) {
// Die Buchung muss auch aus dem Partner-Register entfernt werden.
final UmbuchungKategorie kategorie = (UmbuchungKategorie) buchung.getKategorie();
kategorie.getPartnerRegister(this).buchungen.remove(buchung);
}
this.buchungen.remove(buchung);
}
/**
* Entfernt eine Umbuchung. Hiermit kann eine Umbuchung bei Änderung seines
* Quell- oder Zielregisters sich aus dem alten Register entfernen.
*
* @param buchung
*/
public void loescheUmbuchung(final Umbuchung buchung) {
this.buchungen.remove(buchung);
}
/**
* Sortiert die übergebene Buchung in das Register ein.
* Es wird aufsteigend nach Buchungsdatum sortiert.
*
* @param buchung
* einzusortierende Buchung
* @return Einfüge-Position
*/
public int einsortierenBuchung(final AbstractBuchung buchung) {
final int size = this.buchungen.size();
int pos = 0;
for (int i = size - 1; i >= 0; i--) {
if (buchung.compareTo(this.buchungen.get(i)) > 0) {
pos = i + 1;
i = 0;
}
}
if (pos == size) { // ans Ende
this.buchungen.add(buchung);
} else { // neue Buchung einfuegen
this.buchungen.add(pos, buchung);
}
if (DEBUG) {
LOGGER.info("Register "
+ this.name
+ ": Buchung "
+ buchung.getDatum()
+ "/"
+ buchung.getText()
+ "/"
+ buchung.getWert()
+ " an Positon "
+ (pos + 1)
+ " einsortiert.");
}
return pos;
}
/**
* Sortiert die angegebene Buchung neu ein.
* Dies wird notwendig, wenn das Datum der Buchung geändert
* wurde.
*
* @param buchung
* geänderte Buchung
* @return Einfüge-Position
*/
public int buchungNeusortieren(final AbstractBuchung buchung) {
this.buchungen.remove(buchung);
return einsortierenBuchung(buchung);
}
/**
* Fügt die Buchungen aus einem anderen Register diesem
* Register hinzu und löscht sie dann.
*
* @param registerZumLoeschen
* Register aus dem die Buchungen übernommen werden
*/
public void registerVereinigen(final Register registerZumLoeschen) {
while (registerZumLoeschen.getAnzahlBuchungen() > 0) {
if (registerZumLoeschen.buchungen.get(0).getClass() == Umbuchung.class) {
final Umbuchung umbuchung = (Umbuchung) registerZumLoeschen.buchungen.get(0);
final UmbuchungKategorie alteKategorie = (UmbuchungKategorie) umbuchung.getKategorie();
if (alteKategorie.isSelbstbuchung()) {
// Umbuchung: alte Selbstbuchung
umbuchung.setKategorie(new UmbuchungKategorie(this, this));
// -> automatisch löschen und neu einfügen
} else {
// normale Umbuchung
Register neueQuelle;
Register neuesZiel;
if (alteKategorie.getQuelle() == registerZumLoeschen) {
neueQuelle = this;
} else {
neueQuelle = alteKategorie.getQuelle();
}
if (alteKategorie.getZiel() == registerZumLoeschen) {
neuesZiel = this;
} else {
neuesZiel = alteKategorie.getZiel();
}
if (neueQuelle != neuesZiel) {
umbuchung.setKategorie(new UmbuchungKategorie(neueQuelle, neuesZiel));
} else {
// sonst loeschen:
neueQuelle.loescheUmbuchung(umbuchung);
registerZumLoeschen.buchungen.remove(0);
}
}
} else {
// StandardBuchung + SplitBuchung
einsortierenBuchung(registerZumLoeschen.buchungen.get(0));
registerZumLoeschen.buchungen.remove(0);
}
}
}
/**
* Liefert den Saldo des Registers bis zum angegebenen Datum (exklusiv).
*
* @param datum
* @return Saldo
*/
public Euro getSaldo(final Datum datum) {
Euro saldo = new Euro();
int i = 0;
while ((i < this.buchungen.size()) && (datum.compareTo(getBuchung(i).getDatum()) > 0)) {
if (getBuchung(i).getClass() == Umbuchung.class) {
final UmbuchungKategorie kategorie = (UmbuchungKategorie) getBuchung(i).getKategorie();
if ((kategorie.getQuelle() == this) && !kategorie.isSelbstbuchung()) {
saldo = saldo.sub(getBuchung(i).getWert());
// Falls dieses Register Quelle einer Umbuchung ist:
// Subtrahieren!
// Selbstbuchugen aber nicht!
} else {
saldo.sum(getBuchung(i).getWert());
}
} else { // StandardBuchung + SplitBuchung
saldo.sum(getBuchung(i).getWert());
}
i++;
}
return saldo;
}
/**
* Liefert den Saldo des Registers bis zur angegebenen Zeile.
*
* @param row
* @return Saldo
*/
public Euro getSaldo(final int row) {
Euro saldo = new Euro();
for (int i = 0; i <= row; i++) {
if (getBuchung(i).getClass() == Umbuchung.class) {
final UmbuchungKategorie kategorie = (UmbuchungKategorie) getBuchung(i).getKategorie();
if ((kategorie.getQuelle() == this) && !kategorie.isSelbstbuchung()) {
saldo = saldo.sub(getBuchung(i).getWert());
// Falls dieses Register Quelle einer Umbuchung ist:
// Subtrahieren!
// Selbstbuchugen aber nicht!
} else {
saldo.sum(getBuchung(i).getWert());
}
} else { // StandardBuchung + SplitBuchung
saldo.sum(getBuchung(i).getWert());
}
}
return saldo;
}
// -- E/A-Funktionen -------------------------------------------------------
public void laden(final DataInputStream in, final Datenbasis db) throws IOException {
final int size = in.readInt();
this.buchungen.ensureCapacity(size);
for (int i = 0; i < size; i++) {
final String typ = in.readUTF();
if (typ.equals("Umbuchung")) {
final Umbuchung umbuchung = new Umbuchung();
umbuchung.laden(in, db, this);
} else if (typ.equals("StandardBuchung2")) {
final StandardBuchung standardBuchung = new StandardBuchung();
standardBuchung.laden(in, db);
einsortierenBuchung(standardBuchung);
db.buchungMerken(standardBuchung);
} else if (typ.equals("StandardBuchung")) {
final Datum datum = new Datum();
datum.laden(in);
final String text = in.readUTF();
final int anz = in.readInt();
if (anz == 1) {
final EinzelKategorie kategorie = db.findeOderErzeugeKategorie(in.readUTF());
final Euro betrag = new Euro();
betrag.laden(in);
final StandardBuchung standardBuchung = new StandardBuchung(datum, text, kategorie, betrag);
einsortierenBuchung(standardBuchung);
db.buchungMerken(standardBuchung);
} else {
final SplitBuchung buchung = new SplitBuchung(datum, text);
for (int j = 0; j < anz; j++) {
final String kategorie = in.readUTF();
final Euro betrag = new Euro();
betrag.laden(in);
buchung.add(db.findeOderErzeugeKategorie(kategorie), betrag);
}
einsortierenBuchung(buchung);
db.buchungMerken(buchung);
}
} else if (typ.equals("SplitBuchung")) {
final SplitBuchung splitBuchung = new SplitBuchung();
splitBuchung.laden(in, db);
einsortierenBuchung(splitBuchung);
db.buchungMerken(splitBuchung);
} else if (!typ.equals("Dummy")) {
throw new IOException("Register.laden: Unbekannter Buchungstyp: " + typ);
// "Dummy"-Buchung wird ignoriert!
}
}
}
public void speichern(final DataOutputStream out) throws IOException {
out.writeUTF(this.name);
out.writeInt(this.buchungen.size());
for (int i = 0; i < this.buchungen.size(); i++) {
final AbstractBuchung buchung = getBuchung(i);
if (buchung.getClass() == Umbuchung.class) {
final UmbuchungKategorie kategorie = (UmbuchungKategorie) buchung.getKategorie();
if ((kategorie.getQuelle() == this) && !kategorie.isSelbstbuchung()) {
out.writeUTF("Dummy");
// Umbuchungen werden nur vom Zielregister gespeichert.
// "Dummy"-Buchung wird geschrieben damit die Anzahl stimmt!
// Selbstbuchungen dennoch schreiben!
if (DEBUG) {
LOGGER.info("Register " + this.name + ": Dummy-Buchung gespeichert.");
}
} else {
buchung.speichern(out);
}
} else {
buchung.speichern(out);
}
}
}
public String[][] csvExport() {
final int anzahl = getAnzahlBuchungen();
final String[][] text = new String[anzahl][3];
for (int i = 0; i < anzahl; i++) {
final AbstractBuchung buchung = getBuchung(i);
text[i][0] = "" + buchung.getDatum();
text[i][1] = buchung.getText();
text[i][2] = "" + buchung.getKategorie();
text[i][3] = "" + buchung.getWert();
}
return text;
}
public int compareTo(final Register register) {
return this.name.compareTo(register.name);
}
}