bridge.toolkit.commands.MobileBuilder.java Source code

Java tutorial

Introduction

Here is the source code for bridge.toolkit.commands.MobileBuilder.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.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
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.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.xpath.XPath;

import bridge.toolkit.packaging.ContentPackageCreator;
import bridge.toolkit.util.CopyDirectory;
import bridge.toolkit.util.DMParser;
import bridge.toolkit.util.Keys;
import bridge.toolkit.util.URNMapper;

/**
 * Builds a HTML, CSS, and JQueryMobile based mobile web app output from the S1000D SCPM and referenced
 * data modules. 
 */
public class MobileBuilder implements Command {

    /**
     * Location of the XSLT transform file used to transform the SCPM to mobile app html file.
     */
    private static final String SCPM_TRANSFORM_FILE = "scpmStylesheet.mobile.xsl";

    /**
     * Location of the XSLT transform file used to transform the SCPM to mobile app html file. (includes assessments)
     */
    private static final String SCPM_TRANSFORM_FILE_WITH_ASSESSMENTS = "scpmStylesheetCourse.mobile.xsl";

    /**
     * Location of the XSLT transform file used to transform the data modules to mobile app html file.
     */
    private static final String DM_TRANSFORM_FILE = "dmStylesheet.mobile.xsl";

    /**
     * Location of the XSLT transform file used to transform the data modules to mobile app html file. (includes assessments)
     */
    private static final String DM_TRANSFORM_FILE_WITH_ASSESSMENTS = "dmStylesheetCourse.mobile.xsl";

    /**
     * InputStream for the xsl file that is used to transform the SCPM file and data modules
     * to mobile app html files.
     */
    private static InputStream transform;

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

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

    /**
     * String that represents the location of the SCPM file;
     */
    private static String scpm_file;

    /**
     * List of Strings that represent the files generated to produce the mobile app output.
     */
    private List<String> mobileList = new ArrayList<String>();

    /**
     * Message that is returned if the MobileBuilder is unsuccessful.
     */
    private static final String MOBILEBUILDER_FAILED = "MobileBuilder processing was unsuccessful";

    /**
     * List of files to delete when processing is complete
     */
    private ArrayList<String> files_to_delete;

    /** 
     * The unit of processing work to be performed for the MobileBuilder module.
     * 
     * @see org.apache.commons.chain.Command#execute(org.apache.commons.chain.Context)
     */
    @Override
    public boolean execute(Context ctx) {
        System.out.println("Executing Mobile Builder");
        if ((ctx.get(Keys.SCPM_FILE) != null) && (ctx.get(Keys.RESOURCE_PACKAGE) != null)) {
            CopyDirectory cd = new CopyDirectory();
            src_dir = (String) ctx.get(Keys.RESOURCE_PACKAGE);
            scpm_file = (String) ctx.get(Keys.SCPM_FILE);

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

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

            //write urn map file out to the ViewerApplication directory temporarily.  
            File js = null;
            File jsviewer = null;
            File xsldir = null;
            try {

                jsviewer = new File(System.getProperty("user.dir") + File.separator + "ViewerApplication");
                js = new File(System.getProperty("user.dir") + File.separator
                        + "ViewerApplication/app/urn_resource_map.xml");
                //check if the directory exists if it does use it and set the file object to null so it will not be deleted
                // else copy it from the jar and delete it when finished processing.
                if (!(jsviewer.exists())) {
                    //System.out.println("Path directory creation " + jsapp.getAbsolutePath());
                    jsviewer.mkdirs();
                    cd.CopyJarFiles(this.getClass(), "ViewerApplication", jsviewer.getAbsolutePath());
                } else {
                    jsviewer = null;
                }

                //check if the directory exists if it does use it and set the file object to null so it will not be deleted
                // else copy it from the jar and delete it when finished processing.
                xsldir = new File(System.getProperty("user.dir") + File.separator + "xsl");
                if (!(xsldir.exists())) {
                    //System.out.println("jsviewer directory creation " + jsviewer.getAbsolutePath());
                    xsldir.mkdirs();
                    cd.CopyJarFiles(this.getClass(), "xsl", xsldir.getAbsolutePath());
                } else {
                    xsldir = null;
                }

                XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
                FileWriter writer;
                writer = new FileWriter(js);
                outputter.output(urn_map, writer);
                writer.flush();
                writer.close();

            } catch (IOException e) {
                System.out.println(MOBILEBUILDER_FAILED);
                e.printStackTrace();
                return PROCESSING_COMPLETE;
            }

            String outputPath = "";
            if (ctx.get(Keys.OUTPUT_DIRECTORY) != null) {
                outputPath = (String) ctx.get(Keys.OUTPUT_DIRECTORY);
                if (outputPath.length() > 0) {
                    outputPath = outputPath + File.separator;
                }
            }

            //create new directory and folder for mobile_output
            File newMobApp = createOutputLocation(outputPath);

            //transform SCPM to main index.htm file
            try {
                transformSCPM(newMobApp, ctx.get(Keys.OUTPUT_TYPE));
            } catch (FileNotFoundException e) {
                System.out.println(MOBILEBUILDER_FAILED);
                e.printStackTrace();
                return PROCESSING_COMPLETE;
            } catch (TransformerConfigurationException e1) {
                System.out.println(MOBILEBUILDER_FAILED);
                e1.printStackTrace();
                return PROCESSING_COMPLETE;
            } catch (TransformerException e) {
                System.out.println(MOBILEBUILDER_FAILED);
                e.printStackTrace();
                return PROCESSING_COMPLETE;
            }

            //parse SCPM for list of files in each scoEntry to create the individual mobile app pages
            try {
                generateMobilePages(newMobApp, (String) ctx.get(Keys.OUTPUT_TYPE));

            } catch (TransformerConfigurationException e2) {
                System.out.println(MOBILEBUILDER_FAILED);
                e2.printStackTrace();
                return PROCESSING_COMPLETE;
            } catch (FileNotFoundException e2) {
                System.out.println(MOBILEBUILDER_FAILED);
                e2.printStackTrace();
                return PROCESSING_COMPLETE;
            } catch (JDOMException e2) {
                System.out.println(MOBILEBUILDER_FAILED);
                e2.printStackTrace();
                return PROCESSING_COMPLETE;
            } catch (IOException e2) {
                System.out.println(MOBILEBUILDER_FAILED);
                e2.printStackTrace();
                return PROCESSING_COMPLETE;
            } catch (TransformerFactoryConfigurationError e2) {
                System.out.println(MOBILEBUILDER_FAILED);
                e2.printStackTrace();
                return PROCESSING_COMPLETE;
            } catch (TransformerException e2) {
                System.out.println(MOBILEBUILDER_FAILED);
                e2.printStackTrace();
                return PROCESSING_COMPLETE;
            }

            //create list.js file
            try {
                generateListFile(newMobApp);
            } catch (IOException e1) {
                System.out.println(MOBILEBUILDER_FAILED);
                e1.printStackTrace();
                return PROCESSING_COMPLETE;
            } catch (JDOMException e1) {
                System.out.println(MOBILEBUILDER_FAILED);
                e1.printStackTrace();
                return PROCESSING_COMPLETE;
            }

            try {
                //copy over css and jquery mobile files              
                File mobiApp_loc = new File(System.getProperty("user.dir") + File.separator + "mobiApp");
                //check if the directory exists if it does use it else copy it from the jar
                if (mobiApp_loc.exists()) {
                    cd.copyDirectory(mobiApp_loc, newMobApp);
                } else {
                    cd.CopyJarFiles(this.getClass(), "mobiApp", newMobApp.getAbsolutePath());
                }

                //copy commonmobile.js
                File commonmobile_js = new File(System.getProperty("user.dir") + File.separator + "xsl"
                        + File.separator + "bridge" + File.separator + "toolkit" + File.separator + "commands"
                        + File.separator + "commonmobile.js");
                //check if the directory exists if it does use it else copy it from the jar
                if (commonmobile_js.exists()) {
                    cd.copyDirectory(commonmobile_js, newMobApp);
                }

                //copy over media files           
                File new_media_loc = new File(newMobApp.getAbsolutePath() + File.separator + "media");
                new_media_loc.mkdir();
                Iterator<File> srcIterator = src_files.iterator();
                while (srcIterator.hasNext()) {
                    File src = srcIterator.next();
                    if (!src.getName().endsWith(".xml") && !src.getName().endsWith(".XML")) {
                        cd.copyDirectory(src, new_media_loc);
                    }
                }
            } catch (IOException e) {
                System.out.println(MOBILEBUILDER_FAILED);
                e.printStackTrace();
                return PROCESSING_COMPLETE;
            }

            //delete the urn map from the ViewerApplication location
            ArrayList<String> files_to_delete = new ArrayList<String>();
            if (jsviewer != null) {
                DeleteDirectoryOnExit(jsviewer, files_to_delete);
            }
            if (js != null) {
                files_to_delete.add(js.getAbsolutePath());
            }
            if (xsldir != null) {
                DeleteDirectoryOnExit(xsldir, files_to_delete);
            }
            ctx.put(Keys.MOBLIE_FILES_TO_DELETE, files_to_delete);
            System.out.println("MobileBuilder processing was successful");

        }
        return CONTINUE_PROCESSING;

    }

    /**
     * Iterates over a directory and sets the files inside to be deleted when processing is complete
     * @param dir the directory file to delete
     */
    private void DeleteDirectoryOnExit(File dir, ArrayList<String> files_to_delete) {
        files_to_delete.add(dir.getAbsolutePath());
        if (dir.isDirectory()) {
            String files[] = dir.list();
            for (int f = 0; f < files.length; f++) {
                File innerFile = new File(dir, files[f]);
                if (innerFile.isDirectory()) {
                    DeleteDirectoryOnExit(innerFile, files_to_delete);
                } else {
                    files_to_delete.add(innerFile.getAbsolutePath());
                }
            }
        }
    }

    /**
     * Creates a new directory and folder for the mobile output
     * @param outputDirectory the directory to write the mobile web app to.
     * @return File object that represents the location of the mobile output.
     */
    private File createOutputLocation(String outputDirectory) {
        File mobileLocation = new File(outputDirectory + "mobile");
        if (!mobileLocation.exists()) {
            mobileLocation.mkdirs();
        }

        String newOutput = "output";
        int numberOfPackages = 0;
        String[] outputs = mobileLocation.list();
        File mNewMobApp;
        if (outputs.length >= 1) {
            for (String name : outputs) {
                String temp = name.substring(newOutput.length());
                int currentNum = Integer.parseInt(temp);
                if (currentNum >= numberOfPackages) {
                    numberOfPackages = currentNum + 1;
                }
            }

            mNewMobApp = new File(mobileLocation + File.separator + newOutput + numberOfPackages);
            mNewMobApp.mkdirs();
        } else {
            mNewMobApp = new File(mobileLocation + File.separator + newOutput + "1");
            mNewMobApp.mkdirs();
        }

        return mNewMobApp;
    }

    /**
     * Transforms the SCPM to index.htm file that is the home page for the mobile output.
     * 
     * @param newMobApp File object that represents the location of the mobile output.
     * @throws FileNotFoundException
     * @throws TransformerException
     */
    private void transformSCPM(File newMobApp, Object outputType)
            throws FileNotFoundException, TransformerException {
        if (outputType != "mobileCourse") {
            transform = this.getClass().getResourceAsStream(SCPM_TRANSFORM_FILE);
        } else {
            transform = this.getClass().getResourceAsStream(SCPM_TRANSFORM_FILE_WITH_ASSESSMENTS);
        }
        File index = new File(newMobApp + File.separator + "index.htm");
        mobileList.add("index.htm");
        TransformerFactory tFactory = TransformerFactory.newInstance();
        Transformer transformer = tFactory.newTransformer(new StreamSource(transform));
        transformer.transform(new StreamSource(scpm_file), new StreamResult(new FileOutputStream(index)));
    }

    /**
     * Parse the SCPM file for the list of files in each scoEntry to create the 
     * individual mobile app pages.
     * 
     * @param newMobApp File object that represents the location of the mobile output.
     * @throws JDOMException
     * @throws TransformerException
     * @throws IOException
     */
    private void generateMobilePages(File newMobApp, String outputType)
            throws JDOMException, TransformerException, IOException {
        DMParser dmp = new DMParser();

        XPath xp = XPath.newInstance("//scoEntry[@scoEntryType='scot01']");
        @SuppressWarnings("unchecked")

        List<Element> scos = xp.selectNodes(dmp.getDoc(new File(scpm_file)));
        int folderCount = 1;
        Iterator<Element> iterator = scos.iterator();
        while (iterator.hasNext()) {
            Element sco = iterator.next();
            sco.detach();
            Document temp = new Document(sco);
            List<String> dms = dmp.searchForDmRefs(temp);
            Iterator<String> childIterator = dms.iterator();
            while (childIterator.hasNext()) {
                String urnDM = childIterator.next();
                //System.out.println(urnDM);
                xp = XPath.newInstance("//urn[@name='URN:S1000D:" + urnDM + "']");
                Element urn = (Element) xp.selectSingleNode(urn_map);

                //System.out.println(urn.getChild("target").getValue());

                //The mobile output is meant to be performance support material so all assessments will not be included
                //checks to see if the data module is an overview assessment (post test or pre test) as indicated by the learnEventCode = E
                //or by the learnCode of T87 (Pre test), T88 (post test) or T28 (Terminal Objective).
                //T28 data modules were used in the S1000D bike example to indicate the post test assessments
                //If it an assessment the data module is not transformed and not included in the mobile output
                File dataModule = new File(src_dir + File.separator + urn.getChild("target").getValue());
                Document currDM = dmp.getDoc(dataModule);
                xp = XPath.newInstance("//dmodule/identAndStatusSection/dmAddress/dmIdent/dmCode/@learnEventCode");
                Attribute learnEventCode = (Attribute) xp.selectSingleNode(currDM);
                xp = XPath.newInstance("//dmodule/identAndStatusSection/dmAddress/dmIdent/dmCode/@learnCode");
                Attribute learnCode = (Attribute) xp.selectSingleNode(currDM);

                if (learnEventCode == null || !learnEventCode.getValue().equals("E")
                        || outputType == "mobileCourse") {
                    if (learnCode == null || !learnCode.getValue().equals("T28") || outputType == "mobileCourse") {
                        File newChild = new File(newMobApp + File.separator + Integer.toString(folderCount));
                        newChild.mkdir();
                        if (outputType != "mobileCourse") {
                            transform = this.getClass().getResourceAsStream(DM_TRANSFORM_FILE);
                        } else {
                            transform = this.getClass().getResourceAsStream(DM_TRANSFORM_FILE_WITH_ASSESSMENTS);
                        }
                        TransformerFactory tFactory = TransformerFactory.newInstance();
                        Transformer transformer = tFactory.newTransformer(new StreamSource(transform));

                        String htmName = urn.getChild("target").getValue() + ".htm";
                        File htmlFile = new File(newChild + File.separator + htmName);
                        mobileList.add(newChild.getName() + "/" + htmName);
                        transformer.transform(new StreamSource(dataModule),
                                new StreamResult(new FileOutputStream(htmlFile)));
                    }
                }

            }

            folderCount++;
        }
    }

    /**
     * Generates a JavaScript file that is used to navigate between the data
     * modules in the mobile output. 
     * 
     * @throws IOException
     * @throws JDOMException
     */
    private void generateListFile(File mobAppDir) throws IOException, JDOMException {
        File js = new File(mobAppDir.getAbsolutePath() + File.separator + "list.js");

        FileWriter writer = new FileWriter(js);
        int count = 0;
        writer.write("var scoPages = new Array(" + mobileList.size() + ");\n");
        Iterator<String> pages = mobileList.iterator();
        while (pages.hasNext()) {
            String nextPg = pages.next();
            // = \""+file+"\";\n"
            writer.write("scoPages[" + count + "] = \"" + nextPg + "\";\n");
            count++;
        }
        writer.write("function getArray()\n");
        writer.write("{\n return scoPages;\n}");
        writer.close();
    }
}