Java tutorial
/* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you 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 org.ballerinalang.composer.service.workspace.tools; import com.github.jknack.handlebars.Context; import com.github.jknack.handlebars.Handlebars; import com.github.jknack.handlebars.Template; import com.google.common.base.CaseFormat; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import org.apache.commons.io.FileUtils; import org.ballerinalang.model.tree.Node; import org.ballerinalang.model.tree.NodeKind; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.stream.Collectors; import java.util.stream.Stream; /** * Code generator which will convert java models to javascript. */ public class ModelGenerator { static PrintStream out = System.out; static Map<String, String> alias = new HashMap<String, String>(); static final String TEMPLATE_PATH = "./modules/web/src/plugins/ballerina/model/templates/"; static final String GENERATE_PATH = "./modules/web/src/plugins/ballerina/model/gen/"; public static void main(String args[]) { JsonObject nodes = getContext(); for (Map.Entry<String, JsonElement> entry : nodes.entrySet()) { JsonObject node = (JsonObject) entry.getValue(); String lowerHyphenName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, entry.getKey()); generateSourceFiles(node, "node.hbs", "tree/" + lowerHyphenName + ".js"); generateSourceFiles(node, "abstract-node.hbs", "tree/abstract-tree/" + lowerHyphenName + ".js"); } generateSourceFiles(nodes, "abstract-tree-util.hbs", "abstract-tree-util.js"); generateSourceFiles(nodes, "node-factory.hbs", "node-factory.js"); generateSourceFiles(nodes, "positioning-util.hbs", "positioning-util.js"); generateSourceFiles(nodes, "sizing-util.hbs", "sizing-util.js"); generateSourceFiles(nodes, "error-rendering-util.hbs", "error-rendering-util.js"); } public static JsonObject getContext() { // Set alias for the classes alias.put("ImportNode", "ImportPackageNode"); alias.put("RecordLiteralKeyValueNode", "RecordKeyValueNode"); alias.put("XmlnsNode", "XMLNSDeclarationNode"); alias.put("ArrayLiteralExprNode", "ArrayLiteralNode"); alias.put("BinaryExprNode", "BinaryExpressionNode"); alias.put("ConnectorInitExprNode", "ConnectorInitNode"); alias.put("FieldBasedAccessExprNode", "FieldBasedAccessNode"); alias.put("IndexBasedAccessExprNode", "IndexBasedAccessNode"); alias.put("LambdaNode", "LambdaFunctionNode"); alias.put("RecordLiteralExprNode", "RecordLiteralNode"); alias.put("SimpleVariableRefNode", "SimpleVariableReferenceNode"); alias.put("TernaryExprNode", "TernaryExpressionNode"); alias.put("TypeCastExprNode", "TypeCastNode"); alias.put("TypeConversionExprNode", "TypeConversionNode"); alias.put("UnaryExprNode", "UnaryExpressionNode"); alias.put("XmlAttributeNode", "XMLAttributeNode"); alias.put("XmlCommentLiteralNode", "XMLCommentLiteralNode"); alias.put("XmlElementLiteralNode", "XMLElementLiteralNode"); alias.put("XmlPiLiteralNode", "XMLProcessingInstructionLiteralNode"); alias.put("XmlQnameNode", "XMLQNameNode"); alias.put("XmlQuotedStringNode", "XMLQuotedStringNode"); alias.put("XmlTextLiteralNode", "XMLTextLiteralNode"); alias.put("TryNode", "TryCatchFinallyNode"); alias.put("VariableDefNode", "VariableDefinitionNode"); alias.put("BuiltInRefTypeNode", "BuiltInReferenceTypeNode"); alias.put("EnumeratorNode", "EnumNode"); List<Class<?>> list = ModelGenerator.find("org.ballerinalang.model.tree"); NodeKind[] nodeKinds = NodeKind.class.getEnumConstants(); JsonObject nodes = new JsonObject(); for (NodeKind node : nodeKinds) { String nodeKind = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, node.toString()); String nodeClassName = nodeKind + "Node"; try { String actualClassName = (alias.get(nodeClassName) != null) ? alias.get(nodeClassName) : nodeClassName; Class<?> clazz = list.stream() .filter(nodeClass -> nodeClass.getSimpleName().equals(actualClassName)).findFirst().get(); JsonObject nodeObj = new JsonObject(); nodeObj.addProperty("kind", nodeKind); nodeObj.addProperty("name", nodeClassName); nodeObj.addProperty("fileName", CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, nodeClassName)); JsonArray attr = new JsonArray(); JsonArray bools = new JsonArray(); JsonArray imports = new JsonArray(); List<String> parents = Arrays.asList(clazz.getInterfaces()).stream() .map(parent -> parent.getSimpleName()).collect(Collectors.toList()); // tag object with supper type if (parents.contains("StatementNode")) { nodeObj.addProperty("isStatement", true); JsonObject imp = new JsonObject(); imp.addProperty("returnType", "StatementNode"); imp.addProperty("returnTypeFile", "statement-node"); imports.add(imp); } else { nodeObj.addProperty("isStatement", false); } if (parents.contains("ExpressionNode")) { nodeObj.addProperty("isExpression", true); JsonObject imp = new JsonObject(); imp.addProperty("returnType", "ExpressionNode"); imp.addProperty("returnTypeFile", "expression-node"); imports.add(imp); } else { nodeObj.addProperty("isExpression", false); } if (!parents.contains("StatementNode") && !parents.contains("ExpressionNode")) { JsonObject imp = new JsonObject(); imp.addProperty("returnType", "Node"); imp.addProperty("returnTypeFile", "node"); imports.add(imp); } Method[] methods = clazz.getMethods(); for (Method m : methods) { String methodName = m.getName(); if ("getKind".equals(methodName) || "getWS".equals(methodName) || "getPosition".equals(methodName)) { continue; } if (methodName.startsWith("get")) { JsonObject attribute = new JsonObject(); JsonObject imp = new JsonObject(); attribute.addProperty("property", toJsonName(m.getName(), 3)); attribute.addProperty("methodSuffix", m.getName().substring(3)); attribute.addProperty("list", List.class.isAssignableFrom(m.getReturnType())); attribute.addProperty("isNode", Node.class.isAssignableFrom(m.getReturnType())); if (Node.class.isAssignableFrom(m.getReturnType())) { String returnClass = m.getReturnType().getSimpleName(); if (alias.containsValue(m.getReturnType().getSimpleName())) { returnClass = getKindForAliasClass(m.getReturnType().getSimpleName()); } imp.addProperty("returnType", returnClass); attribute.addProperty("returnType", returnClass); imp.addProperty("returnTypeFile", CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, returnClass)); if (!imports.contains(imp)) { imports.add(imp); } } attr.add(attribute); } if (methodName.startsWith("is")) { JsonObject attribute = new JsonObject(); JsonObject imp = new JsonObject(); attribute.addProperty("property", toJsonName(m.getName(), 2)); attribute.addProperty("methodSuffix", m.getName().substring(2)); attribute.addProperty("list", List.class.isAssignableFrom(m.getReturnType())); attribute.addProperty("isNode", Node.class.isAssignableFrom(m.getReturnType())); if (Node.class.isAssignableFrom(m.getReturnType())) { String returnClass = m.getReturnType().getSimpleName(); if (alias.containsValue(m.getReturnType().getSimpleName())) { returnClass = getKindForAliasClass(m.getReturnType().getSimpleName()); } imp.addProperty("returnType", returnClass); attribute.addProperty("returnType", returnClass); imp.addProperty("returnTypeFile", CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, returnClass)); if (!imports.contains(imp)) { imports.add(imp); } } bools.add(attribute); } } nodeObj.add("attributes", attr); nodeObj.add("bools", bools); nodeObj.add("imports", imports); nodes.add(nodeClassName, nodeObj); } catch (NoSuchElementException e) { out.println("alias.put(\"" + nodeClassName + "\", \"\");"); } } out.println(nodes); return nodes; } private static String getKindForAliasClass(String simpleName) { for (Map.Entry<String, String> entry : alias.entrySet()) { if (simpleName.equals(entry.getValue())) { return entry.getKey(); } } throw new AssertionError("Undefined entry :" + simpleName); } public static void generateSourceFiles(JsonObject node, String tpl, String name) { try { String templateString = FileUtils.readFileToString(new File(TEMPLATE_PATH + tpl), "UTF-8"); Handlebars handlebars = new Handlebars(); Template template = handlebars.compileInline(templateString); Gson gson = new Gson(); Type type = new TypeToken<Map<String, Object>>() { }.getType(); Map<String, Object> map = gson.fromJson(node, type); Context context = Context.newBuilder(map).build(); String generated = template.apply(context); FileUtils.writeStringToFile(new File(GENERATE_PATH + name), generated, "UTF-8"); } catch (IOException e) { throw new AssertionError("Template files should be always defined."); } } private static final char PKG_SEPARATOR = '.'; private static final char DIR_SEPARATOR = '/'; private static final String CLASS_FILE_SUFFIX = ".class"; private static final String BAD_PACKAGE_ERROR = "Unable to get resources from path '%s'. " + "Are you sure the package '%s' exists?"; public static List<Class<?>> find(String scannedPackage) { String scannedPath = scannedPackage.replace(PKG_SEPARATOR, DIR_SEPARATOR); URL scannedUrl = Thread.currentThread().getContextClassLoader().getResource(scannedPath); if (scannedUrl == null) { throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage)); } File scannedDir = new File(scannedUrl.getFile()); List<Class<?>> classes = new ArrayList<Class<?>>(); File[] files = scannedDir.listFiles(); Stream.of(files).forEach((file -> classes.addAll(find(file, scannedPackage)))); return classes; } private static List<Class<?>> find(File file, String scannedPackage) { List<Class<?>> classes = new ArrayList<Class<?>>(); String resource = scannedPackage + PKG_SEPARATOR + file.getName(); if (file.isDirectory()) { File[] files = file.listFiles(); Stream.of(files).forEach((child -> classes.addAll(find(child, resource)))); } else if (resource.endsWith(CLASS_FILE_SUFFIX)) { int endIndex = resource.length() - CLASS_FILE_SUFFIX.length(); String className = resource.substring(0, endIndex); try { classes.add(Class.forName(className)); } catch (ClassNotFoundException ignore) { } } return classes; } private static String toJsonName(String name, int prefixLen) { return Character.toLowerCase(name.charAt(prefixLen)) + name.substring(prefixLen + 1); } }