All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.uscexp.grappa.extension.codegenerator.AstModelGenerator Maven / Gradle / Ivy

There is a newer version: 0.4.0-Beta
Show newest version
/*
 * Copyright (C) 2014 by haui - all rights reserved
 */
package com.github.uscexp.grappa.extension.codegenerator;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.parboiled.BaseParser;
import org.parboiled.Parboiled;

import com.github.uscexp.grappa.extension.annotations.AstCommand;
import com.github.uscexp.grappa.extension.interpreter.AstInterpreter;
import com.github.uscexp.grappa.extension.nodes.AstCommandTreeNode;
import com.github.uscexp.grappa.extension.nodes.AstTreeNode;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JClassAlreadyExistsException;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JDocComment;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JType;

/**
 * The {@link AstModelGenerator} creates the model classes from a {@link BaseParser} extended annotated parser class. It creates the
 * corresponding java source files for the {@link AstCommand} annotated rules in the parser class. The generated classes will extend 
 * AstCommandTreeNode and the to be implemented method interpret.
 *
 * @author  haui
 */
public class AstModelGenerator {

	private static Logger logger = Logger.getLogger(AstModelGenerator.class.getName());

	public  void generateAstModel(String parserClassString, String sourceOutputPath)
		throws ReflectiveOperationException, IOException {
		@SuppressWarnings("unchecked")
		Class> parserClass = (Class>) Class.forName(parserClassString);

		generateAstModel(parserClass, sourceOutputPath);
	}

	public  void generateAstModel(Class> parserClass, String sourceOutputPath)
		throws ReflectiveOperationException, IOException {
		JCodeModel codeModel = new JCodeModel();

		if (sourceOutputPath == null) {
			sourceOutputPath = ".";
		}

		@SuppressWarnings("unchecked")
		BaseParser> parser = (BaseParser>) Parboiled.createParser(parserClass);

		Map annotationMap = new HashMap<>();
		Map> classMap = new HashMap<>();

		AstInterpreter.getAnnotations(parser.getClass(), classMap, annotationMap);

		Set methodNames = classMap.keySet();

		for (String methodName : methodNames) {
			Object annotation = annotationMap.get(methodName);
			if (annotation instanceof AstCommand) {
				String classname = AstInterpreter.getClassname(methodName, (AstCommand) annotation, classMap);
				try {
					String filename = classname.replace('.', '/');
					filename = sourceOutputPath + "/" + filename + ".java";
					File file = new File(filename);
					if (file.exists()) {
						logger.log(Level.INFO, String.format("File %s already exists -> skip ...", file.getName()));
						continue;
					}

					JDefinedClass definedClass = codeModel._class(classname);
					String genericTypeName = "V";
					JClass genericType = codeModel.ref(genericTypeName);
					JClass superClass = codeModel.ref(AstCommandTreeNode.class).narrow(genericType);
					definedClass._extends(superClass);
					definedClass.generify(genericTypeName);
					JDocComment javadoc = definedClass.javadoc();
					javadoc.add(String.format("Command implementation for the %s rule: %s.", parserClass.getSimpleName(),
							methodName));
					addConstructors(codeModel, definedClass);
					addAbstractMethods(codeModel, definedClass);
					codeModel.build(new File(sourceOutputPath));
				} catch (JClassAlreadyExistsException e) {
					logger.log(Level.WARNING, String.format("Class %s already exists -> skip ...", classname));
				}
			}
		}
	}

	public void addConstructors(JCodeModel codeModel, JDefinedClass definedClass) {
		Constructor[] declaredConstructors = AstCommandTreeNode.class.getDeclaredConstructors();
		MethodConstructorWrapper>[] wrappers = wrapConstructors(declaredConstructors);

		addMethods(codeModel, definedClass, wrappers);
	}

	public  String getParameterizedType(JCodeModel codeModel, MethodConstructorWrapper method, Class[] parameterTypes, int i) {
		String result = null;
		Type[] genericParameterTypes = method.getGenericParameterTypes();
		if ((genericParameterTypes != null) && (genericParameterTypes.length == parameterTypes.length) &&
				(genericParameterTypes[i] instanceof ParameterizedType)) {
			ParameterizedType parameterizedType = (ParameterizedType) genericParameterTypes[i];
			result = parameterizedType.getActualTypeArguments()[0].toString();
		}
		return result;
	}

	public String getParameterName(Class[] parameterTypes, TypeVariable[] typeParameters, int i) {
		String name = "arg" + i;
		if ((typeParameters != null) && (typeParameters.length == parameterTypes.length)) {
			name = typeParameters[i].getName();
		}
		return name;
	}

	public void addAbstractMethods(JCodeModel codeModel, JDefinedClass definedClass) {
		Method[] declaredMethods = AstTreeNode.class.getDeclaredMethods();
		MethodConstructorWrapper[] wrappers = wrapMethods(declaredMethods);

		addMethods(codeModel, definedClass, wrappers);
	}

