org.primordion.ef.program.Xholon2CoffeeScript.java Source code

Java tutorial

Introduction

Here is the source code for org.primordion.ef.program.Xholon2CoffeeScript.java

Source

/* Xholon Runtime Framework - executes event-driven & dynamic applications
 * Copyright (C) 2014 Ken Webb
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 */

package org.primordion.ef.program;

import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Window;

import java.util.Date;
import java.util.List;

import org.client.GwtEnvironment;
import org.primordion.xholon.base.IMechanism;
import org.primordion.xholon.base.IXholon;
import org.primordion.xholon.base.IXholonClass;
import org.primordion.xholon.base.PortInformation;
import org.primordion.xholon.base.Xholon;
import org.primordion.xholon.io.xml.IXholon2Xml;
import org.primordion.xholon.io.xml.IXmlWriter;
import org.primordion.xholon.util.ClassHelper;
import org.primordion.xholon.util.IJavaTypes;
import org.primordion.xholon.util.Misc;
import org.primordion.ef.AbstractXholon2ExternalFormat;
import org.primordion.xholon.service.ef.IXholon2ExternalFormat;

/**
 * Export a Xholon model as CoffeeScript.
 * 
 * @author <a href="mailto:ken@primordion.com">Ken Webb</a>
 * @see <a href="http://www.primordion.com/Xholon">Xholon Project website</a>
 * @see <a href="http://coffeescript.org">coffeescript.org</a>
 * @since 0.9.1 (Created on July 23, 2014)
 */
@SuppressWarnings("serial")
public class Xholon2CoffeeScript extends AbstractXholon2ExternalFormat
        implements IXholon2ExternalFormat, IXmlWriter {

    private String outFileName;
    private String outPath = "./ef/coffee/";
    private String modelName;
    private IXholon root;
    private StringBuilder sb;
    private StringBuilder sbPorts;
    private StringBuilder sbTest;
    private StringBuilder sbAttrs;

    /** Current date and time. */
    private Date timeNow;
    private long timeStamp;

    /** used to pass node between writeNode() and writeAttribute() */
    private IXholon currentNode = null;

    /**
     * Constructor.
     */
    public Xholon2CoffeeScript() {
    }

    @Override
    public String getVal_String() {
        return sb.toString();
    }

    /*
     * @see org.primordion.xholon.io.IXholon2ExternalFormat#initialize(java.lang.String, java.lang.String, org.primordion.xholon.base.IXholon)
     */
    public boolean initialize(String fileName, String modelName, IXholon root) {
        timeNow = new Date();
        timeStamp = timeNow.getTime();
        if (fileName == null) {
            this.outFileName = outPath + root.getXhcName() + "_" + root.getId() + "_" + timeStamp + ".coffee";
        } else {
            this.outFileName = fileName;
        }
        this.modelName = modelName;
        this.root = root;
        return true;
    }

    /*
     * @see org.primordion.xholon.io.IXholon2ExternalFormat#writeAll()
     */
    public void writeAll() {
        sb = new StringBuilder();
        sbPorts = new StringBuilder();
        sbTest = new StringBuilder().append("# test\n");
        writeStartDocument();
        writeIhNode(root.getApp().getXhcRoot());
        writeNode(root);
        if (sbPorts.length() > 0) {
            sb.append("# ports\n").append(sbPorts.toString()).append("\n");
        }
        sb.append(sbTest.toString());
        setWriteToTab(isWriteToNewTab());
        if (isWrapInHtml()) {
            wrapInHtml();
        }
        String cs = sb.toString();
        writeToTarget(cs, outFileName, outPath, root);
        if (isOpenInNewBrowserWindow()) {
            openInNewBrowserWindow(cs);
        }
        /*if (isCompileToJavaScript()) {
          loadAndRunCoffeeScriptCompiler(cs);
          // TODO write the JS to a tab
          if (isExecuteJavaScript()) {
            executeJavaScript();
          }
        }*/
    }

    /**
     * Write one node, and its child nodes.
     * @param node The current node in the Xholon composite structure hierarchy (CSH).
     */
    protected void writeNode(IXholon node) {
        if (node == null) {
            return;
        }
        String nodeName = node.getName(getNameTemplate());
        String xhcNodeName = node.getXhcName();
        if (xhcNodeName.endsWith("behavior")) {
            return;
        }
        IXholon pnode = node.getParentNode();
        sb.append(nodeName).append(" = new ").append(xhcNodeName).append(" ").append(node.getId()).append("\n");

        if (node == root) {
            sbTest.append("console.log ").append(nodeName).append("\n").append("console.log ").append(nodeName)
                    .append(".first\n").append("console.log ").append(nodeName).append(".anno\n")
                    .append("console.log ").append(nodeName).append(".first.parent\n").append("console.log ")
                    .append(nodeName).append(".first.next\n").append("console.log ").append(nodeName)
                    .append(".act()\n");
            /* example of using act()
            class Hello extends XholonClass
              act: () ->
                console.log "Hello "
                super
            */
        } else if (node == pnode.getFirstChild()) {
            sb.append(pnode.getName(getNameTemplate())).append(".first(").append(nodeName).append(")\n");
        } else {
            sb.append(node.getPreviousSibling().getName(getNameTemplate())).append(".next(").append(nodeName)
                    .append(")\n");
        }
        if (node != root) {
            sb.append(nodeName).append(".parent(").append(pnode.getName(getNameTemplate())).append(")\n");
        }
        String rn = node.getRoleName();
        if ((rn != null) && (rn.length() > 0)) {
            sb.append(nodeName).append(".role(\"").append(rn).append("\")\n");
        }
        writeLinks(node);
        writeAttributes(node);
        String text = node.getVal_String();
        if (text != null) {
            sb.append(nodeName).append(".text(\"").append(text).append("\")\n");
        }
        if (node.hasAnnotation()) {
            sb.append(nodeName).append(".anno(\"").append(node.getAnnotation()).append("\")\n");
        }
        Object obj = node.getVal_Object();
        if (obj != null) {
            sb.append(nodeName).append(".obj(").append(obj).append(")\n");
        }
        sb.append("\n");
        if (node.hasChildNodes()) {
            IXholon childNode = node.getFirstChild();
            while (childNode != null) {
                writeNode(childNode);
                childNode = childNode.getNextSibling();
            }
        }
    }

    /**
     * Write an inheritance hierarchy (IH) node, and its child nodes.
     * Optionally only include app-specific IH nodes.
     * @param xhcNode The current node in the hierarchy.
     */
    protected void writeIhNode(IXholon xhcNode) {
        if (xhcNode == null) {
            return;
        }
        String xhcNodeName = xhcNode.getName();
        if ("XholonClass".equals(xhcNodeName)) {
            sb.append("class ").append(xhcNodeName).append("\n").append("  constructor: (@id) ->\n")
                    .append("  role: (@role) ->\n").append("  first: (@first) ->\n").append("  next: (@next) ->\n")
                    .append("  parent: (@parent) ->\n").append("  val: (@val) ->\n")
                    .append("  inc: (incAmount) ->\n    @val += incAmount\n")
                    .append("  dec: (decAmount) ->\n    @val += decAmount\n").append("  text: (@text) ->\n")
                    .append("  obj: (@obj) ->\n");
            for (int i = 0; i < root.getApp().getMaxPorts(); i++) {
                sb.append("  port").append(i).append(": (@port").append(id).append(") ->\n");
            }
            sb.append("  anno: (@anno) ->\n").append("  act: () ->\n    @first.act?()\n    @next.act?()\n")
                    .append("\n");
        } else if (xhcNodeName.endsWith("behavior")) {
        } else if ("Chameleon".equals(xhcNodeName)) {
        } else if ("Quantity".equals(xhcNodeName)) {
        } else if (isShouldShowMechanismIhNodes() || (xhcNode.getId() < IMechanism.MECHANISM_ID_START)) {
            sb.append("class ").append(xhcNodeName).append(" extends ").append(xhcNode.getParentNode().getName())
                    .append("\n");
            String classContent = ((IXholonClass) xhcNode).getDefaultContent();
            if (classContent != null) {
                sb.append("  ").append(classContent.trim()).append("\n");
            }
            sb.append("\n");
        } else {
        } // this is a mechanism node that should not be shown, but should still be navigated

        if (xhcNode.hasChildNodes()) {
            IXholon childXhcNode = xhcNode.getFirstChild();
            while (childXhcNode != null) {
                writeIhNode(childXhcNode);
                childXhcNode = childXhcNode.getNextSibling();
            }
        }
    }

    /**
     * Write links from this node to any others, where the Xholon node has connected ports.
     * @param node The current Xholon node.
     */
    protected void writeLinks(IXholon node) {
        if (isShouldShowLinks() == false) {
            return;
        }
        List<PortInformation> portList = node.getAllPorts();
        for (int i = 0; i < portList.size(); i++) {
            writeLink(node, (PortInformation) portList.get(i));
        }
    }

    /**
     * Write one link.
     * @param node The node where the link originates.
     * @param portInfo Information about the port that represents the link.
     */
    protected void writeLink(IXholon node, final PortInformation portInfo) {
        if (portInfo == null) {
            return;
        }
        IXholon remoteNode = portInfo.getReffedNode();
        if (remoteNode == null) {
            return;
        }
        if (!remoteNode.hasAncestor(root.getName())) {
            // remoteNode is outside the scope (not a descendant) of root
            return;
        }
        sbPorts.append(node.getName(getNameTemplate()));
        int fieldNameIndex = portInfo.getFieldNameIndex();
        if (fieldNameIndex == PortInformation.PORTINFO_NOTANARRAY) {
            sbPorts.append(".").append(portInfo.getFieldName()).append(" = ")
                    .append(remoteNode.getName(getNameTemplate())).append("\n");
        } else {
            sbPorts.append(".port").append(fieldNameIndex).append("(").append(remoteNode.getName(getNameTemplate()))
                    .append(")\n");
        }
    }

    /**
     * Write app-specific attributes.
     * @param node The node whose attributes should be written.
     */
    public void writeAttributes(IXholon node) {
        if (!isShouldShowAttributes()) {
            return;
        }
        currentNode = node;
        sbAttrs = new StringBuilder();
        IXholon2Xml xholon2xml = node.getXholon2Xml();
        xholon2xml.setXhAttrStyle(IXholon2Xml.XHATTR_TO_XMLATTR);
        node.toXmlAttributes(xholon2xml, this);
        if (sbAttrs.length() > 0) {
            sb.append(sbAttrs.toString());
        }
    }

    /**
     * Wrap the CoffeeScript inside a simple HTML page.
     */
    protected void wrapInHtml() {
        StringBuilder sbWrap = new StringBuilder();
        sbWrap.append("<!doctype html>\n").append("<html>\n").append("<head>\n").append("<title>CoffeeScript ")
                .append(modelName).append("</title>\n").append("<script src=\"")
                .append(GwtEnvironment.gwtHostPageBaseURL).append("xholon/lib/coffee-script.js\"></script>\n")
                .append("</head>\n").append("<body>\n").append("<h3>CoffeeScript ").append(modelName)
                .append("</h3>\n").append("<script type=\"text/coffeescript\">\n").append(sb.toString())
                .append("</script>\n").append("</body>\n").append("</html>\n");
        sb = sbWrap;
    }

    /**
     * Open the HTML-wrapped CoffeeScript in a new browser window.
     * @param htmlStr The HTML-wrapped CoffeeScript.
     *   This string includes indentation (pairs of space characters "  "),
     *   which must be retained in order for the CoffeeScript parser to work.
     */
    protected void openInNewBrowserWindow(String htmlStr) {
        String url = "data:text/html;charset=UTF-8,"
                + URL.encode(htmlStr).replaceAll("  ", "%20%20").replaceAll("#", "%23");
        root.consoleLog(url);
        Window.open(url, "_blank", ""); // this is internally-generated HTML
    }

    /**
     * Make a JavaScript object with all the parameters for this external format.
     */
    protected native void makeEfParams() /*-{
                                         var p = {};
                                         p.nameTemplate = "^^c_i^";
                                         p.shouldShowLinks = true;
                                         p.shouldShowAttributes = true;
                                         p.shouldShowMechanismIhNodes = false;
                                         p.shouldWriteVal = false;
                                         p.shouldWriteAllPorts = false;
                                         p.shouldWriteLinks = false;
                                         p.writeToNewTab = true;
                                         p.wrapInHtml = false;
                                         p.openInNewBrowserWindow = false;
                                         //p.compileToJavaScript = false;
                                         //p.executeJavaScript = false;
                                         this.efParams = p;
                                         }-*/;

    /**
     * Node name template.
     */
    public native String getNameTemplate() /*-{return this.efParams.nameTemplate;}-*/;

    public native void setNameTemplate(String nameTemplate) /*-{this.efParams.nameTemplate = nameTemplate;}-*/;

    /**
     * Whether or not to show links/ports between nodes.
     */
    public native boolean isShouldShowLinks() /*-{return this.efParams.shouldShowLinks;}-*/;
    //public native void setShouldShowLinks(boolean shouldShowLinks) /*-{this.efParams.shouldShowLinks = shouldShowLinks;}-*/;

    /**
     * Whether or not to show app-specific attributes of nodes.
     */
    public native boolean isShouldShowAttributes() /*-{return this.efParams.shouldShowAttributes;}-*/;
    //public native void sethouldShowAttributes(boolean shouldShowAttributes) /*-{this.efParams.shouldShowAttributes = shouldShowAttributes;}-*/;

    /**
     * Whether or not to show IH nodes that are part of a Xholon mechanism.
     * If this value is false, then only app-specific IH nodes will be shown.
     */
    public native boolean isShouldShowMechanismIhNodes() /*-{return this.efParams.shouldShowMechanismIhNodes;}-*/;
    //public native void setShouldShowMechanismIhNodes(boolean shouldShowMechanismIhNodes) /*-{this.efParams.shouldShowMechanismIhNodes = shouldShowMechanismIhNodes;}-*/;

    /**
     * Whether or not to write to a new tab each time step.
     * true create and write to a new tab
     * false write to the existing out tab
     */
    public native boolean isWriteToNewTab() /*-{return this.efParams.writeToNewTab;}-*/;
    //public native void setWriteToNewTab(boolean writeToNewTab) /*-{this.efParams.writeToNewTab = writeToNewTab;}-*/;

    public native boolean isWrapInHtml() /*-{return this.efParams.wrapInHtml;}-*/;
    //public native void setWrapInHtml(boolean wrapInHtml) /*-{this.efParams.wrapInHtml = wrapInHtml;}-*/;

    public native boolean isOpenInNewBrowserWindow() /*-{return this.efParams.openInNewBrowserWindow;}-*/;
    //public native void setOpenInNewBrowserWindow(boolean openInNewBrowserWindow) /*-{this.efParams.openInNewBrowserWindow = openInNewBrowserWindow;}-*/;

    /** compileToJavaScript */
    //public native boolean isCompileToJavaScript() /*-{return this.efParams.compileToJavaScript;}-*/;
    //public native void setCompileToJavaScript(boolean compileToJavaScript) /*-{this.efParams.compileToJavaScript = compileToJavaScript;}-*/;

    /** executeJavaScript */
    //public native boolean isExecuteJavaScript() /*-{return this.efParams.executeJavaScript;}-*/;
    //public native void setExecuteJavaScript(boolean executeJavaScript) /*-{this.efParams.executeJavaScript = executeJavaScript;}-*/;

    public String getOutFileName() {
        return outFileName;
    }

    public void setOutFileName(String outFileName) {
        this.outFileName = outFileName;
    }

    public String getModelName() {
        return modelName;
    }

    public void setModelName(String modelName) {
        this.modelName = modelName;
    }

    public IXholon getRoot() {
        return root;
    }

    public void setRoot(IXholon root) {
        this.root = root;
    }

    public String getOutPath() {
        return outPath;
    }

    public void setOutPath(String outPath) {
        this.outPath = outPath;
    }

    // #############################################################################
    // methods required to implement IXmlWriter

    @Override
    // DO NOT IMPLEMENT THIS
    public void createNew(Object out) {
    }

    @Override
    public void writeStartDocument() {
        sb.append("# ").append(modelName).append("\n")
                .append("# To compile this CoffeeScript into JavaScript, visit http://coffeescript.org\n")
                .append("\n");
    }

    @Override
    public void writeEndDocument() {
    }

    @Override
    // DO NOT IMPLEMENT THIS
    public void writeStartElement(String prefix, String localName, String namespaceURI) {
    }

    @Override
    // DO NOT IMPLEMENT THIS
    public void writeStartElement(String name) {
    }

    @Override
    // DO NOT IMPLEMENT THIS
    public void writeEndElement(String name, String namespaceURI) {
    }

    @Override
    // DO NOT IMPLEMENT THIS
    public void writeEndElement(String name) {
    }

    @Override
    // DO NOT IMPLEMENT THIS
    public void writeNamespace(String prefix, String namespaceURI) {
    }

    @Override
    // This is for use by Xholon.toXmlAttributes() only
    public void writeAttribute(String name, String value) {
        String nodeName = currentNode.getName(getNameTemplate());
        if ("Val".equalsIgnoreCase(name)) {
            if (isShouldWriteVal()) {
                sbAttrs.append(nodeName).append(".val(").append(value).append(")\n");
            }
            return;
        }
        if ("AllPorts".equalsIgnoreCase(name) && !isShouldWriteAllPorts()) {
            return;
        }
        if ("Links".equalsIgnoreCase(name) && !isShouldWriteLinks()) {
            return;
        }
        if ("roleName".equalsIgnoreCase(name)) {
            return;
        } // roleName is already written out
        if ("implName".equalsIgnoreCase(name)) {
            return;
        }
        switch (Misc.getJavaDataType(value)) {
        case IJavaTypes.JAVACLASS_String:
            sbAttrs.append(nodeName).append(".").append(name).append(" = \"").append(value).append("\"\n");
            break;
        case IJavaTypes.JAVACLASS_int:
        case IJavaTypes.JAVACLASS_long:
        case IJavaTypes.JAVACLASS_double:
        case IJavaTypes.JAVACLASS_float:
        case IJavaTypes.JAVACLASS_boolean:
        case IJavaTypes.JAVACLASS_byte:
        case IJavaTypes.JAVACLASS_char:
        case IJavaTypes.JAVACLASS_short:
            sbAttrs.append(nodeName).append(".").append(name).append(" = ").append(value).append("\n");
            break;
        case IJavaTypes.JAVACLASS_UNKNOWN:
        default:
            break;
        }

    }

    @Override
    // DO NOT IMPLEMENT THIS
    public void writeText(String text) {
    }

    @Override
    public void writeComment(String data) {
    }

    @Override
    public String getWriterName() {
        return "Xholon2Future";
    }

    @Override
    // DO NOT IMPLEMENT THIS
    public void flush() {
    }

    /* 
     * @see org.primordion.xholon.io.xml.IXmlWriter#isShouldWriteVal()
     */
    public native boolean isShouldWriteVal() /*-{
                                             return this.efParams.shouldWriteVal;
                                             }-*/;

    /* 
     * @see org.primordion.xholon.io.xml.IXmlWriter#setShouldWriteVal()
     */
    public native void setShouldWriteVal(boolean shouldWriteVal) /*-{
                                                                 this.efParams.shouldWriteVal = shouldWriteVal;
                                                                 }-*/;

    /* 
     * @see org.primordion.xholon.io.xml.IXmlWriter#isShouldWriteAllPorts()
     */
    public native boolean isShouldWriteAllPorts() /*-{
                                                  return this.efParams.shouldWriteAllPorts;
                                                  }-*/;

    /* 
     * @see org.primordion.xholon.io.xml.IXmlWriter#shouldWriteAllPorts()
     */
    public native void setShouldWriteAllPorts(boolean shouldWriteAllPorts) /*-{
                                                                           this.efParams.shouldWriteAllPorts = shouldWriteAllPorts;
                                                                           }-*/;

    @Override
    public native boolean isShouldWriteLinks() /*-{
                                               return this.efParams.shouldWriteLinks;
                                               }-*/;

    @Override
    public native void setShouldWriteLinks(boolean shouldWriteLinks) /*-{
                                                                     this.efParams.shouldWriteLinks = shouldWriteLinks;
                                                                     }-*/;

    // $wnd.CoffeeScript doesn't exist, although the .js file is obtained
    // possibly use https://github.com/requirejs/require-cs
    //public native void compileToJavaScript(String source) /*-{
    //  $wnd.xh.compiledJS = $wnd.CoffeeScript.compile(source, {bare: on});
    //}-*/;

    /**
     * Load the CoffeeScript compiler, and run the compile.
     */
    /*protected void loadAndRunCoffeeScriptCompiler(String csSource) {
      if (isDefinedCoffeeScript()) {
        compileToJavaScript(csSource);
      }
      else {
        require(this, csSource);
      }
    }*/

    /**
     * use requirejs
     */
    //protected native void require(final IXholon2ExternalFormat xh2Cs, String csSource) /*-{
    //  $wnd.requirejs.config({
    //    enforceDefine: false,
    //    paths: {
    //      coffee: [
    //        "xholon/lib/coffee-script"
    //      ]
    //    }
    //  });
    //  $wnd.require(["coffee"], function(coffee) {
    //    xh2Cs.@org.primordion.ef.program.Xholon2CoffeeScript::compileToJavaScript(Ljava/lang/String;)(csSource);
    //  });
    //}-*/;

    /**
     * Is $wnd.CoffeeScript defined.
     * @return it is defined (true), it's not defined (false)
     */
    //protected native boolean isDefinedCoffeeScript() /*-{
    //  return (typeof $wnd.CoffeeScript != "undefined");
    //}-*/;

    //public void executeJavaScript() {}

}