/**
* Copyright Intellectual Reserve, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.familysearch.platform;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.familysearch.platform.artifacts.ArtifactMetadata;
import org.familysearch.platform.ct.*;
import org.familysearch.platform.discussions.Discussion;
import org.familysearch.platform.ordinances.Ordinance;
import org.familysearch.platform.places.FeedbackInfo;
import org.familysearch.platform.reservations.Reservation;
import org.familysearch.platform.rt.FamilySearchPlatformModelVisitor;
import org.familysearch.platform.users.User;
import org.gedcomx.Gedcomx;
import org.gedcomx.common.ResourceReference;
import org.gedcomx.conclusion.Identifier;
import org.gedcomx.conclusion.Person;
import org.gedcomx.conclusion.Relationship;
import org.gedcomx.rt.*;
import org.gedcomx.rt.json.JsonElementWrapper;
import org.gedcomx.source.SourceDescription;
import org.gedcomx.types.IdentifierType;
import org.gedcomx.types.RelationshipType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
import java.util.*;
/**
* <p>The FamilySearch data types define serialization formats that are specific to the FamilySearch developer platform. These
* data formats are extensions of the <a href="http://gedcomx.org">GEDCOM X</a> media types and provide concepts and data types
* that are specific to FamilySearch and therefore haven't been adopted into a formal, more general specification.</p>
*
* @author Ryan Heaton
*/
@MediaTypeDefinition (
name = "FamilySearch",
description = "The FamilySearch data formats define serialization formats that are specific the FamilySearch developer API.",
version = "1.0",
xmlMediaType = FamilySearchPlatform.XML_MEDIA_TYPE,
jsonMediaType = FamilySearchPlatform.JSON_MEDIA_TYPE,
models = {
@Model (
id = "fs",
namespace = FamilySearchPlatform.NAMESPACE,
label = "FamilySearch Model",
description = "The FamilySearch model defines data types and elements that are specific to FamilySearch."
)
}
)
@XmlRootElement ( name = "familysearch" )
@JsonElementWrapper ( name = "familysearch" )
@XmlType ( name = "FamilySearch", propOrder = {"childAndParentsRelationships", "discussions", "users", "merges", "mergeAnalyses", "features"} )
@DefaultNamespace ( GedcomxConstants.GEDCOMX_NAMESPACE )
@XmlSeeAlso ( {DiscussionReference.class, Tag.class, ChangeInfo.class, MatchInfo.class, FeedbackInfo.class, SearchInfo.class, org.familysearch.platform.Error.class, ArtifactMetadata.class, Reservation.class, Ordinance.class, NameFormInfo.class} )
@JsonInclude ( JsonInclude.Include.NON_NULL )
public class FamilySearchPlatform extends Gedcomx {
public static final String PROJECT_ID = "fs-platform";
public static final String NAMESPACE = "http://familysearch.org/v1/";
public static final String XML_MEDIA_TYPE = "application/x-fs-v1+xml";
public static final String JSON_MEDIA_TYPE = "application/x-fs-v1+json";
private List<MergeAnalysis> mergeAnalyses;
private List<Merge> merges;
private List<ChildAndParentsRelationship> childAndParentsRelationships;
private List<Discussion> discussions;
private List<User> users;
private List<Feature> features;
/**
* The merge analysis results for this data set.
*
* @return The merge analysis results for this data set.
*/
@XmlElement ( name = "mergeAnalysis" )
@JsonProperty ( "mergeAnalyses" ) @org.codehaus.jackson.annotate.JsonProperty ( "mergeAnalyses" )
public List<MergeAnalysis> getMergeAnalyses() {
return mergeAnalyses;
}
/**
* The merge analysis results for this data set.
*
* @param mergeAnalyses The merge analysis results for this data set.
*/
@JsonProperty ( "mergeAnalyses" ) @org.codehaus.jackson.annotate.JsonProperty ( "mergeAnalyses" )
public void setMergeAnalyses(List<MergeAnalysis> mergeAnalyses) {
this.mergeAnalyses = mergeAnalyses;
}
/**
* Add a MergeAnalysis to the data set.
*
* @param mergeAnalysis The MergeAnalysis to be added.
*/
public void addMergeAnalysis(MergeAnalysis mergeAnalysis) {
if (mergeAnalysis != null) {
if (mergeAnalyses == null) {
mergeAnalyses = new LinkedList<MergeAnalysis>();
}
mergeAnalyses.add(mergeAnalysis);
}
}
/**
* The merges for this data set.
*
* @return The merges for this data set.
*/
@XmlElement ( name = "merge" )
@JsonProperty ( "merges" ) @org.codehaus.jackson.annotate.JsonProperty ( "merges" )
public List<Merge> getMerges() {
return merges;
}
/**
* The merges for this data set.
*
* @param merges The merges for this data set.
*/
@JsonProperty ( "merges" ) @org.codehaus.jackson.annotate.JsonProperty ( "merges" )
public void setMerges(List<Merge> merges) {
this.merges = merges;
}
/**
* Add a merge to the data set.
*
* @param merge The merge to be added.
*/
public void addMerge(Merge merge) {
if (merge != null) {
if (merges == null) {
merges = new LinkedList<Merge>();
}
merges.add(merge);
}
}
/**
* The child-and-parents relationships for this data set.
*
* @return The child-and-parents relationships for this data set.
*/
@XmlElement ( name = "childAndParentsRelationship" )
@JsonProperty ( "childAndParentsRelationships" ) @org.codehaus.jackson.annotate.JsonProperty ( "childAndParentsRelationships" )
public List<ChildAndParentsRelationship> getChildAndParentsRelationships() {
return childAndParentsRelationships;
}
/**
* The child-and-parents relationships for this data set.
*
* @param childAndParentsRelationships The child-and-parents relationships for this data set.
*/
@JsonProperty ( "childAndParentsRelationships" ) @org.codehaus.jackson.annotate.JsonProperty ( "childAndParentsRelationships" )
public void setChildAndParentsRelationships(List<ChildAndParentsRelationship> childAndParentsRelationships) {
this.childAndParentsRelationships = childAndParentsRelationships;
}
/**
* Add a childAndParentsRelationship to the data set.
*
* @param childAndParentsRelationship The childAndParentsRelationship to be added.
*/
public void addChildAndParentsRelationship(ChildAndParentsRelationship childAndParentsRelationship) {
if (childAndParentsRelationship != null) {
if (childAndParentsRelationships == null) {
childAndParentsRelationships = new LinkedList<ChildAndParentsRelationship>();
}
childAndParentsRelationships.add(childAndParentsRelationship);
}
}
public ChildAndParentsRelationship findChildAndParentsRelationship(ResourceReference child, ResourceReference parent1, ResourceReference parent2) {
if (child != null && getRelationships() != null && (parent1 != null || parent2 != null)) {
for (ChildAndParentsRelationship relationship : getChildAndParentsRelationships()) {
if (samePerson(relationship.getChild(), child) &&
samePerson(relationship.getFather(), parent1) &&
samePerson(relationship.getMother(), parent2)) {
return relationship;
}
}
}
return null;
}
/**
* Build out this document with a child-and-parents relationship.
*
* @param chap The child-and-parents relationship
* @return this.
*/
public FamilySearchPlatform childAndParentsRelationship(ChildAndParentsRelationship chap) {
addChildAndParentsRelationship(chap);
return this;
}
/**
* The discussions included in this data set.
*
* @return The discussions included in this data set.
*/
@XmlElement ( name = "discussion" )
@JsonProperty ( "discussions" ) @org.codehaus.jackson.annotate.JsonProperty ( "discussions" )
public List<Discussion> getDiscussions() {
return discussions;
}
/**
* The discussions included in this data set.
*
* @param discussions The discussions included in this data set.
*/
@JsonProperty ( "discussions" ) @org.codehaus.jackson.annotate.JsonProperty ( "discussions" )
public void setDiscussions(List<Discussion> discussions) {
this.discussions = discussions;
}
/**
* Add a discussion to the data set.
*
* @param discussion The discussion to be added.
*/
public void addDiscussion(Discussion discussion) {
if (discussion != null) {
if (discussions == null) {
discussions = new LinkedList<Discussion>();
}
discussions.add(discussion);
}
}
/**
* Build out this document with a discussion.
*
* @param discussion The discussion to be added.
* @return this.
*/
public FamilySearchPlatform discussion(Discussion discussion) {
addDiscussion(discussion);
return this;
}
/**
* The users included in this data set.
*
* @return The users included in this genealogical data set.
*/
@XmlElement ( name = "user" )
@JsonProperty ( "users" ) @org.codehaus.jackson.annotate.JsonProperty ( "users" )
public List<User> getUsers() {
return users;
}
/**
* The users included in this data set.
*
* @param users The users included in this data set.
*/
@JsonProperty ( "users" ) @org.codehaus.jackson.annotate.JsonProperty ( "users" )
public void setUsers(List<User> users) {
this.users = users;
}
/**
* Build out this document with a user.
* @param user The user to be added.
* @return this.
*/
public FamilySearchPlatform user(User user) {
addUser(user);
return this;
}
/**
* The set of features defined in the platform API.
*
* @return The set of features defined in the platform API.
*/
@XmlElement ( name = "feature" )
@JsonProperty ( "features" ) @org.codehaus.jackson.annotate.JsonProperty ( "features" )
public List<Feature> getFeatures() {
return features;
}
/**
* The set of features defined in the platform API.
*
* @param features The set of features defined in the platform API.
*/
@JsonProperty ( "features" ) @org.codehaus.jackson.annotate.JsonProperty ( "features" )
public void setFeatures(List<Feature> features) {
this.features = features;
}
/**
* Add a user to the data set.
*
* @param user The user to be added.
*/
public void addUser(User user) {
if (user != null) {
if (users == null) {
users = new LinkedList<User>();
}
users.add(user);
}
}
/**
* Accept a visitor.
*
* @param visitor The visitor.
*/
public void accept(FamilySearchPlatformModelVisitor visitor) {
visitor.visitFamilySearchPlatform(this);
}
@Override
public void accept(GedcomxModelVisitor visitor) {
if (visitor instanceof FamilySearchPlatformModelVisitor) {
((FamilySearchPlatformModelVisitor) visitor).visitFamilySearchPlatform(this);
}
else {
super.accept(visitor);
}
}
@Override
public void embed(Gedcomx gedcomx) {
super.embed(gedcomx);
if (gedcomx instanceof FamilySearchPlatform) {
List<ChildAndParentsRelationship> relationships = ((FamilySearchPlatform) gedcomx).getChildAndParentsRelationships();
if (relationships != null) {
for (ChildAndParentsRelationship relationship : relationships) {
boolean found = false;
if (relationship.getId() != null) {
if (getChildAndParentsRelationships() != null) {
for (ChildAndParentsRelationship target : getChildAndParentsRelationships()) {
if (relationship.getId().equals(target.getId())) {
target.embed(relationship);
found = true;
break;
}
}
}
}
if (!found) {
addChildAndParentsRelationship(relationship);
}
}
}
List<Discussion> discussions = ((FamilySearchPlatform) gedcomx).getDiscussions();
if (discussions != null) {
for (Discussion discussion : discussions) {
boolean found = false;
if (discussion.getId() != null) {
if (getDiscussions() != null) {
for (Discussion target : getDiscussions()) {
if (discussion.getId().equals(target.getId())) {
target.embed(discussion);
found = true;
break;
}
}
}
}
if (!found) {
addDiscussion(discussion);
}
}
}
}
}
public FamilySearchPlatform addParentChildRelationshipForEachChildAndParentsRelationship() {
//for each child-and-parents relationship, add a parent-child relationship.
List<ChildAndParentsRelationship> parentRelationships = getChildAndParentsRelationships();
if (parentRelationships != null) {
for (ChildAndParentsRelationship childAndParentsRelationship : parentRelationships) {
String relationshipId = childAndParentsRelationship.getId();
if (relationshipId == null) {
continue;
}
String childId = childAndParentsRelationship.getChild() != null ? childAndParentsRelationship.getChild().getResourceId() : null;
if (childId == null) {
continue;
}
String fatherId = childAndParentsRelationship.getFather() != null ? childAndParentsRelationship.getFather().getResourceId() : null;
String motherId = childAndParentsRelationship.getMother() != null ? childAndParentsRelationship.getMother().getResourceId() : null;
Identifier primaryIdentifier = null;
if (childAndParentsRelationship.getIdentifiers() != null) {
for (Identifier identifier : childAndParentsRelationship.getIdentifiers()) {
if (identifier.getKnownType() == IdentifierType.Primary) {
primaryIdentifier = identifier;
break;
}
}
}
if (fatherId != null) {
Relationship fatherChildRelationship = new Relationship();
fatherChildRelationship.setId("F" + relationshipId);
fatherChildRelationship.setKnownType(RelationshipType.ParentChild);
fatherChildRelationship.setPerson1(childAndParentsRelationship.getFather());
fatherChildRelationship.setPerson2(childAndParentsRelationship.getChild());
if (primaryIdentifier != null) {
fatherChildRelationship.setIdentifiers(new ArrayList<>(1));
fatherChildRelationship.getIdentifiers().add(new Identifier());
fatherChildRelationship.getIdentifiers().get(0).setType(FamilySearchIdentifierType.ChildAndParentsRelationship.toQNameURI(), true);
fatherChildRelationship.getIdentifiers().get(0).setValue(primaryIdentifier.getValue());
}
for (Map.Entry<String, Object> transientProperty : childAndParentsRelationship.getTransientProperties().entrySet()) {
fatherChildRelationship.setTransientProperty(transientProperty.getKey(), transientProperty.getValue());
}
fatherChildRelationship.setSortKey(childAndParentsRelationship.getSortKey());
addRelationship(fatherChildRelationship);
}
if (motherId != null) {
Relationship motherChildRelationship = new Relationship();
motherChildRelationship.setId("M" + relationshipId);
motherChildRelationship.setKnownType(RelationshipType.ParentChild);
motherChildRelationship.setPerson1(childAndParentsRelationship.getMother());
motherChildRelationship.setPerson2(childAndParentsRelationship.getChild());
if (primaryIdentifier != null) {
motherChildRelationship.setIdentifiers(new ArrayList<>(1));
motherChildRelationship.getIdentifiers().add(new Identifier());
motherChildRelationship.getIdentifiers().get(0).setType(FamilySearchIdentifierType.ChildAndParentsRelationship.toQNameURI(), true);
motherChildRelationship.getIdentifiers().get(0).setValue(primaryIdentifier.getValue());
}
for (Map.Entry<String, Object> transientProperty : childAndParentsRelationship.getTransientProperties().entrySet()) {
motherChildRelationship.setTransientProperty(transientProperty.getKey(), transientProperty.getValue());
}
motherChildRelationship.setSortKey(childAndParentsRelationship.getSortKey());
addRelationship(motherChildRelationship);
}
}
}
return this;
}
@Override
public FamilySearchPlatform fixLocalReferences() {
List<Person> locals = getPersons() == null ? Collections.emptyList() : getPersons();
List<ChildAndParentsRelationship> childAndParentsRelationships = getChildAndParentsRelationships() != null ? getChildAndParentsRelationships() : Collections.emptyList();
List<Ordinance> ordinances = getOrdinances(this);
List<SourceDescription> sds = getSourceDescriptions() == null ? Collections.emptyList() : getSourceDescriptions();
for (Person local : locals) {
String localId = local.getId();
if (localId != null) {
for (ChildAndParentsRelationship relationship : childAndParentsRelationships) {
fixId(relationship.getFather(), localId);
fixId(relationship.getMother(), localId);
fixId(relationship.getChild(), localId);
fixupSourceReferences(sds, relationship);
}
fixupPersonReferencesInOrdinances(ordinances, localId);
}
}
return (FamilySearchPlatform) super.fixLocalReferences();
}
protected static List<Ordinance> getOrdinances(Gedcomx gx) {
List<Ordinance> rtn = new ArrayList<>();
if (gx.getPersons() != null) {
for (Person person : gx.getPersons()) {
if (person.getExtensionElements() != null) {
for (Object extension : person.getExtensionElements()) {
if (extension instanceof List) {
for (Object item : (List) extension) {
if (item instanceof Ordinance) {
rtn.add((Ordinance) item);
}
}
}
}
}
}
}
return rtn;
}
protected static void fixupPersonReferencesInOrdinances(List<Ordinance> ordinances, String personId) {
for (Ordinance ordinance : ordinances) {
fixId(ordinance.getSpouse(), personId);
fixId(ordinance.getFather(), personId);
fixId(ordinance.getMother(), personId);
}
}
}