Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crosswalk-embargo #821

Merged
Merged
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,6 @@ public void setUp() throws Exception {
@Override
public void destroy() throws Exception {
super.destroy();
// After this test has finished running, refresh application context and
// set the expected 'default' versioned handle provider back to ensure other tests don't fail
DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh();
}

private void registerProvider(Class type) {
Expand Down
168 changes: 168 additions & 0 deletions dspace-oai/src/main/java/org/dspace/utils/SpecialItemService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,38 @@

import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.dspace.app.util.DCInput;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.factory.AuthorizeServiceFactory;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataValue;
import org.dspace.content.factory.ClarinServiceFactory;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.content.service.clarin.ClarinItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.handle.factory.HandleServiceFactory;
import org.dspace.handle.service.HandleService;
import org.dspace.xoai.exceptions.InvalidMetadataFieldException;
import org.dspace.xoai.services.impl.DSpaceFieldResolver;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
Expand Down Expand Up @@ -267,6 +281,160 @@ public static Node getAuthor(String mdValue) {
}
}

/**
* Retrieves the earliest available date for an item identified by the given identifier URI.
* This method checks for any embargo date first and then retrieves the "dc.date.available"
* metadata value as a fallback if no embargo date is found.
*
* @param identifierUri The identifier URI of the item whose available date is to be retrieved.
* @return A string representation of the earliest available date, or null if no date is found or an error occurs.
*/
public static String getAvailable(String identifierUri) {
Context context = new Context();
// Find the metadata field for "dc.identifier.uri"
String mtdField = "dc.identifier.uri";
MetadataField metadataField = findMetadataField(context, mtdField);
if (Objects.isNull(metadataField)) {
log.error(String.format("Metadata field for %s not found.", mtdField));
return null;
}

// Retrieve the item using the handle
Item item = findItemByHandle(context, metadataField, identifierUri);
if (Objects.isNull(item)) {
log.error(String.format("Item for handle %s doesn't exist!", identifierUri));
return null;
}

// Check if there is an embargo or get the earliest available date
Date startDate = getEmbargoDate(context, item);
if (Objects.isNull(startDate)) {
startDate = getAvailableDate(context, item);
}
return (Objects.nonNull(startDate)) ? startDate.toString() : null;
}

/**
* Finds the metadata field corresponding to the provided string.
*
* @param context The DSpace context
* @param mtd The metadata field string
* @return The MetadataField object, or null if not found.
*/
private static MetadataField findMetadataField(Context context, String mtd) {
MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService();
try {
return metadataFieldService.findByString(context, mtd, '.');
} catch (SQLException e) {
log.error(String.format("Error finding metadata field %s.", mtd), e);
return null;
}
}

/**
* Finds an item in DSpace using the provided handle and metadata field.
*
* @param context The DSpace context
* @param metadataField The metadata field used for item search
* @param handle The handle (identifier) of the item.
* @return The Item object, or null if no item is found.
*/
private static Item findItemByHandle(Context context, MetadataField metadataField, String handle) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClarinItemService clarinItemService = ClarinServiceFactory.getInstance().getClarinItemService();
try {
List<Item> itemList = clarinItemService.findByHandle(context, metadataField, handle);
return (itemList.isEmpty()) ? null : itemList.get(0);
} catch (SQLException e) {
log.error("Error retrieving item by handle.", e);
return null;
}
}

/**
* Retrieves the embargo start date for the given item bitstreams.
* If an embargo has ended, the end date is returned.
*
* @param context The DSpace context
* @param item The item whose embargo date is to be retrieved.
* @return The start or end date of the embargo, or null if no embargo exists.
*/
private static Date getEmbargoDate(Context context, Item item) {
ResourcePolicyService resPolicyService = AuthorizeServiceFactory.getInstance().getResourcePolicyService();
Date startDate = null;
for (Bundle bundle : item.getBundles()) {
for (Bitstream bitstream : bundle.getBitstreams()) {
List<ResourcePolicy> resPolList;
try {
resPolList = resPolicyService.find(context, bitstream, Constants.READ);
} catch (SQLException e) {
log.error(String.format("Error during finding resource policies READ for bitstream %s",
bitstream.getID().toString()));
return null;
}
for (ResourcePolicy resPol : resPolList) {
Date date = resPol.getStartDate();
// If the embargo has already ended, use the date of its end.
if (Objects.nonNull(date) && Objects.nonNull(resPol.getEndDate())) {
date = resPol.getEndDate();
}
if (Objects.isNull(startDate) || (Objects.nonNull(date) && date.compareTo(startDate) > 0)) {
startDate = date;
}
}
}
}
return startDate;
}

