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

net.anthavio.httl.util.JavaCodeGenerator Maven / Gradle / Ivy

The newest version!
package net.anthavio.httl.util;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Stack;

import javax.tools.SimpleJavaFileObject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Base class for java generators. Subclass should implement parse method that generate AST
 * 
 * @author martin.vanek
 *
 */
public abstract class JavaCodeGenerator {

	protected final Logger logger = LoggerFactory.getLogger(getClass());

	protected Stack stack = new Stack();

	//declare classes as global or keep them inner (local)
	protected boolean doGlobalTypes = false;

	//finished custom objects - for global style declarations
	protected List globals = new ArrayList();

	//base class to extend
	protected String baseClass;

	//interfaces to implement
	protected List interfaces;

	protected String targetPackage;

	protected DateFormat dateFormat;

	public DateFormat getDateFormat() {
		return dateFormat;
	}

	public void setDateFormat(DateFormat dateFormat) {
		this.dateFormat = dateFormat;
	}

	public void setDateFormat(String pattern) {
		this.dateFormat = new SimpleDateFormat(pattern);
	}

	/**
	 * @param className if qualified, then package par will be aslo used
	 * @param reader source of JSON
	 * @return java code
	 */
	public String process(String className, Reader reader) throws IOException {
		if (Cutils.isEmpty(className)) {
			throw new IllegalArgumentException("rootName is empty");
		}
		int dotIdx = className.lastIndexOf('.');
		if (dotIdx != -1) {
			targetPackage = className.substring(0, dotIdx);
			className = className.substring(dotIdx + 1);
		}
		AstNode root = parse(className, reader);
		//System.out.println(root);
		StringWriter sw = new StringWriter();
		write(root, sw);
		return sw.toString();
	}

	public abstract AstNode parse(String className, Reader reader) throws IOException;

	protected String buildFieldName(String name) {
		char c0 = name.charAt(0);
		if (Character.isDigit(c0)) { //OK in JSON, NOT in Java
			name = "f" + name;
		} else if (Character.isUpperCase(c0)) {
			if (name.length() == 1) {
				name = Character.toLowerCase(c0) + "";
			} else {
				name = Character.toLowerCase(c0) + name.substring(1);
			}
		}
		return name;
	}

	protected String buildClassName(String name) {
		char c0 = name.charAt(0);
		if (Character.isDigit(c0)) {
			name = "C" + name;
		} else if (Character.isLowerCase(c0)) {
			if (name.length() == 1) {
				name = Character.toUpperCase(c0) + "";
			} else {
				name = Character.toUpperCase(c0) + name.substring(1);
			}
		}
		return name;
	}

	private String buildFieldType(AstNode field) {
		StringBuilder sb = new StringBuilder();
		AstNode f = field;
		while (f.isArray()) { //nested arrays...
			sb.append("java.util.List<");
			if (f.getElements().isEmpty()) {
				break; //empty array
			}
			f = f.getElements().get(0);
		}

		sb.append(field.getTypeName());

		f = field;
		while (f.isArray()) {
			sb.append(">");
			if (f.getElements().isEmpty()) {
				break; //empty array
			}
			f = f.getElements().get(0);
		}

		return sb.toString();
	}

	public void write(AstNode root, Writer writer) {

		IndentingWriter w = new IndentingWriter(writer);
		if (Cutils.isNotEmpty(targetPackage)) {
			w.println("package " + targetPackage + ";");
		}
		//pw.println("import java.util.List;");
		//pw.println("import java.util.Map;");
		//pw.println("import java.util.Date;");
		w.println("/**");
		w.println(" * Generated by Hatatitla at " + new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(new Date()));
		w.println(" */");
		if (root.isArray()) {

			if (root.getTypeName().equals("java.util.Map")) {
				//heretogenous array - elements has different types
				List elements = root.getElements();
				for (AstNode element : elements) {
					writeClass(element, Modifier.PUBLIC, w);
				}
			} else {
				//homogenous array - type is same so just first
				writeClass(root.getElements().get(0), Modifier.PUBLIC, w);
			}
		} else {
			writeClass(root, Modifier.PUBLIC, w);
		}

		for (AstNode global : globals) {
			writeClass(global, 0, w);
		}

	}

