package edu.byu.cs.roots.opg.nfs; import java.awt.AWTError; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import javax.swing.JOptionPane; import javax.swing.JTextArea; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.Timer; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpResponseException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpParams; import org.familysearch.ws.client.familytree.v2.schema.EventAssertion; import org.familysearch.ws.client.familytree.v2.schema.EventValue; import org.familysearch.ws.client.familytree.v2.schema.FamilyReference; import org.familysearch.ws.client.familytree.v2.schema.FamilyTree; import org.familysearch.ws.client.familytree.v2.schema.GenderAssertion; import org.familysearch.ws.client.familytree.v2.schema.GenderType; import org.familysearch.ws.client.familytree.v2.schema.NameAssertion; import org.familysearch.ws.client.familytree.v2.schema.NameForm; import org.familysearch.ws.client.familytree.v2.schema.NamePiece; import org.familysearch.ws.client.familytree.v2.schema.NamePieceType; import org.familysearch.ws.client.familytree.v2.schema.NameType; import org.familysearch.ws.client.familytree.v2.schema.OrdinanceAssertion; import org.familysearch.ws.client.familytree.v2.schema.OrdinanceType; import org.familysearch.ws.client.familytree.v2.schema.ParentReference; import org.familysearch.ws.client.familytree.v2.schema.ParentsReference; import org.familysearch.ws.client.familytree.v2.schema.Pedigree; import org.familysearch.ws.client.familytree.v2.schema.Person; import org.familysearch.ws.client.familytree.v2.schema.PersonReference; import org.familysearch.ws.client.identity.v2a.schema.Identity; import edu.byu.cs.roots.opg.chart.ChartOptions; import edu.byu.cs.roots.opg.chart.ChartType; import edu.byu.cs.roots.opg.gui.OnePageMainGui; import edu.byu.cs.roots.opg.io.GedcomRecord; import edu.byu.cs.roots.opg.io.HTTPSecureWrapper; import edu.byu.cs.roots.opg.model.Event; import edu.byu.cs.roots.opg.model.EventType; import edu.byu.cs.roots.opg.model.Family; import edu.byu.cs.roots.opg.model.Gender; import edu.byu.cs.roots.opg.model.Individual; import edu.byu.cs.roots.opg.model.OpgSession; import edu.byu.cs.roots.opg.model.SessionState; public class NFSDownloadThread extends SwingWorker<Void, String> { OnePageMainGui gui; OpgSession session; String username, password; String updateText = ""; private final static String devKey = "WCQY-7J1Q-GKVV-7DNM-SQ5M-9Q5H-JX3H-CMJK"; private final static String nfsKey = "3MRF-LC1D-JJ5S-ML42-223W-7PD7-KLR8-6B83"; private final static String baseURL = "https://api.familysearch.org"; private String PersonID; public DefaultHttpClient httpclient; private ResponseHandler<String> responseHandler; private static int famCount = 0; private GedcomRecord gedRecord; private ArrayList<String> waitingQueue; private final int MAX_CALLS = 10; private final int MAX_GENS = 9; //only want to grab 9 generations for the first call private RelationMap relMap; private Timer timer; //used with throttled() private int throttledAmt; private final int UPDATE = 0; private final int GETINFO = 1; private String firstOfQueue = ""; private DownloadOptionDialog options; private final int twentyMinutes = 1200000; private JTextArea textArea; private boolean cancelDownload = false; private DownloadProgress progress; private ThreadSafeClientConnManager cm; public NFSDownloadThread(OpgSession session){ this.session = session; HttpParams params = new BasicHttpParams(); SchemeRegistry registry = new SchemeRegistry(); cm = new ThreadSafeClientConnManager(params, registry); httpclient = new DefaultHttpClient(cm, params); httpclient = HTTPSecureWrapper.wrapClient(httpclient); responseHandler = new BasicResponseHandler(); username = ""; password = ""; ActionListener timerListener = new ActionListener() { public void actionPerformed(ActionEvent evt) { System.out.println("this is getting in here!"); try { getIdentity(); } catch (ClientProtocolException e) { System.out.println("there is an error reobtaining the Identity object"); e.printStackTrace(); } catch (IOException e) { System.out.println("there is an error reobtaining the Identity object"); e.printStackTrace(); } catch (JAXBException e) { System.out.println("there is an error reobtaining the Identity object"); e.printStackTrace(); } } }; timer = new Timer(twentyMinutes, timerListener); } public void prepareLogin(OnePageMainGui gui, String username, String password, DownloadProgress progress){ this.gui = gui; textArea = gui.getProgressBar().getTextArea(); this.username = username; this.password = password; this.progress = progress; } @Override protected void process(List<String> chunks){ for (String s : chunks) textArea.append(s + "\n"); gui.getProgressBar().getProgressBar().setValue(relMap.getAllDone().size()); gui.getProgressBar().getProgressBar().setMaximum(relMap.getTotalPeople()); gui.getProgressBar().refreshProgressString(); } @Override protected void done(){ if (options.getDownload() && !cancelDownload) gui.initializeChart(); progress.finish(); //progress.dispose(); for(String s : relMap.getAllNotDone()){ System.out.println("Not Done: "+s); } } @Override protected Void doInBackground() throws Exception { try { download(username, password); } catch (UsernamePasswordException e) { JOptionPane.showMessageDialog(null, e.getMessage(), "Error!", JOptionPane.ERROR_MESSAGE); } catch (IOException e) { JOptionPane.showMessageDialog(null, "There was an error trying to connect to new.familysearch.org.\n" + "If you tried to download a specific Personal Identifier, make\n" + "sure you typed the Identifier correctly.", "Error!", JOptionPane.ERROR_MESSAGE); } return null; } private void download(String username, String password) throws UsernamePasswordException, IOException{ session.clear(); ChartOptions.firstLoad = true; //session.changeType(ChartType.values()[0]); session.changeType(ChartType.MULTISHEET); newUsernameAndPassword(username, password); int ok = getInformation(null); switch(ok) { case(1): throw new UsernamePasswordException(); case(2): case(3): case(4): throw new IOException(); case(5): return; } //TODO do we actually need this? //doNotContinue(); session.record = getGedcomRecord(); session.record.linkChildrenToParents(); session.record.linkSpouseToFamily(); session.record.setNFS(true); session.hollowchart = null; session.state = SessionState.edit; session.names_dataProvider = session.getIndividualList(); //does this have a gedfile? session.gedfile = "newdownload.ged"; session.setChanged(true); session.getBaseOptions().setRoot(GetRoot(), session); session.resetChanged(); gui.downloadDone(); } /* * After the user decides to download a new pedigree, this method is called, which * resets the global variables, and readies the program to start another download. */ public void newUsernameAndPassword(String un, String pw) { username = un; password = pw; httpclient.getCredentialsProvider().clear(); httpclient.getCredentialsProvider().setCredentials( new AuthScope("api.familysearch.org", 80), new UsernamePasswordCredentials(username, password)); gedRecord = new GedcomRecord(); relMap = new RelationMap(); waitingQueue = new ArrayList<String>(); if (timer.isRunning()) timer.stop(); throttledAmt = 0; firstOfQueue = ""; } public Individual GetRoot() { return gedRecord.getIndividual(PersonID); } /* * This method returns true if all of the individuals have obtained their information */ public boolean GetMore() { return !relMap.getAllNotDone().isEmpty(); } /* * returns the gedRecord. Before the gedRecord is returned, * the relMap is saved in the record. */ public GedcomRecord getGedcomRecord() { gedRecord.setRelationMap(relMap); return gedRecord; } /* * This method preps the pedigree for updates. the tenth generation individuals are * stored in families. */ public void doNotContinue() { // ArrayList<String> tenthGen = relMap.getGenerationList(options.getChoiceGens() + 1); // do { // String ID = ""; // int amt = MAX_CALLS; // if (tenthGen.size() < MAX_CALLS) // amt = tenthGen.size(); // for (int i = 0; i < amt; i++) // ID += tenthGen.get(i) + ","; // ID = ID.substring(0, ID.length()-1); // // try { // String requestUrl = baseURL+"/familytree/v2/person/"; // requestUrl += ID; // //request all the information about the person // requestUrl += "?personas=mine&names=summary&events=summary&ordinances=all&families=summary&children=all"; // //"?personas=mine&names=summary&events=summary&ordinances=all&families=summary&parents=summary&children=all" // String responseBody = httpclient.execute(new HttpGet(requestUrl), responseHandler); // FamilyTree extract = (FamilyTree) JAXBContext.newInstance(FamilyTree.class).createUnmarshaller().unmarshal(new StringReader(responseBody)); // String[] IDS = ID.split(","); // int IDcount = 0; // for (Person person: extract.getPersons()) { // Individual indiNew; // //change ID so it is the correct one if making multiple calls! // if(!gedRecord.containsIndividual(IDS[IDcount])) { // indiNew = new Individual(IDS[IDcount]); // gedRecord.addIndividual(indiNew.id, indiNew); // } else // indiNew = gedRecord.getIndividual(IDS[IDcount]); // // indiNew.version = person.getVersion(); // // indiNew = getNameValues(indiNew, person); // indiNew = getGender(indiNew, person); // indiNew = getEventValues(indiNew, person); // indiNew = getFamilyValues(indiNew, person); // indiNew = getParentValues(indiNew, person); // indiNew = getOrdinanceValues(indiNew, person); // // IDcount++; // } // } catch (ClientProtocolException e) { // System.out.println("getting into ClientProgocolException in doNotContinue"); // } catch (IOException e) { // System.out.println("getting into IOException in doNotContinue"); // } catch (JAXBException e) { // System.out.println("getting into JAXBException in doNotContinue"); // } // for (int i = 0; i < amt; i++) // tenthGen.remove(0); // } while (!tenthGen.isEmpty()); } /* * this method is called when a user wants to open a previously downloaded pedigree */ public void open (GedcomRecord record, Individual root) { gedRecord = record; relMap = gedRecord.getRelationMap(); waitingQueue = new ArrayList<String>(); PersonID = root.id; famCount = record.getFamilies().size(); } /* * If only the username and password need to be reset, this method resets * and stores the new username and password, and then runs Identity to * make sure they both work. */ public void resetUsernameAndPassword(String un, String pw) throws UsernamePasswordException { username = un; password = pw; httpclient.getCredentialsProvider().clear(); httpclient.getCredentialsProvider().setCredentials( new AuthScope(baseURL, 80), new UsernamePasswordCredentials(username, password)); try { getIdentity(); } catch (ClientProtocolException e) { System.out.println("getting into ClientProgocolException in checkUsernameAndPassword"); if (((HttpResponseException) e).getStatusCode() == 401) throw new UsernamePasswordException(); } catch (IOException e) { System.out.println("getting into IOException in checkUsernameAndPassword"); } catch (JAXBException e) { System.out.println("getting into JAXBException in checkUsernameAndPassword"); } } /** * this method goes in and grabs the session ID or key from new.familysearch * @throws IOException * @throws ClientProtocolException * @throws JAXBException */ private void getIdentity() throws ClientProtocolException, IOException, JAXBException { // DefaultHttpClient handles cookies automatically // BasicResponseHandler returns the HTTP response body as a String String responseBody=""; // String requestUrl = "http://www.dev.usys.org/identity/v2/login?key=" + devKey; // String requestUrl = baseURL+"/identity/v2/login?key=" + nfsKey + "&oauth_nonce=123456789&oauth_signature_method=PLAINTEXT&oauth_signature=%26&oauth_timestamp=1252618480"; System.out.println("Getting Identity"); publish("Getting Identity!"); String requestUrl = baseURL +"/identity/v2/login?"; requestUrl += "key="+nfsKey; requestUrl += "&username="+username; requestUrl += "&password="+password; requestUrl += "&agent="+ "OnePageGenealogy"; try { responseBody = new StandardExecuteCallable(httpclient, requestUrl, true).call(); } catch (Exception e) { e.printStackTrace(); }//httpclient.execute(new HttpPost(requestUrl), responseHandler); // Unmarshal the Identity object from the response body and save the session ID Identity identity = (Identity) JAXBContext.newInstance(Identity.class).createUnmarshaller().unmarshal(new StringReader(responseBody)); String sessionId = identity.getSession().getId(); System.out.println("Session ID: " + sessionId); } /** * this is used the first time that the user accesses new FamilySearch. The program goes in * and gets up to 9 generations of the user's pedigree. * */ public int getInformation(ArrayList<String> getInfoFor) { System.out.println("Get Information"); try { if (getInfoFor == null) { getIdentity(); getUserRoot(); //If the information dialog was closed with the x button if (!options.getDownload() || cancelDownload) return 5; //add the user to the relMap relMap.add(PersonID, 0, false); //this is used to determine if a descendant descends from the root relMap.addRootDescendant(PersonID); activateDownloadBar(); getUserPedigree(PersonID); } else waitingQueue = getInfoFor; String getInf; while ((!waitingQueue.isEmpty()) && !cancelDownload ) { getInf = ""; int amt = MAX_CALLS; if (waitingQueue.size() < MAX_CALLS) amt = waitingQueue.size(); for (int i = 0; i < amt; i++) getInf += waitingQueue.get(i) + ","; getInf = getInf.substring(0, getInf.length()-1); getIndividualInfo(getInf); //dequeue after the call is made. for (int i = 0; i < amt; i++) waitingQueue.remove(0); ArrayList<String> smToLg = null; if (waitingQueue.size() < MAX_CALLS) smToLg = relMap.getSmToLgNotDone(); int counter = 0; //check to see if we should do more pedigree calls while (waitingQueue.size() < MAX_CALLS && counter < smToLg.size()) { String nextIDCall = smToLg.get(counter); counter++; if (!waitingQueue.contains(nextIDCall)) { if (relMap.getGeneration(nextIDCall) <= options.getChoiceGens()) getUserPedigree(nextIDCall); else break; } } } } catch (HttpResponseException e){ System.out.println("Getting into HttpResponseException"); if (e.getStatusCode() == 401) return 1; else if (e.getStatusCode() == 503) throttled(0, waitingQueue, GETINFO); else return 2; } catch (ClientProtocolException e) { System.out.print("Getting into exception ClientProtocolException\nstacktrace:"); return 2; } catch (IOException e) { System.out.println("Getting into IOException"); return 3; } catch (JAXBException e) { System.out.println("Getting into JAXBException"); return 4; } // httpclient.getConnectionManager().shutdown(); findPlaceInTree(); return 0; } /** * The user can choose to download their own pedigree, or type in a Person Identifier * from New FamilySearch and download their pedigree instead. * @throws IOException * @throws JAXBException */ public void getUserRoot() throws IOException, JAXBException { options = new DownloadOptionDialog(); activateOptionDialog(); if (!options.getDownload() || cancelDownload) return; if (options.getChoiceID().contentEquals("")) getUser(); else { PersonID = options.getChoiceID().toUpperCase(); } System.out.println("\n--------"); System.out.println("Pedigree for: " + PersonID); } /** * finds the first person/root of the tree, and stores their id * @throws IOException * @throws JAXBException */ public void getUser() throws IOException, JAXBException { String requestUrl = baseURL+"/familytree/v2/person"; publish("Getting User: "); System.out.println("Getting User: "); String responseBody = httpclient.execute(new HttpGet(requestUrl), responseHandler); FamilyTree firstPerson = (FamilyTree) JAXBContext.newInstance(FamilyTree.class).createUnmarshaller().unmarshal(new StringReader(responseBody)); Person fPerson = firstPerson.getPersons().get(0); PersonID = fPerson.getId(); } /** * given the personId, this will get the pedigree! * Each person in that pedigree is added to the waitingqueue, provided: * 1) They aren't already there * 2) If the relationmap doesn't contain them, or if it does and they haven't been processed * @throws IOException * @throws ClientProtocolException * @throws JAXBException */ public void getUserPedigree(String ID) throws ClientProtocolException, IOException, JAXBException { String requestUrl = baseURL+"/familytree/v2/pedigree/"; int neededGens = options.getChoiceGens() - relMap.getGeneration(ID); if(neededGens == 0) return; int reqGens = (neededGens > 9)? 9:neededGens; requestUrl += ID + "?ancestors="+reqGens; System.out.println("Requesting: " + reqGens + " Gens, Pedigree for: " + ID); publish("Getting Pedigree for: " + ID); String responseBody=""; try { responseBody = new StandardExecuteCallable(httpclient, requestUrl, false).call(); } catch (Exception e) { e.printStackTrace(); } //= httpclient.execute(new HttpGet(requestUrl), responseHandler); FamilyTree familytree = (FamilyTree) JAXBContext.newInstance(FamilyTree.class).createUnmarshaller().unmarshal(new StringReader(responseBody)); for (Pedigree pedigree : familytree.getPedigrees()) { System.out.println("Pedigree for: " + pedigree.getId()); for (Person person : pedigree.getPersons()) { if (!(relMap.contains(person.getId()) && relMap.isCompleted(person.getId()))) { if (!waitingQueue.contains(person.getId())){ waitingQueue.add(person.getId()); } } } } } public void getPersonDescendants(String toGet){ } /** * given the personID, this will go in and acquire all the info * @throws JAXBException * @throws IOException * @throws ClientProtocolException */ public void getIndividualInfo(String ID) throws JAXBException, ClientProtocolException, IOException { // GetIndividualInfoThread t = new GetIndividualInfoThread(this, gedRecord, relMap, baseURL, ID, options.getChoiceGens(), options.getChoiceAllDescendants(), options.getChoiceRootDescendants()); // t.start(); // try { // t.join(); // } catch (InterruptedException e) { // e.printStackTrace(); // } String requestUrl = baseURL+"/familytree/v2/person/"; requestUrl += ID; //request all the information about the person requestUrl += "?personas=mine&names=summary&events=summary&ordinances=all&families=summary&parents=summary&children=all"; publish("Getting information for: " + ID); System.out.println("Getting information for: " + ID); String responseBody = httpclient.execute(new HttpGet(requestUrl), responseHandler); FamilyTree extract = (FamilyTree) JAXBContext.newInstance(FamilyTree.class).createUnmarshaller().unmarshal(new StringReader(responseBody)); String[] IDS = ID.split(","); int IDcount = 0; for (Person person: extract.getPersons()) { Individual indiNew; //change ID so it is the correct one if making multiple calls! if(!gedRecord.containsIndividual(IDS[IDcount])) { indiNew = new Individual(IDS[IDcount]); gedRecord.addIndividual(indiNew.id, indiNew); } else indiNew = gedRecord.getIndividual(IDS[IDcount]); indiNew.version = person.getVersion(); indiNew = getNameValues(indiNew, person); indiNew = getGender(indiNew, person); indiNew = getEventValues(indiNew, person); indiNew = getFamilyValues(indiNew, person); indiNew = getParentValues(indiNew, person); indiNew = getOrdinanceValues(indiNew, person); //by this time they should already be in the relMap!! /*if (!relMap.contains(indiNew.id)) System.out.println(indiNew.id + " IS NOT IN THE RELMAP!"); else*/ relMap.nowCompleted(indiNew.id); IDcount++; publish("Processed: " + indiNew.givenName + " (" + indiNew.id + ")"); System.out.println("Processed: " + indiNew.givenName + " (" + indiNew.id + ")"); if (ID.contains("LHQS-93J")) System.out.println(ID); } } /** * Iterates through the name values and assigns them to the correct spots. * because new FamilySearch does not use middle names, no middle names are assigned, * and most of the name is stored in given names. */ private Individual getNameValues(Individual indi, Person p) { if (p.getAssertions().getNames() == null) return indi; indi = initializeNames(indi); for (NameAssertion n: p.getAssertions().getNames()) { if(n.getValue().getType() == NameType.Name) { for (NameForm name: n.getValue().getForms()) { String given = ""; String surname = ""; for (NamePiece na: name.getPieces()) { if (na.getType() == NamePieceType.Given) given += na.getValue() + " "; else if (na.getType() == NamePieceType.Family) surname += na.getValue() + " "; else if (na.getType() == NamePieceType.Prefix) indi.namePrefix = na.getValue(); else if (na.getType() == NamePieceType.Suffix) indi.nameSuffix = na.getValue(); } given = given.trim(); indi.givenName = given; surname = surname.trim(); indi.surname = surname; } } } return indi; } /** * goes through each name string and initializes them to zero. This makes sure that * there are no null pointer exceptions when the program later tries to use the informaiton * @param indi * @return */ private Individual initializeNames(Individual indi) { indi.givenName = ""; indi.middleName = ""; indi.surname = ""; indi.nameSuffix = ""; indi.surnamePrefix = ""; indi.namePrefix = ""; indi.nameSuffix = ""; return indi; } /** * gets the gender of the individual. if there are multiple genders, then it will return * the first one. * @param indi * @param p * @return */ private Individual getGender(Individual indi, Person p) { if (p.getAssertions().getGenders() == null) return indi; GenderAssertion gen = p.getAssertions().getGenders().get(0); if (gen.getValue().getType() == GenderType.Male) indi.gender = Gender.MALE; else if (gen.getValue().getType() == GenderType.Female) indi.gender = Gender.FEMALE; else indi.gender = Gender.UNKNOWN; return indi; } /** * gets the birth and death dates and places of the individual. * The informaiton is left null if there is nothing stored in new FamilySearch * @param indi * @param p * @return */ private Individual getEventValues(Individual indi, Person p) { if (p.getAssertions().getEvents() == null) return indi; for (EventAssertion event: p.getAssertions().getEvents()) { Event ev = null; EventValue evalue = event.getValue(); if (evalue.getType() == org.familysearch.ws.client.familytree.v2.schema.EventType.Birth) { ev = new Event(EventType.BIRTH); indi.birth = ev; } else if (evalue.getType() == org.familysearch.ws.client.familytree.v2.schema.EventType.Death) { ev = new Event(EventType.DEATH); indi.death = ev; } if (ev != null) { if (evalue.getDate() != null) ev.date = evalue.getDate().getNormalized(); if (evalue.getPlace() != null && evalue.getPlace().getNormalized() != null) ev.place = evalue.getPlace().getNormalized().getValue(); } } if (indi.birth != null) indi.birth.parseDateParts(); if (indi.death != null) indi.death.parseDateParts(); return indi; } /** * Creates the Family for the person if their spouse has not already established the family. * stores the children's ids (but doesn't store them in the RelativeMap, we don't want to get * all the children) and also gets the spouse's id (creates a new individual if it is not already * created) and the marriage information (date and place). * * if this is the main person, add the children into the RelationMap. * @param indi * @param p * @return */ private Individual getFamilyValues(Individual indi, Person p) { for (FamilyReference fam: p.getFamilies()) { /* this makes sure that only the first spouse of the family creates a new Family * the other parent is added into the Family in the GedcomRecord. If the second * spouse tries to create the family first, the method determines if the first * spouse is in the relMap or not, and then adds them if it needs to. */ if (!(indi.gender == Gender.MALE && fam.getParents().get(0).getGender() == GenderType.Male) && !(indi.gender == Gender.FEMALE && fam.getParents().get(0).getGender() == GenderType.Female)) { if (!relMap.contains(fam.getParents().get(0).getId())) relMap.add(fam.getParents().get(0).getId(), relMap.getGeneration(indi.id), false); return indi; } String famID; if (famCount < 10) famID = "0F0" + famCount; else if (famCount > 99) { int beg = 0; int end = famCount; while(end > 99){ beg++; end = end - 100; } String middle = "F"; if (end < 10) middle = "F0"; famID = beg + middle + end; } else famID = "0F" + famCount; Family newFamily = new Family(famID); famCount++; indi.famsIds.add(famID); if (fam.getChildren() != null) { for (PersonReference child : fam.getChildren()) { String childId = child.getId(); if (relMap.contains(childId)) { System.out.println("Adding "+indi.id+" as parent to "+childId); relMap.add(indi.id, relMap.getGeneration(childId) + 1, false); } //Here's where descendants get downloaded else if (relMap.contains(indi.id)) { if (options.getChoiceAllDescendants()){ relMap.add(childId, relMap.getGeneration(indi.id), false); } else if (options.getChoiceRootDescendants()){ if (relMap.isRootDescendant(indi.id)) { relMap.add(childId, relMap.getGeneration(indi.id), false); relMap.addRootDescendant(childId); } } } newFamily.childrenXRefIds.add(childId); indi.famcIds.add(childId); } } for(PersonReference parent: fam.getParents()) { //if the spouse is not in the relMap, add them at the same generation level if (!indi.id.equals(parent.getId()) && !relMap.contains(parent.getId())) { System.out.println("Adding "+parent.getId()+" as spouse"); relMap.add(parent.getId(), relMap.getGeneration(indi.id), false); } if (parent.getGender() == GenderType.Male && relMap.getGeneration(indi.id) < options.getChoiceGens()) { newFamily.husbandId = parent.getId(); if (gedRecord.getIndividuals().containsKey(parent.getId())) newFamily.husband = gedRecord.getIndividuals().get(parent.getId()); else { gedRecord.addIndividual(parent.getId(), new Individual(parent.getId())); newFamily.husband = gedRecord.getIndividual(parent.getId()); } } else if (relMap.getGeneration(indi.id) < options.getChoiceGens()){ newFamily.wifeId = parent.getId(); if (gedRecord.getIndividuals().containsKey(parent.getId())) newFamily.wife = gedRecord.getIndividuals().get(parent.getId()); else { gedRecord.addIndividual (parent.getId(), new Individual(parent.getId())); newFamily.wife = gedRecord.getIndividual(parent.getId()); } } } if (fam.getMarriage() != null) { EventValue evalue = fam.getMarriage().getValue(); if (evalue.getType() == org.familysearch.ws.client.familytree.v2.schema.EventType.Marriage) { Event marr = new Event(EventType.MARRIAGE); if (evalue.getDate() != null) marr.date = evalue.getDate().getNormalized(); if (evalue.getPlace() != null && evalue.getPlace().getNormalized() != null) marr.place = evalue.getPlace().getNormalized().getValue(); newFamily.marriage = marr; } //currently, i do not know how to get the divorce. Technically, i could just get ALL //the events, however, if a person has a lot of events on their pedigree, this could //slow down the download EXTREMELY! But maybe at some point they will make the information //readily available and easy for us to grab. Till then, we will not show it! else if (evalue.getType() == org.familysearch.ws.client.familytree.v2.schema.EventType.Divorce) newFamily.divorce = true; } if (indi.fams.size() > 0) { for (Family famCheck : indi.fams){ if (((newFamily.husband != null && famCheck.husband != null && newFamily.husband.id.equals(famCheck.husband.id)) || (newFamily.husband == null && famCheck.husband == null)) && ((newFamily.wife != null && famCheck.wife != null &&newFamily.wife.id.equals(famCheck.wife.id)) || (newFamily.wife == null && famCheck.wife == null))){ famCount--; return indi; } } } gedRecord.addFamily(newFamily.id, newFamily); indi.fams.add(newFamily); } return indi; } /** * does not store information about the ordinance, only if they have been performed/completed * yet. The ordinances it cares about is Baptism, Endowment, Sealing to Spouse, and Sealing * to Parents. * this method is done after the getFamilyValues to make sure the individual already has a family * value created. * @param indi * @param p * @return */ private Individual getOrdinanceValues(Individual indi, Person p) { if (p.getAssertions().getOrdinances() == null) return indi; for (OrdinanceAssertion ordinance: p.getAssertions().getOrdinances()) { if (ordinance.getValue().getType() == OrdinanceType.Baptism) { indi.baptism = true; indi.baptismComplete = true; } else if (ordinance.getValue().getType() == OrdinanceType.Born_in_Covenant || ordinance.getValue().getType() == OrdinanceType.Sealing_to_Parents) { indi.sealingToParents = true; indi.sealingToParentsComplete = true; } else if (ordinance.getValue().getType() == OrdinanceType.Endowment) { indi.endowment = true; indi.endowmentComplete = true; } else if (ordinance.getValue().getType() == OrdinanceType.Sealing_to_Spouse) { indi.sealingToSpouse = true; indi.sealingToSpouseComplete = true; for (int i = 0; i < indi.fams.size(); i++) { Family parentID = indi.fams.get(i); if (parentID.husbandId == indi.id || parentID.wifeId == indi.id) { parentID.sealing = true; parentID.sealingComplete = true; } } } /*where do we get the information if the marriage was annuled? we also need a boolean, so if this comes before the sealing, it won't be marked as true else if (ordinance.getValue().getType() == OrdinanceType.Marriage_Annuled) { indi.sealingToSpouse = false; indi.sealingToSpouseComplete = false; for (int i = 0; i < indi.fams.size(); i++) { Family parentID = indi.fams.get(i); if (parentID.husbandId == indi.id || parentID.wifeId == indi.id) { parentID.sealing = false; parentID.sealingComplete = false; //is this needed? (could not be...tricky to say when i cannot exactly text this) for (int j = 0; j < parentID.children.size(); j++) { Individual child = parentID.children.get(j); child.sealingToParents = false; child.sealingToParents.complete = false; } } } }*/ } return indi; } /** * Stores/creates individuals for the person's parents. * @param indi * @param p * @return */ private Individual getParentValues(Individual indi, Person p) { if (p.getParents() == null) return indi; for (ParentsReference parents: p.getParents()) { for (ParentReference parent: parents.getParents()) { //check to see if the parents are done if (relMap.contains(parent.getId()) && !relMap.contains(indi.id)) relMap.add(indi.id, relMap.getGeneration(parent.getId())-1, false); //adds the parent into relMap if (!relMap.contains(parent.getId()) && relMap.getGeneration(indi.id) < options.getChoiceGens()) { System.out.println("Adding "+parent.getId()+" as parent again"); relMap.add(parent.getId(), relMap.getGeneration(indi.id)+1, false); } if(relMap.getGeneration(indi.id) < options.getChoiceGens()){ if (parent.getGender() == GenderType.Male && !gedRecord.containsIndividual(parent.getId())) { gedRecord.addIndividual(parent.getId(), new Individual(parent.getId())); indi.father = gedRecord.getIndividual(parent.getId()); } else if (parent.getGender() == GenderType.Male && gedRecord.containsIndividual(parent.getId())) indi.father = gedRecord.getIndividual(parent.getId()); else if (parent.getGender() == GenderType.Female && !gedRecord.containsIndividual(parent.getId())) { gedRecord.addIndividual(parent.getId(), new Individual(parent.getId())); indi.mother = gedRecord.getIndividual (parent.getId()); } else if (parent.getGender() == GenderType.Female && gedRecord.containsIndividual(parent.getId())) indi.mother = gedRecord.getIndividual(parent.getId()); } //instead of relying on getUserPedigree(), this will automatically add the parents //into the waitingQueue, which will mean less calls for pedigree's. // if (!(relMap.contains(parent.getId()) && relMap.isCompleted(parent.getId()))) { // if (!waitingQueue.contains(parent.getId()) && relMap.getGeneration(indi.id) < options.getChoiceGens()){ // waitingQueue.add(parent.getId()); // System.out.println("Adding: " +parent.getId() + " as parent"); // } // // } //else if (relMap.getGeneration(indi.id) >= MAX_GENS && !afterNine) //afterNineQueue.add(parent.getId()); } } return indi; } /** * this goes through and for every person, figures out where they are in the tree. * it also determines the total amount of people in the generation line. all this information * is then stored in the individual. */ public void findPlaceInTree() { int youngest = relMap.getYoungestGeneration(); int oldest = relMap.getOldestGeneration(); int total = oldest - youngest + 1; ArrayList<String> allIDs = relMap.getAllDone(); for (int i = 0; i < allIDs.size(); i++) { String indiID = allIDs.get(i); Individual indi = gedRecord.getIndividual(indiID); indi.totalPathLength = total; indi.numberOfAncestors = oldest - relMap.getGeneration(indiID); indi.numberOfDescendants = relMap.getGeneration(indiID) - youngest; // System.out.println(i + " Indi: " + indi.id + " ances: " + indi.numberOfAncestors + // "\ndescen: " + indi.numberOfDescendants + " total: " + indi.totalPathLength); } } /** * This would be only called from a drop down menu. it goes through the whole tree and * checks for changes. If there are any, then the people's information is then asked for again, * and updated. Also checks the leaf people to see if anyone else has been added to the list since * the last time it was updated. * The user can also decide if they have yet to add their family members beyond 9 gens if * they want to add them now. it is not done immediately, however. */ public void update(int c, ArrayList<String> updateQueue) { //TODO implement } /** * A recursive method that grabs the version numbers of everyone in the pedigree */ public ArrayList<String> getVersionNumber(Individual indi, ArrayList<String> versionNumbers) { if (indi != null) { if (indi.version != "") versionNumbers.add(indi.id); versionNumbers = getVersionNumber(indi.father, versionNumbers); versionNumbers = getVersionNumber(indi.mother, versionNumbers); } return versionNumbers; } /** * called by update(), this method asks new.familysearch.org for all the versions of the people in our tree. * if they are different than the ones stored, then the person keep in a list that is passed back to update() */ public ArrayList<String> getVersion(String ID) { ArrayList<String> toUpdate = new ArrayList<String>(); String requestUrl = baseURL+"/familytree/v2/person/"; requestUrl += ID; //requests none of the information about the person (just want the version, which won't take as long) requestUrl += "?names=none&genders=none&events=none"; try { String responseBody = httpclient.execute(new HttpGet(requestUrl), responseHandler); FamilyTree versions = (FamilyTree) JAXBContext.newInstance(FamilyTree.class).createUnmarshaller().unmarshal(new StringReader(responseBody)); String[] IDS = ID.split(","); int count = 0; for (Person person : versions.getPersons()) if (!gedRecord.getIndividual(IDS[count]).version.equals(person.getVersion())) { toUpdate.add(IDS[count]); count++; } } catch (HttpResponseException e) { System.out.println("ID: " + ID); System.out.println("Getting into HttpResponseException."); if (e.getStatusCode() == 503) toUpdate.add("503 error"); } catch (ClientProtocolException e) { System.out.println("ID: " + ID); System.out.println("Getting into ClientProtocolException"); } catch (IOException e) { System.out.println("Getting into IOException"); } catch (JAXBException e) { System.out.println("Getting into JAXBException"); } return toUpdate; } /** * when the program gets throttled, this method takes care of pausing for the * correct amount of time (if it gets throttled multiple times on the same spot, * then the program waits longer (up to 31 seconds) each time until it is finally * allowed to return to business. */ synchronized void throttled(int count, ArrayList<String> queue, int method) { int waitTime[] = {5, 10, 20, 31}; try { System.out.println("Program Throttled on " + method + ". waiting " + waitTime[throttledAmt] + " seconds"); wait(waitTime[throttledAmt] * 1000); } catch (InterruptedException e) { System.out.println("Getting into InterruptedException"); } //this checks to see if it is getting throttled at the same spot or further along in the retrieval if (throttledAmt < (waitTime.length - 1) && firstOfQueue.equals(queue.get(0))) throttledAmt++; firstOfQueue = queue.get(0); switch (method) { case (UPDATE): update(count, queue); break; case (GETINFO): getInformation(queue); break; } } private void activateDownloadBar(){ Dimension screenSize; try { Toolkit tk = Toolkit.getDefaultToolkit(); screenSize = tk.getScreenSize(); } catch (AWTError awe) { screenSize = new Dimension(640, 480); } DownloadProgress p = gui.getProgressBar(); //Dialog dlgPreOrder = new PreOrderDialog(session, getJFrame()); int frameX = screenSize.width / 2 - p.getWidth() / 2; int frameY = screenSize.height / 2 - p.getHeight() / 2; p.setBounds(frameX, frameY, p.getWidth(), p.getHeight()); p.setVisible(true); p.startTimer(); } private void activateOptionDialog(){ Dimension screenSize; try { Toolkit tk = Toolkit.getDefaultToolkit(); screenSize = tk.getScreenSize(); } catch (AWTError awe) { screenSize = new Dimension(640, 480); } //Dialog dlgPreOrder = new PreOrderDialog(session, getJFrame()); int frameX = screenSize.width / 2 - options.getWidth() / 2; int frameY = screenSize.height / 2 - options.getHeight() / 2; options.setBounds(frameX, frameY, options.getWidth(), options.getHeight()); options.setVisible(true); } public void setCancel(boolean set){ cancelDownload = set; } public synchronized void updatePublished(String st){ publish(st); } public synchronized Integer getFamCount(){ return famCount; } public synchronized void incFamCount(){ famCount++; } public synchronized void decFamCount(){ famCount--; } }