View Javadoc

1   package eu.ehri.project.importers.links;
2   
3   import com.google.common.cache.CacheBuilder;
4   import com.google.common.cache.CacheLoader;
5   import com.google.common.cache.LoadingCache;
6   import com.google.common.collect.Iterables;
7   import com.google.common.collect.Sets;
8   import com.tinkerpop.frames.FramedGraph;
9   import com.typesafe.config.Config;
10  import com.typesafe.config.ConfigFactory;
11  import eu.ehri.project.api.Api;
12  import eu.ehri.project.api.ApiFactory;
13  import eu.ehri.project.core.GraphManager;
14  import eu.ehri.project.core.GraphManagerFactory;
15  import eu.ehri.project.definitions.Ontology;
16  import eu.ehri.project.exceptions.DeserializationError;
17  import eu.ehri.project.exceptions.PermissionDenied;
18  import eu.ehri.project.exceptions.SerializationError;
19  import eu.ehri.project.exceptions.ValidationError;
20  import eu.ehri.project.importers.util.ImportHelpers;
21  import eu.ehri.project.models.AccessPoint;
22  import eu.ehri.project.models.EntityClass;
23  import eu.ehri.project.models.Link;
24  import eu.ehri.project.models.base.Accessor;
25  import eu.ehri.project.models.base.Described;
26  import eu.ehri.project.models.base.Description;
27  import eu.ehri.project.models.base.Linkable;
28  import eu.ehri.project.models.cvoc.AuthoritativeItem;
29  import eu.ehri.project.models.cvoc.AuthoritativeSet;
30  import eu.ehri.project.persistence.Bundle;
31  import eu.ehri.project.persistence.Serializer;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  import java.util.Objects;
36  import java.util.Optional;
37  import java.util.Set;
38  import java.util.concurrent.ExecutionException;
39  
40  public class LinkResolver {
41  
42      private final GraphManager manager;
43      private final Api api;
44      private final Serializer mergeSerializer;
45  
46      private static final Logger logger = LoggerFactory.getLogger(LinkResolver.class);
47      private static final Config config = ConfigFactory.load();
48  
49      private final Bundle linkTemplate = Bundle.of(EntityClass.LINK)
50              // TODO: allow overriding link type and text here
51              .withDataValue(Ontology.LINK_HAS_DESCRIPTION, config.getString("io.import.defaultLinkText"))
52              .withDataValue(Ontology.LINK_HAS_TYPE, config.getString("io.import.defaultLinkType"));
53  
54  
55      private final LoadingCache<String, AuthoritativeSet> setCache = CacheBuilder.newBuilder()
56              .build(new CacheLoader<String, AuthoritativeSet>() {
57                  @Override
58                  public AuthoritativeSet load(String key) throws Exception {
59                      return manager.getEntity(key, AuthoritativeSet.class);
60                  }
61              });
62  
63      public LinkResolver(FramedGraph<?> graph, Accessor accessor) {
64          api = ApiFactory.noLogging(graph, accessor);
65          manager = GraphManagerFactory.getInstance(graph);
66          mergeSerializer = new Serializer.Builder(graph).dependentOnly().build();
67      }
68  
69  
70      public int solveUndeterminedRelationships(Described unit) throws ValidationError {
71          logger.debug("Resolving relationships for {}", unit.getId());
72          int created = 0;
73  
74          for (Description desc : unit.getDescriptions()) {
75              // Put the set of relationships into a HashSet to remove duplicates.
76              for (AccessPoint rel : Sets.newHashSet(desc.getAccessPoints())) {
77                  // the wp2 undetermined relationship that can be resolved have a 'cvoc' and a 'concept' attribute.
78                  // they need to be found in the vocabularies that are in the graph
79                  Set<String> relDataKeys = rel.getPropertyKeys();
80                  if (relDataKeys.contains("cvoc")
81                          && (relDataKeys.contains("concept") || relDataKeys.contains("target"))) {
82                      String setId = rel.getProperty("cvoc");
83                      String targetId = Optional
84                              .ofNullable(rel.<String>getProperty("concept"))
85                              .orElseGet(() -> rel.getProperty("target"));
86  
87                      logger.debug(" - found link references: cvoc: {}, concept: {}", setId, targetId);
88                      try {
89                          AuthoritativeSet set = setCache.get(setId);
90                          Optional<AuthoritativeItem> targetOpt = findTarget(set, targetId);
91                          if (targetOpt.isPresent()) {
92                              AuthoritativeItem target = targetOpt.get();
93                              try {
94                                  Optional<Link> linkOpt = findLink(unit, target, rel, linkTemplate);
95                                  if (linkOpt.isPresent()) {
96                                      logger.debug(" - found existing link created between {} and {}", targetId, target.getId());
97                                  } else {
98                                      Link link = api.create(linkTemplate, Link.class);
99                                      unit.addLink(link);
100                                     target.addLink(link);
101                                     link.addLinkBody(rel);
102                                     logger.debug(" - new link created between {} and {}", targetId, target.getId());
103                                     created++;
104                                 }
105                             } catch (PermissionDenied | DeserializationError | SerializationError ex) {
106                                 logger.error("Unexpected error resolving link for " + setId + "/" + targetId, ex);
107                             }
108                         } else {
109                             logger.warn(" - unable to find link target with id: {}", targetId);
110                         }
111                     } catch (ExecutionException ex) {
112                         logger.warn(" - unable to find link set with id: {}", setId);
113                     }
114                 }
115             }
116         }
117         return created;
118     }
119 
120 
121     private Optional<AuthoritativeItem> findTarget(AuthoritativeSet set, String itemId) {
122         for (AuthoritativeItem item : set.getAuthoritativeItems()) {
123             if (Objects.equals(item.getIdentifier(), itemId)) {
124                 return Optional.of(item);
125             }
126         }
127         return Optional.empty();
128     }
129 
130     private Optional<Link> findLink(Described unit, Linkable target, AccessPoint body, Bundle data)
131             throws SerializationError {
132         for (Link link : unit.getLinks()) {
133             for (Linkable connected : link.getLinkTargets()) {
134                 if (target.equals(connected)
135                         && Iterables.contains(link.getLinkBodies(), body)
136                         && mergeSerializer.entityToBundle(link).equals(data)) {
137                     return Optional.of(link);
138                 }
139             }
140         }
141         return Optional.empty();
142     }
143 }