|
25 | 25 | import java.util.UUID; |
26 | 26 | import java.util.regex.Matcher; |
27 | 27 | import java.util.regex.Pattern; |
| 28 | +import java.util.stream.Collectors; |
28 | 29 | import javax.servlet.ServletException; |
29 | 30 | import javax.servlet.http.HttpServletRequest; |
30 | 31 | import javax.servlet.http.HttpServletResponse; |
|
47 | 48 | import org.apache.commons.lang3.StringUtils; |
48 | 49 | import org.apache.logging.log4j.Logger; |
49 | 50 | import org.dspace.app.rest.converter.ConverterService; |
| 51 | +import org.dspace.app.rest.exception.UnprocessableEntityException; |
50 | 52 | import org.dspace.app.rest.model.ClarinFeaturedServiceRest; |
| 53 | +import org.dspace.app.rest.model.refbox.ExportFormatDTO; |
| 54 | +import org.dspace.app.rest.model.refbox.FeaturedServiceDTO; |
| 55 | +import org.dspace.app.rest.model.refbox.FeaturedServiceLinkDTO; |
| 56 | +import org.dspace.app.rest.model.refbox.RefBoxDTO; |
51 | 57 | import org.dspace.app.rest.utils.ContextUtil; |
52 | 58 | import org.dspace.app.rest.utils.Utils; |
| 59 | +import org.dspace.content.DSpaceObject; |
53 | 60 | import org.dspace.content.Item; |
54 | 61 | import org.dspace.content.MetadataValue; |
55 | 62 | import org.dspace.content.clarin.ClarinFeaturedService; |
56 | 63 | import org.dspace.content.clarin.ClarinFeaturedServiceLink; |
57 | 64 | import org.dspace.content.service.ItemService; |
58 | 65 | import org.dspace.core.Context; |
| 66 | +import org.dspace.handle.service.HandleService; |
59 | 67 | import org.dspace.services.ConfigurationService; |
60 | 68 | import org.dspace.xoai.services.api.config.XOAIManagerResolver; |
61 | 69 | import org.dspace.xoai.services.api.config.XOAIManagerResolverException; |
@@ -90,6 +98,13 @@ public class ClarinRefBoxController { |
90 | 98 |
|
91 | 99 | private final static String BIBTEX_TYPE = "bibtex"; |
92 | 100 |
|
| 101 | + /** |
| 102 | + * Default language for the RefBox metadata values |
| 103 | + * This will be changed in the future to support multiple languages, probably fetching the language from the |
| 104 | + * request, but for now there is a mess in the metadata value languages, so we will use the default. |
| 105 | + */ |
| 106 | + private final static String DEFAULT_LANGUAGE = "*"; |
| 107 | + |
93 | 108 | private final Logger log = org.apache.logging.log4j.LogManager.getLogger(ClarinRefBoxController.class); |
94 | 109 |
|
95 | 110 | @Autowired |
@@ -119,6 +134,9 @@ public class ClarinRefBoxController { |
119 | 134 | @Autowired |
120 | 135 | private ItemRepositoryResolver itemRepositoryResolver; |
121 | 136 |
|
| 137 | + @Autowired |
| 138 | + private HandleService handleService; |
| 139 | + |
122 | 140 | private final DSpaceResumptionTokenFormatter resumptionTokenFormat = new DSpaceResumptionTokenFormatter(); |
123 | 141 |
|
124 | 142 | /** |
@@ -163,7 +181,7 @@ public Page<ClarinFeaturedServiceRest> getServices(@RequestParam(name = "id") UU |
163 | 181 | // Check if the item has the metadata for this featured service, if it doesn't have - do NOT return the |
164 | 182 | // featured service. |
165 | 183 | List<MetadataValue> itemMetadata = itemService.getMetadata(item, "local", "featuredService", |
166 | | - featuredServiceName, Item.ANY, false); |
| 184 | + featuredServiceName, DEFAULT_LANGUAGE); |
167 | 185 | if (CollectionUtils.isEmpty(itemMetadata)) { |
168 | 186 | continue; |
169 | 187 | } |
@@ -286,6 +304,190 @@ public ResponseEntity getCitationText(@RequestParam(name = "type") String type, |
286 | 304 | return new ResponseEntity<>(oaiMetadataWrapper, HttpStatus.valueOf(SC_OK)); |
287 | 305 | } |
288 | 306 |
|
| 307 | + /** |
| 308 | + * Get the RefBox information based on the handle. |
| 309 | + * It returns the display text, export formats and featured services. |
| 310 | + */ |
| 311 | + @RequestMapping(method = RequestMethod.GET, produces = "application/json") |
| 312 | + public ResponseEntity<RefBoxDTO> getRefboxInfo( |
| 313 | + @RequestParam(name = "handle") String handle, |
| 314 | + HttpServletRequest request) throws SQLException { |
| 315 | + |
| 316 | + Context context = ContextUtil.obtainContext(request); |
| 317 | + if (context == null) { |
| 318 | + throw new RuntimeException("Cannot obtain the context from the request."); |
| 319 | + } |
| 320 | + |
| 321 | + DSpaceObject dSpaceObject = handleService.resolveToObject(context, handle); |
| 322 | + if (!(dSpaceObject instanceof Item)) { |
| 323 | + throw new UnprocessableEntityException("The handle does not resolve to an Item."); |
| 324 | + } |
| 325 | + Item item = (Item) dSpaceObject; |
| 326 | + |
| 327 | + String title = itemService.getMetadataFirstValue(item, "dc", "title", null, DEFAULT_LANGUAGE); |
| 328 | + String displayText = buildDisplayText(context, item); |
| 329 | + |
| 330 | + // Build exportFormats as a map with "exportFormat" key |
| 331 | + Map<String, List<ExportFormatDTO>> exportFormatsMap = new HashMap<>(); |
| 332 | + exportFormatsMap.put("exportFormat", buildExportFormats(item)); |
| 333 | + |
| 334 | + // Build featuredServices as a map with "featuredService" key |
| 335 | + Map<String, List<FeaturedServiceDTO>> featuredServicesMap = new HashMap<>(); |
| 336 | + featuredServicesMap.put("featuredService", buildFeaturedServices(context, item)); |
| 337 | + |
| 338 | + // Pass these maps to RefBoxDTO |
| 339 | + RefBoxDTO refBoxDTO = new RefBoxDTO( |
| 340 | + displayText, |
| 341 | + exportFormatsMap, |
| 342 | + featuredServicesMap, |
| 343 | + title != null ? title : "" |
| 344 | + ); |
| 345 | + return ResponseEntity.ok(refBoxDTO); |
| 346 | + } |
| 347 | + |
| 348 | + /** |
| 349 | + * Build the display text for the RefBox based on the Item Metadata. |
| 350 | + */ |
| 351 | + private String buildDisplayText(Context context, Item item) { |
| 352 | + // 1. Authors |
| 353 | + List<String> authors = itemService.getMetadata(item, "dc", "contributor", "author", DEFAULT_LANGUAGE) |
| 354 | + .stream().map(MetadataValue::getValue).collect(Collectors.toList()); |
| 355 | + // If there are no authors, try to get the publisher metadata |
| 356 | + if (authors.isEmpty()) { |
| 357 | + authors = itemService.getMetadata(item, "dc", "publisher", null, DEFAULT_LANGUAGE) |
| 358 | + .stream().map(MetadataValue::getValue).collect(Collectors.toList()); |
| 359 | + } |
| 360 | + String authorText = formatAuthors(authors); |
| 361 | + |
| 362 | + // 2. Year |
| 363 | + String year = ""; |
| 364 | + String issued = itemService.getMetadataFirstValue(item, "dc", "date", "issued", DEFAULT_LANGUAGE); |
| 365 | + if (issued != null && !issued.isEmpty()) { |
| 366 | + // The issued date is in the format YYYY-MM-DD, we take the year part |
| 367 | + year = issued.split("-")[0]; |
| 368 | + } |
| 369 | + |
| 370 | + // 3. Title |
| 371 | + String title = itemService.getMetadataFirstValue(item, "dc", "title", null, DEFAULT_LANGUAGE); |
| 372 | + |
| 373 | + // 4. Repository name |
| 374 | + String repository = configurationService.getProperty("dspace.name"); |
| 375 | + |
| 376 | + // 5. Identifier URI (prefer DOI) |
| 377 | + String identifier = itemService.getMetadataFirstValue(item, "dc", "identifier", "doi", DEFAULT_LANGUAGE); |
| 378 | + if (identifier == null) { |
| 379 | + identifier = itemService.getMetadataFirstValue(item, "dc", "identifier", "uri", DEFAULT_LANGUAGE); |
| 380 | + } |
| 381 | + |
| 382 | + // 6. Format |
| 383 | + // Using html tags to format the output because this display text will be rendered in the UI |
| 384 | + StringBuilder sb = new StringBuilder(); |
| 385 | + if (authorText != null && !authorText.isEmpty()) { |
| 386 | + sb.append(authorText); |
| 387 | + } |
| 388 | + if (year != null && !year.isEmpty()) { |
| 389 | + if (sb.length() > 0) { |
| 390 | + sb.append(", "); |
| 391 | + } |
| 392 | + sb.append(year); |
| 393 | + } |
| 394 | + sb.append(", \n <i>").append(title != null ? title : "").append("</i>"); |
| 395 | + if (repository != null && !repository.isEmpty()) { |
| 396 | + sb.append(", ").append(repository); |
| 397 | + } |
| 398 | + sb.append(", \n <a href=\"").append(identifier != null ? identifier : "").append("\">") |
| 399 | + .append(identifier != null ? identifier : "").append("</a>."); |
| 400 | + return sb.toString(); |
| 401 | + } |
| 402 | + |
| 403 | + /** |
| 404 | + * Format the authors for the display text. |
| 405 | + * If there is one author, it will return that author. |
| 406 | + * If there are 2-5 authors, it will join them with "; " and replace the last ";" with " and". |
| 407 | + * If there are more than 5 authors, it will return the first author and "et al.". |
| 408 | + */ |
| 409 | + private String formatAuthors(List<String> authors) { |
| 410 | + String authorText = ""; |
| 411 | + if (authors.size() == 1) { |
| 412 | + authorText = authors.get(0); |
| 413 | + } else if (authors.size() <= 5) { |
| 414 | + authorText = String.join("; ", authors); |
| 415 | + authorText = authorText.replaceAll("; ([^;]*)$", " and $1"); |
| 416 | + } else { |
| 417 | + authorText = authors.get(0) + "; et al."; |
| 418 | + } |
| 419 | + return authorText; |
| 420 | + } |
| 421 | + |
| 422 | + /** |
| 423 | + * Build the export formats for the RefBox based on the Item handle. |
| 424 | + * It returns a list of ExportFormatDTO objects with the URL to the citation data. |
| 425 | + */ |
| 426 | + private List<ExportFormatDTO> buildExportFormats(Item item) { |
| 427 | + List<ExportFormatDTO> exportFormats = new ArrayList<>(); |
| 428 | + String itemHandle = item.getHandle(); |
| 429 | + if (itemHandle != null) { |
| 430 | + String baseUrl = configurationService.getProperty("dspace.server.url") + |
| 431 | + "/api/core/refbox/citations?handle=/" + Utils.getCanonicalHandleUrlNoProtocol(item); |
| 432 | + |
| 433 | + String bibtexUrl = baseUrl + "&type=bibtex"; |
| 434 | + String cmdiUrl = baseUrl + "&type=cmdi"; |
| 435 | + |
| 436 | + exportFormats.add(new ExportFormatDTO("bibtex", bibtexUrl, "json", "")); |
| 437 | + exportFormats.add(new ExportFormatDTO("cmdi", cmdiUrl, "json", "")); |
| 438 | + } else { |
| 439 | + log.error("Item with ID {} does not have a handle, export formats cannot be built.", item.getID()); |
| 440 | + } |
| 441 | + return exportFormats; |
| 442 | + } |
| 443 | + |
| 444 | + /** |
| 445 | + * Build the featured services for the RefBox based on the Item Metadata. |
| 446 | + * This method retrieves the metadata values for the featured services, |
| 447 | + * groups them by service name (qualifier), |
| 448 | + * and constructs a list of FeaturedServiceDTO objects |
| 449 | + * with the full name, URL, description, and links. |
| 450 | + */ |
| 451 | + private List<FeaturedServiceDTO> buildFeaturedServices(Context context, Item item) { |
| 452 | + List<MetadataValue> fsMeta = itemService.getMetadata(item, "local", "featuredService", "*", DEFAULT_LANGUAGE); |
| 453 | + Map<String, List<FeaturedServiceLinkDTO>> serviceLinksMap = new HashMap<>(); |
| 454 | + |
| 455 | + // Group links by service name (qualifier) |
| 456 | + for (MetadataValue mv : fsMeta) { |
| 457 | + String qualifier = mv.getMetadataField().getQualifier(); |
| 458 | + if (qualifier == null) { |
| 459 | + continue; |
| 460 | + } |
| 461 | + String[] parts = mv.getValue().split("\\|"); |
| 462 | + if (parts.length == 2) { |
| 463 | + serviceLinksMap |
| 464 | + .computeIfAbsent(qualifier, k -> new ArrayList<>()) |
| 465 | + .add(new FeaturedServiceLinkDTO(parts[0], parts[1])); |
| 466 | + } else { |
| 467 | + log.error("Invalid metadata value format for featured service: {}. " + |
| 468 | + "Expected format is '<KEY>|<VALUE>'.", mv.getValue()); |
| 469 | + } |
| 470 | + } |
| 471 | + |
| 472 | + List<FeaturedServiceDTO> featuredServiceList = new ArrayList<>(); |
| 473 | + // Iterate over the grouped service links and create FeaturedServiceDTO objects |
| 474 | + for (Map.Entry<String, List<FeaturedServiceLinkDTO>> entry : serviceLinksMap.entrySet()) { |
| 475 | + String name = entry.getKey(); |
| 476 | + String fullname = configurationService.getProperty("featured.service." + name + ".fullname"); |
| 477 | + String url = configurationService.getProperty("featured.service." + name + ".url"); |
| 478 | + String description = configurationService.getProperty("featured.service." + name + ".description"); |
| 479 | + Map<String, List<FeaturedServiceLinkDTO>> linksMap = new HashMap<>(); |
| 480 | + linksMap.put("entry", entry.getValue()); |
| 481 | + featuredServiceList.add(new FeaturedServiceDTO( |
| 482 | + fullname != null ? fullname : name, |
| 483 | + url != null ? url : "", |
| 484 | + description != null ? description : "", |
| 485 | + linksMap |
| 486 | + )); |
| 487 | + } |
| 488 | + return featuredServiceList; |
| 489 | + } |
| 490 | + |
289 | 491 | private void closeContext(Context context) { |
290 | 492 | if (Objects.nonNull(context) && context.isValid()) { |
291 | 493 | context.abort(); |
@@ -422,20 +624,17 @@ public String toString() { |
422 | 624 | * For better response parsing wrap the OAI data to the object. |
423 | 625 | */ |
424 | 626 | class OaiMetadataWrapper { |
425 | | - private String metadata; |
426 | | - |
427 | | - public OaiMetadataWrapper() { |
428 | | - } |
| 627 | + private String value; |
429 | 628 |
|
430 | | - public OaiMetadataWrapper(String metadata) { |
431 | | - this.metadata = metadata; |
| 629 | + public OaiMetadataWrapper(String value) { |
| 630 | + this.value = value; |
432 | 631 | } |
433 | 632 |
|
434 | 633 | public String getMetadata() { |
435 | | - return metadata; |
| 634 | + return value; |
436 | 635 | } |
437 | 636 |
|
438 | | - public void setMetadata(String metadata) { |
439 | | - this.metadata = metadata; |
| 637 | + public void setMetadata(String value) { |
| 638 | + this.value = value; |
440 | 639 | } |
441 | 640 | } |
0 commit comments