bridge.toolkit.commands.PreProcess.java Source code

Java tutorial

Introduction

Here is the source code for bridge.toolkit.commands.PreProcess.java

Source

/**
 * This file is part of the S1000D Transformation Toolkit 
 * project hosted on Sourceforge.net. See the accompanying 
 * license.txt file for applicable licenses.
 */
package bridge.toolkit.commands;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.xpath.XPath;

import bridge.toolkit.ResourceMapException;
import bridge.toolkit.util.DMParser;
import bridge.toolkit.util.Keys;
import bridge.toolkit.util.URNMapper;

/**
 * Module in the toolkit that transforms the SCPM into a imsmanifest.xml
 * file and generates a urn_resource_map.xml file from the resource package.
 */
public class PreProcess implements Command {
    /**
     * Provides a way to parse S1000D files and to find data model codes.
     */
    private static DMParser dmParser;

    /**
     * JDOM Document that is used to create the imsmanifest.xml file.
     */
    private static Document manifest;

    /**
     * JDOM Document that is used to create the urn_resource_map.xml file.
     */
    private static Document urn_map;

    /**
     * Location of the XSLT transform file.
     */
    private static final String TRANSFORM_FILE = "preProcessTransform.xsl";

    /**
     * InputStream for the xsl file that is used to transform the SCPM file to 
     * an imsmanifest.xml file.
     */
    private static InputStream transform;

    /**
     * String that represents the location of the resource package.
     */
    private static String src_dir;

    /**
     * Message that is returned if the conversion from SCPM to imsmanifest.xml 
     * file is unsuccessful.
     */
    private static final String CONVERSION_FAILED = "Conversion of SCPM to IMS Manifest was unsuccessful";

    /**
     * Constructor
     */
    public PreProcess() {
        dmParser = new DMParser();
    }

    /** 
     * The unit of processing work to be performed for the PreProcess module.
     * @see org.apache.commons.chain.Command#execute(org.apache.commons.chain.Context)
     */
    @SuppressWarnings("unchecked")
    public boolean execute(Context ctx) {
        System.out.println("Executing PreProcess");
        if ((ctx.get(Keys.SCPM_FILE) != null) && (ctx.get(Keys.RESOURCE_PACKAGE) != null)) {

            src_dir = (String) ctx.get(Keys.RESOURCE_PACKAGE);

            List<File> src_files = new ArrayList<File>();
            try {
                src_files = URNMapper.getSourceFiles(src_dir);
            } catch (NullPointerException npe) {
                System.out.println(CONVERSION_FAILED);
                System.out.println("The 'Resource Package' is empty.");
                return PROCESSING_COMPLETE;
            } catch (JDOMException e) {
                System.out.println(CONVERSION_FAILED);
                e.printStackTrace();
                return PROCESSING_COMPLETE;
            } catch (IOException e) {
                System.out.println(CONVERSION_FAILED);
                e.printStackTrace();
                return PROCESSING_COMPLETE;
            }

            urn_map = URNMapper.writeURNMap(src_files, "");

            transform = this.getClass().getResourceAsStream(TRANSFORM_FILE);

            try {
                doTransform((String) ctx.get(Keys.SCPM_FILE));

                addResources(urn_map);

                processDeps(mapDependencies());
            } catch (ResourceMapException e) {
                System.out.println(CONVERSION_FAILED);
                e.printTrace();
                return PROCESSING_COMPLETE;
            } catch (JDOMException jde) {
                System.out.println(CONVERSION_FAILED);
                jde.printStackTrace();
                return PROCESSING_COMPLETE;
            } catch (TransformerException te) {
                System.out.println(CONVERSION_FAILED);
                te.printStackTrace();
                return PROCESSING_COMPLETE;
            } catch (IOException e1) {
                System.out.println(CONVERSION_FAILED);
                e1.printStackTrace();
                return PROCESSING_COMPLETE;
            }

            ctx.put(Keys.URN_MAP, urn_map);
            ctx.put(Keys.XML_SOURCE, manifest);

            System.out.println("Conversion of SCPM to IMS Manifest was successful");
        } else {
            System.out.println(CONVERSION_FAILED);
            System.out.println("One of the required Context entries for the " + this.getClass().getSimpleName()
                    + " command to be executed was null");
            return PROCESSING_COMPLETE;
        }
        return CONTINUE_PROCESSING;
    }

