org.dspace.content.crosswalk.XSLTDisseminationCrosswalk.java Source code

Java tutorial

Introduction

Here is the source code for org.dspace.content.crosswalk.XSLTDisseminationCrosswalk.java

Source

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

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DCValue;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.content.Site;
import org.dspace.content.authority.Choices;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.PluginManager;
import org.dspace.handle.HandleManager;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.Verifier;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.transform.XSLTransformException;
import org.jdom.transform.XSLTransformer;

/**
 * Configurable XSLT-driven dissemination Crosswalk
 * <p>
 * See the XSLTCrosswalk superclass for details on configuration.
 * <p>
 * <h3>Additional Configuration of Dissemination crosswalk:</h3>
 * The disseminator also needs to be configured with an XML Namespace
 * (including prefix and URI) and an XML Schema for output format.  This
 * is configured on additional properties in the DSpace Configuration, i.e.:
 * <pre>
 *   crosswalk.dissemination.<i>PluginName</i>.namespace.<i>Prefix</i> = <i>namespace-URI</i>
 *   crosswalk.dissemination.<i>PluginName</i>.schemaLocation = <i>schemaLocation value</i>
 *   crosswalk.dissemination.<i>PluginName</i>.preferList = <i>boolean</i> (default is false)
 * </pre>
 * For example:
 * <pre>
 *   crosswalk.dissemination.qdc.namespace.dc = http://purl.org/dc/elements/1.1/
 *   crosswalk.dissemination.qdc.namespace.dcterms = http://purl.org/dc/terms/
 *   crosswalk.dissemination.qdc.schemaLocation = \
 *      http://purl.org/dc/elements/1.1/ http://dublincore.org/schemas/xmls/qdc/2003/04/02/qualifieddc.xsd
 *   crosswalk.dissemination.qdc.preferList = true
 * </pre>
 *
 * @author Larry Stone
 * @author Scott Phillips
 * @author Pascal-Nicolas Becker
 * @version $Revision$
 * @see XSLTCrosswalk
 */
public class XSLTDisseminationCrosswalk extends XSLTCrosswalk implements DisseminationCrosswalk {
    /** log4j category */
    private static Logger log = Logger.getLogger(XSLTDisseminationCrosswalk.class);

    /** DSpace context, will be created if XSLTDisseminationCrosswalk had been started by command-line. */
    private static Context context;

    private static final String DIRECTION = "dissemination";

    private static String aliases[] = makeAliases(DIRECTION);

    public static String[] getPluginNames() {
        return (String[]) ArrayUtils.clone(aliases);
    }

    // namespace and schema; don't worry about initializing these
    // until there's an instance, so do it in constructor.
    private String schemaLocation = null;

    private Namespace namespaces[] = null;

    private boolean preferList = false;

    // load the namespace and schema from config
    private void init() throws CrosswalkInternalException {
        if (namespaces != null || schemaLocation != null) {
            return;
        }
        String myAlias = getPluginInstanceName();
        if (myAlias == null) {
            log.error(
                    "Must use PluginManager to instantiate XSLTDisseminationCrosswalk so the class knows its name.");
            throw new CrosswalkInternalException(
                    "Must use PluginManager to instantiate XSLTDisseminationCrosswalk so the class knows its name.");
        }

        // all configs for this plugin instance start with this:
        String prefix = CONFIG_PREFIX + DIRECTION + "." + myAlias + ".";

        // get the schema location string, should already be in the
        // right format for value of "schemaLocation" attribute.
        schemaLocation = ConfigurationManager.getProperty(prefix + "schemaLocation");
        if (schemaLocation == null) {
            log.warn("No schemaLocation for crosswalk=" + myAlias + ", key=" + prefix + "schemaLocation");
        }

        // sanity check: schemaLocation should have space.
        else if (schemaLocation.length() > 0 && schemaLocation.indexOf(' ') < 0) {
            log.warn("Possible INVALID schemaLocation (no space found) for crosswalk=" + myAlias + ", key=" + prefix
                    + "schemaLocation" + "\n\tCorrect format is \"{namespace} {schema-URL}\"");
        }

        // grovel for namespaces of the form:
        //  crosswalk.diss.{PLUGIN_NAME}.namespace.{PREFIX} = {URI}
        String nsPrefix = prefix + "namespace.";
        Enumeration<String> pe = (Enumeration<String>) ConfigurationManager.propertyNames();
        List<Namespace> nsList = new ArrayList<Namespace>();
        while (pe.hasMoreElements()) {
            String key = pe.nextElement();
            if (key.startsWith(nsPrefix)) {
                nsList.add(Namespace.getNamespace(key.substring(nsPrefix.length()),
                        ConfigurationManager.getProperty(key)));
            }
        }
        namespaces = nsList.toArray(new Namespace[nsList.size()]);

        preferList = ConfigurationManager.getBooleanProperty(prefix + "preferList", false);
    }

