/**
* FyLLGen - A Java based tool for collecting and distributing family data
*
* Copyright (C) 2007-2011 Christian Packenius
*
* This program 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 3 of the License, or
* any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.chris_soft.fyllgen.menu.extra;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Properties;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.MessageBox;
import de.chris_soft.fyllgen.GUI;
import de.chris_soft.fyllgen.Statics;
import de.chris_soft.fyllgen.data.Family;
import de.chris_soft.fyllgen.data.Person;
import de.chris_soft.fyllgen.data.Relationship;
import de.chris_soft.fyllgen.data.RelationshipParentChild;
import de.chris_soft.fyllgen.data.RelationshipPartners;
import de.chris_soft.fyllgen.ged.GedReader;
import de.chris_soft.fyllgen.ged.GedReaderException;
import de.chris_soft.fyllgen.utilities.SwtUtilities;
import de.chris_soft.fyllgen.widget.dialog.MergeChoiceDialog;
import de.chris_soft.fyllgen.widget.dialog.MergeChoiceRelationshipPersonDialog;
import de.chris_soft.fyllgen.widget.dialog.MergePersonDialog;
import de.chris_soft.fyllgen.widget.dialog.MergeRelationshipDialog;
/**
* Fremddaten �bernehmen. Falls noch keine Fremddaten eingelesen wurden (oder
* die alten schon komplett abgearbeitet sind), kann hier eine weitere Familie
* eingelesen werden. Ansonsten k�nnen die bisherigen Daten weiter �bernommen
* werden.<br>
* Vorgehensweise:<br>
* <code>
* Gibt es schon eingelesene Fremddaten (Consts.mergeFamily != null)?
* NEIN: Datei-�ffnen-Dialog f�r Genealogie-Dateien.
* JA: Beziehung zwischen zwei �bernommenen Personen vorhanden?
* JA: Dialog zur Beziehungs�bernahme.
* NEIN: Beziehung mit einer �bernommenen Person vorhanden?
* NEIN: MergeChoiceDialog und dann MergePersonDialog. [1], [2]
* JA: Dialog zur Beziehungs�bernahme und gleichzeitiger Personenwahl. [3]
* [3] Fakt: Eine Person wurde schon �bernommen. Es existiert eine Beziehung zu einem Kind, Elternteil oder Partner,
* das/der noch nicht �bernommen wurde.
* </code>
* @author Christian Packenius, Juli 2008.
*/
public class MergeData implements Listener {
/**
* Fremddaten �bernehmen.
* @param event Ungenutzter Event.
*/
public void handleEvent(Event event) {
// Neue Merge-Datei �ffnen?
if (Statics.mergeFamily == null) {
FileDialog fd = new FileDialog(GUI.instance.shell, SWT.OPEN);
fd.setFilterExtensions(new String[] { "*.fgf", "*.ged" });
fd.setFilterNames(new String[] { "FyLLGenFamily-Datei", "GEDCOM-Datei" });
fd.setFilterPath(".");
fd.setText("Datei zum Verkn�pfen w�hlen");
String result = fd.open();
if (result != null) {
// *** FyLLGenFile ***
if (result.endsWith(".fgf")) {
Family mergeFamily = new Family();
try {
mergeFamily.loader.load(result);
}
catch (Exception exception) {
messageBoxFileProblems(exception);
return;
}
Statics.mergeFamily = mergeFamily;
}
// *** GEDCOM ***
else if (result.endsWith(".ged")) {
Family mergeFamily = new Family();
try {
readGedFile(result, mergeFamily);
}
catch (Exception exception) {
messageBoxFileProblems(exception);
return;
}
Statics.mergeFamily = mergeFamily;
}
// *** unknown ***
else {
MessageBox mb = new MessageBox(GUI.instance.shell, SWT.OK | SWT.ICON_ERROR);
mb.setMessage("Dateityp unbekannt!");
mb.setText("Fehler!");
mb.open();
return;
}
}
else {
// Der User hat die Dateiauswahl abgebrochen.
return;
}
}
// Personen intern sortieren (einmalig).
Statics.mergeFamily.sortPersons();
// Hier das manuelle und automatische Mergen.
makeMerge();
// Wenn keine Personen mehr in der Familie sind, diese l�schen.
if (Statics.mergeFamily != null && Statics.mergeFamily.getPersonsArray().length == 0) {
Statics.mergeFamily = null;
}
}
private void readGedFile(String result, Family mergeFamily) throws FileNotFoundException, IOException,
GedReaderException {
GedReader ged = new GedReader();
InputStream in = new FileInputStream(result);
ged.setInputStream(in);
ged.read();
in.close();
ged.fillFamily(mergeFamily);
}
private void messageBoxFileProblems(Exception exception) {
MessageBox mb = new MessageBox(GUI.instance.shell, SWT.OK | SWT.ICON_ERROR);
mb.setMessage("Problem beim Laden der Datei!\r\n" + exception.getMessage());
mb.setText("Fehler!");
mb.open();
}
/**
* F�hrt den n�chsten Merge-Schritt aus.
*/
private void makeMerge() {
boolean bNext = true;
while (bNext && Statics.mergeFamily != null) {
// Eventuell gibt es Personen, die noch nicht gel�scht wurden. Dies
// hier pr�fen.
ArrayList<Person> listRemovedPersons = new ArrayList<Person>();
for (Person p : Statics.mergeFamily.getPersonsArray()) {
if (p.isFinishedMerge()) {
Statics.mergeFamily.removeFinishedMergePersons(listRemovedPersons, p);
}
}
// Zuerst schauen, ob eine Beziehung zu Verkn�pfen ist.
Relationship rel = getNextFullRelationship();
if (rel != null) {
bNext = mergeFullRelationship(rel);
}
else {
// Erst nach einer Beziehung mit nur einer �bernommenen Person suchen.
rel = getNextHalfRelationship();
if (rel != null) {
bNext = mergeHalfRelationship(rel);
}
// Dann nach einer Person suchen.
else {
// Eventuell gibt es Personen, die noch nicht gel�scht wurden. Dies
// hier pr�fen.
listRemovedPersons = new ArrayList<Person>();
for (Person p : Statics.mergeFamily.getPersonsArray()) {
if (p.isFinishedMerge()) {
Statics.mergeFamily.removeFinishedMergePersons(listRemovedPersons, p);
}
}
if (Statics.mergeFamily != null && Statics.mergeFamily.getPersonsCount() > 0) {
bNext = mergeNextPerson();
}
else {
Statics.mergeFamily = null;
bNext = false;
}
}
}
}
}
/**
* �bernahme einer Beziehung zweier Personen, bei der beide Personen schon
* bekannt sind.
*/
private boolean mergeFullRelationship(Relationship rel) {
boolean bNext;
if (rel instanceof RelationshipParentChild) {
bNext = mergeParentChildRelation((RelationshipParentChild) rel);
}
else {
bNext = mergePartnerRelation((RelationshipPartners) rel);
}
return bNext;
}
/**
* Merge einer Personenbeziehung, bei der eine der beiden Personen schon
* �bernommen wurde und die andere nicht (daher "Half").
*/
private boolean mergeHalfRelationship(Relationship rel) {
// Schauen, ob wir die andere (nicht �bernommene) Person nicht direkt
// �bernehmen k�nnen.
Person notMergedPerson = rel.partner1.isFinishedMerge() ? rel.partner2 : rel.partner1;
Person p2 = Family.instance.getPersonFromXREFID(notMergedPerson.getValue(Person.XREFID));
if (p2 != null && notMergedPerson.getXREFID().equals(p2.getXREFID())) {
// Person wurde zwar noch nicht �bernommen, aber es gibt sie schon. Nun
// schauen, ob sie nicht ohnehin identisch ist.
if (notMergedPerson.equals(p2)) {
notMergedPerson.setFinishedMerge(true);
return true;
}
}
// Eigentlichen Merge-Dialog aufrufen und auswerten.
boolean bNext;
MergeChoiceRelationshipPersonDialog mcrpd = new MergeChoiceRelationshipPersonDialog(GUI.instance.shell, rel);
mcrpd.open();
bNext = mcrpd.finished;
if (mcrpd.finished) {
MergePersonDialog mpd = new MergePersonDialog(GUI.instance.shell, mcrpd.oldPerson, mcrpd.otherPerson);
mpd.open();
bNext = mpd.finished;
}
else if (mcrpd.finishedNew) {
MergePersonDialog mpd = new MergePersonDialog(GUI.instance.shell, null, mcrpd.otherPerson);
mpd.open();
if (mpd.finished) {
Person originalPerson = Family.instance.getPersonFromXREFID(mcrpd.mergedPerson.getValue(Person.XREFID));
Relationship newRelship;
if (rel instanceof RelationshipParentChild) {
if (rel.partner1 == mcrpd.mergedPerson) {
newRelship = new RelationshipParentChild(originalPerson, mpd.oldPerson, rel.cloneProperties());
}
else {
newRelship = new RelationshipParentChild(mpd.oldPerson, originalPerson, rel.cloneProperties());
}
}
else {
newRelship = new RelationshipPartners(mpd.oldPerson, originalPerson, rel.cloneProperties());
}
originalPerson.addRelationship(newRelship);
Family.instance.setCurrentPerson(mpd.oldPerson, 2);
mcrpd.otherPerson.removeRelationship(rel);
Statics.mergeFamily.removeFinishedMergePersons(new ArrayList<Person>(), rel.partner1);
Statics.mergeFamily.removeFinishedMergePersons(new ArrayList<Person>(), rel.partner2);
}
bNext = mpd.finished;
}
return bNext;
}
/**
* Noch zu verkn�pfende Beziehung suchen. Ist recht einfach: Es m�ssen nur
* beide Personen schon verkn�pft sein.
*/
private Relationship getNextFullRelationship() {
Relationship relship;
for (Person person : Statics.mergeFamily.getPersonsArray()) {
// Eltern und Partner pr�fen (Kind wird indirekt durch Eltern mit
// gepr�ft).
Relationship[] relships = person.getRelationships(Relationship.PARENTS);
if ((relship = getGoodRelationship(relships)) != null) {
return relship;
}
relships = person.getRelationships(Relationship.PARTNERS);
if ((relship = getGoodRelationship(relships)) != null) {
return relship;
}
}
// Aktuell keine abzuarbeitende Verbindung.
return null;
}
/**
* Noch zu verkn�pfende Beziehung suchen. Hier braucht nur eine der beiden
* Personen �bernommen sein.
*/
private Relationship getNextHalfRelationship() {
Relationship relship;
for (Person person : Statics.mergeFamily.getPersonsArray()) {
// Eltern und Partner pr�fen (Kind wird indirekt durch Eltern mit
// gepr�ft).
Relationship[] relships = person.getRelationships(Relationship.PARENTS);
if ((relship = getHalfGoodRelationship(relships)) != null) {
return relship;
}
relships = person.getRelationships(Relationship.PARTNERS);
if ((relship = getHalfGoodRelationship(relships)) != null) {
return relship;
}
}
// Aktuell keine abzuarbeitende Verbindung.
return null;
}
/**
* Pr�ft, ob eine der angegebenen Verbindungen geeignet f�r eine �bernahme
* ist. Dies ist der Fall, sobald beide Personen �bernommen wurden.
* @param relships
* @return
*/
private Relationship getGoodRelationship(Relationship[] relships) {
for (Relationship relationship : relships) {
if (relationship.partner1.isFinishedMerge() && relationship.partner2.isFinishedMerge()) {
return relationship;
}
}
return null;
}
/**
* Pr�ft, ob eine der angegebenen Verbindungen geeignet f�r eine �bernahme
* ist. Dies ist der Fall, wenn eine der beiden Personen bereits �bernommen
* worden ist.
* @param relships
* @return
*/
private Relationship getHalfGoodRelationship(Relationship[] relships) {
for (Relationship relationship : relships) {
if (relationship.partner1.isFinishedMerge() || relationship.partner2.isFinishedMerge()) {
return relationship;
}
}
return null;
}
/**
* Macht den Dialog auf, in dem eine Eltern-Kind-Beziehung �bernommen werden
* kann.
* @return true, wenn es weiter gehen kann, sonst false.
*/
private boolean mergeParentChildRelation(RelationshipParentChild mergeRelationship) {
// Zu dieser Verbindung die Original-Verbindung ermitteln.
Person p1 = Family.instance.getPersonFromXREFID(mergeRelationship.partner1.getXREFID());
Person p2 = Family.instance.getPersonFromXREFID(mergeRelationship.partner2.getXREFID());
Relationship originalRelationship = p1.getRelationship(p2);
// Falls es bisher keine Beziehung gab, dann fragen, ob man sie �bernehmen
// soll.
if (originalRelationship == null) {
String question = "Soll die Eltern-Kind-Beziehung zwischen\r\n\r\n" + p1 + "\r\nund\r\n" + p2 + "\r\n\r\n";
question += "mit dem Status \"" + mergeRelationship.getTypeText() + "\" �bernommen werden?";
int answer = SwtUtilities.askYesNoCancel(GUI.instance.shell, question);
if (answer == SWT.YES) {
RelationshipParentChild rpc = p1.addChild(p2);
rpc.setValue(Relationship.TYPE, mergeRelationship.getType());
Family.instance.setCurrentPerson(p2, 2);
Statics.mergeFamily.removeRelationship(mergeRelationship);
}
else if (answer == SWT.NO) {
Statics.mergeFamily.removeRelationship(mergeRelationship);
}
return answer != SWT.CANCEL;
}
// Falls beide identisch sind, brauchen wir hier keinen Dialog mehr.
if (compareRelationships(mergeRelationship, originalRelationship)) {
// SwtUtilities.sayInfo(Consts.getGui().shell,
// "Identische Beziehung zwischen "
// + p1 + " und " + p2 + " wird gel�scht.");
Statics.mergeFamily.removeRelationship(mergeRelationship);
return true;
}
// Fragen, ob der Typ ge�ndert werden soll.
String question = "Die Eltern-Kind-Beziehung zwischen\r\n\r\n" + originalRelationship.partner1 + "\r\nund\r\n";
question += originalRelationship.partner2 + "\r\n\r\nhat zur Zeit den Status\r\n\r\n"
+ originalRelationship.getTypeText() + ".";
question += "\r\n\r\n Soll dieser Status den neuen Daten gem�� auf\r\n\r\n" + mergeRelationship.getTypeText()
+ "\r\n\r\nge�ndert werden?";
int answer = SwtUtilities.askYesNoCancel(GUI.instance.shell, question);
if (answer == SWT.YES) {
originalRelationship.setValue(Relationship.TYPE, mergeRelationship.getType());
}
if (answer != SWT.CANCEL) {
Statics.mergeFamily.removeRelationship(mergeRelationship);
}
return answer != SWT.CANCEL;
}
/**
* Pr�ft, ob die angegebene zu verkn�pfende Beziehung identisch zur
* Original-Beziehung ist (wenn es diese gibt).
* @param mergeRelationship Beziehung aus der Merge-Family.
* @param originalRelationship
* @return true, falls identisch oder Original-Beziehung mit �bergeordneten
* Informationen, sonst false.
*/
private boolean compareRelationships(Relationship mergeRelationship, Relationship originalRelationship) {
// Daten der beiden Beziehungen ermitteln.
Properties propMerge = mergeRelationship.cloneProperties();
Properties propOriginal = originalRelationship.cloneProperties();
// Falls die neuen Daten leer oder unn�tz sind, braucht keine �bernahme der
// Daten stattfinden.
if (propMerge.size() == 0) {
return true;
}
String type = propMerge.getProperty(Relationship.TYPE);
if (propMerge.size() == 1 && type != null && type.equals("?")) {
return true;
}
// Wenn beide unterschiedlich sind, auf jeden Fall vergleichen.
if (propMerge.size() != propOriginal.size()) {
return false;
}
// Alles vergleichen.
Enumeration<Object> en = propMerge.keys();
while (en.hasMoreElements()) {
String key = (String) en.nextElement();
String val2 = propOriginal.getProperty(key);
if (val2 == null) {
return false;
}
if (!propMerge.getProperty(key).equals(val2)) {
return false;
}
}
return true;
}
/**
* Macht den Dialog auf, in dem eine Partnerschaft �bernommen werden kann.
* @return true, wenn der Anwender die Daten �bernommen hat, sonst false.
*/
private boolean mergePartnerRelation(RelationshipPartners newRelship) {
Person p1 = Family.instance.getPersonFromXREFID(newRelship.partner1.getXREFID());
Person p2 = Family.instance.getPersonFromXREFID(newRelship.partner2.getXREFID());
RelationshipPartners oldRelship = p1.getPartnersRelationship(p2);
// Fragen, ob Partnerschaft �bernommen werden soll.
if (oldRelship == null) {
String question = "Soll die Partnerschaft zwischen\r\n\r\n" + p1 + "\r\nund\r\n" + p2 + "\r\n\r\n";
question += "mit dem Status \"" + newRelship.getTypeText() + "\" �bernommen werden?";
int answer = SwtUtilities.askYesNoCancel(GUI.instance.shell, question);
if (answer == SWT.YES) {
String type = newRelship.getType();
RelationshipPartners rpc = p1.addPartner(p2, type);
rpc.setValue(Relationship.TYPE, type);
Family.instance.setCurrentPerson(p2, 2);
Statics.mergeFamily.removeRelationship(newRelship);
}
else if (answer == SWT.NO) {
Statics.mergeFamily.removeRelationship(newRelship);
}
return answer != SWT.CANCEL;
}
// Vor dem �ffnen des Dialogs erstmal feststellen, ob es �berhaupt einen
// Unterschied gibt.
// Falls nicht, kann man sich den Dialog auch sparen.
if (oldRelship.equals(newRelship)) {
Statics.mergeFamily.removeRelationship(newRelship);
return true;
}
// SwtUtilities.sayInfo(Consts.getGui().shell,
// "TO-DO - openAbsorbPartnerRelationDialog!");
MergeRelationshipDialog mrd = new MergeRelationshipDialog(GUI.instance.shell, oldRelship, newRelship);
mrd.open();
if (mrd.finished) {
Statics.mergeFamily.removeRelationship(newRelship);
}
return mrd.finished;
}
/**
* Ermittelt die n�chste Person, die �bernommen werden kann. Sie wird auch
* direkt �bernommen bzw. es wird eine neue Person erzeugt.
* @return false, wenn der Anwender einen Dialog abgebrochen hat, sonst true.
*/
private boolean mergeNextPerson() {
MergeChoiceDialog npcd = new MergeChoiceDialog(GUI.instance.shell);
npcd.open();
if (npcd.finished) {
MergePersonDialog mpd = new MergePersonDialog(GUI.instance.shell, npcd.oldPerson, npcd.newPerson);
mpd.open();
return mpd.finished;
}
return false;
}
}