	private  void addMethods(JCodeModel codeModel, JDefinedClass definedClass, MethodConstructorWrapper[] methods) {
		for (MethodConstructorWrapper method : methods) {
			if (Modifier.isAbstract(method.getModifiers()) || method.isConstructor()) {
				int modifier = JMod.PUBLIC;
				if (Modifier.isProtected(method.getModifiers())) {
					modifier = JMod.PROTECTED;
				}
				JMethod jMethod = null;

				if (method.isConstructor()) {
					jMethod = definedClass.constructor(modifier);
				} else {
					JType type = codeModel._ref(method.getReturnType());
					jMethod = definedClass.method(modifier, type, method.getName());
					jMethod.annotate(Override.class);
				}

				// throws declarations
				@SuppressWarnings("unchecked")
				Class[] exceptionTypes = (Class[]) method.getExceptionTypes();

				for (Class exeptionType : exceptionTypes) {
					jMethod._throws(exeptionType);
				}

				// parameters
				Class[] parameterTypes = method.getParameterTypes();
				TypeVariable[] typeParameters = method.getTypeParameters();

				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.append("super(");
				for (int i = 0; i < parameterTypes.length; i++) {
					String name = getParameterName(parameterTypes, typeParameters, i);
					String parameterizedTypeString = getParameterizedType(codeModel, method, parameterTypes, i);

					if (parameterizedTypeString != null) {
						JClass genericType = codeModel.ref(parameterizedTypeString);
						JClass pType = codeModel.ref(parameterTypes[i]).narrow(genericType);
						jMethod.param(pType, name);
					} else {
						jMethod.param(parameterTypes[i], name);
					}
					if (i > 0) {
						stringBuilder.append(", ");
					}
					stringBuilder.append(name);
				}
				stringBuilder.append(");");

				JBlock body = jMethod.body();
				if (method.isConstructor()) {
					body.directStatement(stringBuilder.toString());
				} else {
					codeModel.ref(RuntimeException.class);
					body.directStatement("throw new RuntimeException(\"not yet implemented\");");
				}
			}
		}
	}

	public static void main(String[] args) {
		try {
			AstModelGenerator astModelGenerator = new AstModelGenerator();

			String sourceOutputPath = args[1];
			astModelGenerator.generateAstModel(args[0], sourceOutputPath);
		} catch (Exception e) {
			logger.log(Level.SEVERE, "Unexpected error occured", e);

			logger.log(Level.SEVERE,
				"Syntax: java -cp  com.github.uscexp.grappa.extension.codegenerator.AstModelGenerator pasrserClass sourceOutputPath");
		}
	}

	private MethodConstructorWrapper[] wrapMethods(Method[] methods) {
		@SuppressWarnings("unchecked")
		MethodConstructorWrapper[] result = new MethodConstructorWrapper[methods.length];

		for (int i = 0; i < methods.length; i++) {
			result[i] = new MethodConstructorWrapper(methods[i]);
		}
		return result;
	}

	private MethodConstructorWrapper>[] wrapConstructors(Constructor[] constructors) {
		@SuppressWarnings("unchecked")
		MethodConstructorWrapper>[] result = new MethodConstructorWrapper[constructors.length];

		for (int i = 0; i < constructors.length; i++) {
			result[i] = new MethodConstructorWrapper>(constructors[i]);
		}
		return result;
	}

	public class MethodConstructorWrapper {

		private final T method;

		public MethodConstructorWrapper(final T method) {
			this.method = method;
		}

		public boolean isConstructor() {
			return method instanceof Constructor;
		}

		public String getName() {
			if (method instanceof Method)
				return ((Method) method).getName();
			else
				return ((Constructor) method).getName();
		}

		public int getModifiers() {
			if (method instanceof Method)
				return ((Method) method).getModifiers();
			else
				return ((Constructor) method).getModifiers();
		}

		public TypeVariable[] getTypeParameters() {
			if (method instanceof Method)
				return ((Method) method).getTypeParameters();
			else
				return ((Constructor) method).getTypeParameters();
		}

		public Class getReturnType() {
			if (method instanceof Method)
				return ((Method) method).getReturnType();
			else
				return null;
		}

		public Class[] getParameterTypes() {
			if (method instanceof Method)
				return ((Method) method).getParameterTypes();
			else
				return ((Constructor) method).getParameterTypes();
		}

		public Type[] getGenericParameterTypes() {
			if (method instanceof Method)
				return ((Method) method).getGenericParameterTypes();
			else
				return ((Constructor) method).getGenericParameterTypes();
		}

		public Class[] getExceptionTypes() {
			if (method instanceof Method)
				return ((Method) method).getExceptionTypes();
			else
				return ((Constructor) method).getExceptionTypes();
		}

		public boolean equals(Object obj) {
			if (method instanceof Method)
				return ((Method) method).equals(obj);
			else
				return ((Constructor) method).equals(obj);
		}

		public int hashCode() {
			if (method instanceof Method)
				return ((Method) method).hashCode();
			else
				return ((Constructor) method).hashCode();
		}

		public String toString() {
			if (method instanceof Method)
				return ((Method) method).toString();
			else
				return ((Constructor) method).toString();
		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy