001package edu.pdx.cs410J.family; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Date; 006import java.io.Serializable; 007 008/** 009 * This class represents a person in a family tree. Each person has a 010 * required unique id. Additionally, a person may have a first, 011 * middle, and last name, a date of birth, a date of death, and may be 012 * involved in one or more marriages. 013 * 014 * @author David Whitlock 015 */ 016public class Person implements Serializable { 017 public static Gender MALE = Gender.MALE; 018 public static Gender FEMALE = Gender.FEMALE; 019 020 public enum Gender { FEMALE, MALE, UNKNOWN }; 021 022 /** A constant representing the id of an unknown person */ 023 public static final int UNKNOWN = -1; 024 025 private int id; 026 private Gender gender; 027 private String firstName; 028 private String middleName; 029 private String lastName; 030 private Person father; 031 private Person mother; 032 private Collection<Marriage> marriages; 033 private Date dob; // Date of birth 034 private Date dod; // Date of death 035 036 /** The id of this person's mother. We need this for the parsers 037 who read a person's id before the Person is created. */ 038 private int motherId = UNKNOWN; 039 040 /** The id of this person's father. We need this for the parsers 041 who read a person's id before the Person is created. */ 042 private int fatherId = UNKNOWN; 043 044 /** 045 * Creates a new <code>Person</code> with a given id and gender. 046 * An {@link #UNKNOWN} person cannot be created. 047 * 048 * @throws FamilyTreeException 049 * <code>id</code> is less than 1 or <code>gender</code> is 050 * neither {@link #MALE} nor {@link #FEMALE} 051 */ 052 public Person(int id, Gender gender) { 053 if (id < 1) { 054 String m = "A person's id must be greater than 1: " + id; 055 throw new FamilyTreeException(m); 056 } 057 058 if (gender != MALE && gender != FEMALE) { 059 String s = "Gender must be MALE or FEMALE"; 060 throw new FamilyTreeException(s); 061 } 062 063 this.id = id; 064 this.gender = gender; 065 this.marriages = new ArrayList<Marriage>(); 066 } 067 068 /** 069 * Creates a person of unknown gender. Note that this method is 070 * package protected and is meant to be invoked by parsers, etc. in 071 * which we have to create a person before we know its gender. 072 */ 073 Person(int id) { 074 if (id < 1) { 075 String m = "A person's id must be greater than 1: " + id; 076 throw new FamilyTreeException(m); 077 } 078 079 this.id = id; 080 this.gender = Gender.UNKNOWN; 081 this.marriages = new ArrayList<Marriage>(); 082 } 083 084 /** 085 * Default constructor for deserialization 086 */ 087 private Person() { 088 089 } 090 091 /////////////////////// Instance Methods /////////////////////// 092 093 /** 094 * Returns this person's id. 095 */ 096 public int getId() { 097 return this.id; 098 } 099 100 /** 101 * Sets this person's gender. Note that this method is package 102 * protected. 103 * 104 * @throws FamilyTreeException 105 * The <code>gender</code> is neither {@link #MALE} nor 106 * {@link #FEMALE}. 107 * 108 * @see #Person(int) 109 */ 110 void setGender(Gender gender) { 111 if (gender == MALE || gender == FEMALE) { 112 this.gender = gender; 113 114 } else { 115 String s = "Invalid gender: " + gender; 116 throw new FamilyTreeException(s); 117 } 118 } 119 120 /** 121 * Returns this person's gender 122 */ 123 public Gender getGender() { 124 return this.gender; 125 } 126 127 /** 128 * Sets this person's first name. 129 */ 130 public void setFirstName(String firstName) { 131 this.firstName = firstName; 132 } 133 134 /** 135 * Returns this person's first name. 136 */ 137 public String getFirstName() { 138 return this.firstName; 139 } 140 141 /** 142 * Sets this person's middle name. 143 */ 144 public void setMiddleName(String middleName) { 145 this.middleName = middleName; 146 } 147 148 /** 149 * Returns this person's middle name. 150 */ 151 public String getMiddleName() { 152 return this.middleName; 153 } 154 155 /** 156 * Sets this person's last name. 157 */ 158 public void setLastName(String lastName) { 159 this.lastName = lastName; 160 } 161 162 /** 163 * Returns this person's last name. 164 */ 165 public String getLastName() { 166 return this.lastName; 167 } 168 169 /** 170 * Returns this person's full (first, middle, and last) name. 171 */ 172 public String getFullName() { 173 StringBuffer fullName = new StringBuffer(); 174 175 if (this.firstName != null) { 176 fullName.append(this.firstName); 177 fullName.append(' '); 178 } 179 180 if (this.middleName != null) { 181 fullName.append(this.middleName); 182 fullName.append(' '); 183 } 184 185 if (this.lastName != null) { 186 fullName.append(this.lastName); 187 } 188 189 return fullName.toString().trim(); 190 } 191 192 /** 193 * Sets this person's father. 194 * 195 * @throws FamilyTreeException 196 * <code>father</code> is not {@link #MALE} 197 */ 198 public void setFather(Person father) { 199 if (father.getGender() != Person.MALE) { 200 String s = "Father " + father + " must be MALE"; 201 throw new FamilyTreeException(s); 202 } 203 204 this.fatherId = father.getId(); 205 this.father = father; 206 } 207 208 /** 209 * Returns the id of this person's father. 210 * 211 * @return {@link #UNKNOWN}, if this person's father is not known 212 */ 213 public int getFatherId() { 214 if (this.father == null) { 215 return UNKNOWN; 216 217 } else { 218// assert this.fatherId != UNKNOWN; 219 return this.father.getId(); 220 } 221 } 222 223 /** 224 * Returns this person's father. 225 */ 226 public Person getFather() { 227 return this.father; 228 } 229 230 /** 231 * Sets the id of this person's father. This method is package 232 * protected because it is only intended to be access by parsers and 233 * other objects that would see a peron's id before the person is 234 * created. 235 * 236 * @see #patchUp 237 */ 238 void setFatherId(int id) { 239 this.fatherId = id; 240 } 241 242 /** 243 * Sets this person's mother. 244 * 245 * @throws FamilyTreeException 246 * <code>mother</code>'s gender is not {@link #FEMALE} 247 */ 248 public void setMother(Person mother) { 249 if (mother.getGender() != Person.FEMALE) { 250 String s = "Person " + mother.getId() + "(mother of " + 251 this.getId() + ") must be FEMALE"; 252 throw new FamilyTreeException(s); 253 } 254 255 this.mother = mother; 256 } 257 258 /** 259 * Returns the id of this person's mother. 260 * 261 * @return {@link #UNKNOWN}, if this person's father is not known 262 */ 263 public int getMotherId() { 264 if (this.mother == null) { 265 return UNKNOWN; 266 267 } else { 268 return this.mother.getId(); 269 } 270 } 271 272 /** 273 * Returns this person's mother. 274 */ 275 public Person getMother() { 276 return this.mother; 277 } 278 279 /** 280 * Sets the id of this person's mother. This method is package 281 * protected because it is only intended to be accessed by parsers 282 * and other objects that would see a peron's id before the person 283 * is created. 284 * 285 * @see #patchUp 286 */ 287 void setMotherId(int id) { 288 this.motherId = id; 289 } 290 291 /** 292 * "Patches up" a person's mother and father <code>Person</code> 293 * objects. This method is package protected because it is only 294 * intended to be accessed by parsers and other objects that would 295 * see a peron's id before the person is created. 296 * 297 * @throws FamilyTreeException 298 * Either the mother or father does not existin in 299 * <code>tree</code> or if the gender has not been set 300 */ 301 void patchUp(FamilyTree tree) { 302 if (this.father == null && this.fatherId != UNKNOWN) { 303 Person father = tree.getPerson(this.fatherId); 304 if (father == null) { 305 String s = "Father " + this.fatherId + " does not exist"; 306 throw new FamilyTreeException(s); 307 } 308 this.setFather(father); 309 } 310 311 if (this.mother == null && this.motherId != UNKNOWN) { 312 Person mother = tree.getPerson(this.motherId); 313 if (mother == null) { 314 String s = "Mother " + this.motherId + " does not exist"; 315 throw new FamilyTreeException(s); 316 } 317 this.setMother(mother); 318 } 319 320 if (this.gender == Gender.UNKNOWN) { 321 String s = "Gender has not been set yet!"; 322 throw new FamilyTreeException(s); 323 } 324 } 325 326 /** 327 * Sets this person's date of birth. 328 */ 329 public void setDateOfBirth(Date dob) { 330 this.dob = dob; 331 } 332 333 /** 334 * Returns this person's date of birth. 335 */ 336 public Date getDateOfBirth() { 337 return this.dob; 338 } 339 340 /** 341 * Sets this person's date of death. 342 * 343 * @throws FamilyTreeException 344 * If this person's data of birth is known and 345 * <code>dod</code> occurs before it. 346 */ 347 public void setDateOfDeath(Date dod) { 348 if (this.dob != null && dod != null && this.dob.after(dod)) { 349 String s = "Date of death (" + dod + 350 ") cannot occur before date of birth (" + this.dob + ")"; 351 throw new FamilyTreeException(s); 352 } 353 354 this.dod = dod; 355 } 356 357 /** 358 * Returns this person's date of death. 359 */ 360 public Date getDateOfDeath() { 361 return this.dod; 362 } 363 364 /** 365 * Makes note of a marriage this person was involved in. 366 * 367 * @throws FamilyTreeException 368 * If this person is not one of the spouses in the marriage 369 */ 370 public void addMarriage(Marriage marriage) { 371 if (this.getGender() == Person.MALE) { 372 if (!marriage.getHusband().equals(this)) { 373 String s = "This person (" + this.getFullName() + 374 ") is not the husband in " + marriage; 375 throw new FamilyTreeException(s); 376 } 377 378 } else { 379 if (!marriage.getWife().equals(this)) { 380 String s = "This person (" + this.getFullName() + 381 ") is not the wife in " + marriage; 382 throw new FamilyTreeException(s); 383 } 384 } 385 386 this.marriages.add(marriage); 387 } 388 389 /** 390 * Returns the marriages that this person was involved in. 391 */ 392 public Collection<Marriage> getMarriages() { 393 return this.marriages; 394 } 395 396 ////////////////////// Utility Methods //////////////////////// 397 398 /** 399 * Determines whether or not this <code>Person</code> is equal to 400 * another <code>Person</code>. Two <code>Person</code>s are 401 * considered equal if they have the same id. 402 */ 403 public boolean equals(Object o) { 404 if (o == null) { 405 return false; 406 } 407 408 if (!(o instanceof Person)) { 409 return false; 410 } 411 412 Person other = (Person) o; 413 return this.getId() == other.getId(); 414 } 415 416 /** 417 * Returns a brief description of this person. 418 */ 419 public String toString() { 420 StringBuffer sb = new StringBuffer(); 421 422 sb.append("Person ").append(this.id).append(": ").append(this.getFullName()); 423 if (this.dob != null) { 424 sb.append("\nBorn: "); 425 sb.append(this.dob); 426 } 427 if (this.dod != null) { 428 sb.append(", Died: "); 429 sb.append(this.dod); 430 } 431 432 if (this.mother != null) { 433 sb.append("\nMother: "); 434 sb.append(this.mother.getFullName()); 435 } 436 if (this.father != null) { 437 sb.append(", Father: "); 438 sb.append(this.father.getFullName()); 439 } 440 441 sb.append("\nMarried "); 442 sb.append(this.marriages.size()); 443 sb.append(" times"); 444 445 return sb.toString(); 446 } 447 448}