    /**
     * Performs the transformation of the S1000D file to the imsmanifest.xml.
     * 
     * @param scpm_source String that represents the location of the S1000D 
     * SCPM file.
     * @throws IOException
     * @throws JDOMException
     * @throws TransformerException
     */
    private static void doTransform(String scpm_source) throws IOException, JDOMException, TransformerException {
        TransformerFactory tFactory = TransformerFactory.newInstance();

        Transformer transformer = tFactory.newTransformer(new StreamSource(transform));

        File the_manifest = File.createTempFile("imsmanifest", ".xml");

        transformer.transform(new StreamSource(scpm_source), new StreamResult(new FileOutputStream(the_manifest)));

        manifest = dmParser.getDoc(the_manifest);

    }

    /**
     * Adds 'resource' elements to the imsmanifest.xml file for every file found
     * in the resource package that is provided.
     * 
     * @param urn_map JDOM Document that is used to create the urn_resource_map.xml file.
     * @throws ResourceMapException  Exception that is thrown when a URN is generated from in two different 
     * files in the Resource Package.
     * @throws IOException 
     * @throws JDOMException 
     */
    @SuppressWarnings("unchecked")
    private static void addResources(Document urn_map) throws ResourceMapException, JDOMException, IOException {
        Element resources = manifest.getRootElement().getChild("resources", null);
        Namespace ns = resources.getNamespace();
        Namespace adlcpNS = Namespace.getNamespace("adlcp", "http://www.adlnet.org/xsd/adlcp_v1p3");
        List<Element> urns = urn_map.getRootElement().getChildren();
        for (int i = 0; i < urns.toArray().length; i++) {
            String the_href = "resources/s1000d/" + urns.get(i).getChildText("target", null);
            String the_name = urns.get(i).getAttributeValue("name").replace("URN:S1000D:", "");

            Element resource = new Element("resource");
            Attribute id = new Attribute("identifier", the_name);
            Attribute type = new Attribute("type", "webcontent");
            Attribute scormtype = new Attribute("scormType", "asset", adlcpNS);
            Attribute href = new Attribute("href", the_href);
            resource.setAttribute(id);
            resource.setAttribute(type);
            resource.setAttribute(scormtype);
            resource.setAttribute(href);

            Element file = new Element("file", ns);
            Attribute fileHref = new Attribute("href", the_href);
            file.setAttribute(fileHref);
            resource.addContent(file);

            // get the parent element namespace (implicit - the default
            // namespace) and assign to resource element to prevent empty
            // default namespace in resource element

            resources.addContent(resource);
            resource.setNamespace(ns);

        }

    }

    /**
     * Walks through the 'resource' elements in the imsmanifest.xml file (that 
     * were generated from the 'scoEntry' elements in the SCPM) and creates a map
     * using the 'identifier' attribute of each 'resource' element as the key 
     * and a list of all of the 'identifierref' attributes of the 'dependency' 
     * child elements for that 'resource' element as the value.  This map is then
     * used by the processDeps method.
     * 
     * @return Map<String, List<String>> A Map that holds the 'identifierref' 
     * attributes of all of a SCO 'resource' elements and a List of all of 
     * 'dependency' children. 
     * @throws ResourceMapException  Exception that is thrown when a URN is generated from in two different 
     * files in the Resource Package.
     * @throws IOException 
     * @throws JDOMException 
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private static Map<String, List<String>> mapDependencies()
            throws ResourceMapException, JDOMException, IOException {
        Namespace ns = Namespace.getNamespace("ns", "http://www.imsglobal.org/xsd/imscp_v1p1");
        Map<String, List<String>> sco_map = new HashMap<String, List<String>>();

        // get the manifest sco resources
        XPath xp;
        xp = XPath.newInstance("//ns:resource[@adlcpNS:scormType='sco']");
        xp.addNamespace("ns", "http://www.imsglobal.org/xsd/imscp_v1p1");
        xp.addNamespace("adlcpNS", "http://www.adlnet.org/xsd/adlcp_v1p3");
        List<Element> scos = (ArrayList) xp.selectNodes(manifest);

        // iterate through the sco list
        Iterator<Element> iter = scos.iterator();
        while (iter.hasNext()) {
            Element sco = iter.next();
            String sco_identifier = sco.getAttributeValue("identifier");
            // CHANGED STW 11/16 - get list of sco dependencies vice files - use
            // the identifierref to get the resource identifer/href
            List<Element> resFiles = sco.getChildren("dependency", ns);

            List<String> idrefs = new ArrayList<String>();
            for (int i = 0; i < resFiles.size(); i++) {
                Element resFile = resFiles.get(i);

                String identifierref = (resFile.getAttributeValue("identifierref"));
                if (identifierref != "") {
                    idrefs.add(identifierref);
                }
            }

            sco_map.put(sco_identifier, idrefs);
        }

        return sco_map;

    }

    /**
     * Walks through all of the 'dependency' elements for each SCO 'resource'
     * element in the imsmanifest.xml file and searches for referenced media
     * (ICN) files and referenced data modules (DMC) files. The found referenced
     * files are then added as 'dependency' elements to the specific SCO
     * 'resource' element. 
     * 
     * @param sco_map Map<String, List<String>> that holds the 'identifierref' 
     * attributes of all of a SCO 'resource' elements and a List of all of 
     * 'dependency' children.
     * @throws ResourceMapException Exception that is thrown when a URN is generated from in two different 
     * files in the Resource Package.
     * @throws JDOMException
     * @throws IOException 
     */
    private static void processDeps(@SuppressWarnings("rawtypes") Map sco_map)
            throws ResourceMapException, JDOMException, IOException {
        Namespace default_ns = manifest.getRootElement().getChild("resources", null).getNamespace();
        Element sco_resource = null;
        List<String> dependencies = null;
        XPath xp = null;
        String sco_key;

        // the updated map will track dependency addition to the sco to prevent
        // duplicate entries before writing to the manifest
        // iterate the sco map entries
        @SuppressWarnings("unchecked")
        Iterator<Entry<String, List<String>>> iter = sco_map.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<String, List<String>> pairs = (Map.Entry<String, List<String>>) iter.next();
            // store the sco identifier
            sco_key = pairs.getKey();
            xp = XPath.newInstance("//ns:resource[@identifier='" + sco_key + "']");
            xp.addNamespace("ns", "http://www.imsglobal.org/xsd/imscp_v1p1");
            sco_resource = (Element) xp.selectSingleNode(manifest);

            Iterator<String> value = pairs.getValue().iterator();
            // store the icn references
            dependencies = new ArrayList<String>();
            while (value.hasNext()) {
                Element resource = null;
                String str_current = value.next();
                Document dmDoc = getResourceHref(sco_key, str_current);
                dependencies = addICNDependencies(dependencies, dmDoc);

                // get the dm refs
                List<String> dmrefs = searchForDmRefs(dmDoc, sco_resource);
                Iterator<String> dmref_iter = dmrefs.iterator();
                while (dmref_iter.hasNext()) {
                    String dmref = dmref_iter.next();
                    if (!dependencies.contains(dmref)) {
                        dependencies.add(dmref);
                        //add dmref icn files as dependencies
                        Document dmRefDoc = getResourceHref(sco_key, dmref);
                        dependencies = addICNDependencies(dependencies, dmRefDoc);
                    }
                }
            }
            // add the dependencies
            // Iterate the icn list add dependency elements to manifest
            Iterator<String> dependency_iter = dependencies.iterator();
            while (dependency_iter.hasNext()) {
                String identifierref = dependency_iter.next();
                Element dependency = new Element("dependency");

                dependency.setAttribute("identifierref", identifierref);
                sco_resource.addContent(dependency);
                dependency.setNamespace(default_ns);
            }
        }
    }

    /**
     * Adds ICN references from referenced data modules to the list of files
     * to be used as dependencies in the resources section.  
     * 
     * @param dependencies - List of Strings that will be used as "Dependency" elements for "SCO" resources.
     * @param dmDoc - Document object that represents the data module file being used. 
     * @throws JDOMException
     */
    private static List<String> addICNDependencies(List<String> dependencies, Document dmDoc) throws JDOMException {
        // reach into the dm docs and find ICN references
        List<String> icnRefs = searchForICN(dmDoc);

        Iterator<String> icn_iter = icnRefs.iterator();
        while (icn_iter.hasNext()) {
            String the_icn = icn_iter.next();
            if (!dependencies.contains(the_icn)) {
                dependencies.add(the_icn);
            }
        }

        return dependencies;
    }

    /**
     * Finds the file that is referenced in a 'href' attribute associated with a 
     * given 'resource' element in imsmanifest.xml file. 
     * 
     * @param sco_key - String that represents the value of 'identifier' attribute of a
     *                  'resource' element that contains the 'resource' being retrieved as 
     *                  a 'dependency' element.  
     * @param str_current - String that represents the value of 'identifier' attribute of a
     *                  'resource' element being retrieved. 
     * @return - Document object that represents the file in the 'resource' element. 
     * @throws JDOMException
     * @throws ResourceMapException
     * @throws IOException
     */
    private static Document getResourceHref(String sco_key, String str_current)
            throws JDOMException, ResourceMapException, IOException {
        XPath xp;
        Element resource;
        xp = XPath.newInstance("//ns:resource[@identifier='" + str_current + "']");
        xp.addNamespace("ns", "http://www.imsglobal.org/xsd/imscp_v1p1");
        resource = (Element) xp.selectSingleNode(manifest);

        String[] split;
        String src_href = "";
        try {
            split = resource.getAttributeValue("href").split("/");
            src_href = split[split.length - 1];
        } catch (NullPointerException npe) {
            throw new ResourceMapException(str_current, sco_key);
        }

        String resource_path = src_dir + "//" + src_href;
        File file = new File(resource_path);
        Document dmDoc = dmParser.getDoc(file);
        return dmDoc;
    }

    /**
     * Searches through a data module file for referenced data modules.
     * 
     * @param dmDoc JDOM Document that represents the data module being searched
     * for dmRef instances.
     * @param sco_resource JDOM Element that represents the current SCO 
     * 'resource' element.
     * @return List<String> List of all of the referenced data modules found in
     * the specified data module.
     */
    @SuppressWarnings({ "unchecked" })
    private static List<String> searchForDmRefs(Document dmDoc, Element sco_resource) {
        Namespace ns = Namespace.getNamespace("http://www.imsglobal.org/xsd/imscp_v1p1");
        List<String> referencedDMs = new ArrayList<String>();

        Iterator<String> referencedDMsIterator = dmParser.searchForDmRefs(dmDoc).iterator();
        while (referencedDMsIterator.hasNext()) {
            String dmc = referencedDMsIterator.next();
            boolean found = false;

            // CHANGED STW 11/16
            List<Element> sco_resources = sco_resource.getChildren("dependency", ns);
            Iterator<Element> iter = sco_resources.iterator();
            String identifierref = "";
            while (iter.hasNext()) {
                Element current_el = iter.next();

                identifierref = current_el.getAttributeValue("identifierref");

                if (identifierref.contains(dmc)) {
                    found = true;
                }
            }
            if (!referencedDMs.contains(dmc) && found == false && identifierref != "") {
                referencedDMs.add(dmc);
            }

        }

        return referencedDMs;
    }

    /**
     * Searches through a data module file for referenced media (ICN) files .
     * 
     * @param doc JDOM Document that represents the data module being searched
     * for dmRef instances.
     * @return List<String> List of all of the referenced media (ICN) files 
     * found in the specified data module.
     * @throws JDOMException 
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private static List<String> searchForICN(Document doc) throws JDOMException {
        List<String> icnRefs = new ArrayList<String>();
        List<Element> els = (ArrayList) XPath.selectNodes(doc, "//*[@infoEntityIdent]");

        for (int i = 0; i < els.size(); i++) {
            Element e = (Element) els.get(i);
            String icn = null;
            if (e.getAttribute("infoEntityIdent") != null) {
                icn = e.getAttributeValue("infoEntityIdent");

                if (!icnRefs.contains(icn)) {
                    icnRefs.add(icn);
                }
            }
        }
        return icnRefs;
    }

}