package edu.pdx.cs410J.family;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.io.Serializable;
/**
* This class represents a person in a family tree. Each person has a
* required unique id. Additionally, a person may have a first,
* middle, and last name, a date of birth, a date of death, and may be
* involved in one or more marriages.
*
* @author David Whitlock
*/
public class Person implements Serializable {
public static Gender MALE = Gender.MALE;
public static Gender FEMALE = Gender.FEMALE;
public enum Gender { FEMALE, MALE, UNKNOWN };
/** A constant representing the id of an unknown person */
public static final int UNKNOWN = -1;
private int id;
private Gender gender;
private String firstName;
private String middleName;
private String lastName;
private Person father;
private Person mother;
private Collection<Marriage> marriages;
private Date dob; // Date of birth
private Date dod; // Date of death
/** The id of this person's mother. We need this for the parsers
who read a person's id before the Person is created. */
private int motherId = UNKNOWN;
/** The id of this person's father. We need this for the parsers
who read a person's id before the Person is created. */
private int fatherId = UNKNOWN;
/**
* Creates a new <code>Person</code> with a given id and gender.
* An {@link #UNKNOWN} person cannot be created.
*
* @throws FamilyTreeException
* <code>id</code> is less than 1 or <code>gender</code> is
* neither {@link #MALE} nor {@link #FEMALE}
*/
public Person(int id, Gender gender) {
if (id < 1) {
String m = "A person's id must be greater than 1: " + id;
throw new FamilyTreeException(m);
}
if (gender != MALE && gender != FEMALE) {
String s = "Gender must be MALE or FEMALE";
throw new FamilyTreeException(s);
}
this.id = id;
this.gender = gender;
this.marriages = new ArrayList<Marriage>();
}
/**
* Creates a person of unknown gender. Note that this method is
* package protected and is meant to be invoked by parsers, etc. in
* which we have to create a person before we know its gender.
*/
Person(int id) {
if (id < 1) {
String m = "A person's id must be greater than 1: " + id;
throw new FamilyTreeException(m);
}
this.id = id;
this.gender = Gender.UNKNOWN;
this.marriages = new ArrayList<Marriage>();
}
/**
* Default constructor for deserialization
*/
private Person() {
}
/////////////////////// Instance Methods ///////////////////////
/**
* Returns this person's id.
*/
public int getId() {
return this.id;
}
/**
* Sets this person's gender. Note that this method is package
* protected.
*
* @throws FamilyTreeException
* The <code>gender</code> is neither {@link #MALE} nor
* {@link #FEMALE}.
*
* @see #Person(int)
*/
void setGender(Gender gender) {
if (gender == MALE || gender == FEMALE) {
this.gender = gender;
} else {
String s = "Invalid gender: " + gender;
throw new FamilyTreeException(s);
}
}
/**
* Returns this person's gender
*/
public Gender getGender() {
return this.gender;
}
/**
* Sets this person's first name.
*/
public void setFirstName(String firstName) {
this.firstName = firstName;
}
/**
* Returns this person's first name.
*/
public String getFirstName() {
return this.firstName;
}
/**
* Sets this person's middle name.
*/
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
/**
* Returns this person's middle name.
*/
public String getMiddleName() {
return this.middleName;
}
/**
* Sets this person's last name.
*/
public void setLastName(String lastName) {
this.lastName = lastName;
}
/**
* Returns this person's last name.
*/
public String getLastName() {
return this.lastName;
}
/**
* Returns this person's full (first, middle, and last) name.
*/
public String getFullName() {
StringBuffer fullName = new StringBuffer();
if (this.firstName != null) {
fullName.append(this.firstName);
fullName.append(' ');
}
if (this.middleName != null) {
fullName.append(this.middleName);
fullName.append(' ');
}
if (this.lastName != null) {
fullName.append(this.lastName);
}
return fullName.toString().trim();
}
/**
* Sets this person's father.
*
* @throws FamilyTreeException
* <code>father</code> is not {@link #MALE}
*/
public void setFather(Person father) {
if (father.getGender() != Person.MALE) {
String s = "Father " + father + " must be MALE";
throw new FamilyTreeException(s);
}
this.fatherId = father.getId();
this.father = father;
}
/**
* Returns the id of this person's father.
*
* @return {@link #UNKNOWN}, if this person's father is not known
*/
public int getFatherId() {
if (this.father == null) {
return UNKNOWN;
} else {
// assert this.fatherId != UNKNOWN;
return this.father.getId();
}
}
/**
* Returns this person's father.
*/
public Person getFather() {
return this.father;
}
/**
* Sets the id of this person's father. This method is package
* protected because it is only intended to be access by parsers and
* other objects that would see a peron's id before the person is
* created.
*
* @see #patchUp
*/
void setFatherId(int id) {
this.fatherId = id;
}
/**
* Sets this person's mother.
*
* @throws FamilyTreeException
* <code>mother</code>'s gender is not {@link #FEMALE}
*/
public void setMother(Person mother) {
if (mother.getGender() != Person.FEMALE) {
String s = "Person " + mother.getId() + "(mother of " +
this.getId() + ") must be FEMALE";
throw new FamilyTreeException(s);
}
this.mother = mother;
}
/**
* Returns the id of this person's mother.
*
* @return {@link #UNKNOWN}, if this person's father is not known
*/
public int getMotherId() {
if (this.mother == null) {
return UNKNOWN;
} else {
return this.mother.getId();
}
}
/**
* Returns this person's mother.
*/
public Person getMother() {
return this.mother;
}
/**
* Sets the id of this person's mother. This method is package
* protected because it is only intended to be accessed by parsers
* and other objects that would see a peron's id before the person
* is created.
*
* @see #patchUp
*/
void setMotherId(int id) {
this.motherId = id;
}
/**
* "Patches up" a person's mother and father <code>Person</code>
* objects. This method is package protected because it is only
* intended to be accessed by parsers and other objects that would
* see a peron's id before the person is created.
*
* @throws FamilyTreeException
* Either the mother or father does not existin in
* <code>tree</code> or if the gender has not been set
*/
void patchUp(FamilyTree tree) {
if (this.father == null && this.fatherId != UNKNOWN) {
Person father = tree.getPerson(this.fatherId);
if (father == null) {
String s = "Father " + this.fatherId + " does not exist";
throw new FamilyTreeException(s);
}
this.setFather(father);
}
if (this.mother == null && this.motherId != UNKNOWN) {
Person mother = tree.getPerson(this.motherId);
if (mother == null) {
String s = "Mother " + this.motherId + " does not exist";
throw new FamilyTreeException(s);
}
this.setMother(mother);
}
if (this.gender == Gender.UNKNOWN) {
String s = "Gender has not been set yet!";
throw new FamilyTreeException(s);
}
}
/**
* Sets this person's date of birth.
*/
public void setDateOfBirth(Date dob) {
this.dob = dob;
}
/**
* Returns this person's date of birth.
*/
public Date getDateOfBirth() {
return this.dob;
}
/**
* Sets this person's date of death.
*
* @throws FamilyTreeException
* If this person's data of birth is known and
* <code>dod</code> occurs before it.
*/
public void setDateOfDeath(Date dod) {
if (this.dob != null && dod != null && this.dob.after(dod)) {
String s = "Date of death (" + dod +
") cannot occur before date of birth (" + this.dob + ")";
throw new FamilyTreeException(s);
}
this.dod = dod;
}
/**
* Returns this person's date of death.
*/
public Date getDateOfDeath() {
return this.dod;
}
/**
* Makes note of a marriage this person was involved in.
*
* @throws FamilyTreeException
* If this person is not one of the spouses in the marriage
*/
public void addMarriage(Marriage marriage) {
if (this.getGender() == Person.MALE) {
if (!marriage.getHusband().equals(this)) {
String s = "This person (" + this.getFullName() +
") is not the husband in " + marriage;
throw new FamilyTreeException(s);
}
} else {
if (!marriage.getWife().equals(this)) {
String s = "This person (" + this.getFullName() +
") is not the wife in " + marriage;
throw new FamilyTreeException(s);
}
}
this.marriages.add(marriage);
}
/**
* Returns the marriages that this person was involved in.
*/
public Collection<Marriage> getMarriages() {
return this.marriages;
}
////////////////////// Utility Methods ////////////////////////
/**
* Determines whether or not this <code>Person</code> is equal to
* another <code>Person</code>. Two <code>Person</code>s are
* considered equal if they have the same id.
*/
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (!(o instanceof Person)) {
return false;
}
Person other = (Person) o;
return this.getId() == other.getId();
}
/**
* Returns a brief description of this person.
*/
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("Person ").append(this.id).append(": ").append(this.getFullName());
if (this.dob != null) {
sb.append("\nBorn: ");
sb.append(this.dob);
}
if (this.dod != null) {
sb.append(", Died: ");
sb.append(this.dod);
}
if (this.mother != null) {
sb.append("\nMother: ");
sb.append(this.mother.getFullName());
}
if (this.father != null) {
sb.append(", Father: ");
sb.append(this.father.getFullName());
}
sb.append("\nMarried ");
sb.append(this.marriages.size());
sb.append(" times");
return sb.toString();
}
}