	private void writeClass(AstNode node, int modifiers, IndentingWriter w) {
		if (Modifier.isPublic(modifiers)) {
			w.print("public ");
		} else if (Modifier.isProtected(modifiers)) {
			w.print("protected ");
		} else if (Modifier.isProtected(modifiers)) {
			w.print("private ");
		}
		if (Modifier.isStatic(modifiers)) {
			w.print("static ");
		}
		if (Modifier.isFinal(modifiers)) {
			w.print("final ");
		}
		w.print("class " + node.getTypeName());

		if (Cutils.isNotEmpty(baseClass)) {
			w.print(" extends " + baseClass);
		}
		if (interfaces != null && interfaces.size() != 0) {
			w.print(" implements ");
			for (int i = 0; i < interfaces.size(); ++i) {
				String itf = interfaces.get(i);
				w.print(itf);
				if (i < interfaces.size() - 1) {
					w.print(", ");
				}
			}
		}
		w.print(" {");
		w.println();
		w.incLevel();

		List fields = node.getElements();
		for (AstNode field : fields) {
			writeFieldDeclaration(w, field);
		}

		for (AstNode field : fields) {
			writeFieldGetSet(w, field);
		}

		//local types
		for (AstNode local : node.locals) {
			writeClass(local, Modifier.PUBLIC | Modifier.STATIC, w);
		}

		w.decLevel();
		w.println("}");
	}

	private void writeFieldGetSet(IndentingWriter w, AstNode field) {
		String type = buildFieldType(field);
		String fieldName = buildFieldName(field.getName());
		String name;
		if (fieldName.length() == 1) {
			name = Character.toUpperCase(fieldName.charAt(0)) + "";
		} else {
			name = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
		}
		//getter
		w.print("public ");
		w.print(type);
		w.print(" get" + name + "() {");
		w.incLevel();
		w.println();
		w.println("return this." + fieldName + ";");
		w.decLevel();
		w.println("}");
		//setter
		w.print("public void ");
		w.print(" set" + name + "(" + type + " " + fieldName + ") {");
		w.incLevel();
		w.println();
		w.println("this." + fieldName + " = " + fieldName + ";");
		w.decLevel();
		w.println("}");

	}

	private void writeFieldDeclaration(IndentingWriter w, AstNode field) {
		String type = buildFieldType(field);
		String name = buildFieldName(field.getName());
		w.print("private " + type + " " + name + ";");
		w.println();
	}

	public boolean isDoGlobalTypes() {
		return doGlobalTypes;
	}

	public void setDoGlobalTypes(boolean doGlobalTypes) {
		this.doGlobalTypes = doGlobalTypes;
	}

	public String getBaseClass() {
		return baseClass;
	}

	public void setBaseClass(Class clazz) {
		this.baseClass = clazz.getName();
	}

	public void setBaseClass(String className) {
		this.baseClass = className;
	}

	public List getInterfaces() {
		return interfaces;
	}

	public void setInterfaces(List> interfaces) {
		this.interfaces = new ArrayList();
		for (Class clazz : interfaces) {
			this.interfaces.add(clazz.getName());
		}
	}

	public void addInterface(Class interfacex) {
		addInterface(interfacex.getName());
	}

	public void addInterface(String interfaceName) {
		if (this.interfaces == null) {
			this.interfaces = new ArrayList();
		}
		//disallow duplicities
		if (this.interfaces.indexOf(interfaceName) == -1) {
			this.interfaces.add(interfaceName);
		}
	}

	public String getTargetPackage() {
		return targetPackage;
	}

	public void setTargetPackage(String targetPackage) {
		this.targetPackage = targetPackage;
	}

}

class AstNode {

	//localy declared types
	public List locals = new ArrayList();

	private final String name;

	private final List fielems = new ArrayList(); //fields for Class, elements for Array

	private String typeName;

	private final boolean array;

	private boolean simpleType;

	public AstNode(String name, Class clazz, boolean array) {
		if (Cutils.isEmpty(name)) {
			throw new IllegalArgumentException("Name is empty");
		}
		this.name = name;

		if (clazz != null) {
			setTypeName(clazz);
		}

		this.array = array;
	}

	public AstNode(String name, String typeName, boolean array) {
		if (Cutils.isEmpty(name)) {
			throw new IllegalArgumentException("Name is empty");
		}
		this.name = name;
		setTypeName(typeName);
		this.array = array;

	}

	public String getName() {
		return name;
	}

	public String getTypeName() {
		return typeName;
	}

	public boolean isSimpleType() {
		return simpleType;
	}

