PageRenderTime 54ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/code/Family.java

http://silkin.googlecode.com/
Java | 829 lines | 610 code | 46 blank | 173 comment | 236 complexity | 852c1eeecf4677f916a2dadae6076be8 MD5 | raw file
  1. import java.util.*;
  2. import java.text.*;
  3. import java.io.*;
  4. import java.awt.Point;
  5. /**
  6. Represents a male and a female who either:
  7. (1) are recognized as married in the local culture, or
  8. (2) have produced at least one child.
  9. Each Family is either an actual Family in the culture under study, or is
  10. a hypothetical Family created to illustrate the kinship terms of a particular domain theory.
  11. In the Kinship system, societies are composed only of Families and {@link Individual}s.
  12. @author Gary Morris, Northern Virginia Community College garymorris2245@verizon.net
  13. */
  14. public class Family extends Marriage implements Serializable {
  15. /** A unique, system-assigned ID for this family unit. May not be changed once assigned. */
  16. public int serialNmbr;
  17. boolean deleted = false;
  18. boolean divorceStatusUnknown = true;
  19. /** <code>children</code> is an ArrayList<Object> of Individuals born to this Family. */
  20. public ArrayList<Object> children = new ArrayList<Object>();
  21. /** <code>dataChangeDate</code> is the last date that any field was changed for this person. */
  22. public String dataChangeDate,
  23. dataAuthor;
  24. /**
  25. By convention, divorceDate = <code>null</code> means we have no information.
  26. But divorceDate = "" means they are known to be (or required to be) not divorced.
  27. And divorceDate = "yyyy-mm-dd" means we know (require) that they are divorced.
  28. *
  29. By convention, <code>marriageDate</code> = null means we have no information,
  30. and "yyyy-mm-dd" is an actual or estimated date the union commenced. */
  31. private String marriageMM, marriageDD, divorceMM, divorceDD;
  32. public Individual husband, wife;
  33. int nmbrOfKids = 0;
  34. public String getMarriageYr() {
  35. if (marriageYr == null) {
  36. return "";
  37. } else {
  38. return marriageYr;
  39. }
  40. }
  41. public String getMarriageMM() {
  42. if (marriageMM == null) {
  43. return "";
  44. } else {
  45. return marriageMM;
  46. }
  47. }
  48. public String getMarriageDD() {
  49. if (marriageDD == null) {
  50. return "";
  51. } else {
  52. return marriageDD;
  53. }
  54. }
  55. public String getMarriageDate() {
  56. try {
  57. return UDate.formatAsXSD(marriageYr, marriageMM, marriageDD);
  58. }catch(Exception exc) {
  59. return "";
  60. }
  61. }
  62. public boolean hasMarriageDate() {
  63. return ! getMarriageDate().equals("");
  64. }
  65. public boolean hasNoMarriageDate() {
  66. return getMarriageDate().equals("");
  67. }
  68. public String getDivorceYr() {
  69. if (divorceYr == null) {
  70. return "";
  71. } else {
  72. return divorceYr;
  73. }
  74. }
  75. public String getDivorceMM() {
  76. if (divorceMM == null) {
  77. return "";
  78. } else {
  79. return divorceMM;
  80. }
  81. }
  82. public String getDivorceDD() {
  83. if (divorceDD == null) {
  84. return "";
  85. } else {
  86. return divorceDD;
  87. }
  88. }
  89. public String getDivorceDate() {
  90. try {
  91. return UDate.formatAsXSD(divorceYr, divorceMM, divorceDD);
  92. } catch (Exception exc) {
  93. return ""; // exceptions should never happen
  94. }
  95. }
  96. public boolean hasDivorceDate() {
  97. return ! getDivorceDate().equals("");
  98. }
  99. public boolean hasNoDivorceDate() {
  100. return getDivorceDate().equals("");
  101. }
  102. /** Make the <code>marriageDate</code> of this family <code>date</code>.
  103. *
  104. @param date Example: '2003-06-10'
  105. */
  106. public void setMarriageDate(String date) {
  107. if (date == null) date = "";
  108. String[] components = UDate.readXSDComponents(date);
  109. marriageYr = components[0];
  110. marriageMM = components[1];
  111. marriageDD = components[2];
  112. dataChangeDate = UDate.today();
  113. }
  114. public void setDivorceDate(String date) {
  115. if (date == null) { // null = Unknown
  116. date = "";
  117. } else divorceStatusUnknown = false; // non-null = known
  118. String[] components = UDate.readXSDComponents(date);
  119. divorceYr = components[0];
  120. divorceMM = components[1];
  121. divorceDD = components[2];
  122. dataChangeDate = UDate.today();
  123. }
  124. /** For use in constructing hypotheticals: return a date-of-birth (yyyy-mm-dd) for <code>recipient</code>.
  125. Starting year is father's birth year plus 20 years plus <code>offset</code>.
  126. However, yyyy is increased by <code>offset</code> (age is reduced)
  127. if that birth year will collide with the birth year of a sibling of <code>recipient</code>; adjust by as
  128. many years as it takes to avoid collisions, in the direction of <code>offset</code>.
  129. @param dadDOB date of birth of father of <code>recipient</code>
  130. @param offset number of years to add to a birthyear to avoid collisions with sibling birth years
  131. @param recipient person whose birth year is being constructed
  132. @return a String in yyyy-mm-dd format. Example: '2003-01-15'
  133. */
  134. public static String newDOB(String dadDOB, int offset, Individual recipient) {
  135. String mmdd;
  136. int dash1 = dadDOB.indexOf("-");
  137. if (dash1 == -1) {
  138. dash1 = dadDOB.length();
  139. mmdd = "01-22";
  140. }else mmdd = dadDOB.substring(dash1+1);
  141. int year, max = 0, min = 99999, kidYOB, momYOD;
  142. String yyyy = dadDOB.substring(0, dash1);
  143. year = Integer.parseInt(yyyy) + offset; // year is now the target year of birth for recipient
  144. boolean collision = false;
  145. Family fam = recipient.birthFamily;
  146. if (fam != null) { // if recipient doesn't have a birthFamily yet, skip this
  147. Individual kid;
  148. Iterator kidIter = fam.children.iterator();
  149. while (kidIter.hasNext()) {
  150. kid = (Individual) kidIter.next();
  151. if (kid.hasDoB()) {
  152. kidYOB = Integer.parseInt(kid.getBirthYr());
  153. if (kidYOB == year) {
  154. collision = true;
  155. }
  156. if (kidYOB > max) {
  157. max = kidYOB;
  158. }
  159. if (kidYOB < min) {
  160. min = kidYOB;
  161. }
  162. } // end of if-DOB-non-null
  163. } // end of for-each-kid-in-recipient's-birthFamily
  164. if (collision) {
  165. if (offset < 0) {
  166. year = min - 1;
  167. } else if (offset > 0) {
  168. year = max + 1;
  169. }
  170. }
  171. // Finally, make sure DOB isn't after death of mother
  172. if ((fam.wife != null) && (fam.wife.hasDoD())) {
  173. momYOD = Integer.parseInt(fam.wife.getDeathYr());
  174. year = Math.min(year, momYOD);
  175. } // end of check-for-mom's-death
  176. } // end of if-recipient-has-a-birthFamily
  177. return year + "-" + mmdd;
  178. } // end of method newDOB
  179. /**
  180. Return an ArrayList<Object> containing everything in <code>list</code> except <code>element</code>.
  181. */
  182. public static ArrayList<Object> listMinus(ArrayList<Object> list, Object element) {
  183. //
  184. ArrayList<Object> newList = new ArrayList<Object>(list);
  185. int where = newList.indexOf(element);
  186. if (where >= 0) {
  187. newList.remove(where);
  188. }
  189. return newList;
  190. } // end of public class method listMinus
  191. /**
  192. Constructor with 0 arguments: for use ONLY by Serialization. */
  193. public Family() {
  194. }
  195. public Family(Point loc) {
  196. location = loc;
  197. serialNmbr = Context.current.famSerNumGen++;
  198. Context.current.addFamily(this);
  199. } // end of constructor for Marriage-oriented family
  200. /**
  201. Constructor with a context and a boolean: for use by birth-family-creation screen. */
  202. public Family(Context ctxt, boolean newFam) {
  203. if (newFam) {
  204. serialNmbr = ctxt.famSerNumGen++;
  205. ctxt.addFamily(this);
  206. }
  207. }
  208. /**
  209. Constructor with a context & the 2 persons who are married. For use by the PersonEditor.
  210. @param ctxt the context (society) in which the marriage is to be placed
  211. @param person1 one person in the marriage
  212. @param person2 the other one
  213. */
  214. public Family(Context ctxt, Individual person1, Individual person2) {
  215. serialNmbr = ctxt.famSerNumGen++;
  216. ctxt.addFamily(this);
  217. if ((person1.gender.equals("M")) || (person2.gender.equals("F"))) {
  218. husband = person1; // we assume 1 male, 1 female
  219. wife = person2;
  220. } else {
  221. husband = person2;
  222. wife = person1;
  223. } // end else-clause
  224. person1.addMarriage(this);
  225. person2.addMarriage(this);
  226. } // end of constructor with context and 2 Individuals
  227. /**
  228. Constructor with 2 arguments: the persons who are married.
  229. @param person1
  230. @param person2
  231. */
  232. public Family(Individual person1, Individual person2) {
  233. serialNmbr = Context.current.famSerNumGen++;
  234. Context.current.addFamily(this);
  235. if ((person1.gender.equals("M")) || (person2.gender.equals("F"))) {
  236. husband = person1; // we assume 1 male, 1 female
  237. wife = person2;
  238. } // end of if-person1=male
  239. else if ((person2.gender.equals("M")) || (person1.gender.equals("F"))) {
  240. husband = person2; // we assume 1 male, 1 female
  241. wife = person1;
  242. } // end of if-person2=male
  243. else { // must be both are "?"
  244. husband = person2;
  245. wife = person1;
  246. } // end else-clause
  247. person1.addMarriage(this);
  248. person2.addMarriage(this);
  249. if ((person1.hasNoDoB()) && (person2.hasDoB())) {
  250. person1.setDateOfBirth(person2.getDateOfBirth());
  251. } else if ((person2.hasNoDoB()) && (person1.hasDoB())) {
  252. person2.setDateOfBirth(person1.getDateOfBirth());
  253. }
  254. // Having set ages if possible, now set marriage date
  255. generateMarriageDate();
  256. } // end of constructor with 2 Individuals
  257. /**
  258. Constructor with 3 arguments: the persons who are married & date of marriage.
  259. @param person1
  260. @param person2
  261. @param date String: the date as we want it to appear in <code>marriageDate</code>
  262. */
  263. public Family(Individual person1, Individual person2, String date) {
  264. serialNmbr = Context.current.famSerNumGen++;
  265. Context.current.addFamily(this);
  266. if (person1.gender.equals("M")) {
  267. husband = person1; // we assume 1 male, 1 female
  268. wife = person2;
  269. } // end of if-person1=male
  270. else {
  271. husband = person2;
  272. wife = person1;
  273. } // end else-clause
  274. setMarriageDate(date);
  275. person1.addMarriage(this);
  276. person2.addMarriage(this);
  277. if ((person1.hasNoDoB()) && (person2.hasDoB())) {
  278. person1.setDateOfBirth(person2.getDateOfBirth());
  279. } else if ((person2.hasNoDoB()) && (person1.hasDoB())) {
  280. person2.setDateOfBirth(person1.getDateOfBirth());
  281. }
  282. dataChangeDate = UDate.today();
  283. } // end of constructor with 2 Individuals & marriage-date
  284. /**
  285. Constructor provided for un-remembered, long-ago partners OR generic parents.
  286. A 'Presumed Spouse' will be automatically generated.
  287. @param person1
  288. */
  289. public Family(Individual person1) {
  290. serialNmbr = Context.current.famSerNumGen++;
  291. Context.current.addFamily(this);
  292. if (person1.gender.equals("F")) {
  293. wife = person1;
  294. wife.addMarriage(this);
  295. husband = new Individual("Presumed Spouse", "M", this);
  296. } // end person1=female
  297. else if (person1.gender.equals("M")) {
  298. husband = person1;
  299. husband.addMarriage(this);
  300. wife = new Individual("Presumed Spouse", "F", this);
  301. } // end person1=male
  302. else { // neuter person
  303. husband = person1;
  304. husband.addMarriage(this);
  305. wife = new Individual("Presumed Spouse", "?", this);
  306. } // end of person1 = neuter
  307. if ((husband.hasNoDoB()) && (wife.hasDoB())) {
  308. husband.setDateOfBirth(wife.getDateOfBirth());
  309. } else if ((wife.hasNoDoB()) && (husband.hasDoB())) {
  310. wife.setDateOfBirth(husband.getDateOfBirth());
  311. }
  312. // Having set ages if possible, now set marriage date
  313. generateMarriageDate();
  314. dataChangeDate = UDate.today();
  315. } // end of constructor with 1 Individual & a presumed partner
  316. /**
  317. Constructor provided for one parent with a divorce requirement found in <code>divSpecs</code>.
  318. A 'Presumed Spouse' will be automatically generated. This constructor is used only in Example Generation.
  319. @param person1 the known parent
  320. @param argname the name of the variable which this person satisfies in a Horn Clause
  321. */
  322. Family(Individual person1, String argName, TreeMap divSpecs) {
  323. serialNmbr = Context.current.famSerNumGen++;
  324. Context.current.addFamily(this);
  325. if (person1.gender.equals("F")) {
  326. wife = person1;
  327. wife.addMarriage(this);
  328. husband = new Individual("Presumed Spouse", "M", this);
  329. } // end person1=female
  330. else if (person1.gender.equals("M")) {
  331. husband = person1;
  332. husband.addMarriage(this);
  333. wife = new Individual("Presumed Spouse", "F", this);
  334. } // end person1=male
  335. else { // neuter person
  336. husband = person1;
  337. husband.addMarriage(this);
  338. wife = new Individual("Presumed Spouse", "?", this);
  339. } // end of person1 = neuter
  340. if ((husband.hasNoDoB()) && (wife.hasDoB())) {
  341. husband.setDateOfBirth(wife.getDateOfBirth());
  342. } else if ((wife.hasNoDoB()) && (husband.hasDoB())) {
  343. wife.setDateOfBirth(husband.getDateOfBirth());
  344. }
  345. // Having set ages if possible, now set marriage date
  346. generateMarriageDate();
  347. // Set divorce date (or positive lack of one)
  348. String divSpec = (String) divSpecs.get(argName);
  349. if (divSpec != null) {
  350. Literal lit = new Literal(new Predicate("dummy"));
  351. lit.assignDivDate(this, divSpec);
  352. } // end of if-divSpec-non-null
  353. dataChangeDate = UDate.today();
  354. } // end of constructor with 1 Individual & divorce specs
  355. /**
  356. Constructor provided for 2 parents with a divorce requirement found in <code>divSpecs</code>.
  357. This constructor is used only in Example Generation.
  358. @param person1
  359. @param person2
  360. @param argname1 the name of the variable which person1 satisfies in a Horn Clause
  361. @param argname2 the name of the variable which person2 satisfies in a Horn Clause
  362. @param divReq should this marriage have a definite divorce date?
  363. */
  364. Family(Individual person1, Individual person2, String argName1, String argName2, boolean divReq, TreeMap divSpecs)
  365. throws KSConstraintInconsistency {
  366. // provided for 2 parents w/ divSpecs and a divReq
  367. serialNmbr = Context.current.famSerNumGen++;
  368. Context.current.addFamily(this);
  369. if ((person1.gender.equals("M")) || (person2.gender.equals("F"))) {
  370. husband = person1; // we assume 1 male, 1 female
  371. wife = person2;
  372. } // end of if-person1=male
  373. else if ((person2.gender.equals("M")) || (person1.gender.equals("F"))) {
  374. husband = person2; // we assume 1 male, 1 female
  375. wife = person1;
  376. } // end of if-person2=male
  377. else { // must be both are "?"
  378. husband = person2;
  379. wife = person1;
  380. } // end else-clause
  381. person1.addMarriage(this);
  382. person2.addMarriage(this);
  383. if ((husband.hasNoDoB()) && (wife.hasDoB())) {
  384. husband.setDateOfBirth(wife.getDateOfBirth());
  385. } else if ((wife.hasNoDoB()) && (husband.hasDoB())) {
  386. wife.setDateOfBirth(husband.getDateOfBirth());
  387. }
  388. // Having set ages if possible, now set marriage date
  389. generateMarriageDate();
  390. // Set divorce date (or positive lack of one)
  391. String divSpec1 = (String) divSpecs.get(argName1), divSpec2 = (String) divSpecs.get(argName2);
  392. // if either spec = undivorced, then THIS marriage can't have a divorce
  393. // but if one is divorced and the other isn't, div'd one maybe needs a prior marriage.
  394. Literal lit = new Literal(new Predicate("dummy"));
  395. Family fam2;
  396. if ((divSpec1 != null) && (divSpec2 != null) && (!(divSpec1.equals(divSpec2)))) {
  397. // if divReq==true, we're inconsistent -- one spouse is 'undivorced' but this is a divorce
  398. if (divReq) {
  399. if (MainPane.activity == null) {
  400. MainPane.createActivityLog(MainPane.desktop, MainPane.menuView);
  401. }
  402. String msg = "ERROR: Divorce is required by divorce predicate for " + argName1 + " and "
  403. + argName2 + ",";
  404. msg += "but " + argName1 + "'s divorce constraint is <" + divSpec1 + "> and "
  405. + argName2 + "'s is <" + divSpec2 + ">.\n\n";
  406. MainPane.activity.log.append(msg);
  407. throw new KSConstraintInconsistency(msg);
  408. } // end of inconsistency-halt
  409. setDivorceDate("");
  410. Individual divorcee;
  411. if (divSpec1.equals("divorced")) {
  412. divorcee = person1;
  413. } else {
  414. divorcee = person2;
  415. }
  416. if (!(lit.meetsDivSpec(divorcee, "divorced", "commit"))) {
  417. // meetsDivSpec finds a prior divorce or makes a prior marriage fail, if possible.
  418. fam2 = new Family(divorcee);
  419. lit.assignDivDate(fam2, "divorced");
  420. } // end of didn't have a divorce yet
  421. } // end of differing-divSpecs
  422. else if (((divSpec1 != null) && (divSpec2 != null) && (divSpec1.equals("undivorced")))
  423. || ((divSpec1 != null) && (divSpec2 == null) && (divSpec1.equals("undivorced")))
  424. || ((divSpec1 == null) && (divSpec2 != null) && (divSpec2.equals("undivorced")))) {
  425. if (!(divReq)) {
  426. setDivorceDate("");
  427. } else {
  428. if (MainPane.activity == null) {
  429. MainPane.createActivityLog(MainPane.desktop, MainPane.menuView);
  430. }
  431. String msg = "ERROR: Divorce is required by divorce predicate for " + argName1 + " and " + argName2 + ",";
  432. msg += "but " + argName1 + "'s divorce constraint is <" + divSpec1 + "> and "
  433. + argName2 + "'s is <" + divSpec2 + ">.\n\n";
  434. MainPane.activity.log.append(msg);
  435. throw new KSConstraintInconsistency(msg);
  436. } // end of inconsistency-halt
  437. } // end of both-specs-are-undivorced
  438. else if (((divSpec1 != null) && (divSpec2 != null) && (divSpec1.equals("divorced")))
  439. || ((divSpec1 != null) && (divSpec2 == null) && (divSpec1.equals("divorced")))
  440. || ((divSpec1 == null) && (divSpec2 != null) && (divSpec2.equals("divorced")))) {
  441. if (divReq) {
  442. lit.assignDivDate(this, "divorced");
  443. } else { // both-need-divorces-but-it-can't-be-this-marriage
  444. fam2 = new Family(person1);
  445. lit.assignDivDate(fam2, "divorced");
  446. fam2 = new Family(person2);
  447. lit.assignDivDate(fam2, "divorced");
  448. } // end of both-need-divorces-but-it-can't-be-this-marriage
  449. } // end of if-divSpec-consistent-with-divorce
  450. // if we get here, both divSpecs are null, so let divReq call the shot
  451. else if (!(divReq)) {
  452. setDivorceDate("");
  453. } else {
  454. lit.assignDivDate(this, "divorced");
  455. }
  456. dataChangeDate = UDate.today();
  457. } // end of constructor with 2 Individuals, a divorce req, & divorce specs
  458. /** Update data for this Family from later information.
  459. @param a Family with later data.
  460. */
  461. public void updateFrom(Family newRec) {
  462. deleted = newRec.deleted;
  463. children = newRec.children;
  464. setMarriageDate(newRec.getMarriageDate());
  465. setDivorceDate(newRec.getDivorceDate());
  466. dataChangeDate = newRec.dataChangeDate;
  467. comment = newRec.comment;
  468. husband = newRec.husband;
  469. wife = newRec.wife;
  470. nmbrOfKids = newRec.nmbrOfKids;
  471. } // end of method updateFrom
  472. /**
  473. Generate a <code>marriageDate</code> for this family based on the ages of the husband and wife
  474. and the dates of any prior marriages, childbirths, divorces & widowhoods.
  475. */
  476. public void generateMarriageDate() {
  477. int husBirthYr = 18, wifBirthYr = 18, candiDate, candiDate2, candiDate3,
  478. husbLatestYr, wifLatestYr;
  479. if (husband.hasDoB()) {
  480. husBirthYr += Integer.parseInt(husband.getBirthYr());
  481. }
  482. if (wife.hasDoB()) {
  483. wifBirthYr += Integer.parseInt(wife.getBirthYr());
  484. } // end of if-husb-or-wife-has-non-null-birthdate
  485. if (!(Context.current.polygamyPermit)) {
  486. Literal lit = new Literal(new Predicate("dummy"));
  487. husbLatestYr = 6 + lit.findLatestYear(Family.listMinus(husband.marriages, this));
  488. wifLatestYr = 6 + lit.findLatestYear(Family.listMinus(wife.marriages, this));
  489. // findLatestYear returns the last date of (marriage, parenthood, or divorce) + 5 years
  490. candiDate2 = Math.max(husBirthYr, wifBirthYr);
  491. candiDate3 = Math.max(husbLatestYr, wifLatestYr);
  492. candiDate = Math.max(candiDate2, candiDate3);
  493. } // end of if-polygamy-not-permitted
  494. else // polygamy-OK
  495. {
  496. candiDate = Math.max(husBirthYr, wifBirthYr);
  497. }
  498. if (candiDate > 1000) {
  499. setMarriageDate(candiDate + "-06-10");
  500. }
  501. } // end of method generateMarriageDate()
  502. /**
  503. Create a String with summary information about the Family.
  504. @return the String, suitable for display or printing.
  505. */
  506. public String toString() {
  507. String image = serialNmbr + ": " + getMarriageDate();
  508. if (hasDivorceDate()) {
  509. image += " to " + getDivorceDate();
  510. }
  511. if (comment.length() > 7) {
  512. image += "\n" + comment;
  513. }
  514. image += "\n H = #" + husband.serialNmbr + ", W =";
  515. image += " #" + wife.serialNmbr + ".\n";
  516. image += " " + children.size();
  517. if (children.size() != 1) {
  518. image += " children.\n";
  519. } else {
  520. image += " child.\n";
  521. }
  522. if (children.size() >= 1) {
  523. for (int i = 0; i < children.size(); i++) {
  524. image += " #" + ((Individual) children.get(i)).serialNmbr
  525. + " (" + ((Individual) children.get(i)).gender + ").\n";
  526. }
  527. }
  528. return image;
  529. } // end of method toString()
  530. /** This method builds a string that represents a family in a SILKin data (_.silk) file. */
  531. public String toSILKString() throws KSDateParseException {
  532. String result = "<family n=\"" + serialNmbr + "\">";
  533. result += " <location x=\"" + location.x + "\" y=\""
  534. + location.y + "\"/>" + XFile.Eol;
  535. result += " <reason>" + reason + "</reason>" + XFile.Eol;
  536. if (comment != null && comment.length() > 0) {
  537. result += " <comments txt=\"" + comment + "\"/>" + XFile.Eol;
  538. }
  539. // result += toXMLString(); // Inserts all Marriage components - DELETED
  540. result += " <deleted>" + deleted + "</deleted>\n";
  541. int serial = (husband == null ? -1 : husband.serialNmbr);
  542. result += " <husband n=\"" + serial + "\"/>\n";
  543. serial = (wife == null ? -1 : wife.serialNmbr);
  544. result += " <wife n=\"" + serial + "\"/>\n";
  545. result += " <children>";
  546. if (children.size() > 0) {
  547. for (Object o : children) {
  548. Individual kid = (Individual) o;
  549. result += "\n <kid n=\"" + kid.serialNmbr + "\"/>";
  550. }
  551. result += "\n";
  552. } // end of printing children
  553. result += " </children>\n";
  554. String marrD = getMarriageDate(), enDate = getDivorceDate();
  555. if (marrD != null && marrD.length() > 0) {
  556. if (!UDate.validXSD(marrD)) {
  557. setMarriageDate(UDate.convertToXSD(marrD));
  558. }
  559. result += " <marriageDate value=\"" + getMarriageDate() + "\"/>\n";
  560. }
  561. if (enDate != null && enDate.length() > 0) {
  562. if (!UDate.validXSD(enDate)) {
  563. setDivorceDate(UDate.convertToXSD(enDate));
  564. }
  565. result += " <endDate value=\"" + getDivorceDate() + "\"/>\n";
  566. }
  567. if (dataAuthor != null && dataAuthor.length() > 0) {
  568. result += " <dataAuthor name=\"" + dataAuthor + "\"/>\n";
  569. }
  570. if (!UDate.validXSD(dataChangeDate)) {
  571. dataChangeDate = UDate.convertToXSD(dataChangeDate);
  572. }
  573. result += " <dataChangeDate value=\"" + (dataChangeDate == null ? "" : dataChangeDate) + "\"/>\n";
  574. result += "</family>\n";
  575. return result;
  576. } // end of method toSILKString
  577. /**
  578. Append the String <code>not</code> to the 'comment' field for this Family.
  579. @param not the String to be added.
  580. */
  581. public void addNote(String not) {
  582. comment += not;
  583. dataChangeDate = UDate.today();
  584. }
  585. /**
  586. Set dates of birth for all family members, if possible, via internal consistency checks.
  587. */
  588. public void checkFamDOBs() {
  589. boolean dadDone = false, momDone = false;
  590. Individual kid;
  591. if ((husband.hasNoDoB()) || (husband.getDateOfBirth().length() < 7)) {
  592. if ((wife.hasDoB()) && (wife.getDateOfBirth().length() >= 7)) {
  593. husband.setDateOfBirth(wife.getDateOfBirth());
  594. } else if (children.size() > 0) {
  595. Iterator kidIter = children.iterator();
  596. while ((kidIter.hasNext()) && (!(dadDone))) {
  597. kid = (Individual) kidIter.next();
  598. if ((kid.hasDoB()) && (kid.getDateOfBirth().length() >= 7)) {
  599. dadDone = true;
  600. husband.setDateOfBirth(Family.newDOB(kid.getDateOfBirth(), (0 - nmbrOfKids - 20), husband));
  601. } // end of found-one!
  602. } // end of for-each-kid-until-dad-Done
  603. } // end of has-kids-with-DOBs
  604. } // end of set-dad's-DOB
  605. if (((wife.hasNoDoB()) || (wife.getDateOfBirth().length() < 7))
  606. && ((husband.hasDoB()) && (husband.getDateOfBirth().length() >= 7))) {
  607. wife.setDateOfBirth(husband.getDateOfBirth());
  608. }
  609. if ((husband.hasDoB()) && (husband.getDateOfBirth().length() >= 7) && (children.size() > 0)) {
  610. for (int i = 0; i < children.size(); i++) {
  611. kid = (Individual) children.get(i);
  612. if ((kid.hasNoDoB()) || (kid.getDateOfBirth().length() < 7)) {
  613. kid.setDateOfBirth(Family.newDOB(husband.getDateOfBirth(), (i + 21), kid));
  614. }
  615. } // end of for-each-kid
  616. } // end of set-each-kid's-DOB-if-possible
  617. } // end of method checkFamDOBs
  618. /**
  619. Add the Individual <code>mate</code> as a parent in this Family.
  620. @param mate Individual to be added.
  621. */
  622. public void addSpouse(Individual mate) throws KSInternalErrorException {
  623. if (mate.gender.equals("F")) {
  624. if (wife == null) {
  625. wife = mate;
  626. } else {
  627. throw new KSInternalErrorException("Family.addSpouse asked to add a 2nd wife.");
  628. }
  629. } else {
  630. if (husband == null) {
  631. husband = mate;
  632. } else {
  633. throw new KSInternalErrorException("Family.addSpouse asked to add a 2nd husband.");
  634. }
  635. }
  636. mate.addMarriage(this);
  637. dataChangeDate = UDate.today();
  638. } // end of method addSpouse
  639. /**
  640. Remove the Individual <code>mate</code> as a parent in this Family.
  641. @param mate Individual to be deleted.
  642. */
  643. public void deleteSpouse(Individual mate) throws KSInternalErrorException {
  644. if (mate == husband) {
  645. husband = null;
  646. } else if (mate == wife) {
  647. wife = null;
  648. } else {
  649. throw new KSInternalErrorException("Family.deleteSpouse asked to drop spouse who is unknown.");
  650. }
  651. mate.marriages.remove(this);
  652. dataChangeDate = UDate.today();
  653. } // end of method deleteSpouse
  654. /**
  655. Add the Individual <code>kid</code> as a child of this Family.
  656. @param kid Individual to be added.
  657. */
  658. public void addChild(Individual kid) {
  659. nmbrOfKids++;
  660. children.add(kid);
  661. kid.birthFamily = this;
  662. dataChangeDate = UDate.today();
  663. } // end of method addChild
  664. /**
  665. Delete the Individual <code>kid</code> as a child of this Family.
  666. @param kid Individual to be dropped.
  667. */
  668. public void deleteChild(Individual kid) throws KSInternalErrorException {
  669. if (children.contains(kid)) {
  670. nmbrOfKids--;
  671. children.remove(kid);
  672. if (kid.birthFamily == this) {
  673. kid.birthFamily = null; // maybe already changed
  674. }
  675. dataChangeDate = UDate.today();
  676. } else {
  677. throw new KSInternalErrorException("Family.deleteChild asked to drop a kid who is unknown.");
  678. }
  679. } // end of method deleteChild
  680. /**
  681. Delete this Family. Remove it from the marriages list of any spouses. If there are any
  682. children, throw an exception. Kids must be removed before deletion.
  683. */
  684. public void delete() throws KSInternalErrorException {
  685. if (!children.isEmpty()) {
  686. throw new KSInternalErrorException("Children must be deleted before a family is deleted.");
  687. }
  688. deleted = true;
  689. if (husband != null) {
  690. husband.marriages.remove(this);
  691. }
  692. if (wife != null) {
  693. wife.marriages.remove(this);
  694. }
  695. comment += "Pointers to this family removed from husband & wife. ";
  696. dataChangeDate = UDate.today();
  697. } // end of method delete
  698. /**
  699. If we can substitute <code>parent</code> for one of the spouses in this family (who
  700. has no ties to the outside world) then do the switch and return 'true'.
  701. Used only for Example-Generation.
  702. @param parent Individual we'd like to inject into this family.
  703. @return true if successful; else false.
  704. */
  705. public boolean subForDummyParent(Individual parent) {
  706. if ((!(parent.gender.equals("F"))) && (husband.birthFamily == null)
  707. && (husband.name.equals("*&^%$"))) { // bingo
  708. husband.marriages.clear();
  709. husband = parent;
  710. parent.addMarriage(this);
  711. return true;
  712. } // end of husband-makes-good-dummy
  713. else if ((!(parent.gender.equals("M"))) && (wife.birthFamily == null)
  714. && (wife.name.equals("*&^%$"))) { // she'll do
  715. wife.marriages.clear();
  716. wife = parent;
  717. parent.addMarriage(this);
  718. return true;
  719. } // end of wife-makes-good-dummy
  720. else {
  721. return false;
  722. }
  723. } // end of method subForDummyParent
  724. /**Write out one Family record in GEDCOM 5.5 format.
  725. * <p>
  726. * Record will contain:
  727. <ul>
  728. <li> Family Serial Number as a "F-" family ID number. </li>
  729. <li> Husband's Individual Serial Number </li>
  730. <li> Wife's Individual Serial Number </li>
  731. <li> MarriageDate, DivorceDate, if any </li>
  732. <li> Children: Individual Serial Numbers of each child </li>
  733. <li> Notes, if any </li>
  734. <li> Data Change date. </li>
  735. </ul>
  736. @param out a PrintWriter to write to.
  737. @param today String: today's date as it should appear in the DataChange field of GEDCOM record.
  738. */
  739. public void exportGEDCOM(PrintWriter out, String today) {
  740. out.println("0 @F-" + serialNmbr + "@ FAM");
  741. out.println("1 HUSB @I-" + husband.serialNmbr + "@");
  742. out.println("1 WIFE @I-" + wife.serialNmbr + "@");
  743. if (!(getMarriageDate().equals(""))) {
  744. out.println("1 MARR ");
  745. out.println("2 DATE " + getMarriageDate());
  746. } // end of wedding_date output
  747. if (hasDivorceDate()) {
  748. out.println("1 DIV");
  749. out.println("2 DATE " + getDivorceDate());
  750. } // end of divorce_date output
  751. if (nmbrOfKids > 0) {
  752. for (int i = 0; i < nmbrOfKids; i++) {
  753. Individual kid = (Individual) children.get(i);
  754. out.println("1 CHIL @I-" + kid.serialNmbr + "@");
  755. } // end of for-each-kid
  756. } // end of if-kids
  757. if (comment.length() > 7) {
  758. out.println("1 NOTE " + comment.substring(7));
  759. }
  760. out.println("1 CHAN");
  761. out.println("2 DATE " + today);
  762. return;
  763. } // end of method exportGEDCOM
  764. } // end of public class Family