package eu.dnetlib.dhp.schema.oaf; import static java.util.Objects.*; import java.io.Serializable; import java.util.*; import java.util.stream.Collectors; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import com.fasterxml.jackson.annotation.JsonProperty; import eu.dnetlib.dhp.schema.common.AccessRightComparator; import eu.dnetlib.dhp.schema.common.ModelConstants; import eu.dnetlib.dhp.schema.oaf.utils.CleaningFunctions; /** * The type Result. */ public class Result extends OafEntity implements Serializable { private static final long serialVersionUID = 5597290552671699408L; /** * ( article | book ) processing charges. */ private Field processingchargeamount; /** * currency - alphabetic code describe in ISO-4217. */ private Field processingchargecurrency; /** * The Author. */ private List author; /** * The Resulttype. */ // resulttype allows subclassing results into publications | datasets | software private Qualifier resulttype; /** * Temporary field suporting the analysis of the new COAR-based resource types */ private Qualifier metaResourceType; /** * The Language. */ // common fields private Qualifier language; /** * The Country. */ private List country; /** * The Subject. */ private List subject; /** * The Title. */ private List title; /** * The Relevantdate. */ private List relevantdate; /** * The Description. */ private List> description; /** * The Dateofacceptance. */ private Field dateofacceptance; /** * The Publisher. */ private Field publisher; /** * The Embargoenddate. */ private Field embargoenddate; /** * The Source. */ private List> source; /** * The list of direct fulltext URLs. */ private List> fulltext; /** * The Format. */ private List> format; /** * The Contributor. */ private List> contributor; /** * The Resourcetype. */ private Qualifier resourcetype; /** * The Coverage. */ private List> coverage; /** * The Bestaccessright. */ private Qualifier bestaccessright; /** * The External reference. */ private List externalReference; /** * The Instance. */ private List instance; /** * EOSC Interoperability Framework Guidelines */ private List eoscifguidelines; @JsonProperty("isGreen") private Boolean isGreen; private OpenAccessColor openAccessColor; @JsonProperty("isInDiamondJournal") private Boolean isInDiamondJournal; private Boolean publiclyFunded; private String transformativeAgreement; public String getTransformativeAgreement() { return transformativeAgreement; } public void setTransformativeAgreement(String transformativeAgreement) { this.transformativeAgreement = transformativeAgreement; } public Field getProcessingchargeamount() { return processingchargeamount; } public void setProcessingchargeamount(Field processingchargeamount) { this.processingchargeamount = processingchargeamount; } public Field getProcessingchargecurrency() { return processingchargecurrency; } public void setProcessingchargecurrency(Field processingchargecurrency) { this.processingchargecurrency = processingchargecurrency; } /** * Gets author. * * @return the author */ public List getAuthor() { return author; } /** * Sets author. * * @param author the author */ public void setAuthor(List author) { this.author = author; } /** * Gets resulttype. * * @return the resulttype */ public Qualifier getResulttype() { return resulttype; } /** * Sets resulttype. * * @param resulttype the resulttype */ public void setResulttype(Qualifier resulttype) { this.resulttype = resulttype; } public Qualifier getMetaResourceType() { return metaResourceType; } public void setMetaResourceType(Qualifier metaResourceType) { this.metaResourceType = metaResourceType; } /** * Gets language. * * @return the language */ public Qualifier getLanguage() { return language; } /** * Sets language. * * @param language the language */ public void setLanguage(Qualifier language) { this.language = language; } /** * Gets country. * * @return the country */ public List getCountry() { return country; } /** * Sets country. * * @param country the country */ public void setCountry(List country) { this.country = country; } /** * Gets subject. * * @return the subject */ public List getSubject() { return subject; } /** * Sets subject. * * @param subject the subject */ public void setSubject(List subject) { this.subject = subject; } /** * Gets title. * * @return the title */ public List getTitle() { return title; } /** * Sets title. * * @param title the title */ public void setTitle(List title) { this.title = title; } /** * Gets relevantdate. * * @return the relevantdate */ public List getRelevantdate() { return relevantdate; } /** * Sets relevantdate. * * @param relevantdate the relevantdate */ public void setRelevantdate(List relevantdate) { this.relevantdate = relevantdate; } /** * Gets description. * * @return the description */ public List> getDescription() { return description; } /** * Sets description. * * @param description the description */ public void setDescription(List> description) { this.description = description; } /** * Gets dateofacceptance. * * @return the dateofacceptance */ public Field getDateofacceptance() { return dateofacceptance; } /** * Sets dateofacceptance. * * @param dateofacceptance the dateofacceptance */ public void setDateofacceptance(Field dateofacceptance) { this.dateofacceptance = dateofacceptance; } /** * Gets publisher. * * @return the publisher */ public Field getPublisher() { return publisher; } /** * Sets publisher. * * @param publisher the publisher */ public void setPublisher(Field publisher) { this.publisher = publisher; } /** * Gets embargoenddate. * * @return the embargoenddate */ public Field getEmbargoenddate() { return embargoenddate; } /** * Sets embargoenddate. * * @param embargoenddate the embargoenddate */ public void setEmbargoenddate(Field embargoenddate) { this.embargoenddate = embargoenddate; } /** * Gets source. * * @return the source */ public List> getSource() { return source; } /** * Sets source. * * @param source the source */ public void setSource(List> source) { this.source = source; } /** * Gets fulltext. * * @return the fulltext */ public List> getFulltext() { return fulltext; } /** * Sets fulltext. * * @param fulltext the fulltext */ public void setFulltext(List> fulltext) { this.fulltext = fulltext; } /** * Gets format. * * @return the format */ public List> getFormat() { return format; } /** * Sets format. * * @param format the format */ public void setFormat(List> format) { this.format = format; } /** * Gets contributor. * * @return the contributor */ public List> getContributor() { return contributor; } /** * Sets contributor. * * @param contributor the contributor */ public void setContributor(List> contributor) { this.contributor = contributor; } /** * Gets resourcetype. * * @return the resourcetype */ public Qualifier getResourcetype() { return resourcetype; } /** * Sets resourcetype. * * @param resourcetype the resourcetype */ public void setResourcetype(Qualifier resourcetype) { this.resourcetype = resourcetype; } /** * Gets coverage. * * @return the coverage */ public List> getCoverage() { return coverage; } /** * Sets coverage. * * @param coverage the coverage */ public void setCoverage(List> coverage) { this.coverage = coverage; } /** * Gets bestaccessright. * * @return the bestaccessright */ public Qualifier getBestaccessright() { return bestaccessright; } /** * Sets bestaccessright. * * @param bestaccessright the bestaccessright */ public void setBestaccessright(Qualifier bestaccessright) { this.bestaccessright = bestaccessright; } /** * Gets external reference. * * @return the external reference */ public List getExternalReference() { return externalReference; } /** * Sets external reference. * * @param externalReference the external reference */ public void setExternalReference(List externalReference) { this.externalReference = externalReference; } /** * Gets instance. * * @return the instance */ public List getInstance() { return instance; } /** * Sets instance. * * @param instance the instance */ public void setInstance(List instance) { this.instance = instance; } public List getEoscifguidelines() { return eoscifguidelines; } public void setEoscifguidelines(List eoscifguidelines) { this.eoscifguidelines = eoscifguidelines; } public Boolean getIsGreen() { return isGreen; } public void setIsGreen(Boolean green) { isGreen = green; } public OpenAccessColor getOpenAccessColor() { return openAccessColor; } public void setOpenAccessColor(OpenAccessColor openAccessColor) { this.openAccessColor = openAccessColor; } public Boolean getIsInDiamondJournal() { return isInDiamondJournal; } public void setIsInDiamondJournal(Boolean inDiamondJournal) { isInDiamondJournal = inDiamondJournal; } public Boolean getPubliclyFunded() { return publiclyFunded; } public void setPubliclyFunded(Boolean publiclyFunded) { this.publiclyFunded = publiclyFunded; } /** * Is an enrichment boolean. * * @param e the e * @return the boolean */ public static boolean isAnEnrichment(OafEntity e) { return e.getDataInfo()!= null && e.getDataInfo().getProvenanceaction()!= null && ModelConstants.PROVENANCE_ENRICH.equalsIgnoreCase(e.getDataInfo().getProvenanceaction().getClassid()); } /** * Normalize pid string. * * @param pid the pid * @return the string */ private static String extractKeyFromPid(final StructuredProperty pid) { if (pid == null) return null; final StructuredProperty normalizedPid = CleaningFunctions.normalizePidValue(pid); return String.format("%s::%s", normalizedPid.getQualifier().getClassid(), normalizedPid.getValue()); } /** * Valid pid boolean. * * @param p the p * @return the boolean */ private static boolean validPid(final StructuredProperty p) { return p.getValue()!= null && p.getQualifier()!= null && p.getQualifier().getClassid()!=null; } /** * This method converts the list of instance enrichments * into a Map where the key is the normalized identifier * and the value is the instance itself * * @param ri the list of enrichment instances * @return the result map */ public static Map toInstanceMap(final List ri) { return ri .stream() .filter(i -> i.getPid() != null || i.getAlternateIdentifier() != null) .flatMap(i -> { final List> result = new ArrayList<>(); if (i.getPid() != null) i.getPid().stream().filter(Result::validPid).forEach(p -> result.add(new ImmutablePair<>(extractKeyFromPid(p), i))); if (i.getAlternateIdentifier() != null) i.getAlternateIdentifier().stream().filter(Result::validPid).forEach(p -> result.add(new ImmutablePair<>(extractKeyFromPid(p), i))); return result.stream(); }).collect(Collectors.toMap( Pair::getLeft, Pair::getRight, (a, b) -> a )); } /** * This utility method finds the list of enrichment instances * that match one or more PIDs in the input list * * @param pids the list of PIDs * @param enrichments the List of enrichment instances having the same pid * @return the list */ private static List findEnrichmentsByPID(final List pids, final Map enrichments) { if (pids == null || enrichments == null) return null; return pids .stream() .map(Result::extractKeyFromPid) .map(enrichments::get) .filter(Objects::nonNull) .collect(Collectors.toList()); } /** * This method apply enrichment on a single instance * The enrichment consists of replacing values on * single attribute only if in the current instance is missing * The only repeatable field enriched is measures * * @param currentInstance the current instance * @param enrichment the enrichment instance */ private static void applyEnrichment(final Instance currentInstance, final Instance enrichment) { if (currentInstance == null || enrichment == null) return; //ENRICH accessright if (enrichment.getAccessright()!=null && currentInstance.getAccessright() == null) currentInstance.setAccessright(enrichment.getAccessright()); //ENRICH license if (enrichment.getLicense()!=null && currentInstance.getLicense() == null) currentInstance.setLicense(enrichment.getLicense()); //ENRICH instanceType if (enrichment.getInstancetype()!=null && currentInstance.getInstancetype() == null) currentInstance.setInstancetype(enrichment.getInstancetype()); //ENRICH hostedby if (enrichment.getHostedby()!=null && currentInstance.getHostedby() == null) currentInstance.setHostedby(enrichment.getHostedby()); //ENRICH distributionlocation if (enrichment.getDistributionlocation()!=null && currentInstance.getDistributionlocation() == null) currentInstance.setDistributionlocation(enrichment.getDistributionlocation()); //ENRICH collectedfrom if (enrichment.getCollectedfrom()!=null && currentInstance.getCollectedfrom() == null) currentInstance.setCollectedfrom(enrichment.getCollectedfrom()); //ENRICH dateofacceptance if (enrichment.getDateofacceptance()!=null && currentInstance.getDateofacceptance() == null) currentInstance.setDateofacceptance(enrichment.getDateofacceptance()); //ENRICH processingchargeamount if (enrichment.getProcessingchargeamount()!=null && currentInstance.getProcessingchargeamount() == null) currentInstance.setProcessingchargeamount(enrichment.getProcessingchargeamount()); //ENRICH refereed if (enrichment.getRefereed()!=null && currentInstance.getRefereed() == null) currentInstance.setRefereed(enrichment.getRefereed()); //ENRICH measures if (enrichment.getMeasures()!=null) if (currentInstance.getMeasures() == null) { currentInstance.setMeasures(enrichment.getMeasures()); } else { enrichment.getMeasures().forEach(currentInstance.getMeasures()::add); } } /** * This main method apply the enrichment of the instances * * @param toEnrichInstances the instances that could be enriched * @param enrichmentInstances the enrichment instances * @return list of instances possibly enriched */ private static List enrichInstances(final List toEnrichInstances,final List enrichmentInstances) { final List enrichmentResult = new ArrayList<>(); if (toEnrichInstances == null) { return enrichmentResult; } if (enrichmentInstances == null) { return toEnrichInstances; } Map ri = toInstanceMap(enrichmentInstances); toEnrichInstances.forEach(i -> { final List e = findEnrichmentsByPID(i.getPid(), ri); if (e!= null && !e.isEmpty()) { e.forEach(enr -> applyEnrichment(i, enr)); } else { final List a = findEnrichmentsByPID(i.getAlternateIdentifier(), ri); if (a!= null && !a.isEmpty()) { a.forEach(enr -> applyEnrichment(i, enr)); } } enrichmentResult.add(i); }); return enrichmentResult; } @Override public void mergeFrom(OafEntity e) { super.mergeFrom(e); if (!Result.class.isAssignableFrom(e.getClass())) { return; } Result r = (Result) e; if(processingchargeamount == null || StringUtils.isBlank(processingchargeamount.getValue())){ processingchargeamount = r.getProcessingchargeamount(); processingchargecurrency = r.getProcessingchargecurrency(); } if (Objects.isNull(getTransformativeAgreement()) && Objects.nonNull(r.getTransformativeAgreement())) { setTransformativeAgreement(r.getTransformativeAgreement()); } eoscifguidelines = mergeLists(eoscifguidelines, r.getEoscifguidelines()); setIsGreen(mergeBooleanOR(getIsGreen(), r.getIsGreen())); setIsInDiamondJournal(mergeBooleanOR(getIsInDiamondJournal(), r.getIsInDiamondJournal())); setPubliclyFunded(mergeBooleanOR(getPubliclyFunded(), r.getPubliclyFunded())); if (Boolean.logicalXor(nonNull(getOpenAccessColor()), nonNull(r.getOpenAccessColor()))) { setOpenAccessColor(ObjectUtils.firstNonNull(getOpenAccessColor(), r.getOpenAccessColor())); } else if (!Objects.equals(getOpenAccessColor(), r.getOpenAccessColor())) { setOpenAccessColor(null); } if( !isAnEnrichment(this) && !isAnEnrichment(e)) instance = mergeLists(instance, r.getInstance()); else { final List enrichmentInstances = isAnEnrichment(this) ? instance : r.getInstance(); final List enrichedInstances= isAnEnrichment(this) ? r.getInstance(): instance; if (isAnEnrichment(this)) setDataInfo(e.getDataInfo()); instance = enrichInstances(enrichedInstances,enrichmentInstances); } if (r.getBestaccessright() != null && new AccessRightComparator().compare(r.getBestaccessright(), bestaccessright) < 0) bestaccessright = r.getBestaccessright(); if (r.getResulttype() != null && compareTrust(this, r) < 0) resulttype = r.getResulttype(); if (r.getLanguage() != null && compareTrust(this, r) < 0) language = r.getLanguage(); if (nonNull(r.getDateofacceptance())) { if (isNull(getDateofacceptance())) { dateofacceptance = r.getDateofacceptance(); } else if (compareTrust(this, r) < 0) { dateofacceptance = r.getDateofacceptance(); } } country = mergeLists(country, r.getCountry()); subject = mergeLists(subject, r.getSubject()); // merge title lists: main title with higher trust and distinct between the others StructuredProperty baseMainTitle = null; if (title != null) { baseMainTitle = getMainTitle(title); if (baseMainTitle != null) { final StructuredProperty p = baseMainTitle; title = title.stream().filter(t -> t != p).collect(Collectors.toList()); } } StructuredProperty newMainTitle = null; if (r.getTitle() != null) { newMainTitle = getMainTitle(r.getTitle()); if (newMainTitle != null) { final StructuredProperty p = newMainTitle; r.setTitle(r.getTitle().stream().filter(t -> t != p).collect(Collectors.toList())); } } if (newMainTitle != null && compareTrust(this, r) < 0) { baseMainTitle = newMainTitle; } title = mergeLists(title, r.getTitle()); if (title != null && baseMainTitle != null) { title.add(baseMainTitle); } relevantdate = mergeLists(relevantdate, r.getRelevantdate()); description = longestLists(description, r.getDescription()); if (r.getPublisher() != null && compareTrust(this, r) < 0) publisher = r.getPublisher(); if (r.getEmbargoenddate() != null && compareTrust(this, r) < 0) embargoenddate = r.getEmbargoenddate(); source = mergeLists(source, r.getSource()); fulltext = mergeLists(fulltext, r.getFulltext()); format = mergeLists(format, r.getFormat()); contributor = mergeLists(contributor, r.getContributor()); if (r.getResourcetype() != null) resourcetype = r.getResourcetype(); coverage = mergeLists(coverage, r.getCoverage()); externalReference = mergeLists(externalReference, r.getExternalReference()); } private Boolean mergeBooleanOR(Boolean a, Boolean b) { if (Boolean.logicalXor(isNull(a), isNull(b))) { return ObjectUtils.firstNonNull(a, b); } else if (nonNull(a) & nonNull(b)) { return a || b; } else { return null; } } /** * Longest lists list. * * @param a the a * @param b the b * @return the list */ private List> longestLists(List> a, List> b) { if (a == null || b == null) return a == null ? b : a; if (a.size() == b.size()) { int msa = a .stream() .filter(i -> i != null && i.getValue() != null) .map(i -> i.getValue().length()) .max(Comparator.naturalOrder()) .orElse(0); int msb = b .stream() .filter(i -> i != null && i.getValue() != null) .map(i -> i.getValue().length()) .max(Comparator.naturalOrder()) .orElse(0); return msa > msb ? a : b; } return a.size() > b.size() ? a : b; } /** * Gets main title. * * @param titles the titles * @return the main title */ private StructuredProperty getMainTitle(List titles) { // need to check if the list of titles contains more than 1 main title? (in that case, we should chose which // main title select in the list) for (StructuredProperty t : titles) { if (t.getQualifier() != null && t.getQualifier().getClassid() != null) if (t.getQualifier().getClassid().equals("main title")) return t; } return null; } }