package at.ac.univie.mminf.qskos4j.issues.labels;
import at.ac.univie.mminf.qskos4j.issues.Issue;
import at.ac.univie.mminf.qskos4j.issues.concepts.InvolvedConcepts;
import at.ac.univie.mminf.qskos4j.issues.labels.util.LabelConflict;
import at.ac.univie.mminf.qskos4j.issues.labels.util.LabelType;
import at.ac.univie.mminf.qskos4j.issues.labels.util.LabeledConcept;
import at.ac.univie.mminf.qskos4j.issues.labels.util.SimilarityLiteral;
import at.ac.univie.mminf.qskos4j.progress.MonitoredIterator;
import at.ac.univie.mminf.qskos4j.result.CollectionResult;
import at.ac.univie.mminf.qskos4j.util.vocab.SparqlPrefix;
import org.openrdf.OpenRDFException;
import org.openrdf.model.Literal;
import org.openrdf.model.Resource;
import org.openrdf.model.Value;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.query.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
/**
* Finds concepts having the same preferred labels (
* <a href="https://github.com/cmader/qSKOS/wiki/Quality-Issues#wiki-Overlapping_Labels">Overlapping Labels</a>
* ).
*/
public class OverlappingLabels extends Issue<CollectionResult<LabelConflict>> {
private final Logger logger = LoggerFactory.getLogger(OverlappingLabels.class);
private Set<LabelConflict> labelConflicts;
private Map<Literal, Set<LabeledConcept>> conceptLabels;
private InvolvedConcepts involvedConcepts;
public OverlappingLabels(InvolvedConcepts involvedConcepts) {
super(involvedConcepts,
"ol",
"Overlapping Labels",
"Finds concepts with similar (identical) labels",
IssueType.ANALYTICAL,
new URIImpl("https://github.com/cmader/qSKOS/wiki/Quality-Issues#overlapping-labels"));
this.involvedConcepts = involvedConcepts;
}
@Override
protected CollectionResult<LabelConflict> invoke() throws OpenRDFException {
generateConceptsLabelMap();
generateLabelConflictResults();
return new CollectionResult<LabelConflict>(labelConflicts);
}
private void generateConceptsLabelMap() throws OpenRDFException
{
conceptLabels = new HashMap<Literal, Set<LabeledConcept>>();
Iterator<Resource> it = new MonitoredIterator<Resource>(involvedConcepts.getResult().getData(), progressMonitor);
progressMonitor.setTaskDescription("Collecting resource labels");
while (it.hasNext()) {
Resource concept = it.next();
try {
TupleQuery query = repCon.prepareTupleQuery(QueryLanguage.SPARQL, createConceptLabelQuery(concept));
Set<LabeledConcept> labeledConcepts = createLabeledConceptsFromResult(concept, query.evaluate());
addToLabelsMap(labeledConcepts);
}
catch (OpenRDFException e) {
logger.error("Error finding labels of concept '" +concept+ "'");
}
}
}
private void addToLabelsMap(Set<LabeledConcept> labels) {
for (LabeledConcept label : labels) {
SimilarityLiteral literal = new SimilarityLiteral(label.getLiteral());
Set<LabeledConcept> affectedConcepts = conceptLabels.get(literal);
if (affectedConcepts == null) {
affectedConcepts = new HashSet<>();
}
// We're not interested in conflicts within the same concept
addIfResourceUnique(label, affectedConcepts);
conceptLabels.put(literal, affectedConcepts);
}
}
private void addIfResourceUnique(LabeledConcept newLabeledResource, Set<LabeledConcept> otherLabeledResources) {
for (LabeledConcept labeledResource : otherLabeledResources) {
if (labeledResource.getConcept().equals(newLabeledResource.getConcept())) return;
}
otherLabeledResources.add(newLabeledResource);
}
private String createConceptLabelQuery(Value concept) {
return SparqlPrefix.SKOS+
"SELECT ?prefLabel ?altLabel ?hiddenLabel "+
"WHERE {{<"+concept.stringValue()+"> skos:prefLabel ?prefLabel .} UNION "+
"{<"+concept.stringValue()+"> skos:altLabel ?altLabel .} UNION "+
"{<"+concept.stringValue()+"> skos:hiddenLabel ?hiddenLabel .}}";
}
private Set<LabeledConcept> createLabeledConceptsFromResult(Resource concept, TupleQueryResult result)
throws QueryEvaluationException
{
Set<LabeledConcept> ret = new HashSet<LabeledConcept>();
while (result.hasNext()) {
BindingSet queryResult = result.next();
for (String bindingName : queryResult.getBindingNames()) {
try {
Literal literal = (Literal) queryResult.getValue(bindingName);
LabeledConcept skosLabel = new LabeledConcept(
concept,
literal,
getLabelTypeForBindingName(bindingName));
ret.add(skosLabel);
}
catch (ClassCastException e) {
logger.error("Literal label expected for concept " +concept.toString()+ ", " +e.toString());
}
}
}
return ret;
}
private LabelType getLabelTypeForBindingName(String bindingName) {
if (bindingName.equals("prefLabel")) {
return LabelType.PREF_LABEL;
}
if (bindingName.equals("altLabel")) {
return LabelType.ALT_LABEL;
}
if (bindingName.equals("hiddenLabel")) {
return LabelType.HIDDEN_LABEL;
}
return null;
}
private void generateLabelConflictResults() {
labelConflicts = new HashSet<LabelConflict>();
for (Set<LabeledConcept> conflicts : conceptLabels.values()) {
if (conflicts.size() > 1) {
labelConflicts.add(new LabelConflict(conflicts));
}
}
}
}