Skip to content

Commit

Permalink
Merge pull request #2469 from mwoodiupui/DS-4300
Browse files Browse the repository at this point in the history
[DS-4300] Optionally apply given Handles when importing community/collection structure
  • Loading branch information
tdonohue committed Jul 28, 2022
2 parents 13e5809 + 953f37d commit 4d4f129
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 62 deletions.
91 changes: 68 additions & 23 deletions dspace-api/src/main/java/org/dspace/administer/StructBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Collection;
import org.dspace.content.Community;
Expand All @@ -55,6 +56,8 @@
import org.dspace.core.Context;
import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.EPersonService;
import org.dspace.handle.factory.HandleServiceFactory;
import org.dspace.handle.service.HandleService;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
Expand All @@ -79,6 +82,7 @@
* </community>
* </import_structure>
* }</pre>
*
* <p>
* It can be arbitrarily deep, and supports all the metadata elements
* that make up the community and collection metadata. See the system
Expand Down Expand Up @@ -107,12 +111,14 @@ public class StructBuilder {
*/
private static final Map<String, MetadataFieldName> communityMap = new HashMap<>();

protected static CommunityService communityService
protected static final CommunityService communityService
= ContentServiceFactory.getInstance().getCommunityService();
protected static CollectionService collectionService
protected static final CollectionService collectionService
= ContentServiceFactory.getInstance().getCollectionService();
protected static EPersonService ePersonService
protected static final EPersonService ePersonService
= EPersonServiceFactory.getInstance().getEPersonService();
protected static final HandleService handleService
= HandleServiceFactory.getInstance().getHandleService();

/**
* Default constructor
Expand All @@ -138,6 +144,7 @@ private StructBuilder() { }
* @throws SQLException passed through.
* @throws FileNotFoundException if input or output could not be opened.
* @throws TransformerException if the input document is invalid.
* @throws XPathExpressionException passed through.
*/
public static void main(String[] argv)
throws ParserConfigurationException, SQLException,
Expand All @@ -148,6 +155,7 @@ public static void main(String[] argv)
options.addOption("h", "help", false, "Print this help message.");
options.addOption("?", "help");
options.addOption("x", "export", false, "Export the current structure as XML.");
options.addOption("k", "keep-handles", false, "Apply Handles from input document.");

options.addOption(Option.builder("e").longOpt("eperson")
.desc("User who is manipulating the repository's structure.")
Expand Down Expand Up @@ -223,7 +231,8 @@ public static void main(String[] argv)
inputStream = new FileInputStream(input);
}

importStructure(context, inputStream, outputStream);
boolean keepHandles = options.hasOption("k");
importStructure(context, inputStream, outputStream, keepHandles);
// save changes from import
context.complete();
}
Expand All @@ -236,14 +245,17 @@ public static void main(String[] argv)
* @param context
* @param input XML which describes the new communities and collections.
* @param output input, annotated with the new objects' identifiers.
* @param keepHandles true if Handles should be set from input.
* @throws IOException
* @throws ParserConfigurationException
* @throws SAXException
* @throws TransformerException
* @throws SQLException
*/
static void importStructure(Context context, InputStream input, OutputStream output)
throws IOException, ParserConfigurationException, SQLException, TransformerException, XPathExpressionException {
static void importStructure(Context context, InputStream input,
OutputStream output, boolean keepHandles)
throws IOException, ParserConfigurationException, SQLException,
TransformerException, XPathExpressionException {

// load the XML
Document document = null;
Expand Down Expand Up @@ -271,7 +283,19 @@ static void importStructure(Context context, InputStream input, OutputStream out
NodeList identifierNodes = (NodeList) xPath.compile("//*[@identifier]")
.evaluate(document, XPathConstants.NODESET);
if (identifierNodes.getLength() > 0) {
System.err.println("The input document has 'identifier' attributes, which will be ignored.");
if (!keepHandles) {
System.err.println("The input document has 'identifier' attributes, which will be ignored.");
} else {
for (int i = 0; i < identifierNodes.getLength() ; i++) {
String identifier = identifierNodes.item(i).getAttributes().item(0).getTextContent();
if (handleService.resolveToURL(context, identifier) != null) {
System.err.printf("The input document contains handle %s,"
+ " which is in use already. Aborting...%n",
identifier);
System.exit(1);
}
}
}
}

// load the mappings into the member variable hashmaps
Expand All @@ -296,7 +320,7 @@ static void importStructure(Context context, InputStream input, OutputStream out
.evaluate(document, XPathConstants.NODESET);

// run the import starting with the top level communities
elements = handleCommunities(context, first, null);
elements = handleCommunities(context, first, null, keepHandles);
} catch (TransformerException ex) {
System.err.format("Input content not understood: %s%n", ex.getMessage());
System.exit(1);
Expand Down Expand Up @@ -619,31 +643,36 @@ private static String getStringValue(Node node) {
* @param context the context of the request
* @param communities a nodelist of communities to create along with their sub-structures
* @param parent the parent community of the nodelist of communities to create
* @param keepHandles use Handles from input.
* @return an element array containing additional information regarding the
* created communities (e.g. the handles they have been assigned)
*/
private static Element[] handleCommunities(Context context, NodeList communities, Community parent)
throws TransformerException, SQLException, AuthorizeException, XPathExpressionException {
private static Element[] handleCommunities(Context context, NodeList communities,
Community parent, boolean keepHandles)
throws TransformerException, SQLException, AuthorizeException,
XPathExpressionException {
Element[] elements = new Element[communities.getLength()];
XPath xPath = XPathFactory.newInstance().newXPath();

for (int i = 0; i < communities.getLength(); i++) {
Community community;
Element element = new Element("community");
Node tn = communities.item(i);
Node identifier = tn.getAttributes().getNamedItem("identifier");

// create the community or sub community
if (parent != null) {
Community community;
if (null == identifier
|| StringUtils.isBlank(identifier.getNodeValue())
|| !keepHandles) {
community = communityService.create(parent, context);
} else {
community = communityService.create(null, context);
community = communityService.create(parent, context, identifier.getNodeValue());
}

// default the short description to be an empty string
communityService.setMetadataSingleValue(context, community,
MD_SHORT_DESCRIPTION, null, " ");

// now update the metadata
Node tn = communities.item(i);
for (Map.Entry<String, MetadataFieldName> entry : communityMap.entrySet()) {
NodeList nl = (NodeList) xPath.compile(entry.getKey()).evaluate(tn, XPathConstants.NODESET);
if (nl.getLength() == 1) {
Expand All @@ -669,6 +698,7 @@ private static Element[] handleCommunities(Context context, NodeList communities
// but it's here to keep it separate from the create process in
// case
// we want to move it or make it switchable later
Element element = new Element("community");
element.setAttribute("identifier", community.getHandle());

Element nameElement = new Element("name");
Expand Down Expand Up @@ -711,12 +741,16 @@ private static Element[] handleCommunities(Context context, NodeList communities
}

// handle sub communities
NodeList subCommunities = (NodeList) xPath.compile("community").evaluate(tn, XPathConstants.NODESET);
Element[] subCommunityElements = handleCommunities(context, subCommunities, community);
NodeList subCommunities = (NodeList) xPath.compile("community")
.evaluate(tn, XPathConstants.NODESET);
Element[] subCommunityElements = handleCommunities(context,
subCommunities, community, keepHandles);

// handle collections
NodeList collections = (NodeList) xPath.compile("collection").evaluate(tn, XPathConstants.NODESET);
Element[] collectionElements = handleCollections(context, collections, community);
NodeList collections = (NodeList) xPath.compile("collection")
.evaluate(tn, XPathConstants.NODESET);
Element[] collectionElements = handleCollections(context,
collections, community, keepHandles);

int j;
for (j = 0; j < subCommunityElements.length; j++) {
Expand All @@ -741,21 +775,31 @@ private static Element[] handleCommunities(Context context, NodeList communities
* @return an Element array containing additional information about the
* created collections (e.g. the handle)
*/
private static Element[] handleCollections(Context context, NodeList collections, Community parent)
private static Element[] handleCollections(Context context,
NodeList collections, Community parent, boolean keepHandles)
throws SQLException, AuthorizeException, XPathExpressionException {
Element[] elements = new Element[collections.getLength()];
XPath xPath = XPathFactory.newInstance().newXPath();

for (int i = 0; i < collections.getLength(); i++) {
Element element = new Element("collection");
Collection collection = collectionService.create(context, parent);
Node tn = collections.item(i);
Node identifier = tn.getAttributes().getNamedItem("identifier");

// Create the Collection.
Collection collection;
if (null == identifier
|| StringUtils.isBlank(identifier.getNodeValue())
|| !keepHandles) {
collection = collectionService.create(context, parent);
} else {
collection = collectionService.create(context, parent, identifier.getNodeValue());
}

// default the short description to the empty string
collectionService.setMetadataSingleValue(context, collection,
MD_SHORT_DESCRIPTION, Item.ANY, " ");

// import the rest of the metadata
Node tn = collections.item(i);
for (Map.Entry<String, MetadataFieldName> entry : collectionMap.entrySet()) {
NodeList nl = (NodeList) xPath.compile(entry.getKey()).evaluate(tn, XPathConstants.NODESET);
if (nl.getLength() == 1) {
Expand All @@ -766,6 +810,7 @@ private static Element[] handleCollections(Context context, NodeList collections

collectionService.update(context, collection);

Element element = new Element("collection");
element.setAttribute("identifier", collection.getHandle());

Element nameElement = new Element("name");
Expand Down

0 comments on commit 4d4f129

Please sign in to comment.