com.ikanow.infinit.e.harvest.enrichment.custom.JavaScriptUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.ikanow.infinit.e.harvest.enrichment.custom.JavaScriptUtils.java

Source

/*******************************************************************************
 * Copyright 2012, The Infinit.e Open Source Project.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 * 
 * 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package com.ikanow.infinit.e.harvest.enrichment.custom;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;

import javax.script.ScriptEngine;
import javax.script.ScriptException;

import com.ikanow.infinit.e.data_model.utils.IkanowSecurityManager;
import com.mongodb.BasicDBList;
//import com.mongodb.BasicDBObject; // (used directly in the javascript now)

/**
 * JavaScriptUtils
 * @author cvitter
 */
public class JavaScriptUtils {
    // initScript - used to pass document in to the script engine
    public static String initScript = "var _doc = eval('(' + document + ')'); \n";

    // initScript - used to pass document in to the script engine
    public static String initOnUpdateScript = "var _old_doc = eval('(' + old_document + ')'); \n";

    // scripts to pass entity and event json objects into javascript methods
    public static String iteratorDocScript = "var _iterator = eval('(' + _iterator + ')'); \n";

    // scripts to pass entity and event json objects into javascript methods
    public static String iteratorMetaScript = "var _metadata = eval('(' + _metadata + ')'); \n";

    // genericFunctionCall - all functions passed in via $SCRIPT get the following name
    public static String genericFunctionCall = "getValue";

    /**
     * getScript
     * Extracts JavaScript code from $SCRIPT() and wraps in getVal function
     * @param script - $SCRIPT()
     * @return String - function getVal()
     */
    public static String getScript(String script) {
        // The start and stop index use to substring the script
        int start = script.indexOf("(");
        int end = script.lastIndexOf(")");

        try {
            if (script.toLowerCase().startsWith("$script")) {
                // Remove $SCRIPT() wrapper and then wrap script in 'function getValue() { xxxxxx }'
                return "function " + genericFunctionCall + "() { " + script.substring(start + 1, end) + " }";
            } else {
                // Simply remove $FUNC() wrapper
                return script.substring(start + 1, end);
            }
        } catch (Exception e) {
            throw new RuntimeException("Malformed script: " + script);
        }
    }

    /**
     * containsScript
     * Determines whether or not value passed in contains script or a function
     * call via $SCRIPT or $FUNC
     * @param s
     * @return
     */
    public static Boolean containsScript(String s) {
        if (s.toLowerCase().startsWith("$script") || s.toLowerCase().startsWith("$func")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * getJavaScriptFile
     * Retrieve a JavaScript file located at the fileUrl
     * @param fileUrl - http://somewhere.com/javascript.js
     * @return
     * @throws IOException 
     */
    public static String getJavaScriptFile(String fileUrl, IkanowSecurityManager secManager) throws IOException {
        StringBuffer javaScript = new StringBuffer();
        try {
            if (null != secManager) {
                secManager.setSecureFlag(true);
            } //TESTED
              // Create java.net.URL from fileUrl if possible
            URL url = new URL(fileUrl);

            // Read the contents of the url into a BufferedReader
            BufferedReader r = new BufferedReader(new InputStreamReader(url.openStream()));

            // Read the contents of r line by line and append to our StringBuffer
            String s;
            while (null != (s = r.readLine())) {
                javaScript.append(s);
            }

            // Close our reader
            r.close();
        } finally {
            secManager.setSecureFlag(false);
        } //TESTED
        return javaScript.toString();
    }//TESTED

    // Generate a script to convert the native JS objects into something
    // we can parse (NativeObject and NativeArrays can't be handled at the "user level" annoyingly)

    public static String generateParsingScript() {
        StringBuffer sbSub1 = new StringBuffer();
        sbSub1.append("function s1(el) {").append('\n');
        sbSub1.append("if (el == null) {}").append('\n');
        sbSub1.append("else if (el instanceof Array) {").append('\n');
        sbSub1.append("s2(el, 1);").append('\n');
        sbSub1.append("}").append('\n');
        sbSub1.append("else if (typeof el == 'object') {").append('\n');
        sbSub1.append("outList.add(s3(el));").append('\n');
        sbSub1.append("}").append('\n');
        sbSub1.append("else {").append('\n');
        sbSub1.append("outList.add(el.toString());").append('\n');
        sbSub1.append("}").append('\n');
        sbSub1.append("}").append('\n');

        StringBuffer sbSub2 = new StringBuffer();
        sbSub2.append("function s2(el, master_list) {").append('\n');
        sbSub2.append("var list = (1 == master_list)?outList:listFactory.clone();").append('\n');
        sbSub2.append("for (var i = 0; i < el.length; ++i) {").append('\n');
        sbSub2.append("var subel = el[i];").append('\n');
        sbSub2.append("if (subel == null) {}").append('\n');
        sbSub2.append("else if (subel instanceof Array) {").append('\n');
        sbSub2.append("list.add(s2(subel, 0));").append('\n');
        sbSub2.append("}").append('\n');
        sbSub2.append("else if (typeof subel == 'object') {").append('\n');
        sbSub2.append("list.add(s3(subel));").append('\n');
        sbSub2.append("}").append('\n');
        sbSub2.append("else {").append('\n');
        sbSub2.append("list.add(subel.toString());").append('\n');
        sbSub2.append("}").append('\n');
        sbSub2.append("}").append('\n');
        sbSub2.append("return list; }").append('\n');

        StringBuffer sbSub3 = new StringBuffer();
        sbSub3.append("function s3(el) {").append('\n');
        sbSub3.append("el.constructor.toString();").append("\n"); // Will crash out if is too complex
        // Replaced with 2 lines following, so I can create objects with smaller initial capacity
        //sbSub3.append("var currObj = objFactory.clone();").append('\n');
        sbSub3.append("var len = 0; for (var prop in el) { len++; }").append('\n');
        sbSub3.append("var currObj = new com.mongodb.BasicDBObject(Math.ceil(1.3*len));").append('\n');
        sbSub3.append("for (var prop in el) {").append('\n');
        sbSub3.append("var subel = el[prop];").append('\n');
        sbSub3.append("if (subel == null) {}").append('\n');
        sbSub3.append("else if (subel instanceof Array) {").append('\n');
        sbSub3.append("currObj.put(prop, s2(subel, 0));").append('\n');
        sbSub3.append("}").append('\n');
        sbSub3.append("else if (typeof subel == 'object') {").append('\n');
        sbSub3.append("currObj.put(prop, s3(subel));").append('\n');
        sbSub3.append("}").append('\n');
        sbSub3.append("else {").append('\n');
        sbSub3.append("currObj.put(prop, subel.toString());").append('\n');
        sbSub3.append("}").append('\n');
        sbSub3.append("}").append('\n');
        sbSub3.append("return currObj; }").append('\n');

        StringBuffer sbMain = new StringBuffer();
        sbMain.append(sbSub1);
        sbMain.append(sbSub2);
        sbMain.append(sbSub3);

        return sbMain.toString();
    }// TESTED (including null values, converts to string)

    // Convert a native JS complex object into a JSON-like map

    public static BasicDBList parseNativeJsObject(Object returnVal, ScriptEngine engine) throws ScriptException {
        try {
            engine.put("output", returnVal);

            // Use BasicDBObject directly so I can reduce memory usage by setting the initial capacity depending on the size of the JSON array
            //         BasicDBObject objFactory = new BasicDBObject();
            //         engine.put("objFactory", objFactory);
            BasicDBList listFactory = new BasicDBList();
            engine.put("listFactory", listFactory);
            BasicDBList outList = new BasicDBList();
            engine.put("outList", outList);

            engine.eval("s1(output);");

            return outList;
        } catch (Exception e) {
            throw new RuntimeException("1 Cannot parse return non-JSON object: " + returnVal.getClass().toString()
                    + ":" + returnVal.toString()
                    + "; if embedding JAVA, considering using eg \"X = '' + X\" to convert back to native JS strings.");
        }
    }

}