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
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
76 for (AccessPoint rel : Sets.newHashSet(desc.getAccessPoints())) {
77
78
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 }