Package org.dspace.administer

Source Code of org.dspace.administer.StructBuilder

/**
* 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.administer;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.PosixParser;
import org.apache.xpath.XPathAPI;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
* This class deals with importing community and collection structures from
* an XML file.
*
* The XML file structure needs to be:
*
* <import_structure>
*     <community>
*         <name>....</name>
*         <community>...</community>
*         <collection>
*             <name>....</name>
*         </collection>
*     </community>
* </import_structure>
*
* it can be arbitrarily deep, and supports all the metadata elements
* that make up the community and collection metadata.  See the system
* documentation for more details
*
* @author Richard Jones
*
*/

public class StructBuilder
{
    /** the output xml document which will contain updated information about the
     * imported structure
     */
    private static org.jdom.Document xmlOutput = new org.jdom.Document(new Element("imported_structure"));
   
    /** a hashtable to hold metadata for the collection being worked on */
    private static Map<String, String> collectionMap = new HashMap<String, String>();
   
    /** a hashtable to hold metadata for the community being worked on */
    private static Map<String, String> communityMap = new HashMap<String, String>();
   
    /**
     * Main method to be run from the command line to import a structure into
     * DSpace
     *
     * This is of the form:
     *
     * StructBuilder -f [xml source] -e [administrator email] -o [output file]
     *
     * The output file will contain exactly the same as the source xml document, but
     * with the handle for each imported item added as an attribute.
     */
    public static void main(String[] argv)
      throws Exception
    {
        CommandLineParser parser = new PosixParser();

      Options options = new Options();

      options.addOption( "f", "file", true, "file");
      options.addOption( "e", "eperson", true, "eperson");
      options.addOption("o", "output", true, "output");
     
      CommandLine line = parser.parse( options, argv );
     
      String file = null;
      String eperson = null;
      String output = null;
     
      if (line.hasOption('f'))
      {
          file = line.getOptionValue('f');
      }
     
      if (line.hasOption('e'))
      {
          eperson = line.getOptionValue('e');
      }
     
      if (line.hasOption('o'))
      {
          output = line.getOptionValue('o');
      }
     
      if (output == null || eperson == null || file == null)
      {
          usage();
          System.exit(0);
      }
     
        // create a context
        Context context = new Context();
       
        // set the context
        context.setCurrentUser(EPerson.findByEmail(context, eperson));
        // load the XML
        Document document = loadXML(file);
       
        // run the preliminary validation, to be sure that the the XML document
        // is properly structured
        validate(document);
       
        // load the mappings into the member variable hashmaps
        communityMap.put("name", "name");
        communityMap.put("description", "short_description");
        communityMap.put("intro", "introductory_text");
        communityMap.put("copyright", "copyright_text");
        communityMap.put("sidebar", "side_bar_text");
       
        collectionMap.put("name", "name");
        collectionMap.put("description", "short_description");
        collectionMap.put("intro", "introductory_text");
        collectionMap.put("copyright", "copyright_text");
        collectionMap.put("sidebar", "side_bar_text");
        collectionMap.put("license", "license");
        collectionMap.put("provenance", "provenance_description");
       
        // get the top level community list
        NodeList first = XPathAPI.selectNodeList(document, "/import_structure/community");
       
        // run the import starting with the top level communities
        Element[] elements = handleCommunities(context, first, null);
       
        // generate the output
        Element root = xmlOutput.getRootElement();
        for (int i = 0; i < elements.length; i++)
        {
            root.addContent(elements[i]);
        }
       
        // finally write the string into the output file
        try
        {
            BufferedWriter out = new BufferedWriter(new FileWriter(output));
            out.write(new XMLOutputter().outputString(xmlOutput));
            out.close();
        }
        catch (IOException e)
        {
            System.out.println("Unable to write to output file " + output);
            System.exit(0);
        }
       
        context.complete();
    }
   
    /**
     * Output the usage information
     */
    private static void usage()
    {
        System.out.println("Usage: java StructBuilder -f <source XML file> -o <output file> -e <eperson email>");
        System.out.println("Communities will be created from the top level, and a map of communities to handles will be returned in the output file");
        return;
    }
   
    /**
     * Validate the XML document.  This method does not return, but if validation
     * fails it generates an error and ceases execution
     *
     * @param  document  the XML document object
     * @throws TransformerException
     *
     */
    private static void validate(org.w3c.dom.Document document)
      throws TransformerException
    {
        StringBuffer err = new StringBuffer();
        boolean trip = false;
       
        err.append("The following errors were encountered parsing the source XML\n");
        err.append("No changes have been made to the DSpace instance\n\n");
       
        NodeList first = XPathAPI.selectNodeList(document, "/import_structure/community");
        if (first.getLength() == 0)
        {
            err.append("-There are no top level communities in the source document");
            System.out.println(err.toString());
            System.exit(0);
        }
       
        String errs = validateCommunities(first, 1);
        if (errs != null)
        {
            err.append(errs);
            trip = true;
        }
       
        if (trip)
        {
            System.out.println(err.toString());
            System.exit(0);
        }
    }
   
    /**
     * Validate the communities section of the XML document.  This returns a string
     * containing any errors encountered, or null if there were no errors
     *
     * @param communities the NodeList of communities to validate
     * @param level the level in the XML document that we are at, for the purposes
     *       of error reporting
     *
     * @return the errors that need to be generated by the calling method, or null if
     *       no errors.
     */
    private static String validateCommunities(NodeList communities, int level)
      throws TransformerException
    {
        StringBuffer err = new StringBuffer();
        boolean trip = false;
        String errs = null;
       
        for (int i = 0; i < communities.getLength(); i++)
        {
            Node n = communities.item(i);
          NodeList name = XPathAPI.selectNodeList(n, "name");
          if (name.getLength() != 1)
          {
              String pos = Integer.toString(i + 1);
              err.append("-The level " + level + " community in position " + pos);
              err.append(" does not contain exactly one name field\n");
              trip = true;
          }
         
          // validate sub communities
          NodeList subCommunities = XPathAPI.selectNodeList(n, "community");
          String comErrs = validateCommunities(subCommunities, level + 1);
          if (comErrs != null)
          {
              err.append(comErrs);
              trip = true;
          }
         
          // validate collections
          NodeList collections = XPathAPI.selectNodeList(n, "collection");
          String colErrs = validateCollections(collections, level + 1);
          if (colErrs != null)
          {
              err.append(colErrs);
              trip = true;
          }
        }
       
        if (trip)
        {
            errs = err.toString();
        }
       
        return errs;
    }
   
    /**
     * validate the collection section of the XML document.  This generates a
     * string containing any errors encountered, or returns null if no errors
     *
     * @param collections a NodeList of collections to validate
     * @param level the level in the XML document for the purposes of error reporting
     *
     * @return the errors to be generated by the calling method, or null if none
     */
    private static String validateCollections(NodeList collections, int level)
      throws TransformerException
    {
        StringBuffer err = new StringBuffer();
        boolean trip = false;
        String errs = null;
       
        for (int i = 0; i < collections.getLength(); i++)
        {
            Node n = collections.item(i);
          NodeList name = XPathAPI.selectNodeList(n, "name");
          if (name.getLength() != 1)
          {
              String pos = Integer.toString(i + 1);
              err.append("-The level " + level + " collection in position " + pos);
              err.append(" does not contain exactly one name field\n");
              trip = true;
          }
        }
       
        if (trip)
        {
            errs = err.toString();
        }
       
        return errs;
    }
   
    /**
     * Load in the XML from file.
     *
     * @param filename
     *            the filename to load from
     *
     * @return the DOM representation of the XML file
     */
    private static org.w3c.dom.Document loadXML(String filename)
      throws IOException, ParserConfigurationException, SAXException
    {
        DocumentBuilder builder = DocumentBuilderFactory.newInstance()
                .newDocumentBuilder();

        org.w3c.dom.Document document = builder.parse(new File(filename));
       
        return document;
    }
   
    /**
     * Return the String value of a Node
     *
     * @param node the node from which we want to extract the string value
     *
     * @return the string value of the node
     */
    public static String getStringValue(Node node)
    {
        String value = node.getNodeValue();

        if (node.hasChildNodes())
        {
            Node first = node.getFirstChild();

            if (first.getNodeType() == Node.TEXT_NODE)
            {
                return first.getNodeValue().trim();
            }
        }

        return value;
    }
   
    /**
     * Take a node list of communities and build the structure from them, delegating
     * to the relevant methods in this class for sub-communities and collections
     *
     * @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
     *
     * @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, Exception
    {
        Element[] elements = new Element[communities.getLength()];
       
        for (int i = 0; i < communities.getLength(); i++)
        {
            Community community;
            Element element = new Element("community");
           
            // create the community or sub community
            if (parent != null)
            {
                community = parent.createSubcommunity();
            }
            else
            {
                community = Community.create(null, context);
            }
           
            // default the short description to be an empty string
            community.setMetadata("short_description", " ");
           
            // now update the metadata
            Node tn = communities.item(i);
            for (Map.Entry<String, String> entry : communityMap.entrySet())
            {
                NodeList nl = XPathAPI.selectNodeList(tn, entry.getKey());
                if (nl.getLength() == 1)
                {
                    community.setMetadata(entry.getValue(), getStringValue(nl.item(0)));
                }
            }
           
            // FIXME: at the moment, if the community already exists by name
            // then this will throw a PSQLException on a duplicate key
            // violation
            // Ideally we'd skip this row and continue to create sub
            // communities
            // and so forth where they don't exist, but it's proving
            // difficult
            // to isolate the community that already exists without hitting
            // the database directly.
            community.update();
           
            // build the element with the handle that identifies the new
            // community
            // along with all the information that we imported here
            // This looks like a lot of repetition of getting information
            // from above
            // 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.setAttribute("identifier", community.getHandle());
           
            Element nameElement = new Element("name");
            nameElement.setText(community.getMetadata("name"));
            element.addContent(nameElement);
           
            if (community.getMetadata("short_description") != null)
            {
                Element descriptionElement = new Element("description");
                descriptionElement.setText(community.getMetadata("short_description"));
                element.addContent(descriptionElement);
            }
           
            if (community.getMetadata("introductory_text") != null)
            {
                Element introElement = new Element("intro");
                introElement.setText(community.getMetadata("introductory_text"));
                element.addContent(introElement);
            }
           
            if (community.getMetadata("copyright_text") != null)
            {
                Element copyrightElement = new Element("copyright");
                copyrightElement.setText(community.getMetadata("copyright_text"));
                element.addContent(copyrightElement);
            }
           
            if (community.getMetadata("side_bar_text") != null)
            {
                Element sidebarElement = new Element("sidebar");
                sidebarElement.setText(community.getMetadata("side_bar_text"));
                element.addContent(sidebarElement);
            }
           
            // handle sub communities
            NodeList subCommunities = XPathAPI.selectNodeList(tn, "community");
            Element[] subCommunityElements = handleCommunities(context, subCommunities, community);
           
            // handle collections
            NodeList collections = XPathAPI.selectNodeList(tn, "collection");
            Element[] collectionElements = handleCollections(context, collections, community);
           
            int j;
            for (j = 0; j < subCommunityElements.length; j++)
            {
                element.addContent(subCommunityElements[j]);
            }
            for (j = 0; j < collectionElements.length; j++)
            {
                element.addContent(collectionElements[j]);
            }
           
            elements[i] = element;
        }
       
        return elements;
    }
   
    /**
     *  Take a node list of collections and create the structure from them
     *
     * @param context the context of the request
     * @param collections the node list of collections to be created
     * @param parent the parent community to whom the collections belong
     *
     * @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)
      throws TransformerException, SQLException, AuthorizeException, IOException, Exception
    {
        Element[] elements = new Element[collections.getLength()];
       
        for (int i = 0; i < collections.getLength(); i++)
        {
            Element element = new Element("collection");
            Collection collection = parent.createCollection();
           
            // default the short description to the empty string
            collection.setMetadata("short_description", " ");
           
            // import the rest of the metadata
            Node tn = collections.item(i);
            for (Map.Entry<String, String> entry : collectionMap.entrySet())
            {
                NodeList nl = XPathAPI.selectNodeList(tn, entry.getKey());
                if (nl.getLength() == 1)
                {
                    collection.setMetadata(entry.getValue(), getStringValue(nl.item(0)));
                }
            }
           
            collection.update();
           
            element.setAttribute("identifier", collection.getHandle());
           
            Element nameElement = new Element("name");
            nameElement.setText(collection.getMetadata("name"));
            element.addContent(nameElement);
           
            if (collection.getMetadata("short_description") != null)
            {
                Element descriptionElement = new Element("description");
                descriptionElement.setText(collection.getMetadata("short_description"));
                element.addContent(descriptionElement);
            }
           
            if (collection.getMetadata("introductory_text") != null)
            {
                Element introElement = new Element("intro");
                introElement.setText(collection.getMetadata("introductory_text"));
                element.addContent(introElement);
            }
           
            if (collection.getMetadata("copyright_text") != null)
            {
                Element copyrightElement = new Element("copyright");
                copyrightElement.setText(collection.getMetadata("copyright_text"));
                element.addContent(copyrightElement);
            }
           
            if (collection.getMetadata("side_bar_text") != null)
            {
                Element sidebarElement = new Element("sidebar");
                sidebarElement.setText(collection.getMetadata("side_bar_text"));
                element.addContent(sidebarElement);
            }
           
            if (collection.getMetadata("license") != null)
            {
                Element sidebarElement = new Element("license");
                sidebarElement.setText(collection.getMetadata("license"));
                element.addContent(sidebarElement);
            }
           
            if (collection.getMetadata("provenance_description") != null)
            {
                Element sidebarElement = new Element("provenance");
                sidebarElement.setText(collection.getMetadata("provenance_description"));
                element.addContent(sidebarElement);
            }
           
            elements[i] = element;
        }
       
        return elements;
    }
   
}
TOP

Related Classes of org.dspace.administer.StructBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.