    /**
     * Return the namespace used by this crosswalk.
     *
     * @see DisseminationCrosswalk
     */
    public Namespace[] getNamespaces() {
        try {
            init();
        } catch (CrosswalkInternalException e) {
            log.error(e.toString());
        }
        return (Namespace[]) ArrayUtils.clone(namespaces);
    }

    /**
     * Return the schema location used by this crosswalk.
     *
     * @see DisseminationCrosswalk
     */
    public String getSchemaLocation() {
        try {
            init();
        } catch (CrosswalkInternalException e) {
            log.error(e.toString());
        }
        return schemaLocation;
    }

    /**
     * Disseminate the DSpace item, collection, or community.
     *
     * @see DisseminationCrosswalk
     */
    public Element disseminateElement(DSpaceObject dso)
            throws CrosswalkException, IOException, SQLException, AuthorizeException {
        int type = dso.getType();
        if (!(type == Constants.ITEM || type == Constants.COLLECTION || type == Constants.COMMUNITY)) {
            throw new CrosswalkObjectNotSupported(
                    "XSLTDisseminationCrosswalk can only crosswalk items, collections, and communities.");
        }

        init();

        XSLTransformer xform = getTransformer(DIRECTION);
        if (xform == null) {
            throw new CrosswalkInternalException(
                    "Failed to initialize transformer, probably error loading stylesheet.");
        }

        try {
            Document ddim = new Document(createDIM(dso));
            Document result = xform.transform(ddim);
            Element root = result.getRootElement();
            root.detach();
            return root;
        } catch (XSLTransformException e) {
            log.error("Got error: " + e.toString());
            throw new CrosswalkInternalException("XSL translation failed: " + e.toString(), e);
        }
    }

    /**
     * Disseminate the DSpace item, collection, or community.
     *
     * @see DisseminationCrosswalk
     */
    public List<Element> disseminateList(DSpaceObject dso)
            throws CrosswalkException, IOException, SQLException, AuthorizeException {
        int type = dso.getType();
        if (!(type == Constants.ITEM || type == Constants.COLLECTION || type == Constants.COMMUNITY)) {
            throw new CrosswalkObjectNotSupported(
                    "XSLTDisseminationCrosswalk can only crosswalk a items, collections, and communities.");
        }

        init();

        XSLTransformer xform = getTransformer(DIRECTION);
        if (xform == null) {
            throw new CrosswalkInternalException(
                    "Failed to initialize transformer, probably error loading stylesheet.");
        }

        try {
            return xform.transform(createDIM(dso).getChildren());
        } catch (XSLTransformException e) {
            log.error("Got error: " + e.toString());
            throw new CrosswalkInternalException("XSL translation failed: " + e.toString(), e);
        }
    }

    /**
     * Determine is this crosswalk can dessiminate the given object.
     *
     * @see DisseminationCrosswalk
     */
    public boolean canDisseminate(DSpaceObject dso) {
        return dso.getType() == Constants.ITEM;
    }

    /**
     * return true if this crosswalk prefers the list form over an single
     * element, otherwise false.
     *
     * @see DisseminationCrosswalk
     */
    public boolean preferList() {
        try {
            init();
        } catch (CrosswalkInternalException e) {
            log.error(e.toString());
        }
        return preferList;
    }

    /**
     * Generate an intermediate representation of a DSpace object.
     *
     * @param dso The dspace object to build a representation of.
     */
    public static Element createDIM(DSpaceObject dso, DCValue[] dcvs) {
        Element dim = new Element("dim", DIM_NS);
        String type = Constants.typeText[dso.getType()];
        dim.setAttribute("dspaceType", type);

        for (int i = 0; i < dcvs.length; i++) {
            DCValue dcv = dcvs[i];
            Element field = createField(dcv.schema, dcv.element, dcv.qualifier, dcv.language, dcv.value,
                    dcv.authority, dcv.confidence);
            dim.addContent(field);
        }
        return dim;
    }

    /**
     * Generate an intermediate representation of a DSpace object.
     *
     * @param dso The dspace object to build a representation of.
     */
    public static Element createDIM(DSpaceObject dso) {
        if (dso.getType() == Constants.ITEM) {
            Item item = (Item) dso;
            return createDIM(dso, item.getMetadata(Item.ANY, Item.ANY, Item.ANY, Item.ANY));
        } else {
            Element dim = new Element("dim", DIM_NS);
            String type = Constants.typeText[dso.getType()];
            dim.setAttribute("dspaceType", type);

            if (dso.getType() == Constants.COLLECTION) {
                Collection collection = (Collection) dso;

                String description = collection.getMetadata("introductory_text");
                String description_abstract = collection.getMetadata("short_description");
                String description_table = collection.getMetadata("side_bar_text");
                String identifier_uri = "hdl:" + collection.getHandle();
                String provenance = collection.getMetadata("provenance_description");
                String rights = collection.getMetadata("copyright_text");
                String rights_license = collection.getMetadata("license");
                String title = collection.getMetadata("name");

                dim.addContent(createField("dc", "description", null, null, description));
                dim.addContent(createField("dc", "description", "abstract", null, description_abstract));
                dim.addContent(createField("dc", "description", "tableofcontents", null, description_table));
                dim.addContent(createField("dc", "identifier", "uri", null, identifier_uri));
                dim.addContent(createField("dc", "provenance", null, null, provenance));
                dim.addContent(createField("dc", "rights", null, null, rights));
                dim.addContent(createField("dc", "rights", "license", null, rights_license));
                dim.addContent(createField("dc", "title", null, null, title));
            } else if (dso.getType() == Constants.COMMUNITY) {
                Community community = (Community) dso;

                String description = community.getMetadata("introductory_text");
                String description_abstract = community.getMetadata("short_description");
                String description_table = community.getMetadata("side_bar_text");
                String identifier_uri = "hdl:" + community.getHandle();
                String rights = community.getMetadata("copyright_text");
                String title = community.getMetadata("name");

                dim.addContent(createField("dc", "description", null, null, description));
                dim.addContent(createField("dc", "description", "abstract", null, description_abstract));
                dim.addContent(createField("dc", "description", "tableofcontents", null, description_table));
                dim.addContent(createField("dc", "identifier", "uri", null, identifier_uri));
                dim.addContent(createField("dc", "rights", null, null, rights));
                dim.addContent(createField("dc", "title", null, null, title));
            } else if (dso.getType() == Constants.SITE) {
                Site site = (Site) dso;

                String identifier_uri = "hdl:" + site.getHandle();
                String title = site.getName();
                String url = site.getURL();

                //FIXME: adding two URIs for now (site handle and URL), in case site isn't using handles
                dim.addContent(createField("dc", "identifier", "uri", null, identifier_uri));
                dim.addContent(createField("dc", "identifier", "uri", null, url));
                dim.addContent(createField("dc", "title", null, null, title));
            }
            // XXX FIXME: Nothing to crosswalk for bitstream?
            return dim;
        }
    }

    /**
    * Create a new DIM field element with the given attributes.
    *
    * @param schema The schema the DIM field belongs to.
    * @param element The element the DIM field belongs to.
    * @param qualifier The qualifier the DIM field belongs to.
    * @param language The language the DIM field belongs to.
    * @param value The value of the DIM field.
    * @return A new DIM field element
    */
    private static Element createField(String schema, String element, String qualifier, String language,
            String value) {
        return createField(schema, element, qualifier, language, value, null, -1);
    }

    /**
     * Create a new DIM field element with the given attributes.
     *
     * @param schema The schema the DIM field belongs to.
     * @param element The element the DIM field belongs to.
     * @param qualifier The qualifier the DIM field belongs to.
     * @param language The language the DIM field belongs to.
     * @param value The value of the DIM field.
     * @param authority The authority
     * @param confidence confidence in the authority
     * @return A new DIM field element
     */
    private static Element createField(String schema, String element, String qualifier, String language,
            String value, String authority, int confidence) {
        Element field = new Element("field", DIM_NS);
        field.setAttribute("mdschema", schema);
        field.setAttribute("element", element);
        if (qualifier != null) {
            field.setAttribute("qualifier", qualifier);
        }
        if (language != null) {
            field.setAttribute("lang", language);
        }

        field.setText(checkedString(value));

        if (authority != null) {
            field.setAttribute("authority", authority);
            field.setAttribute("confidence", Choices.getConfidenceText(confidence));
        }

        return field;
    }

    // Return string with non-XML characters (i.e. low control chars) excised.
    private static String checkedString(String value) {
        if (value == null) {
            return null;
        }
        String reason = Verifier.checkCharacterData(value);
        if (reason == null) {
            return value;
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Filtering out non-XML characters in string, reason=" + reason);
            }
            StringBuffer result = new StringBuffer(value.length());
            for (int i = 0; i < value.length(); ++i) {
                char c = value.charAt(i);
                if (Verifier.isXMLCharacter((int) c)) {
                    result.append(c);
                }
            }
            return result.toString();
        }
    }

    /**
     * Simple command-line rig for testing the DIM output of a stylesheet.
     * Usage:  java XSLTDisseminationCrosswalk  <crosswalk-name> <handle> [output-file]
     */
    public static void main(String[] argv) throws Exception {
        log.error("started.");
        if (argv.length < 2 || argv.length > 3) {
            System.err.println("Usage:  java XSLTDisseminationCrosswalk <crosswalk-name> <handle> [output-file]");
            log.error("You started Dissemination Crosswalk Test/Export with a wrong number of parameters.");
            System.exit(1);
        }

        String xwalkname = argv[0];
        String handle = argv[1];
        OutputStream out = System.out;
        if (argv.length > 2) {
            try {
                out = new FileOutputStream(argv[2]);
            } catch (FileNotFoundException e) {
                System.err.println("Can't write to the specified file: " + e.getMessage());
                System.err.println("Will write output to stdout.");
            }
        }

        DisseminationCrosswalk xwalk = (DisseminationCrosswalk) PluginManager
                .getNamedPlugin(DisseminationCrosswalk.class, xwalkname);
        if (xwalk == null) {
            System.err.println("Error: Cannot find a DisseminationCrosswalk plugin for: \"" + xwalkname + "\"");
            log.error("Cannot find the Dissemination Crosswalk plugin.");
            System.exit(1);
        }

        context = new Context();
        context.turnOffAuthorisationSystem();

        DSpaceObject dso = null;
        try {
            dso = HandleManager.resolveToObject(context, handle);
        } catch (SQLException e) {
            System.err.println(
                    "Error: A problem with the database connection occurred, check logs for further information.");
            System.exit(1);
        }

        if (null == dso) {
            System.err.println("Can't find a DSpaceObject with the handle \"" + handle + "\"");
            System.exit(1);
        }

        if (!xwalk.canDisseminate(dso)) {
            System.err.println("Dissemination Crosswalk can't disseminate this DSpaceObject.");
            log.error("Dissemination Crosswalk can't disseminate this DSpaceObject.");
            System.exit(1);
        }

        Element root = null;
        try {
            root = xwalk.disseminateElement(dso);
        } catch (Exception e) {
            // as this script is for testing dissemination crosswalks, we want
            // verbose information in case of an exception.
            System.err.println("An error occurred while processing the dissemination crosswalk.");
            System.err.println("=== Error Message ===");
            System.err.println(e.getMessage());
            System.err.println("===  Stack Trace  ===");
            e.printStackTrace();
            System.err.println("=====================");
            log.error("Caught: " + e.toString() + ".");
            log.error(e.getMessage());
            log.error(e.getStackTrace());
            System.exit(1);
        }

        try {
            XMLOutputter xmlout = new XMLOutputter(Format.getPrettyFormat());
            xmlout.output(new Document(root), out);
        } catch (Exception e) {
            // as this script is for testing dissemination crosswalks, we want
            // verbose information in case of an exception.
            System.err.println("An error occurred after processing the dissemination crosswalk.");
            System.err.println("The error occurred while trying to print the generated XML.");
            System.err.println("=== Error Message ===");
            System.err.println(e.getMessage());
            System.err.println("===  Stack Trace  ===");
            System.err.println(e.getStackTrace());
            System.err.println("=====================");
            log.error("Caught: " + e.toString() + ".");
            log.error(e.getMessage());
            log.error(e.getStackTrace());
            System.exit(1);
        }

        context.complete();
        if (out instanceof FileOutputStream) {
            out.close();
        }
    }
}