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

com.github.skjolber.log.domain.codegen.stackdriver.PayloadGenerator Maven / Gradle / Ivy

The newest version!
package com.github.skjolber.log.domain.codegen.stackdriver;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.lang.model.element.Modifier;

import org.apache.commons.lang3.ClassUtils;

import com.github.skjolber.log.domain.model.Domain;
import com.github.skjolber.log.domain.model.Key;
import com.github.skjolber.log.domain.stackdriver.utils.DomainPayload;
import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeSpec.Builder;

public class PayloadGenerator {
	
	public static final String MARKER = "Payload";
	public static final String BUILDER = "PayloadBuilder";
	protected static final String PARENT_FIELD_NAME = "parent";
	private static TagGenerator tagGenerator = new TagGenerator();

	public static JavaFile marker(Domain ontology) {
		// Note: omitting equals-method because of the way the way LogstashMarker compares references. 
		
		List keys = ontology.getKeys();
		
		ClassName name = getName(ontology);

		ClassName superClassName = ClassName.get(DomainPayload.class);

		Builder builder = TypeSpec.classBuilder(name)
					.superclass(superClassName)
					.addJavadoc(composeJavadoc(ontology, name))
					.addModifiers(Modifier.PUBLIC);

		com.squareup.javapoet.MethodSpec.Builder constructor = MethodSpec.constructorBuilder()
			.addModifiers(Modifier.PUBLIC)
			.addStatement("super(QUALIFIER)")
			.addStatement("$T $N = mdc.get()", name, PARENT_FIELD_NAME)
			.beginControlFlow("if($N != null)", PARENT_FIELD_NAME);
			
		for(Key key : keys) {
			constructor.addStatement("this.$N = $N.$N", key.getId(), PARENT_FIELD_NAME, key.getId());
		}
		
		if(ontology.hasTags()) {
			constructor
				.addStatement("this.tags = $N.tags", PARENT_FIELD_NAME);
		}
		
		constructor
			.addStatement("this.$N = $N", PARENT_FIELD_NAME, PARENT_FIELD_NAME)
			.endControlFlow();
		
		builder.addMethod(constructor.build());

		boolean global = !ontology.hasQualifier();
		
		// private static final long serialVersionUID = 1L;
		builder.addField(FieldSpec.builder(long.class, "serialVersionUID", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL).initializer("1L").build());
		builder.addField(FieldSpec.builder(String.class, "QUALIFIER", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("$S", !global ? ontology.getQualifier() : "").build());

		TypeName mdcName = MdcGenerator.getName(ontology);
		
		builder.addField(FieldSpec.builder(mdcName, "mdc", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL).build());

		builder.addStaticBlock(CodeBlock.builder().addStatement("mdc = new $T()", mdcName).addStatement("mdc.register()").build());

		builder.addMethod(MethodSpec.methodBuilder("getMdc")
				.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
				.addStatement("return mdc")
				.returns(mdcName)
				.build());
		
		ClassName tags;
		if(ontology.hasTags()) {
			tags = tagGenerator.getName(ontology);
		} else {
			tags = null;
		}
		
		for(Key key : keys) {
			builder.addField(getField(name, key));
		}

		if(ontology.hasTags()) {
			builder.addField(FieldSpec.builder(ArrayTypeName.of(tags), "tags", Modifier.PRIVATE).build());
		}
		
		for(Key key : keys) {
			builder.addMethod(getMethod(name, key));
		}

		for(Key key : keys) {
			builder.addMethod(getGetter(name, key));
		}
		
		builder
			.addMethod(getWriterMethod(keys, name, tags, global))
			.addMethod(getPopMethod(ontology))
			.addMethod(getPushMethod(ontology))
			.addMethod(getSetKeyMethod(keys))
			.addMethod(getParseAndSetKeyMethod(keys))
			.addMethod(MdcGenerator.getDefinesKeyMethod(keys))
			;
		
		if(ontology.hasTags()) {
			builder
				.addMethod(getTagsBuilderMethod(name, tags, ontology.getTags().size()))
				.addMethod(getTagsGetterMethod(tags));
		}

		builder
			.addMethod(getEqualToMethod(keys, name, tags, ontology.hasTags() ? ontology.getTags().size() : 0))
			.addMethod(getToStringBuilderMethod(ontology))
			;
		
		return JavaFile.builder(name.packageName(), builder.build()).build();
	}

	private static MethodSpec getParseAndSetKeyMethod(List keys) {
		ParameterSpec keyParameter = ParameterSpec.builder(String.class, "key").build();
		ParameterSpec valueParameter = ParameterSpec.builder(Object.class, "value").build();
		
		MethodSpec.Builder builder = MethodSpec.methodBuilder("parseAndSetKey")
				.addModifiers(Modifier.PUBLIC)
				.addParameter(keyParameter)
				.addParameter(valueParameter);
				;
				
		com.squareup.javapoet.CodeBlock.Builder switchBlock = CodeBlock.builder();
		
		switchBlock.beginControlFlow("switch($N)", keyParameter);
				
		for(Key key : keys) {
			Class type = parseTypeFormat(key.getType(), key.getFormat());
			if(type.isPrimitive()) {
				type = ClassUtils.primitiveToWrapper(type);
			}
			
			switchBlock.beginControlFlow("case $S :", key.getId());
			
			
			if(type == Date.class || type == String.class) {
				switchBlock.addStatement("this.$N = ($T)$N", key.getId(), type, valueParameter);
			} else if(type == Integer.class){
				switchBlock.addStatement("this.$N = asInteger($N)", key.getId(), valueParameter);
			} else if(type == Long.class){
				switchBlock.addStatement("this.$N = asLong($N)", key.getId(), valueParameter);
			} else if(type == Float.class){
				switchBlock.addStatement("this.$N = asFloat($N)", key.getId(), valueParameter);
			} else if(type == Double.class){
				switchBlock.addStatement("this.$N = asDouble($N)", key.getId(), valueParameter);
			} else {
				throw new IllegalArgumentException("Unknown type " + type.getName());
			}
					
			switchBlock
					.addStatement("break")
					.endControlFlow();
		}
		
		switchBlock.endControlFlow();

		builder.addCode(switchBlock.build());

		return builder.build();
	}

	public static ClassName getName(Domain ontology) {
		return ClassName.get(ontology.getTargetPackage(), ontology.getName() + MARKER);
	}

	private static MethodSpec getEqualToMethod(List fields, ClassName name, ClassName tags, int size) {
		ParameterSpec parameter = ParameterSpec.builder(DomainPayload.class, "marker").build();

		MethodSpec.Builder builder = MethodSpec.methodBuilder("equalTo")
				.addModifiers(Modifier.PUBLIC)
				.addParameter(parameter)
				.beginControlFlow("if($N instanceof $T)", parameter, name)
					.addStatement("$T domainMarker = ($T)$N", name, name, parameter)
				;

		for(Key key : fields) {
			builder
					.addCode(CodeBlock.builder()
					.beginControlFlow("if(!$T.equals(this.$N, domainMarker.$N))", Objects.class, key.getId(), key.getId())
					.addStatement("return false")
					.endControlFlow().build());
		}

		if(tags != null) {
			// tags
			builder
					.addCode(
							CodeBlock.builder()
							.beginControlFlow("if(!$T.equals(this.tags, domainMarker.tags))", Arrays.class)
								.addStatement("return false")
							.endControlFlow()
							.build());
		}
		
		
		
		return builder
			.addStatement("return true")
			.endControlFlow()
			.addStatement("return false")
			.returns(boolean.class)
			
			.build();
	}
	
	private static MethodSpec getToStringBuilderMethod(Domain ontology) {
		// output like in map
		ParameterSpec parameter = ParameterSpec.builder(StringBuilder.class, "builder").build();

		MethodSpec.Builder builder = MethodSpec.methodBuilder("writeToString")
				.addModifiers(Modifier.PUBLIC)
				.addParameter(parameter)
				;

		if(ontology.hasQualifier()) {
			builder.addStatement("$N.append($S)", parameter, ontology.getQualifier() + "{");
		} else {
			builder.addStatement("$N.append($S)", parameter, "{");
		}
		
		for(Key key : ontology.getKeys()) {
			builder
				.beginControlFlow("if(this.$N != null)", key.getId())
					.addStatement("$N.append($S)", parameter, key.getId() + "=")
					.addStatement("$N.append(this.$N)", parameter, key.getId())
					.addStatement("$N.append($S)", parameter, ", ")
				.endControlFlow();
		}

		int size;
		if(ontology.hasQualifier()) {
			size = ontology.getQualifier().length() + 1;
		} else {
			size = 1;
		}
		
		if(ontology.hasTags()) {
			ClassName tags = tagGenerator.getName(ontology);
			// tags
			builder
				.beginControlFlow("if(this.$N != null)", "tags")
				.addStatement("int mark = $N.length()", parameter)
					.addStatement("$N.append($S)", parameter, "tags=[") // size 6
					.beginControlFlow("for($T tag : tags)", tags)
						.beginControlFlow("if(tag != null)")
							.addStatement("$N.append(tag.getId())", parameter)
							.addStatement("$N.append($S)", parameter, ", ")
						.endControlFlow()
					.endControlFlow()
					.beginControlFlow("if(mark + 6 < $N.length())", parameter)
						.addComment("at least one tag was present")
						.addStatement("$N.setLength($N.length() - 2)", parameter, parameter)
						.addStatement("$N.append($S)", parameter, "]")
					.nextControlFlow("else if(mark == " + size + ")", parameter)
						.addComment("no fields")
						.addStatement("$N.setLength(mark)", parameter)
					.nextControlFlow("else")
						.addComment("at least one field was present, remove extra comma")
						.addStatement("$N.setLength(mark - 2)", parameter)
					.endControlFlow()
				.nextControlFlow("else if($N.length() != " + size + ")", parameter)
					.addStatement("$N.setLength($N.length() - 2)", parameter, parameter)
				.endControlFlow();
		} else {
			builder
			.beginControlFlow("if($N.length() != " + size + ")", parameter)
				.addComment("remove extra comma")
				.addStatement("$N.setLength($N.length() - 2)", parameter, parameter)
			.endControlFlow();
		}
		
		
		
		return builder
				.addStatement("$N.append($S)", parameter, "}")
				.addStatement("super.writeToString($N)", parameter)
				.build();
	}


	private static MethodSpec getWriterMethod(List fields, ClassName name, ClassName tags, boolean global) {
		
		ParameterizedTypeName map = ParameterizedTypeName.get(ClassName.get(Map.class), ClassName.get(String.class),  ClassName.get(Object.class));

		ParameterSpec parameter = ParameterSpec.builder(map, "parent").build();
		
		// signature: public abstract void build(Map map);
		MethodSpec.Builder builder = MethodSpec.methodBuilder("build")
				.addModifiers(Modifier.PUBLIC)
				.addParameter(parameter);

		ParameterSpec mapParameter;
		if(!global) {
			ParameterizedTypeName hashMap = ParameterizedTypeName.get(ClassName.get(HashMap.class), ClassName.get(String.class),  ClassName.get(Object.class));

			mapParameter = ParameterSpec.builder(map, "map").build();
			
			builder.addStatement("$T $N = new $T()", map, mapParameter, hashMap);
		} else {
			mapParameter = parameter;
		}
		
		for(Key key : fields) {
			builder.addCode(CodeBlock.builder()
					.beginControlFlow("if(this.$N != null)", key.getId())
					.addStatement("$N.put($S, this.$N)", mapParameter, key.getId(), key.getId())
					.endControlFlow()
					.build());
		}

		if(tags != null) {
			// tags
			ParameterizedTypeName list = ParameterizedTypeName.get(ClassName.get(List.class), ClassName.get(String.class));
			ParameterizedTypeName arrayList = ParameterizedTypeName.get(ClassName.get(ArrayList.class), ClassName.get(String.class));
			
			ParameterSpec listParameter = ParameterSpec.builder(list, "list").build();
			
			builder.addCode(CodeBlock.builder()
					.beginControlFlow("if(this.$N != null)", "tags")
					.addStatement("$T $N = new $T()", list, listParameter, arrayList)
					.beginControlFlow("for($T tag : tags)", tags)
					.beginControlFlow("if(tag != null)")
					.addStatement("$N.add(tag.getId())", listParameter)
					.endControlFlow()
					.endControlFlow()
					.beginControlFlow("if(!$N.isEmpty())", listParameter)
					.addStatement("$N.put($S, $N)", mapParameter, "tags", listParameter)
					.endControlFlow()

					.endControlFlow()
					.build());
		}
		
		if(!global) {
			builder.addCode(CodeBlock.builder()
					.beginControlFlow("if(!$N.isEmpty())", mapParameter)
					.addStatement("$N.put($N, $N)", parameter, "QUALIFIER", mapParameter)
					.endControlFlow()
					.build());
		}
		return builder.build();
	}

	private static FieldSpec getField(ClassName name, Key key) {
		Class type = parseTypeFormat(key.getType(), key.getFormat());
		
		if(type.isPrimitive()) {
			type = ClassUtils.primitiveToWrapper(type);
		}
		
		return FieldSpec.builder(type, key.getId(), Modifier.PRIVATE).build();
	}

	private static String composeJavadoc(Domain ontology, ClassName name) {
		StringBuilder builder = new StringBuilder();
		
		builder.append(ontology.getName());
		builder.append(" log-support.");
		if(ontology.getDescription() != null) {
			builder.append("
\n"); builder.append(ontology.getDescription()); } builder.append("

"); builder.append("\n"); builder.append("\n"); builder.append("Usage:

"); builder.append("\n"); builder.append("\n"); builder.append("import static "); builder.append(name.reflectionName()); builder.append(".*"); builder.append("\n"); builder.append("\n"); return builder.toString(); } private static MethodSpec getTagsBuilderMethod(ClassName name, ClassName tags, int size) { ParameterSpec parameter = ParameterSpec.builder(ArrayTypeName.of(tags), "tags").build(); return MethodSpec.methodBuilder("tags") .addModifiers(Modifier.PUBLIC) .addParameter(parameter) .varargs() .addStatement("$T clone = new $T[" + size + "]", ArrayTypeName.of(tags), tags) .beginControlFlow("if(this.tags != null)") .addStatement("System.arraycopy(this.tags, 0, clone, 0, " + size + ")") .endControlFlow() .beginControlFlow("for($T tag : tags)", tags) .addStatement("clone[tag.ordinal()] = tag") .endControlFlow() .addStatement("this.tags = clone") .returns(name) .addStatement("return this") .build(); } private static MethodSpec getTagsGetterMethod(ClassName tags) { return MethodSpec.methodBuilder("getTags") .addModifiers(Modifier.PUBLIC) .returns(ArrayTypeName.of(tags)) .addStatement("return tags") .build(); } private static MethodSpec getMethod(ClassName name, Key key) { Class type = parseTypeFormat(key.getType(), key.getFormat()); if(type.isPrimitive()) { type = ClassUtils.primitiveToWrapper(type); } ParameterSpec parameter = ParameterSpec.builder(type, key.getId()).build(); return MethodSpec.methodBuilder(key.getId()) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addParameter(parameter) .addStatement("this.$N = $N", key.getId(), parameter) .addJavadoc(key.getDescription()) .returns(name) .addStatement("return this") .build(); } private static MethodSpec getGetter(ClassName name, Key key) { Class type = parseTypeFormat(key.getType(), key.getFormat()); if(type.isPrimitive()) { type = ClassUtils.primitiveToWrapper(type); } String id = key.getId(); return getGetter(key, type, id); } private static MethodSpec getGetter(Key key, Class type, String id) { return MethodSpec.methodBuilder("get" + Character.toUpperCase(id.charAt(0)) + id.substring(1)) .addModifiers(Modifier.PUBLIC) .addJavadoc(key.getDescription()) .returns(type) .addStatement("return $N", id) .build(); } protected static Class parseTypeFormat(String type, String format) { switch(type) { case "integer" : { if(format != null) { if(format.equals("int32")) { return Integer.class; } else if(format.equals("int64")) { return Long.class; } } break; } case "string" : { if(format == null) { return String.class; } else if(format.equals("date")) { return Date.class; } else if(format.equals("date-time")) { return Date.class; } else if(format.equals("password")) { return String.class; } else if(format.equals("byte")) { return String.class; } else if(format.equals("binary")) { return String.class; } break; } case "number" : { if(format != null) { if(format.equals("float")) { return Float.class; } else if(format.equals("double")) { return Double.class; } } break; } } throw new IllegalArgumentException("Unknown type " + type + " format " + format); } public static JavaFile markerBuilder(Domain ontology) { List keys = ontology.getKeys(); ClassName name = ClassName.get(ontology.getTargetPackage(), ontology.getName()+ BUILDER); Builder builder = TypeSpec.classBuilder(name) .addJavadoc(composeJavadoc(ontology, name)) .addModifiers(Modifier.PUBLIC); ClassName markerName = getName(ontology); for(Key key : keys) { builder.addMethod(getBuilderMethod(markerName, key)); } if(ontology.hasTags()) { ClassName tagsName = tagGenerator.getName(ontology); ParameterSpec parameter = ParameterSpec.builder(ArrayTypeName.of(tagsName), "value").build(); builder.addMethod(MethodSpec.methodBuilder("tags") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(parameter).varargs() .addStatement("$T marker = new $T()", markerName, markerName) .addStatement("marker.tags($N)", parameter) .returns(markerName) .addStatement("return marker") .build()); } return JavaFile.builder(name.packageName(), builder.build()).build(); } private static MethodSpec getBuilderMethod(ClassName name, Key key) { Class type = parseTypeFormat(key.getType(), key.getFormat()); ParameterSpec parameter = ParameterSpec.builder(type, "value").build(); return MethodSpec.methodBuilder(key.getId()) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(parameter) .addStatement("$T marker = new $T()", name, name) .addStatement("marker." + key.getId() + "($N)", parameter) .addJavadoc(key.getDescription()) .returns(name) .addStatement("return marker") .build(); } private static MethodSpec getPopMethod(Domain ontology) { return MethodSpec.methodBuilder("popContext") .addModifiers(Modifier.PUBLIC) .addStatement("super.popContext()") .addStatement("mdc.pop(this)") .build(); } private static MethodSpec getPushMethod(Domain ontology) { return MethodSpec.methodBuilder("pushContext") .addModifiers(Modifier.PUBLIC) .addStatement("mdc.push(this)") .addStatement("super.pushContext()") .build(); } private static MethodSpec getSetKeyMethod(List fields) { ParameterSpec keyParameter = ParameterSpec.builder(String.class, "key").build(); ParameterSpec valueParameter = ParameterSpec.builder(Object.class, "value").build(); MethodSpec.Builder builder = MethodSpec.methodBuilder("setKey") .addModifiers(Modifier.PUBLIC) .addParameter(keyParameter) .addParameter(valueParameter); ; com.squareup.javapoet.CodeBlock.Builder switchBlock = CodeBlock.builder(); switchBlock.beginControlFlow("switch($N)", keyParameter); for(Key key : fields) { Class type = parseTypeFormat(key.getType(), key.getFormat()); if(type.isPrimitive()) { type = ClassUtils.primitiveToWrapper(type); } switchBlock .beginControlFlow("case $S :", key.getId()) .addStatement("this.$N = ($T)$N", key.getId(), type, valueParameter) // TODO microoptimize by checking for type .addStatement("break") .endControlFlow(); } switchBlock.endControlFlow(); builder.addCode(switchBlock.build()); return builder.build(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy