com.karura.javascript.JavaScriptDependencyProcessor.java Source code

Java tutorial

Introduction

Here is the source code for com.karura.javascript.JavaScriptDependencyProcessor.java

Source

/**
============== GPL License ==============
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    
============== Commercial License==============
https://github.com/karuradev/licenses/blob/master/toc.txt
*/

package com.karura.javascript;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class JavaScriptDependencyProcessor {

    public static void printUsage(String... message) {
        System.err.println("Karura Single Page Apps Javascript dependency processor");
        System.err.println("---------------------------------------------");
        System.err.println("java -jar spadep.jar [html_file] [output_folder] [debug]");
        System.err.println("html_file : Path to html file containing karura dependency script");
        System.err.println("output_folder : Path to directory where  processed [html_file] needs to be generated");

        System.err.println("---------------------------------------------");
        if (message.length > 0) {
            System.err.println("Error : " + message[0]);
            System.err.println("---------------------------------------------");
        }
    }

    /**
     * @param args
     */

    static boolean debug = false;

    public static void main(String[] args) {

        if (args.length < 2 || args.length > 3) {
            printUsage();
            System.exit(0);
        }

        if (args.length == 3) {
            debug = true;
        }

        if (!new File(args[0]).isFile()) {
            printUsage("Please specify a valid source html file");
            System.exit(0);
        }

        if (!new File(args[1]).isDirectory()) {
            printUsage("Please specify a valid destination directory");
            System.exit(0);
        }

        try {

            System.out.println("----------------------------");
            System.out.println("Processing :" + args[0]);
            System.out.println("Dumping results to : " + args[1]);
            System.out.println("----------------------------");

            generateHtmlWithCorrectDependencies(args[0], args[1]);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void generateHtmlWithCorrectDependencies(String sourceFile, String destDir)
            throws ParserConfigurationException, SAXException, IOException, TransformerException {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc = dBuilder.parse(sourceFile);

        doc.getDocumentElement().normalize();

        NodeList nList = doc.getElementsByTagName("script");

        for (int index = 0; index < nList.getLength(); index++) {

            Node nNode = nList.item(index);
            //System.out.println(">" + nNode.getNodeName() + " " + nNode.getNodeType());

            if (nNode.getNodeType() == Node.ELEMENT_NODE) {
                Element eElement = (Element) nNode;

                NamedNodeMap al = eElement.getAttributes();
                for (int k = 0; k < al.getLength(); k++) {
                    Node attributeNode = al.item(k);

                    if (attributeNode.getNodeName().equals("type")
                            && attributeNode.getNodeValue().equals("javascript/dependency")) {
                        NodeList headNodes = doc.getElementsByTagName("head");
                        headNodes.item(0).removeChild(eElement);

                        NodeList depNodes = eElement.getChildNodes();

                        if (eElement.getChildNodes().getLength() == 1
                                && eElement.getFirstChild().getNodeType() == Node.TEXT_NODE) {

                            final HashMap<String, Set<String>> moduleMap = new HashMap<String, Set<String>>();
                            final HashMap<String, JsonObject> moduleJsonMap = new HashMap<String, JsonObject>();

                            parseModuleDependencyGraph(eElement, moduleMap, moduleJsonMap);

                            ArrayList<String> sortedKeys = sortDependencies(moduleMap.keySet().toArray(),
                                    moduleMap);

                            createDocumentNodes(doc, sortedKeys.toArray(), moduleJsonMap);
                            createOutputFile(doc, destDir);

                        } else {
                            printUsage(
                                    "Dependency structure was not found as expected. Please correct the structure and try again.");
                            System.exit(0);
                        }
                    }
                }
            }
        }
    }

    static void parseModuleDependencyGraph(Element eElement, HashMap<String, Set<String>> moduleMap,
            HashMap<String, JsonObject> moduleJsonMap) {
        String dependencyText = eElement.getFirstChild().getNodeValue();
        JsonArray modules = (JsonArray) new JsonParser().parse(dependencyText);

        for (int j = 0; j < modules.size(); j++) {
            if (modules.get(j) instanceof JsonNull)
                continue;
            JsonObject module = (JsonObject) modules.get(j);
            if (module != null) {

                String modName = module.get("name").getAsString();
                moduleJsonMap.put(modName, module);
                if (moduleMap.get(modName) == null) {
                    moduleMap.put(modName, new HashSet<String>());
                }
                if (module.get("depends") != null) {
                    JsonArray deps = module.get("depends").getAsJsonArray();
                    for (int l = 0; l < deps.size(); l++) {
                        JsonElement dep = deps.get(l);
                        String depName = dep.getAsString();
                        if (moduleMap.get(depName) == null) {
                            moduleMap.put(depName, new HashSet<String>());
                        }

                        moduleMap.get(modName).add(depName);
                    }
                }
                //System.out.println(module.toString());
            }
        }
    }

    static ArrayList<String> sortDependencies(Object[] keysToSort, HashMap<String, Set<String>> moduleMap) {
        ArrayList<String> sortedKeys = new ArrayList<String>();
        sortedKeys.add((String) keysToSort[0]);

        for (int i = 1; i < keysToSort.length; i++) {
            String next_key = (String) keysToSort[i];
            boolean inserted = false;

            for (int l = 0; l < sortedKeys.size(); l++) {
                String already_sorted_key = sortedKeys.get(l);

                if (search(moduleMap, null, already_sorted_key, already_sorted_key, next_key)) {
                    sortedKeys.add(l, next_key);
                    inserted = true;
                    break;

                }
            }
            if (!inserted) {
                sortedKeys.add(next_key);
            }
        }
        return sortedKeys;
    }

    static boolean search(HashMap<String, Set<String>> moduleMap, Stack<String> processedStack, String base_module,
            String current_module, String module_for_dep_check) {
        if (debug)
            System.out.println("Checking if  " + module_for_dep_check + " is dependent on " + current_module);

        if (processedStack == null) {
            processedStack = new Stack<String>();
        }

        if (debug)
            System.out.println(">" + processedStack);

        if (processedStack.contains(module_for_dep_check)) {
            return false;
        }

        if (processedStack.contains(current_module)) {
            System.err.println(
                    "There was a circular dependency detected in " + base_module + " and " + module_for_dep_check);
            System.err.println("The dependency was detected at " + current_module);
            System.err.println(processedStack);
            System.exit(1);
        }

        Set<String> leftDeps = moduleMap.get(current_module);

        if (current_module.compareTo(module_for_dep_check) == 0) {
            return false;
        }

        if (leftDeps.size() == 0) {
            //Since I have got no dependencies so rh cannot be dependent on me, and
            //both me and rh are of same value
            return false;
        }

        // if right is a dependency of left, then right should come first in our list
        if (leftDeps.contains(module_for_dep_check)) {
            return true;
        }

        //now since right is not present in the list of left, we have to check if it present in the depencies 
        //of any of what
        for (String entry : leftDeps) {
            //if rh was found as a dependency on one of the entries than lh > rh
            processedStack.push(current_module);
            boolean result = search(moduleMap, processedStack, base_module, entry, module_for_dep_check);
            processedStack.pop();
            if (result)
                return true;
        }
        return false;
    }

    static void createDocumentNodes(Document doc, Object[] sortedKeys, HashMap<String, JsonObject> moduleJsonMap) {
        NodeList nList = doc.getElementsByTagName("head");
        for (Object key : sortedKeys) {
            JsonObject module = moduleJsonMap.get((String) key);

            if (module == null) {
                System.err.println("Something is not correct in dependency declaration : " + key);
                System.exit(0);
            }

            if (module.get("path") == null) {
                System.err.println("Module path not defined for module : " + key);
                System.exit(0);
            }
            String path = module.get("path").getAsString();
            Element scriptNode = doc.createElement("script");
            scriptNode.setAttribute("type", "text/javascript");
            scriptNode.setAttribute("src", path);
            nList.item(0).appendChild(scriptNode);

            //System.out.println(key + " : " + path + " : ");
        }
    }

    static void createOutputFile(Document doc, String destDir)
            throws TransformerException, FileNotFoundException, UnsupportedEncodingException {

        //(1)
        TransformerFactory tf = TransformerFactory.newInstance();
        tf.setAttribute("indent-number", new Integer(2));

        Transformer transformer = tf.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

        OutputStream out = new FileOutputStream(new File(destDir, "index.html").getAbsolutePath());
        StreamResult result = new StreamResult(new OutputStreamWriter(out, "utf-8"));
        DOMSource source = new DOMSource(doc);
        transformer.transform(source, result);
    }
}