Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
## v2.0.0 (IN PROGRESS)
- Add LIGHT_RESOURCE type [MODLD-980](https://folio-org.atlassian.net/browse/MODLD-980)
- Concept label generation now orders labels as FOCUS first, followed by sorted SUB_FOCUS entries [MODLD-913](https://folio-org.atlassian.net/browse/MODLD-913)

## v1.9.9 (13-02-2026)
- Update Mapping for 300$b & $e (Physical description & Accompanying material) [MODLD-508](https://folio-org.atlassian.net/browse/MODLD-508)
Expand Down Expand Up @@ -30,7 +32,6 @@
- Update the IRI of ID_LOCAL to http://library.link/identifier/LOCAL [MODLD-939](https://folio-org.atlassian.net/browse/MODLD-939)
- Add resource types FAST, GND, LCCSH, LCDGT, LCGFT, LCMPT, LCNAF, LCSH, MESH, VIAF and WIKIID [MODLD-939](https://folio-org.atlassian.net/browse/MODLD-939)
- Create HubLabelGenerator to generate labels for Hubs [MODLD-970](https://folio-org.atlassian.net/browse/MODLD-970)
- Add LIGHT_RESOURCE type [MODLD-980](https://folio-org.atlassian.net/browse/MODLD-980)

## v1.0.1 (03-12-2025)
- Initial release
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
package org.folio.ld.dictionary.label.generators;

import static java.util.Comparator.comparing;
import static java.lang.String.CASE_INSENSITIVE_ORDER;
import static org.folio.ld.dictionary.PredicateDictionary.FOCUS;
import static org.folio.ld.dictionary.PredicateDictionary.SUB_FOCUS;
import static org.folio.ld.dictionary.ResourceTypeDictionary.CONCEPT;
import static org.folio.ld.dictionary.ResourceTypeDictionary.FORM;
import static org.folio.ld.dictionary.ResourceTypeDictionary.PLACE;
import static org.folio.ld.dictionary.ResourceTypeDictionary.TEMPORAL;
import static org.folio.ld.dictionary.ResourceTypeDictionary.TOPIC;

import java.util.Comparator;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.folio.ld.dictionary.ResourceTypeDictionary;
import org.folio.ld.dictionary.label.LabelGenerator;
import org.folio.ld.dictionary.model.Resource;
import org.folio.ld.dictionary.model.ResourceEdge;

public class ConceptLabelGenerator implements LabelGenerator {
private static final String SEPARATOR_CONCEPT = " -- ";
private static final Comparator<ResourceEdge> CONCEPT_EDGES_COMPARATOR = new ConceptEdgesComparator();

@Override
public boolean supports(Resource resource) {
Expand All @@ -24,13 +32,37 @@ public String getLabel(Resource resource) {
return resource.getOutgoingEdges()
.stream()
.filter(re -> re.getPredicate() == FOCUS || re.getPredicate() == SUB_FOCUS)
.sorted((o1, o2) -> {
Comparator<ResourceEdge> comparing = comparing(o -> o.getPredicate().name());
return comparing.thenComparing(re -> re.getTarget().getLabel())
.compare(o1, o2);
})
.sorted(CONCEPT_EDGES_COMPARATOR)
.map(ResourceEdge::getTarget)
.map(Resource::getLabel)
.collect(Collectors.joining(SEPARATOR_CONCEPT));
}

private static final class ConceptEdgesComparator implements Comparator<ResourceEdge> {
private static final Map<ResourceTypeDictionary, Integer> TYPE_ORDER_INDEX = Map.of(
TOPIC, 0,
PLACE, 1,
TEMPORAL, 2,
FORM, 3
);

@Override
public int compare(ResourceEdge edge1, ResourceEdge edge2) {
return Comparator.comparing((ResourceEdge edge) -> edge.getPredicate().name())
.thenComparingInt(this::getSubFocusTypeOrder)
.thenComparing(e -> e.getTarget().getLabel(), Comparator.nullsFirst(CASE_INSENSITIVE_ORDER))
.compare(edge1, edge2);
}

private int getSubFocusTypeOrder(ResourceEdge edge) {
if (edge.getPredicate() != SUB_FOCUS) {
return -1;
}
return edge.getTarget().getTypes().stream()
.map(TYPE_ORDER_INDEX::get)
.filter(Objects::nonNull)
.min(Integer::compareTo)
.orElse(TYPE_ORDER_INDEX.size());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
import static org.folio.ld.dictionary.PredicateDictionary.FOCUS;
import static org.folio.ld.dictionary.PredicateDictionary.SUB_FOCUS;
import static org.folio.ld.dictionary.ResourceTypeDictionary.CONCEPT;
import static org.folio.ld.dictionary.ResourceTypeDictionary.FORM;
import static org.folio.ld.dictionary.ResourceTypeDictionary.INSTANCE;
import static org.folio.ld.dictionary.ResourceTypeDictionary.PLACE;
import static org.folio.ld.dictionary.ResourceTypeDictionary.TEMPORAL;
import static org.folio.ld.dictionary.ResourceTypeDictionary.TOPIC;

import org.folio.ld.dictionary.model.Resource;
import org.folio.ld.dictionary.model.ResourceEdge;
Expand Down Expand Up @@ -112,7 +116,7 @@ void generateLabel_shouldReturnEmptyStringForConceptWithNoFocusOrSubFocus() {
}

@Test
void generateLabel_shouldGenerateLabelForConceptWithMultipleFocusAndSubFocus() {
void generateLabel_shouldGenerateLabelForConceptWithFocusAndMultipleSortedSubFocus() {
// given
var focusResource1 = new Resource()
.setId(2L)
Expand All @@ -124,25 +128,45 @@ void generateLabel_shouldGenerateLabelForConceptWithMultipleFocusAndSubFocus() {

var subFocusResource1 = new Resource()
.setId(4L)
.setLabel("SubFocus 1");
.setLabel("zebra")
.addType(FORM);

var subFocusResource2 = new Resource()
.setId(5L)
.setLabel("SubFocus 2");
.setLabel("Beta")
.addType(TEMPORAL);

var subFocusResource3 = new Resource()
.setId(6L)
.setLabel("alpha")
.addType(TEMPORAL);

var subFocusResource4 = new Resource()
.setId(7L)
.setLabel("topic-a")
.addType(TOPIC);

var subFocusResource5 = new Resource()
.setId(8L)
.setLabel("place-a")
.addType(PLACE);

var concept = new Resource()
.setId(1L)
.addType(CONCEPT);

concept.addOutgoingEdge(new ResourceEdge(concept, focusResource1, FOCUS));
concept.addOutgoingEdge(new ResourceEdge(concept, subFocusResource5, SUB_FOCUS));
concept.addOutgoingEdge(new ResourceEdge(concept, subFocusResource2, SUB_FOCUS));
concept.addOutgoingEdge(new ResourceEdge(concept, focusResource2, FOCUS));
concept.addOutgoingEdge(new ResourceEdge(concept, subFocusResource3, SUB_FOCUS));
concept.addOutgoingEdge(new ResourceEdge(concept, focusResource1, FOCUS));
concept.addOutgoingEdge(new ResourceEdge(concept, subFocusResource1, SUB_FOCUS));
concept.addOutgoingEdge(new ResourceEdge(concept, subFocusResource2, SUB_FOCUS));
concept.addOutgoingEdge(new ResourceEdge(concept, subFocusResource4, SUB_FOCUS));

// when
var label = generator.getLabel(concept);

// then
assertThat(label).isEqualTo("Focus 1 -- Focus 2 -- SubFocus 1 -- SubFocus 2");
assertThat(label).isEqualTo("Focus 1 -- Focus 2 -- topic-a -- place-a -- alpha -- Beta -- zebra");
}
}