/**
* Retrieves the available date for the given item by checking the "dc.date.available" metadata.
*
* @param context The DSpace context
* @param item The item whose available date is to be retrieved.
* @return The available date, or null if no available date is found.
*/
private static Date getAvailableDate(Context context, Item item) {
DSpaceFieldResolver dSpaceFieldResolver = new DSpaceFieldResolver();
List<MetadataValue> metadataValueList = item.getMetadata();
String mtdField = "dc.date.available";
int fieldID;
try {
fieldID = dSpaceFieldResolver.getFieldID(context, mtdField);
} catch (SQLException | InvalidMetadataFieldException e) {
log.error(String.format("Error during finding ID of metadata field %s.", mtdField));
return null;
}
Date startDate = null;
for (MetadataValue mtd : metadataValueList) {
if (mtd.getMetadataField().getID() == fieldID) {
Date availableDate = parseDate(mtd.getValue());
if (Objects.isNull(startDate) || (Objects.nonNull(availableDate)
&& availableDate.compareTo(startDate) > 0)) {
startDate = availableDate;
}
}
}
return startDate;
}

/**
* Parses a date string in the format "yyyy-MM-dd" into a Date object.
*
* @param dateString The date string to be parsed.
* @return A Date object representing the parsed date, or null if parsing fails.
*/
private static Date parseDate(String dateString) {
String format = "yyyy-MM-dd";
SimpleDateFormat dateFormat = new SimpleDateFormat(format); // Example format
dateFormat.setLenient(false); // Set lenient to false to avoid parsing incorrect dates
try {
return dateFormat.parse(dateString); // Attempt to parse the date
} catch (ParseException e) {
log.warn(String.format("Date %s cannot be parsed using the format %s.", dateString, format));
return null;
}
}

public static boolean hasOwnMetadata(List<MetadataValue> metadataValues) {
if (metadataValues.size() == 1 && metadataValues.get(0).getValue().equalsIgnoreCase("true")) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.dspace.xoai.services.impl.resources.functions.BibtexifyFn;
import org.dspace.xoai.services.impl.resources.functions.FormatFn;
import org.dspace.xoai.services.impl.resources.functions.GetAuthorFn;
import org.dspace.xoai.services.impl.resources.functions.GetAvailableFn;
import org.dspace.xoai.services.impl.resources.functions.GetContactFn;
import org.dspace.xoai.services.impl.resources.functions.GetFundingFn;
import org.dspace.xoai.services.impl.resources.functions.GetLangForCodeFn;
Expand Down Expand Up @@ -54,7 +55,7 @@ public class DSpaceResourceResolver implements ResourceResolver {
new UriToLicenseFn(), new LogMissingMsgFn(), new UriToRestrictionsFn(), new ShortestIdFn(),
new GetContactFn(), new GetAuthorFn(), new GetFundingFn(), new GetLangForCodeFn(),
new GetPropertyFn(), new GetSizeFn(), new GetUploadedMetadataFn(), new LogMissingFn(),
new BibtexifyFn(), new FormatFn()
new BibtexifyFn(), new FormatFn(), new GetAvailableFn()
);

SaxonTransformerFactory saxonTransformerFactory = (SaxonTransformerFactory) transformerFactory;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.xoai.services.impl.resources.functions;

import org.dspace.utils.SpecialItemService;

/**
* The GetAvailableFn class extends the StringXSLFunction to provide a custom function
* that retrieves the availability status of an item based on its identifier.
* It uses the SpecialItemService to fetch the available information.
* This function is intended to be used in XSL transformations where the
* "getAvailable" function is called with an item's identifier as a parameter.
*
* @author Michaela Paurikova(michaela.paurikova at dataquest.sk)
*/
public class GetAvailableFn extends StringXSLFunction {
@Override
protected String getFnName() {
return "getAvailable";
}

@Override
protected String getStringResult(String param) {
return SpecialItemService.getAvailable(param);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ final public XdmValue call(XdmValue[] xdmValues) throws SaxonApiException {
log.warn("Empty value in call of function of StringXslFunction type");
val = "";
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks better :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added line but I got checkstyle violation so I had to remove it again....

return new XdmAtomicValue(checks(getStringResult(val)));
}

Expand Down
17 changes: 4 additions & 13 deletions dspace/config/crosswalks/oai/metadataFormats/datacite_openaire.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -141,19 +141,10 @@
<date dateType="Accepted">
<xsl:value-of select="doc:metadata/doc:element[@name='dc']/doc:element[@name='date']/doc:element[@name='accessioned']/doc:element/doc:field[@name='value']"/>
</date>
<xsl:choose>
<xsl:when test="doc:metadata/doc:element[@name='dc']/doc:element[@name='date']/doc:element[@name='available']/doc:element/doc:field[@name='value']">
<date dateType="Available">
<xsl:value-of select="doc:metadata/doc:element[@name='dc']/doc:element[@name='date']/doc:element[@name='available']/doc:element/doc:field[@name='value']"/>
</date>
</xsl:when>
<xsl:otherwise>
<!-- empty available means embargo. item will be available on the embargo date -->
<date dateType="Available">
<xsl:value-of select="doc:metadata/doc:element[@name='local']/doc:element[@name='embargo']/doc:element[@name='termslift']/doc:element/doc:field[@name='value']"/>
</date>
</xsl:otherwise>
</xsl:choose>
<date dateType="Available">
<xsl:variable name="handle" select="doc:metadata/doc:element[@name='dc']/doc:element[@name='identifier']/doc:element/doc:element/doc:field[@name='value']"/>
<xsl:value-of select="fn:getAvailable($handle)"/>
</date>
</dates>
</xsl:template>

Expand Down