	//For Array
	public void setTypeName(Class clazz) {
		String name = clazz.getName();
		if (name.startsWith("java.lang")) {
			setTypeName(clazz.getSimpleName());
		} else {
			setTypeName(clazz.getName());
		}
	}

	//For Object (custom)
	public void setTypeName(String name) {
		this.typeName = name;
		if (name.startsWith("java.lang")) {
			this.simpleType = true;
		} else {
			this.simpleType = false;
		}
	}

	//Fields (Object) or Elements (Array)
	public List getElements() {
		return fielems;
	}

	public AstNode addField(String name, Class type) {
		if (Cutils.isEmpty(name)) {
			throw new IllegalArgumentException("Name is empty");
		}
		AstNode node = new AstNode(name, type, false);
		fielems.add(node);
		return node;
	}

	public void addField(AstNode entry) {
		fielems.add(entry);
	}

	public boolean isArray() {
		return array;
	}

	@Override
	public String toString() {
		return "{name=" + name + ", typeName=" + typeName + ", list=" + array + ", fields=" + fielems + "}";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((fielems == null) ? 0 : fielems.hashCode());
		result = prime * result + (array ? 1231 : 1237);
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + ((typeName == null) ? 0 : typeName.hashCode());
		return result;
	}

	public boolean equalsFields(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		AstNode other = (AstNode) obj;
		if (array != other.array)
			return false;
		if (fielems == null) {
			if (other.fielems != null)
				return false;
		} else if (!array) {
			if (simpleType) { //when simple - type must be same
				if (typeName == null) {
					if (other.typeName != null)
						return false;
				} else if (!typeName.equals(other.typeName))
					return false;
			} else { //when class - field must be precisely the same
				if (!fielems.equals(other.fielems))
					return false;
			}
		} else if (array) { //when array - only type of fields must by same
			//we will determine field's type by own type
			if (typeName == null) {
				if (other.typeName != null)
					return false;
			} else if (!typeName.equals(other.typeName))
				return false;
		}

		/*
		if (typeName == null) {
			if (other.typeName != null)
				return false;
		} else if (!typeName.equals(other.typeName))
			return false;
		*/
		return true;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		AstNode other = (AstNode) obj;
		if (fielems == null) {
			if (other.fielems != null)
				return false;
		} else if (!fielems.equals(other.fielems))
			return false;
		if (array != other.array)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (typeName == null) {
			if (other.typeName != null)
				return false;
		} else if (!typeName.equals(other.typeName))
			return false;
		return true;
	}
}

class Clazz {

	private final Class clazz;

	private final String name;

	/**
	 * Custom class
	 */
	public Clazz(String name) {
		this.name = name;
		this.clazz = null;
	}

	/**
	 * String/Integer/Date
	 */
	public Clazz(Class clazz) {
		if (clazz == null) {
			throw new IllegalArgumentException("null clazz");
		}
		this.clazz = clazz;
		this.name = null;
	}

	public String getName() {
		if (clazz != null) {
			String name = clazz.getName();
			if (name.startsWith("java.lang")) {
				return clazz.getSimpleName();
			} else {
				return clazz.getName();
			}
		} else {
			return name;
		}
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((clazz == null) ? 0 : clazz.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Clazz other = (Clazz) obj;
		if (clazz == null) {
			if (other.clazz != null)
				return false;
		} else if (!clazz.equals(other.clazz))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "Clazz [clazz=" + clazz + ", name=" + name + "]";
	}

}

class IndentingWriter {

	private String token = "\t";

	private int level = 0;

	private final PrintWriter pw;

	private boolean newline;

	public IndentingWriter(OutputStream stream) {
		this.pw = new PrintWriter(stream);
	}

	public IndentingWriter(Writer writer) {
		this.pw = new PrintWriter(writer);
	}

	public int incLevel() {
		return ++level;
	}

	public int decLevel() {
		return --level;
	}

	public void print(Object object) {
		if (newline) {
			for (int i = 0; i < level; ++i) {
				pw.print(token);
			}
			newline = false;
		}
		pw.print(object);
	}

	public void println() {
		pw.println();
		this.newline = true;
	}

	public void println(Object object) {
		print(object);
		pw.println();
		this.newline = true;
	}

	public void close() {
		pw.close();
	}

}

class StringJavaObject extends SimpleJavaFileObject {
	private String contents = null;

	public StringJavaObject(String className, String classCode) {
		super(URI.create(className), Kind.SOURCE);
		this.contents = classCode;
	}

	public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
		return contents;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy