PageRenderTime 70ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/code/Individual.java

http://silkin.googlecode.com/
Java | 1370 lines | 1036 code | 81 blank | 253 comment | 469 complexity | d3b6a68e9541daa354d9c145ebe37e37 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. import java.util.* ;
  2. import java.io.* ;
  3. import java.awt.Point;
  4. /**
  5. Each Individual is either an actual person in the culture under study, or is
  6. a hypothetical person created to illustrate the kinship terms in a particular domain theory.
  7. Individuals are born into {@link Family}s and participate in them via marriages.
  8. In the Kinship system, societies are composed only of Families and Individuals.
  9. @author Gary Morris, Northern Virginia Community College garymorris2245@verizon.net
  10. */
  11. public class Individual extends Person implements Serializable {
  12. /** A unique, system-assigned ID for this individual. May not be changed once assigned. */
  13. public int serialNmbr,
  14. tempTreeLevel;
  15. String surname, firstNames, gender, dataChangeDate, dataAuthor;
  16. ArrayList<Object> nameHistory = new ArrayList<Object>();
  17. boolean deleted = false;
  18. int seenB4 = 0;
  19. Node node = null;
  20. Dyad dyad;
  21. /**
  22. By convention, dateOfDeath = null means we have no information and don't care whether he's dead,
  23. "" means he is known to be (or required to be) alive, and "mm/dd/yy" means we know (require) he is dead.
  24. */
  25. private String birthMM, birthDD, deathMM, deathDD;
  26. /** The Family containing this Individual's biological mother/father pair. */
  27. public Family birthFamily; //
  28. /** This field is used only by Example-Generation. */
  29. boolean hasGenderNeutralKinTerm = false;
  30. /** A list of all Families created by former & current marriages (& child births). */
  31. public ArrayList<Object> marriages = new ArrayList<Object>();
  32. /** A TreeMap of all the User-Defined Properties (aka *properties) for this Individual. */
  33. public TreeMap userDefinedProperties;
  34. /** A TreeMap of all non-genealogical relationships for this Individual, i.e. a bond defined
  35. by some shared UDP (*-property). */
  36. public ArrayList<Object> starLinks;
  37. public String getBirthMM() {
  38. if (birthMM == null) {
  39. return "";
  40. }else {
  41. return birthMM;
  42. }
  43. }
  44. public String getBirthDD() {
  45. if (birthDD == null) {
  46. return "";
  47. }else {
  48. return birthDD;
  49. }
  50. }
  51. public String getBirthYr() {
  52. if (birthYr == null) {
  53. return "";
  54. }else {
  55. return birthYr;
  56. }
  57. }
  58. public String getDeathMM() {
  59. if (deathMM == null) {
  60. return "";
  61. }else {
  62. return deathMM;
  63. }
  64. }
  65. public String getDeathDD() {
  66. if (deathDD == null) {
  67. return "";
  68. }else {
  69. return deathDD;
  70. }
  71. }
  72. public String getDeathYr() {
  73. if (deathYr == null) {
  74. return "";
  75. }else {
  76. return deathYr;
  77. }
  78. }
  79. public String getDateOfBirth() {
  80. try {
  81. return UDate.formatAsXSD(birthYr, birthMM, birthDD);
  82. }catch(Exception ex) {
  83. return "";
  84. }
  85. }
  86. public String getDateOfDeath() {
  87. try {
  88. return UDate.formatAsXSD(deathYr, deathMM, deathDD);
  89. }catch(Exception ex) {
  90. return "";
  91. }
  92. }
  93. public void setDateOfBirth(String dob) {
  94. if (dob == null) dob = "";
  95. String[] components = UDate.readXSDComponents(dob);
  96. birthYr = components[0];
  97. birthMM = components[1];
  98. birthDD = components[2];
  99. }
  100. public void setDateOfDeath(String dod) {
  101. if (dod == null) dod = "";
  102. String[] components = UDate.readXSDComponents(dod);
  103. deathYr = components[0];
  104. deathMM = components[1];
  105. deathDD = components[2];
  106. }
  107. public boolean hasDoB() {
  108. String dob = "";
  109. try {
  110. dob = UDate.formatAsXSD(birthYr, birthMM, birthDD);
  111. }catch(Exception ex) {
  112. return false;
  113. }
  114. return (! dob.equals(""));
  115. }
  116. public boolean hasNoDoB() {
  117. return ! hasDoB();
  118. }
  119. public boolean hasDoD() {
  120. String dod = "";
  121. try {
  122. dod = UDate.formatAsXSD(deathYr, deathMM, deathDD);
  123. }catch(Exception ex) {
  124. return false;
  125. }
  126. return (! dod.equals(""));
  127. }
  128. public boolean hasNoDoD() {
  129. return ! hasDoD();
  130. }
  131. /** Constructor with w/ name, sex, birth-family & birth date.
  132. * NOTE: As of 10/26/2010 this constructor has never been used.
  133. @param name String to go in the FullName field
  134. @param sex 'M' or 'F' or '?' (neuter: doesn't matter or don't know)
  135. @param ofOrigin Family where this Individual is a child.
  136. @param birthdate Example: 'Jan 1, 1970'
  137. */
  138. public Individual(String name, String sex, Family ofOrigin, String birthdate) {
  139. serialNmbr = Context.current.indSerNumGen++;
  140. this.name = name;
  141. processNames(name);
  142. setDateOfBirth(birthdate);
  143. gender = parseGender(sex);
  144. Context.current.addIndividual(this);
  145. if (Context.current.userDefinedProperties != null)
  146. userDefinedProperties = makeNewUDPTreeMap(Context.current.userDefinedProperties);
  147. ofOrigin.addChild(this);
  148. dataChangeDate = UDate.today();
  149. } // end of constructor
  150. /**
  151. Constructor with w/ name, sex, & birthdate.
  152. @param name String to go in the FullName field
  153. @param sex 'M' or 'F' or '?' (neuter: doesn't matter or don't know)
  154. @param birthdate Example: 'Jan 1, 1970'
  155. */
  156. public Individual(String name, String sex, String birthdate) {
  157. serialNmbr = Context.current.indSerNumGen++;
  158. this.name = name;
  159. node = new Node();
  160. processNames(name);
  161. setDateOfBirth(birthdate);
  162. gender = parseGender(sex);
  163. Context.current.addIndividual(this);
  164. if (Context.current.userDefinedProperties != null)
  165. userDefinedProperties = makeNewUDPTreeMap(Context.current.userDefinedProperties);
  166. dataChangeDate = UDate.today();
  167. } // end of constructor w/ name, sex, & birthdate
  168. /**
  169. Constructor with w/ sex & location.
  170. @param gend one of the 2 basic kinds: Male and Female
  171. @param loc A point with x- and y-coordinates
  172. */
  173. public Individual(Kind gend, Point loc) {
  174. sex = gend;
  175. gender = parseGender(sex.name);
  176. setLocation(loc);
  177. serialNmbr = Context.current.indSerNumGen++;
  178. myId = serialNmbr + 1;
  179. name = "Person " + serialNmbr;
  180. Context.current.addIndividual(this);
  181. if (Context.current.userDefinedProperties != null) {
  182. userDefinedProperties = makeNewUDPTreeMap(Context.current.userDefinedProperties);
  183. }
  184. dataChangeDate = UDate.today();
  185. } // end of constructor w/ name, sex only.
  186. /**
  187. Constructor with w/ name & sex.
  188. @param name String to go in the FullName field
  189. @param sex 'M' or 'F' or '?' (neuter: doesn't matter or don't know)
  190. */
  191. public Individual(String name, String sex) {
  192. serialNmbr = Context.current.indSerNumGen++;
  193. this.name = name;
  194. node = new Node();
  195. processNames(name);
  196. gender = parseGender(sex);
  197. Context.current.addIndividual(this);
  198. if (Context.current.userDefinedProperties != null)
  199. userDefinedProperties = makeNewUDPTreeMap(Context.current.userDefinedProperties);
  200. dataChangeDate = UDate.today();
  201. } // end of constructor w/ name, sex only.
  202. /**
  203. Constructor with w/ a single string == "root". This constructor is intended for use ONLY by graphical
  204. routines; they must sometimes create an invisible node as the root of the tree they're drawing. The person
  205. created by this constructor always has a serial number of -1 and a name of "/".
  206. @param name String which must = "root"
  207. */
  208. public Individual(String root) {
  209. if (! root.equals("root")) {
  210. System.out.println("\n\nERROR: called Individual's root-constructor with argument not = 'root'");
  211. System.exit(9);
  212. }
  213. serialNmbr = -1;
  214. name = "/";
  215. gender = "?";
  216. } // end of constructor for "root".
  217. /**
  218. Constructor with w/ context & sex. Intended for use by PersonEditor
  219. @param context Specific context in which this person must be created.
  220. @param sex 'M' or 'F' or '?' (neuter: doesn't matter or don't know)
  221. */
  222. public Individual(Context context, String sex) {
  223. serialNmbr = context.indSerNumGen++;
  224. gender = parseGender(sex);
  225. context.addIndividual(this);
  226. if (context.userDefinedProperties != null)
  227. userDefinedProperties = makeNewUDPTreeMap(context.userDefinedProperties);
  228. dataChangeDate = UDate.today();
  229. } // end of constructor w/ context, name, sex, & marriage-family.
  230. /**
  231. Constructor with w/ context, sex, & marriage.
  232. @param context Specific context in which this person must be created.
  233. @param sex 'M' or 'F' or '?' (neuter: doesn't matter or don't know)
  234. @param marriage Family where this Individual is a spouse.
  235. */
  236. public Individual(Context context, String sex, Family marriage) {
  237. serialNmbr = context.indSerNumGen++;
  238. marriages.add(marriage);
  239. gender = parseGender(sex);
  240. context.addIndividual(this);
  241. if (context.userDefinedProperties != null)
  242. userDefinedProperties = makeNewUDPTreeMap(context.userDefinedProperties);
  243. dataChangeDate = UDate.today();
  244. } // end of constructor w/ context, name, sex, & marriage-family.
  245. /**
  246. Constructor with w/ context, name, sex, & birthFamily.
  247. @param context Specific context in which this person must be created.
  248. @param name String to go in the FullName field
  249. @param sex 'M' or 'F' or '?' (neuter: doesn't matter or don't know)
  250. @param birthFam Family where this Individual is a child.
  251. */
  252. public Individual(Context context, String name, String sex, Family birthFam) {
  253. serialNmbr = context.indSerNumGen++;
  254. this.name = name;
  255. processNames(name);
  256. birthFamily = birthFam;
  257. birthFamily.children.add(this);
  258. gender = parseGender(sex);
  259. context.addIndividual(this);
  260. if (context.userDefinedProperties != null)
  261. userDefinedProperties = makeNewUDPTreeMap(context.userDefinedProperties);
  262. dataChangeDate = UDate.today();
  263. } // end of constructor w/ context, name, sex, & marriage-family.
  264. /**
  265. Constructor with w/ name, sex, & marriage-family.
  266. @param name String to go in the FullName field
  267. @param sex 'M' or 'F' or '?' (neuter: doesn't matter or don't know)
  268. @param marriage Family where this Individual is a parent.
  269. */
  270. public Individual(String name, String sex, Family marriage) {
  271. serialNmbr = Context.current.indSerNumGen++;
  272. this.name = name;
  273. node = new Node();
  274. processNames(name);
  275. marriages.add(marriage);
  276. gender = parseGender(sex);
  277. Context.current.addIndividual(this);
  278. if (Context.current.userDefinedProperties != null)
  279. userDefinedProperties = makeNewUDPTreeMap(Context.current.userDefinedProperties);
  280. dataChangeDate = UDate.today();
  281. } // end of constructor w/ name, sex only.
  282. /**
  283. Monster constructor which handles all constraints from example generation.
  284. Used only by Example-Generation. Does NOT assign default values to UDPs.
  285. @param name String to go in the FullName field
  286. @param sex 'M' or 'F' or '?' (neuter: doesn't matter or don't know)
  287. @param ofOrigin Family where this Individual is a child.
  288. @param birthdate Example: 'Jan 1, 1970'
  289. @param argName name of the variable (in a Horn Clause) to be bound to this Individual.
  290. @param spouseInd spouse, if known.
  291. @param bindings TreeMap of all bindings (of variables to Individuals)
  292. @param constraints TreeMap of all known constraints on attributes of this Individual.
  293. @param vari The {@link Variable} we propose to bind to.
  294. @param failFlag used to signal back to the calling method when we cannot satisfy a constraint
  295. or requirement (e.g. no conforming value can be found for a UDP).
  296. @throws KSConstraintInconsistency if any conflicting constraints are encountered.
  297. */
  298. Individual(String name, String sex, Family ofOrigin, String birthdate, String argName, Individual spouseInd,
  299. TreeMap bindings, ArrayList<Object> starBindings, ConstraintObj constraints, Variable vari, BoolFlag failFlag, ClauseBody cb)
  300. throws KSConstraintInconsistency, KSBadHornClauseException, ClassNotFoundException, KSInternalErrorException {
  301. serialNmbr = Context.current.indSerNumGen++;
  302. Context.current.addIndividual(this);
  303. // Set any UserDefinedProperties (UDPs) that may apply
  304. if (Context.current.userDefinedProperties != null) {
  305. userDefinedProperties = makeNewUDPTreeMap(Context.current.userDefinedProperties, false);
  306. TreeMap thisVarsUDPs = (TreeMap) constraints.userDefined.get(vari);
  307. if (thisVarsUDPs != null) { // thisVarsUDPs = a TreeMap with Keys = starPropertyNames and Values = MathVars, Constants, or Variables
  308. Iterator iter = thisVarsUDPs.entrySet().iterator();
  309. Map.Entry entry;
  310. String starPropName;
  311. Argument arg;
  312. while (iter.hasNext()) { // provide value(s) for this property as needed
  313. entry = (Map.Entry) iter.next();
  314. starPropName = (String) entry.getKey();
  315. arg = (Argument) entry.getValue();
  316. if (!findConformingValue(starPropName, arg, starBindings, bindings, constraints, "commit", cb)) {
  317. failFlag.value = true;
  318. failFlag.reason = starPropName;
  319. } // end of failed to find a conforming value.
  320. } // end of loop thru star-properties
  321. } // end of thisVarsUDPs-is-not-null
  322. } // end of UDP processing
  323. this.name = name;
  324. processNames(name);
  325. gender = parseGender(sex);
  326. node = new Node();
  327. // node.setGender((gender.equals("M") ? 1 : 2));
  328. Literal lit = new Literal(new Predicate("dummy"));
  329. if (gender.equals("?")) { // neuter
  330. ArrayList<Object> genderList = lit.resolveOppo(bindings, constraints, vari);
  331. if (genderList == null) {
  332. gender = "?";
  333. } else if (genderList.contains("F")) {
  334. gender = "F";
  335. } else if (genderList.contains("M")) {
  336. gender = "M";
  337. }
  338. // if no clues in the gender constraints - check variable for clues
  339. if ((gender.equals("?")) && (vari != null) && (!vari.neuterOK)) {
  340. if (vari.lastGenderTried.equals("F")) {
  341. gender = "M";
  342. } else {
  343. gender = "F";
  344. }
  345. vari.lastGenderTried = gender;
  346. } // end of still-neuter-so-check-variable
  347. } // end of if-gender-is-neuter
  348. if (ofOrigin != null) {
  349. ofOrigin.addChild(this);
  350. }
  351. // Set DateOfBirth
  352. ArrayList<Object> ageSpecList = (ArrayList<Object>) constraints.age.get(argName);
  353. if (ageSpecList == null) {
  354. ageSpecList = new ArrayList<Object>();
  355. }
  356. // 1st choice: direct assignment (manual override)
  357. if (birthdate != null && !birthdate.equals("")) {
  358. setDateOfBirth(birthdate);
  359. }
  360. // 2nd choice: base age on parent's ages & number of siblings, but constrained by AgeSpecList
  361. else {
  362. if ((ofOrigin != null) && (ofOrigin.husband != null) && (ofOrigin.husband.hasDoB())) {
  363. setDateOfBirth(Family.newDOB(ofOrigin.husband.getDateOfBirth(), (ofOrigin.nmbrOfKids + 20), this));
  364. } else if ((ofOrigin != null) && (ofOrigin.wife != null) && (ofOrigin.wife.hasDoB())) {
  365. setDateOfBirth(Family.newDOB(ofOrigin.wife.getDateOfBirth(), (ofOrigin.nmbrOfKids + 20), this));
  366. }
  367. if (lit.meetsAgeSpec(this, ageSpecList, bindings, "commit") && hasDoB()) {
  368. // OK!! That birthdate fits the ageSpecs (if any)
  369. // 3rd choice: ageSpec dictates
  370. } else {
  371. if ((ageSpecList.size() > 0) && (lit.meetsAgeSpec(this, ageSpecList, bindings, "commit"))
  372. && hasDoB()) { // end of 3rd-choice:
  373. // 4th choice: base age on spouse's age
  374. } else if ((spouseInd != null) && (spouseInd.hasDoB())) {
  375. setDateOfBirth(spouseInd.getDateOfBirth());
  376. }
  377. }
  378. // 5th choice: leave blank for later synchronization. Do nothing.
  379. }
  380. // Now check DivSpec, & generate a divorce if necessary
  381. String divSpec = (String) constraints.divorce.get(argName);
  382. Family marriage;
  383. if ((divSpec != null) && (divSpec.equals("divorced"))) // if it doesn't, we need not do anything
  384. {
  385. marriage = new Family(this, argName, constraints.divorce);
  386. }
  387. // Now set dateOfDeath, if necessary
  388. String dthSpec = (String) constraints.death.get(argName);
  389. if (dthSpec != null) {
  390. lit.assignDeathDate(this, dthSpec);
  391. }
  392. dataChangeDate = UDate.today();
  393. } // end of Monster constructor with all args
  394. /**
  395. Constructor with 0 arguments: for use ONLY by Serialization. */
  396. public Individual() {
  397. }
  398. void updateNames() { // invoked when firstNames or surname is edited.
  399. name = firstNames + " " + surname;
  400. }
  401. /** Update data for this Individual from later information.
  402. @param an individual with later data.
  403. */
  404. public void updateFrom(Individual newRec) {
  405. name = newRec.name;
  406. surname = newRec.surname;
  407. firstNames = newRec.firstNames;
  408. gender = newRec.gender;
  409. comment = newRec.comment;
  410. setDateOfBirth(newRec.getDateOfBirth());
  411. setDateOfDeath(newRec.getDateOfDeath());
  412. dataChangeDate = newRec.dataChangeDate;
  413. dataAuthor = newRec.dataAuthor;
  414. deleted = newRec.deleted;
  415. birthFamily = newRec.birthFamily;
  416. marriages = newRec.marriages;
  417. userDefinedProperties = newRec.userDefinedProperties;
  418. starLinks = newRec.starLinks;
  419. } // end of method updateFrom
  420. /**
  421. Create a String with summary information about this Individual.
  422. @return the String, suitable for display or printing.
  423. */
  424. public String toString() {
  425. String image = "#" + serialNmbr + ": "+ name + " (" + gender + ")";
  426. if (hasDoB()) image += ", born " + getDateOfBirth();
  427. if ((hasDoD()) && (getDateOfDeath().length() > 2))
  428. image += " & died " + getDateOfDeath();
  429. if (dyad != null && dyad.pcString.length() > 0)
  430. image += "\n PC_String = " + dyad.pcString;
  431. image += ".\n";
  432. // image += "First name = " + firstNames + ". Surname = " + surname + ".\n";
  433. if (node != null) {
  434. if (node.kinTermsRef.size() > 0) {
  435. image += "Kin Terms: " + (String) node.kinTermsRef.get(0) + node.ktSuffix;
  436. for (int i = 1; i < node.kinTermsRef.size(); i++) {
  437. image += ", " + (String) node.kinTermsRef.get(i) + node.ktSuffix;
  438. }
  439. image += ".\n";
  440. } // end of if-node.kinTermsRef-has-any.
  441. if (node.extKinTermsRef.size() > 0) {
  442. image += "Extended Kin Terms: " + (String) node.extKinTermsRef.get(0) + node.ktSuffix;
  443. for (int i = 1; i < node.extKinTermsRef.size(); i++) {
  444. image += ", " + (String) node.extKinTermsRef.get(i) + node.ktSuffix;
  445. }
  446. image += ".\n";
  447. } // end of if-node.extKinTermsRef-has-any.
  448. if (node.kinTermsAddr.size() > 0) {
  449. image += "*Kin Terms: " + (String) node.kinTermsAddr.get(0) + node.ktSuffix;
  450. for (int i = 1; i < node.kinTermsAddr.size(); i++) {
  451. image += ", " + (String) node.kinTermsAddr.get(i) + node.ktSuffix;
  452. }
  453. image += ".\n";
  454. } // end of if-node.kinTermsAddr-has-any.
  455. if (node.extKinTermsAddr.size() > 0) {
  456. image += "*Extended Kin Terms: " + (String) node.extKinTermsAddr.get(0) + node.ktSuffix;
  457. for (int i = 1; i < node.extKinTermsAddr.size(); i++) {
  458. image += ", " + (String) node.extKinTermsAddr.get(i) + node.ktSuffix;
  459. }
  460. image += ".\n";
  461. } // end of if-node.extKinTermsAddr-has-any.
  462. }
  463. if (nameHistory.size() > 0) {
  464. image += "Kin Term Name History: ";
  465. for (int j=0; j < nameHistory.size(); j++)
  466. image += nameHistory.get(j) + " ";
  467. image += ".\n";
  468. } // end of if-nameHistory-not-empty
  469. if (comment.length() > 7) image += comment + "\n";
  470. if (birthFamily != null) image += " BirthFamily: " + birthFamily.serialNmbr + "\n";
  471. if (marriages.size() > 0) {
  472. image += " Marriages:\n";
  473. for (int i=0; i < marriages.size(); i++) {
  474. Family fam = (Family)marriages.get(i);
  475. image += " Family#" + fam.serialNmbr + ": I-" + fam.husband.serialNmbr + ", I-" + fam.wife.serialNmbr + "\n";
  476. } // end of for-each-marriage
  477. } // end of if-marriages
  478. if (userDefinedProperties != null) {
  479. Iterator iter = userDefinedProperties.entrySet().iterator();
  480. image += "User Defined Properties:\n";
  481. while (iter.hasNext()) {
  482. Map.Entry entry = (Map.Entry)iter.next();
  483. image += " " + entry.getValue().toString() + "\n";
  484. } // end of iteration thru userDefinedProperties
  485. } // end of if-userDefinedProperties-are-present
  486. if (starLinks != null) {
  487. image += "Star Links: ";
  488. for (int i=0; i < starLinks.size(); i++) {
  489. Individual linkee = (Individual)starLinks.get(i);
  490. image += "I-" + linkee.serialNmbr + ((i < starLinks.size() - 1) ? ", " : "\n");
  491. } // end of loop thru starLinks
  492. } // end of if-starLinks-are-present
  493. return image;
  494. } // end of method toString()
  495. /** This method builds a string that represents an Individual in a SILKin data (_.silk) file. */
  496. public String toSILKString() throws KSDateParseException {
  497. String result = "<individual n=\"" + serialNmbr + "\">";
  498. result += "<sex>" + (sex instanceof Female ? "F" : "M") + "</sex>\n";
  499. result += " <location x=\"" + location.x + "\" y=\"" + location.y + "\"/>\n";
  500. if (comment != null && comment.length() > 0) {
  501. result += " <comments txt=\"" + comment + "\"/>\n";
  502. }
  503. result += " <surname value=\"" + (surname == null ? "" : surname) + "\"/>\n";
  504. result += " <firstNames value=\"" + (firstNames == null ? "" : firstNames) + "\"/>\n";
  505. String dob = getDateOfBirth(), dod = getDateOfDeath();
  506. if (hasDoB()) {
  507. if (!UDate.validXSD(dob)) {
  508. setDateOfBirth(UDate.convertToXSD(dob));
  509. }
  510. result += " <dateOfBirth value=\"" + getDateOfBirth() + "\"/>\n";
  511. }
  512. if (hasDoD()) {
  513. if (!UDate.validXSD(dod)) {
  514. setDateOfDeath(UDate.convertToXSD(dod));
  515. }
  516. result += " <dateOfDeath value=\"" + getDateOfDeath() + "\"/>\n";
  517. }
  518. if (!UDate.validXSD(dataChangeDate)) {
  519. dataChangeDate = UDate.convertToXSD(dataChangeDate);
  520. }
  521. result += " <dataChangeDate value=\"" + dataChangeDate + "\"/>\n";
  522. if (dataAuthor != null) {
  523. result += " <dataAuthor name=\"" + dataAuthor + "\"/>\n";
  524. }
  525. result += " <deleted>" + deleted + "</deleted>\n";
  526. result += " <birthFamily>";
  527. result += (birthFamily == null ? " " : birthFamily.serialNmbr) + "</birthFamily>\n";
  528. result += " <marriages>";
  529. if (marriages.size() > 0) {
  530. result += "\n";
  531. for (int i = 0; i < marriages.size(); i++) {
  532. result += " <mar n=\"" + ((Family) marriages.get(i)).serialNmbr + "\"/>\n";
  533. }
  534. } // end of loop thru marriages
  535. result += " </marriages>\n";
  536. if (userDefinedProperties != null) {
  537. result += " <userDefinedProperties>\n";
  538. Iterator iter = userDefinedProperties.entrySet().iterator();
  539. Map.Entry entry;
  540. while (iter.hasNext()) {
  541. result += "<UDP>\n";
  542. entry = (Map.Entry) iter.next();
  543. String propName = (String) entry.getKey();
  544. UserDefinedProperty udp = (UserDefinedProperty) entry.getValue();
  545. result += "<propertyName>" + propName + "</propertyName>\n";
  546. result += udp.toSILKString("short");
  547. result += "\n</UDP>\n";
  548. } // end of loop thru UDPs
  549. result += " </userDefinedProperties>\n";
  550. } // end of optional UDPs
  551. if (starLinks != null && starLinks.size() > 0) {
  552. result += " <starLinks>";
  553. result += "\"#" + ((Individual) starLinks.get(0)).serialNmbr + "\"";
  554. for (int i = 1; i < starLinks.size(); i++) {
  555. result += "\", #" + ((Individual) starLinks.get(i)).serialNmbr + "\"";
  556. } // end of loop thru starLinks
  557. result += "</starLinks>\n";
  558. }
  559. result += "</individual>\n";
  560. return result;
  561. } // end of method toSILKString
  562. /**
  563. Write out one Individual record in GEDCOM 5.5 format.
  564. <p>
  565. Record will contain:
  566. <ul>
  567. <li> Individual Serial Number as an "I-" personal ID number.
  568. <li> Fullname
  569. <li> BirthDate and DeathDate (if any)
  570. <li> Marriages: Family Serial Numbers of each one
  571. <li> BirthFamily: Family Serial Number
  572. <li> Notes, if any
  573. <li> Data Change date.
  574. </ul>
  575. <p>
  576. The purpose of a GEDCOM export is to display a chart of Example Individuals who illustrate the
  577. kinship terms from a particular domain theory. Therefore, the FullName field is filled with a
  578. list of all primary kinTerms and a list (in square brackets) of extended kinTerms that this
  579. Individual exemplifies.
  580. @param out a PrintWriter to write to.
  581. @param today String: today's date as it should appear in the DataChange field of GEDCOM record.
  582. */
  583. public void exportGEDCOM(PrintWriter out, String today, String choice, ArrayList<Object> nonTerms) {
  584. // Write out one Individual record in GEDCOM format
  585. out.println("0 @I-" + serialNmbr + "@ INDI");
  586. String outText = "#" + serialNmbr + ": ";
  587. int startLength = outText.length();
  588. if (node != null) {
  589. if (node.extKinTermsAddr.size() > 0) {
  590. // if a kinTerm appears as both primary & extended, remove it from extendedKinTermsAddr
  591. int where;
  592. for (int i = 0; i < node.kinTermsAddr.size(); i++) {
  593. where = node.extKinTermsAddr.indexOf(node.kinTermsAddr.get(i));
  594. if (where != -1) {
  595. node.extKinTermsAddr.remove(where);
  596. }
  597. } // end of loop thru primary kinTerms
  598. } // end of extended-node.kinTermsAddr-is-not-empty
  599. if (node.extKinTermsRef.size() > 0) {
  600. // if a kinTerm appears as both primary & extended, remove it from extendedKinTermsRef
  601. int where;
  602. for (int i = 0; i < node.kinTermsRef.size(); i++) {
  603. where = node.extKinTermsRef.indexOf(node.kinTermsRef.get(i));
  604. if (where != -1) {
  605. node.extKinTermsRef.remove(where);
  606. }
  607. } // end of loop thru primary kinTerms
  608. } // end of extended-node.kinTermsRef-is-not-empty
  609. boolean showFlags = true;
  610. if (choice.equals("Don't Include")) {
  611. showFlags = false;
  612. }
  613. String term = "";
  614. for (int i = 0; i < node.kinTermsRef.size(); i++) {
  615. term = (String) node.kinTermsRef.get(i) + node.ktSuffix;
  616. if ((showFlags || (term.indexOf("<[") == -1))
  617. && (!nonTerms.contains(term))) {
  618. outText += term;
  619. if (i < (node.kinTermsRef.size() - 1)) {
  620. outText += ", ";
  621. }
  622. }
  623. } // end of loop thru primary node.kinTermsRef
  624. if (node.extKinTermsRef.size() > 0) {
  625. outText += " [";
  626. for (int i = 0; i < node.extKinTermsRef.size(); i++) {
  627. term = (String) node.extKinTermsRef.get(i) + node.ktSuffix;
  628. if ((showFlags || (term.indexOf("[") == -1))
  629. && (!nonTerms.contains(term))) {
  630. outText += term;
  631. if (i < (node.extKinTermsRef.size() - 1)) {
  632. outText += ", ";
  633. }
  634. }
  635. } // end of loop thru primary node.extKinTermsRef
  636. outText += "]";
  637. } // end of if-extended-KinTermsRef-isn't-empty
  638. for (int i = 0; i < node.kinTermsAddr.size(); i++) {
  639. term = (String) node.kinTermsAddr.get(i) + node.ktSuffix;
  640. if ((showFlags || (term.indexOf("[") == -1))
  641. && (!nonTerms.contains(term))) {
  642. outText += "*" + term;
  643. if (i < (node.kinTermsAddr.size() - 1)) {
  644. outText += ", ";
  645. }
  646. }
  647. } // end of loop thru primary node.kinTermsAddr
  648. if (node.extKinTermsAddr.size() > 0) {
  649. outText += " [";
  650. for (int i = 0; i < node.extKinTermsAddr.size(); i++) {
  651. term = (String) node.extKinTermsAddr.get(i) + node.ktSuffix;
  652. if ((showFlags || (term.indexOf("[") == -1))
  653. && (!nonTerms.contains(term))) {
  654. outText += "*" + term;
  655. if (i < (node.extKinTermsAddr.size() - 1)) {
  656. outText += ", ";
  657. }
  658. }
  659. } // end of loop thru primary node.extKinTermsAddr
  660. outText += "]";
  661. } // end of if-extended-KinTermsAddr-isn't-empty
  662. }
  663. if (outText.length() == startLength) {
  664. outText += "No Term";
  665. }
  666. if (!(gender.equals("?"))) {
  667. outText += " (" + gender + ")";
  668. }
  669. out.println("1 NAME " + outText);
  670. name = outText;
  671. if (birthYr != null) {
  672. out.println("1 BIRT");
  673. out.println("2 DATE " + getDateOfBirth());
  674. } // end of birthdate output
  675. if (deathYr != null) {
  676. out.println("1 DEAT");
  677. out.println("2 DATE " + getDateOfDeath());
  678. } // end of death_date output
  679. if ((gender.equals("M")) || (gender.equals("F"))) {
  680. out.println("1 SEX " + gender);
  681. }
  682. if (marriages.size() > 0) {
  683. for (int i = 0; i < marriages.size(); i++) {
  684. Family fam = (Family) marriages.get(i);
  685. out.println("1 FAMS @F-" + fam.serialNmbr + "@");
  686. } // end of for-each-marriage
  687. } // end of if-marriages
  688. if (birthFamily != null) {
  689. out.println("1 FAMC @F-" + birthFamily.serialNmbr + "@");
  690. }
  691. if (comment.length() > 7) {
  692. out.println("1 NOTE " + comment.substring(7));
  693. }
  694. out.println("1 CHAN");
  695. out.println("2 DATE " + today);
  696. return;
  697. } // end of method exportGEDCOM
  698. /**
  699. Convert various forms of gender designation into 'M' or 'F'.
  700. @param sex 'Male' or 'male' or'M' or 'm' etc.
  701. @return a String with 'M' or 'F'. Return '?' if the string cannot be understood, and complain.
  702. */
  703. String parseGender(String sex) {
  704. if (sex.equalsIgnoreCase("M") || sex.equalsIgnoreCase("Male")) return "M";
  705. else if (sex.equalsIgnoreCase("F") || sex.equalsIgnoreCase("Female")) return "F";
  706. else if (sex.equals("?")) return "?";
  707. else {
  708. if (MainPane.activity == null) MainPane.createActivityLog(MainPane.desktop, MainPane.menuView);
  709. MainPane.activity.log.append("\nCAUTION: Default gender of '?' was assigned to Person #" + serialNmbr);
  710. MainPane.activity.log.append("Name: " + name + ". Neutral gender should be chosen by 'sex = ?'");
  711. return "?";
  712. } // default = "?" but complain that it wasn't chosen properly
  713. // end of else-clause
  714. } // end of method parseGender
  715. void processNames() { processNames(name); }
  716. /**
  717. Break a FullName into firstNames & surname.
  718. @param nam Example: Gary Dean /Morris/ Jr. -- a GEDCOM standard format
  719. */
  720. void processNames(String nam) {
  721. int comma = nam.indexOf(","), slash = nam.indexOf("/"), start, stop;
  722. String hold;
  723. if (comma != -1) { // name's presumed format: Morris, Gary D.
  724. surname = nam.substring(0, comma);
  725. firstNames = nam.substring(++comma);
  726. } // end of if-comma-present
  727. else if (slash != -1) { // name's presumed format: Gary Dean /Morris/ Jr.
  728. start = slash + 1;
  729. if (start == nam.length()) {
  730. if (MainPane.activity == null) MainPane.createActivityLog(MainPane.desktop, MainPane.menuView);
  731. MainPane.activity.log.append("Name '" + nam + "' is improper format. 0 or 2 slashes allowed.");
  732. }else { // first slash wasn't last character of the string
  733. stop = nam.indexOf("/", start);
  734. if (stop == -1) {
  735. if (MainPane.activity == null) MainPane.createActivityLog(MainPane.desktop, MainPane.menuView);
  736. MainPane.activity.log.append("Name '" + nam + "' is improper format. 0 or 2 slashes allowed.");
  737. }else { // 2 slashes are present
  738. surname = nam.substring(start, stop);
  739. firstNames = nam.substring(0, slash);
  740. if (stop < (nam.length() - 1))
  741. firstNames += nam.substring(stop);
  742. } // end of else-2-slashes-are-present
  743. }
  744. } // end of else-if-slash-present
  745. else if (nam.indexOf(" ") == -1) { // name is 1-word
  746. surname = nam;
  747. firstNames = "";
  748. } // end of 1-word-name
  749. else { // name's presumed format: Gary D. Morris
  750. stop = nam.length() - 2; // don't want a trailing blank
  751. start = 1 + nam.substring(0, stop).lastIndexOf(" "); // blank before last word, or 0
  752. surname = nam.substring(start, nam.length());
  753. if (start >= 2) firstNames = nam.substring(0, start).trim();
  754. } // end of default processing
  755. } // end of method processNames
  756. /**
  757. Append the String <code>not</code> to the 'comment' field for this Individual.
  758. @param not the String to be added.
  759. */
  760. public void addNote(String not) {
  761. comment += not;
  762. dataChangeDate = UDate.today();
  763. }
  764. /**
  765. Append <code>newfam</code> to the 'marriages' field for this Individual.
  766. @param newfam the marriage to be added.
  767. */
  768. public void addMarriage(Family newfam) {
  769. marriages.add(newfam);
  770. dataChangeDate = UDate.today();
  771. } // for type-checking purposes.
  772. /**
  773. Delete this Individual. Do not remove from the individualCensus, but mark as deleted and
  774. remove from any marriages and birth family.
  775. */
  776. public void delete() throws KSInternalErrorException {
  777. deleted = true;
  778. if (birthFamily != null) {
  779. birthFamily.deleteChild(this);
  780. }
  781. for (Object f : marriages) {
  782. ((Family) f).deleteSpouse(this);
  783. }
  784. dataChangeDate = UDate.today();
  785. } // end of method delete
  786. public Individual oppSexKin() {
  787. // Find a relative of the opposite sex & return them. Or null if none found.
  788. Iterator iter = marriages.iterator();
  789. while (iter.hasNext()) {
  790. Family fam = (Family) iter.next();
  791. if (fam.husband == this && fam.wife.hasNoDoD()) {
  792. return fam.wife;
  793. } else if (fam.husband.hasNoDoD()) {
  794. return fam.husband;
  795. }
  796. }
  797. if (birthFamily != null) {
  798. if (gender.equals("M")) {
  799. return birthFamily.wife;
  800. } else {
  801. return birthFamily.husband;
  802. }
  803. }
  804. return null;
  805. } // end of method oppSexKin()
  806. /**
  807. Return serial# of Family for marriage of this Individual with <code>partner</code>, or -1 if none found.
  808. @param partner Individual alleged to be this person's spouse.
  809. */
  810. public int marriageWith(Individual partner) {
  811. ListIterator iter = marriages.listIterator();
  812. Family candidate;
  813. if (gender.equals("M")) { // if male
  814. while (iter.hasNext()) {
  815. candidate = (Family)iter.next();
  816. if (candidate.wife == partner) return candidate.serialNmbr;
  817. } // end of while-there-are-marriages-to-search
  818. } // end of if-male
  819. else { // must be female
  820. while (iter.hasNext()) {
  821. candidate = (Family)iter.next();
  822. if (candidate.husband == partner) return candidate.serialNmbr;
  823. } // end of while-there-are-marriages-to-search
  824. } // end of else-must-be-female
  825. return -1; // only get here if none found
  826. } // end of method to find a particular marriage of this person
  827. /**
  828. By "find a conforming value" we mean choose or accept a value of the correct type (or from among the permissible values)
  829. which does not violate any of the constraints contained on the {@link Argument} or duplicate a (failed) priorValue.
  830. For example, if a property takes a single int value, and a MathVariable has the constraint that its value must be lessThan {3, 6}
  831. and also that its value must be greaterThan {0, 1} then only 2 is a "conforming value." If some other constraint prohibits 2, then
  832. no conforming value will ever be possible and a KSBadHornClauseException is thrown (constraint conflict).
  833. This is different from a situation where there are possible values, but candidate's value is not one of them
  834. (candidate is not a good match).
  835. <p>
  836. If <code>queryOrCommit</code> is "query" then we do not change any values on MathVariable or this Individual; we merely
  837. return true if a conforming value is possible. If <code>queryOrCommit</code> is "commit" then we make the changes.
  838. @param starPropName the name of a user defined property (which must start with '*')
  839. @param arg the Argument whose constraints we must honor and whose value we may bind
  840. @param starBindings the list (possibly empty) of bindings made to date. Add to it if we bind anything.
  841. @param queryOrCommit the string "query" or "commit" to signify whether we should change values or
  842. just report on the feasibility of doing so.
  843. @throws KSBadHornClauseException if the constraints on mathVar make it impossible for any value to be accepted
  844. @throws ClassNotFoundException if there are errors in the type information on a constraint
  845. @return true only if the proposed Values, if any, are conforming, or a conforming value has been generated.
  846. */
  847. public boolean findConformingValue(String starPropName, Argument arg, ArrayList<Object> starBindings, TreeMap bindings,
  848. ConstraintObj constraints, String queryOrCommit, ClauseBody cb)
  849. throws KSBadHornClauseException, ClassNotFoundException, KSConstraintInconsistency, KSInternalErrorException {
  850. UserDefinedProperty udp = (UserDefinedProperty)userDefinedProperties.get(starPropName);
  851. Class requiredClass = getUDPClass(udp.typ);
  852. ArrayList<Object> proposedVals = udp.value;
  853. StarPropertyBinding spb;
  854. if ((proposedVals != null) && (proposedVals.size() > 0)) {
  855. // Values have been proposed. Test whether they're all acceptable. Return true only if ALL are OK.
  856. if ((proposedVals.size() > 1) && udp.singleValue)
  857. throw new KSBadHornClauseException("Multiple values proposed for single-valued property " + starPropName);
  858. for (int i=0; i < proposedVals.size(); i++) { // check each proposed value for conformity to the variable's constraints
  859. if (requiredClass != proposedVals.get(i).getClass())
  860. throw new KSBadHornClauseException("A value of type " + proposedVals.get(i).getClass().getName() +
  861. " was proposed for a property of type " + requiredClass.getName());
  862. if (! checkProposedVal(requiredClass, proposedVals.get(i), arg, bindings, starBindings, constraints))
  863. return false;
  864. } // end of loop thru all proposed values. Now check the "contains" constraint if a MathVariable
  865. if ((arg instanceof MathVariable) && (queryOrCommit.equals("commit"))
  866. && (! udp.singleValue) && (((MathVariable)arg).contains != null))
  867. assureContains((MathVariable)arg, proposedVals, ((MathVariable)arg).contains, starPropName, starBindings,
  868. bindings, requiredClass, constraints, cb);
  869. if ((arg instanceof Variable) && (queryOrCommit.equals("commit"))
  870. && (! udp.singleValue) && (((Variable)arg).containedBy != null))
  871. assureContainedBy(arg, proposedVals, ((Variable)arg).containedBy, starPropName, starBindings,
  872. bindings, requiredClass, constraints, cb);
  873. // if we get this far, all proposed values have type-checked OK and are conforming
  874. if ((queryOrCommit.equals("commit")) && (arg instanceof MathVariable))
  875. yoke((MathVariable)arg, null, null, null, udp, arg.argName, bindings, starBindings);
  876. else if ((queryOrCommit.equals("commit")) && (arg instanceof Variable))
  877. yoke(null, (Variable)arg, null, null, udp, arg.argName, bindings, starBindings);
  878. return true;
  879. } // end of if-proposedVals!=null
  880. // No values are proposed. See if arg already has a conforming value
  881. else if ((arg instanceof MathVariable) && (arg.getVal() != null) && (arg.getVal().size() > 0)) {
  882. // Assumption: if arg has a value already, it is conforming.
  883. if (queryOrCommit.equals("commit"))
  884. yoke((MathVariable)arg, null, null, null, udp, arg.argName, bindings, starBindings);
  885. return true;
  886. } // end of its-a-mathVar-and-it-has-a-value
  887. else if ((arg instanceof Variable) && (arg.getVal() != null) && (arg.getVal().size() > 0)) {
  888. if (! udp.singleValue)
  889. throw new KSBadHornClauseException("Personal variable '" + arg.argName + "' set equal to value of a multi-valued UDP.");
  890. if (udp.typeCheck(arg.getVal().get(0)) &&
  891. ((udp.validEntries == null) || (udp.validEntries.isEmpty()) || (udp.validEntries.contains(arg.getVal().get(0))))) {
  892. if (queryOrCommit.equals("commit")) {
  893. udp.value.add(arg.bindingVal());
  894. yoke(null, (Variable)arg, null, null, udp, arg.argName, bindings, starBindings);
  895. }
  896. return true;
  897. } else return false;
  898. } // end of its-a-Variable-with-value
  899. else if (arg instanceof Constant) { // Constants by definition always have a value
  900. if (udp.typeCheck(arg.getVal().get(0)) &&
  901. ((udp.validEntries == null) || (udp.validEntries.isEmpty()) || (udp.validEntries.contains(arg.getVal().get(0))))) {
  902. if (queryOrCommit.equals("commit")) {
  903. udp.value.add(arg.getVal().get(0));
  904. yoke(null, null, (Constant)arg, null, udp, arg.argName, bindings, starBindings);
  905. }
  906. return true;
  907. } else return false;
  908. } // end of its-a-Constant
  909. // No values are proposed. See if the udp has a default value
  910. else if ((udp.defaultValue != null) && (checkProposedVal(requiredClass, udp.defaultValue, arg, bindings, starBindings, constraints))) {
  911. // Default value is conforming. Yay!
  912. if (queryOrCommit.equals("commit")) {
  913. udp.value.add(udp.defaultValue);
  914. if (arg instanceof MathVariable) // it can't be a Constant, 'cuz it has no value
  915. yoke((MathVariable)arg, null, null, null, udp, arg.argName, bindings, starBindings);
  916. else yoke(null, (Variable)arg, null, null, udp, arg.argName, bindings, starBindings);
  917. }
  918. return true;
  919. } // end of check-the-udp's-default-value
  920. // No values are proposed; arg does not have one. See if a conforming value can be found in the valid entries
  921. else if ((udp.validEntries != null) && (udp.validEntries.size() > 0)) {
  922. for (int j=0; j < udp.validEntries.size(); j++) {
  923. if (checkProposedVal(requiredClass, udp.validEntries.get(j), arg, bindings, starBindings, constraints)) {
  924. if (queryOrCommit.equals("commit")) { // found-one!
  925. MathVariable mathVar = (MathVariable)arg; // since Constants always have a value, and ValidEntries not legal for type Person
  926. udp.value.add(udp.validEntries.get(j));
  927. if ((! udp.singleValue) && (mathVar.contains != null))
  928. assureContains(mathVar, udp.value, mathVar.contains, starPropName, starBindings, bindings,
  929. requiredClass, constraints, cb);
  930. if ((! udp.singleValue) && (mathVar.containedBy != null))
  931. assureContainedBy(arg, udp.value, ((Variable)mathVar).containedBy, starPropName, starBindings,
  932. bindings, requiredClass, constraints, cb);
  933. yoke(mathVar, null, null, null, udp, mathVar.argName, bindings, starBindings);
  934. } // end of commit
  935. return true;
  936. } // end of found-one!
  937. } // end of search thru valid entries
  938. return false; // We'll never find one -- all valid entries are non-conforming to the arg's constraints
  939. } // end of validEntries-exist
  940. // OK. All easy routes failed; generate a conforming value if possible
  941. ArrayList<Object> genVals = generateCandidateValues(requiredClass, arg, bindings, starBindings, constraints, udp);
  942. for (int i=0; i < genVals.size(); i++)
  943. if (checkProposedVal(requiredClass, genVals.get(i), arg, bindings, starBindings, constraints)) {
  944. if (queryOrCommit.equals("commit")) { // since Constants always have a value, and
  945. if (arg instanceof MathVariable) { // arg doesn't, it must be a MathVar or Variable
  946. MathVariable mathVar = (MathVariable)arg;
  947. udp.value.add(genVals.get(i));
  948. if ((! udp.singleValue) && (mathVar.contains != null))
  949. assureContains(mathVar, udp.value, mathVar.contains, starPropName, starBindings,
  950. bindings, requiredClass, constraints, cb);
  951. if ((! udp.singleValue) && (mathVar.containedBy != null))
  952. assureContainedBy(arg, udp.value, ((Variable)mathVar).containedBy, starPropName, starBindings,
  953. bindings, requiredClass, constraints, cb);
  954. yoke(mathVar, null, null, null, udp, mathVar.argName, bindings, starBindings);
  955. }else { // it is a Variable
  956. udp.value.add(genVals.get(i));
  957. yoke(null, (Variable)arg, null, null, udp, arg.argName, bindings, starBindings);
  958. }
  959. } // end of commit
  960. return true;
  961. } // end of search thru generated values
  962. return false; // last hope is dashed. report failure (and hang head)
  963. } // end of method findConformingValue
  964. public void yoke(MathVariable mathVar, Variable vari, Constant konstant, Argument personArg,
  965. UserDefinedProperty udp, String bindingMade, TreeMap bindings, ArrayList<Object> starBindings)
  966. throws KSInternalErrorException {
  967. Object boundVal = null;
  968. if (mathVar != null) {
  969. mathVar.link(udp);
  970. mathVar.updatePriorVals(udp.value);
  971. boundVal = mathVar.bindingVal();
  972. }else if (vari != null) {
  973. vari.addVal(udp.value);
  974. boundVal = vari.bindingVal();
  975. }else { // must be konstant != null
  976. Object obj = konstant.getVal().get(0);
  977. if (! udp.value.contains(obj))
  978. udp.value.add(obj);
  979. boundVal = konstant.bindingVal();
  980. }
  981. if ((personArg != null) && (bindingMade.equals(personArg.argName)))
  982. bindings.put(bindingMade, this);
  983. else bindings.put(bindingMade, boundVal);
  984. StarPropertyBinding spb = n

Large files files are truncated, but you can click here to view the full file