1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package eu.ehri.project.exporters.eag;
21
22 import com.google.common.base.Splitter;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.ImmutableMap;
25 import com.google.common.collect.Lists;
26 import com.google.common.collect.Sets;
27 import eu.ehri.project.api.Api;
28 import eu.ehri.project.definitions.ContactInfo;
29 import eu.ehri.project.definitions.Isdiah;
30 import eu.ehri.project.exporters.xml.AbstractStreamingXmlExporter;
31 import eu.ehri.project.models.Address;
32 import eu.ehri.project.models.Country;
33 import eu.ehri.project.models.MaintenanceEvent;
34 import eu.ehri.project.models.MaintenanceEventAgentType;
35 import eu.ehri.project.models.MaintenanceEventType;
36 import eu.ehri.project.models.Repository;
37 import eu.ehri.project.models.RepositoryDescription;
38 import eu.ehri.project.models.base.Actioner;
39 import eu.ehri.project.models.base.Described;
40 import eu.ehri.project.models.base.Description;
41 import eu.ehri.project.models.events.SystemEvent;
42 import eu.ehri.project.utils.LanguageHelpers;
43 import org.joda.time.DateTime;
44 import org.joda.time.format.DateTimeFormat;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 import javax.xml.stream.XMLStreamWriter;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Optional;
52 import java.util.Set;
53 import java.util.stream.Collectors;
54
55
56
57
58 public final class Eag2012Exporter extends AbstractStreamingXmlExporter<Repository> implements EagExporter {
59 private static final Logger logger = LoggerFactory.getLogger(Eag2012Exporter.class);
60 private static final String DEFAULT_NAMESPACE = "http://www.archivesportaleurope.net/Portal/profiles/eag_2012/";
61
62 private final Api api;
63
64 private static final Map<Isdiah, String> descriptiveTextMappings = ImmutableMap.<Isdiah, String>builder()
65 .put(Isdiah.buildings, "buildinginfo/building")
66 .put(Isdiah.holdings, "holdings")
67 .put(Isdiah.conditions, "termsOfUse")
68 .put(Isdiah.researchServices, "services/searchroom/researchServices")
69 .build();
70
71 private static final List<Isdiah> historyElements = ImmutableList.of(
72 Isdiah.history, Isdiah.geoculturalContext, Isdiah.mandates);
73
74 private static final Map<String, String> NAMESPACES = namespaces(
75 "xlink", "http://www.w3.org/1999/xlink",
76 "ape", "http://www.archivesportaleurope.eu/functions",
77 "xsi", "http://www.w3.org/2001/XMLSchema-instance");
78
79 private static final Map<String, String> ATTRS = attrs(
80 "audience", "external"
81 );
82
83 public Eag2012Exporter(final Api api) {
84 this.api = api;
85 }
86
87 @Override
88 public void export(XMLStreamWriter sw, Repository repository, String langCode) {
89
90 comment(sw, resourceAsString("export-boilerplate.txt"));
91
92 Country country = repository.getCountry();
93
94 root(sw, "eag", DEFAULT_NAMESPACE, ATTRS, NAMESPACES, () -> {
95
96 attribute(sw, "http://www.w3.org/2001/XMLSchema-instance", "schemaLocation",
97 DEFAULT_NAMESPACE + " http://schemas.archivesportaleurope.net/profiles/eag.xsd");
98
99 LanguageHelpers.getBestDescription(repository, Optional.empty(), langCode).ifPresent(desc -> {
100
101 addControlSection(sw, repository, country, desc);
102
103 tag(sw, "archguide", () -> {
104 addIdentitySection(sw, repository, desc);
105
106 tag(sw, ImmutableList.of("desc", "repositories", "repository"), () -> {
107 tag(sw, "geogarea", LanguageHelpers.countryCodeToContinent(country.getCode()).orElse("Europe"));
108
109 List<Address> addresses = Lists.newArrayList(desc.as(RepositoryDescription.class).getAddresses());
110 if (addresses.isEmpty()) {
111
112 tag(sw, "location", attrs("localType", "postal address"), () -> {
113 tag(sw, "country", LanguageHelpers.countryCodeToName(country.getCode()));
114 tag(sw, "municipalityPostalcode", (String) null);
115 tag(sw, "street", (String) null);
116 });
117 } else {
118 for (Address address : addresses) {
119 tag(sw, "location", attrs("localType", "postal address"), () -> {
120 String cc = Optional.ofNullable(((String) address.getProperty(ContactInfo.countryCode)))
121 .orElse(country.getCode());
122 tag(sw, "country", LanguageHelpers.countryCodeToName(cc));
123 tag(sw, "municipalityPostalcode", address.<String>getProperty(ContactInfo.postalCode));
124 tag(sw, "street", address.<String>getProperty(ContactInfo.street));
125 });
126 }
127 }
128
129 for (ContactInfo contact : new ContactInfo[]{ContactInfo.telephone, ContactInfo.fax}) {
130 for (Address address : (desc.as(RepositoryDescription.class)).getAddresses()) {
131 for (Object value : coerceList(address.getProperty(contact))) {
132 tag(sw, contact.name(), value.toString());
133 }
134 }
135 }
136 for (ContactInfo ref : new ContactInfo[]{ContactInfo.email, ContactInfo.webpage}) {
137 for (Address address : (desc.as(RepositoryDescription.class)).getAddresses()) {
138 for (Object value : coerceList(address.getProperty(ref))) {
139 tag(sw, ref.name(), value.toString(), attrs("href", value.toString()));
140 }
141 }
142 }
143
144 List<String> elems = historyElements
145 .stream().<String>map(desc::getProperty)
146 .filter(v -> v != null).collect(Collectors.toList());
147 if (!elems.isEmpty()) {
148 tag(sw, ImmutableList.of("repositorhist", "descriptiveNote"), () -> {
149 for (String e : elems) {
150 tag(sw, "p",
151 attrs("xml:lang", desc.getLanguageOfDescription()), () -> cData(sw, e));
152 }
153 });
154 }
155
156 addTextElements(sw, desc, Isdiah.buildings, Isdiah.holdings);
157
158 tag(sw, ImmutableList.of("timetable", "opening"), desc.<String>getProperty(Isdiah.openingTimes));
159
160 tag(sw, "access", attrs("question", "yes"), () ->
161 Optional.ofNullable(desc.<String>getProperty(Isdiah.conditions)).ifPresent(terms ->
162 tag(sw, "termsOfUse", terms)
163 )
164 );
165
166 tag(sw, "accessibility", desc.getProperty(Isdiah.accessibility), attrs("question", "yes"));
167 addTextElements(sw, desc, Isdiah.researchServices);
168 });
169
170 });
171 });
172 });
173 }
174
175 private void addControlSection(XMLStreamWriter sw, Repository repository, Country country, Description desc) {
176 tag(sw, "control", () -> {
177 tag(sw, "recordId", String.format("%s-%s", country.getCode().toUpperCase(),
178 repository.getIdentifier()));
179
180 tag(sw, "otherRecordId", repository.getId(), attrs("localType", "yes"));
181
182 tag(sw, "maintenanceAgency", () -> {
183 tag(sw, "agencyCode", "EHRI");
184 tag(sw, "agencyName", "The EHRI Consortium");
185 });
186 tag(sw, "maintenanceStatus", "revised");
187
188 addRevisionDesc(sw, repository, desc);
189 });
190 }
191
192 private void addIdentitySection(XMLStreamWriter sw, Repository repository, Description desc) {
193
194 tag(sw, "identity", () -> {
195 tag(sw, "repositorid", null,
196 attrs("countrycode", repository.getCountry().getCode().toUpperCase()));
197 tag(sw, "autform", desc.getName(), attrs("xml:lang", desc.getLanguageOfDescription()));
198
199 Optional.ofNullable(desc.getProperty(Isdiah.parallelFormsOfName)).ifPresent(parNames -> {
200 List values = parNames instanceof List ? (List) parNames : ImmutableList.of(parNames);
201 for (Object value : values) {
202 tag(sw, "parform", value.toString());
203 }
204 });
205 Optional.ofNullable(desc.getProperty(Isdiah.otherFormsOfName)).ifPresent(parNames -> {
206 List values = parNames instanceof List ? (List) parNames : ImmutableList.of(parNames);
207 for (Object value : values) {
208 tag(sw, "parform", value.toString());
209 }
210 });
211 });
212 }
213
214 private void addRevisionDesc(XMLStreamWriter sw, Described entity, Description desc) {
215 tag(sw, "maintenanceHistory", () -> {
216
217 List<MaintenanceEvent> maintenanceEvents = ImmutableList.copyOf(desc.getMaintenanceEvents());
218 for (MaintenanceEvent event : maintenanceEvents) {
219 tag(sw, "maintenanceEvent", () -> {
220 tag(sw, "agent", "EHRI");
221 tag(sw, "agentType", MaintenanceEventAgentType.human.name());
222 tag(sw, "eventDateTime", event.<String>getProperty("date"));
223 tag(sw, "eventType", event.getEventType().name());
224 });
225 }
226
227 List<List<SystemEvent>> systemEvents = ImmutableList.copyOf(api.events().aggregateForItem(entity));
228 for (List<SystemEvent> agg : Lists.reverse(systemEvents)) {
229 SystemEvent event = agg.get(0);
230
231 tag(sw, "maintenanceEvent", () -> {
232 tag(sw, "agent", Optional.ofNullable(event.getActioner())
233 .map(Actioner::getName).orElse(null));
234 tag(sw, "agentType", MaintenanceEventAgentType.human.name());
235 DateTime dateTime = new DateTime(event.getTimestamp());
236 tag(sw, "eventDateTime", DateTimeFormat.longDateTime().print(dateTime), attrs(
237 "standardDateTime", dateTime.toString()));
238 tag(sw, "eventType", MaintenanceEventType
239 .fromSystemEventType(event.getEventType()).name());
240 });
241 }
242
243
244 if (maintenanceEvents.isEmpty() && systemEvents.isEmpty()) {
245 logger.debug("No events found for element {}, using fallback", entity.getId());
246
247 tag(sw, "maintenanceEvent", () -> {
248 tag(sw, "agent", entity.getId());
249 tag(sw, "agentType", MaintenanceEventAgentType.machine.name());
250 DateTime dateTime = DateTime.now();
251 tag(sw, "eventDateTime", DateTimeFormat.longDateTime().print(dateTime), attrs(
252 "standardDateTime", dateTime.toString()));
253 tag(sw, "eventType", MaintenanceEventType.created.name());
254 });
255 }
256 });
257 }
258
259 private void addTextElements(XMLStreamWriter sw, Description desc, Isdiah... toAdd) {
260 Set<Isdiah> adding = Sets.newHashSet(toAdd);
261 final Map<String, String> paraAttrs = attrs("xml:lang", desc.getLanguageOfDescription());
262 for (Map.Entry<Isdiah, String> entry : descriptiveTextMappings.entrySet()) {
263 if (adding.contains(entry.getKey())) {
264 Optional.ofNullable(desc.<String>getProperty(entry.getKey())).ifPresent(prop -> {
265 List<String> tags = Splitter.on("/").splitToList(entry.getValue());
266 tag(sw, tags, () ->
267 tag(sw, ImmutableList.of("descriptiveNote", "p"), paraAttrs, () -> cData(sw, prop)));
268 });
269 }
270 }
271 }
272 }