manifold.js.rt.parser.tree.ClassNode Maven / Gradle / Ivy
/*
* Copyright (c) 2018 - Manifold Systems LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package manifold.js.rt.parser.tree;
import java.util.*;
import java.util.stream.Collectors;
public class ClassNode extends Node {
/*Boiler plate code segments taken from babel.js*/
//Used for defining object properties
private static final String CREATE_CLASS = "var _createClass = function () { " +
"function defineProperties(target, props) { for (var i = 0; i < props.length; i++) " +
"{ var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; " +
"descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; " +
"Object.defineProperty(target, descriptor.key, descriptor); } } " +
"return function (Constructor, protoProps, staticProps) { if (protoProps) " +
"defineProperties(Constructor.prototype, protoProps); if (staticProps) " +
"defineProperties(Constructor, staticProps); return Constructor; }; }();\n";
//Used to make sure classes can not be called as a function
private static final String CLASS_CALL_CHECK = "function _classCallCheck(instance, Constructor) { " +
"if (!(instance instanceof Constructor)) { " +
"throw new TypeError(\"Cannot call a class as a function\") } }\n";
//name of generated supertype object
public static final String SUPERTYPE_OBJECT = "_superClassObject";
private String _superClass = null;
public ClassNode(String name ) {
super(name);
}
public ClassNode(String name, String superClass ) {
super(name);
_superClass = superClass;
}
public void setSuperClass(String superClass) {
_superClass = superClass;
}
public String getSuperClass() {
return _superClass;
}
@Override
public String genCode() {
StringBuilder code = new StringBuilder(CLASS_CALL_CHECK); //Makes sure constructor is called correctly
if (!getChildren(PropertyNode.class).isEmpty()) code.append(CREATE_CLASS); //Defines getters and setters
code.append("var ").append(getName()).append(" = function(")
.append((getSuperClass() == null? "" : "_" + getSuperClass()))
.append(") { ");
String constructorCode;
if (getChildren(ConstructorNode.class).isEmpty()) {
//Gen default constructor if no child found
constructorCode = "\n\t" + new ConstructorNode(getName() ).genCode();
} else {
//Should only have one constructor
constructorCode = "\n\t" + getFirstChild(ConstructorNode.class).genCode();
}
//If superclass exists, instantiate superclass object inside constructor
if (getSuperClass() != null) {
StringBuilder superClassObjectCode = new StringBuilder();
String superClassArg = "_" + getSuperClass();
//Create extended superclass object
superClassObjectCode.append("\n\tvar ").append(SUPERTYPE_OBJECT)
.append("= new (Java.extend(").append(superClassArg).append("))(){")
.append(genOverrideFunctionCode(getChildren(ClassFunctionNode.class))) //Add overridden methods
.append("};");
//Create property reference for the superclass object
superClassObjectCode.append("\n\t").append("this.").append(SUPERTYPE_OBJECT)
.append(" = ").append(SUPERTYPE_OBJECT).append(";");
constructorCode = constructorCode.replaceFirst("[{]", "{" + superClassObjectCode.toString());
}
code.append(constructorCode);
//Create method for getting super object
if (getSuperClass() != null) {
code.append("\n\t").append(getName()).append(".prototype._getSuperClass = function _getSuperClass(){" +
"return this._superClassObject}");
}
for (ClassFunctionNode node : getChildren(ClassFunctionNode.class)) {
if (!node.isOverride()) code.append("\n\t").append(node.genCode());
}
code.append(genPropertyObjectCode(getChildren(PropertyNode.class)));
code.append("\n\treturn " + getName() + ";\n}(")
.append((getSuperClass() == null? "" : getSuperClass())) //Possibly give superclass as arg
.append(");");
code.append("\n" + getName() + ";");
return code.toString();
}
private String genOverrideFunctionCode(List functionNodes) {
return String.join(",", functionNodes.stream()
.filter(node -> node.isOverride())
.map(node -> node.genCode())
.collect(Collectors.toList()));
}
private String genPropertyObjectCode (List propertyNodes) {
//Wrapper to hold getters and setters for the same property
class PropertyNodeWrapper {
private String _name;
private boolean _isStatic;
private PropertyNode _getter = null;
private PropertyNode _setter = null;
public PropertyNodeWrapper(String name) {
_name = name;
}
public void add(PropertyNode node) {
if (node.isSetter()) _setter = node;
else _getter = node;
}
public String genCode() {
return "\n\t\t" + "{key: \"" + _name + "\"," +
(_setter != null?_setter.genCode()+",":"") +
(_getter != null?_getter.genCode():"") +
"}";
}
}
String propCode = "";
//combines getters and setters for each property
if (!propertyNodes.isEmpty()) {
//Separate static and non-static properties
HashMap propertyNodeBucket = new HashMap();
HashMap staticPropertyNodeBucket = new HashMap();
propCode += "\n\t_createClass(" + getName() + ", ";
for (PropertyNode node : propertyNodes) {
//Get wrapper by property name, and insert name
PropertyNodeWrapper wrapper;
if (node.isStatic()) {
wrapper = staticPropertyNodeBucket.get(node.getName());
if (wrapper == null) wrapper = new PropertyNodeWrapper(node.getName());
staticPropertyNodeBucket.put(node.getName(), wrapper);
}
else {
wrapper = propertyNodeBucket.get(node.getName());
if (wrapper == null) wrapper = new PropertyNodeWrapper(node.getName());
propertyNodeBucket.put(node.getName(), wrapper);
}
wrapper.add(node);
}
//Combine the properties into an array
String nonStaticProps = (propertyNodeBucket.isEmpty()) ? "null" :
"[" + String.join(",", propertyNodeBucket.values().stream()
.map(prop->prop.genCode())
.collect(Collectors.toList())) + "]";
String staticProps = (staticPropertyNodeBucket.isEmpty()) ? "null" :
"[" + String.join(",", staticPropertyNodeBucket.values().stream()
.map(prop->prop.genCode())
.collect(Collectors.toList())) + "]";
propCode += nonStaticProps + "," + staticProps + ");";
}
return propCode;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ClassNode)) return false;
ClassNode node = (ClassNode) obj;
return getName().equals(node.getName()) && getSuperClass().equals(node.getSuperClass());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy