![JAR search and dependency download from the Maven repository](/logo.png)
net.maritimecloud.msdl.plugins.javagen.JavaGenMessageGenerator Maven / Gradle / Ivy
The newest version!
/* Copyright (c) 2011 Danish Maritime Authority.
*
* 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 net.maritimecloud.msdl.plugins.javagen;
import static java.util.Objects.requireNonNull;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import net.maritimecloud.internal.message.Hashing;
import net.maritimecloud.internal.message.MessageHelper;
import net.maritimecloud.internal.msdl.parser.antlr.StringUtil;
import net.maritimecloud.message.Message;
import net.maritimecloud.message.MessageReader;
import net.maritimecloud.message.MessageSerializer;
import net.maritimecloud.message.MessageWriter;
import net.maritimecloud.message.ValueSerializer;
import net.maritimecloud.msdl.model.Annotatable;
import net.maritimecloud.msdl.model.BaseMessage;
import net.maritimecloud.msdl.model.BaseType;
import net.maritimecloud.msdl.model.EndpointDefinition;
import net.maritimecloud.msdl.model.EndpointMethod;
import net.maritimecloud.msdl.model.FieldOrParameter;
import net.maritimecloud.msdl.model.ListOrSetType;
import net.maritimecloud.msdl.model.MapType;
import net.maritimecloud.msdl.model.MsdlFile;
import net.maritimecloud.msdl.model.Type;
import net.maritimecloud.msdl.plugins.javagen.annotation.JavaImplements;
import net.maritimecloud.net.BroadcastMessage;
import net.maritimecloud.util.Binary;
import org.cakeframework.internal.codegen.CodegenClass;
import org.cakeframework.internal.codegen.CodegenMethod;
/**
*
* @author Kasper Nielsen
*/
public class JavaGenMessageGenerator {
final Annotatable anno;
final CodegenClass c;
final List fields;
final MsdlFile file;
final boolean isMessage;
final String name;
final CodegenClass parent;
final String serializerName;
final String fullName;
final JavaGenPlugin plugin;
JavaGenMessageGenerator(JavaGenPlugin plugin, CodegenClass parent, BaseMessage msg) {
this.parent = parent;
this.anno = msg;
this.name = msg.getName();
this.file = msg.getFile();
this.fields = msg.getFields();
this.c = parent == null ? new CodegenClass() : parent.addInnerClass();
this.isMessage = true;
this.serializerName = "Serializer";
fullName = msg.getFullName();
this.plugin = requireNonNull(plugin);
}
JavaGenMessageGenerator(JavaGenPlugin plugin, CodegenClass parent, String name, EndpointMethod met) {
this.parent = parent;
this.anno = met.getEndpoint();
this.name = name;
this.file = met.getEndpoint().getFile();
this.fields = met.getParameters();
this.c = parent == null ? new CodegenClass() : parent.addInnerClass();
this.isMessage = false;
this.serializerName = name + "Serializer";
fullName = met.getFullName();
this.plugin = requireNonNull(plugin);
}
JavaGenMessageGenerator generate() {
generateClass();
generateFields();
// We only generate constructors if we have at least one field, otherwise we rely on default constructors
// generated by javac
if (fields.size() > 0) {
generateConstructorEmpty();
generateConstructorParser();
if (isMessage) {
generateConstructorImmutable();
}
}
generateWriteTo(c, fields, file);
generateAccessors();
generateParser();
JavaGenMessageImmutableGenerator.generate(this);
generateToFrom();
if (isMessage) {
generateHashCode();
generateEquals();
}
return this;
}
void generateAccessors() {
for (FieldOrParameter f : fields) {
String beanPrefix = StringUtil.capitalizeFirstLetter(f.getName());
BaseType t = f.getType().getBaseType();
JavaGenType los = new JavaGenType(f.getType());
String r = los.render(c, file);
// Getter
// GETTER
CodegenMethod get = c.addMethod("public ", r, " get", beanPrefix, "()");
if (f.getComment().getMain() != null) {
get.addJavadoc("Returns " + f.getComment().getMainUncapitalized());
}
// if (f.getComment() != null && f.getComment().getCommentUncapitalized() != null) {
// get.addJavadoc("Returns " + f.getComment().getCommentUncapitalized());
// }
if (t.isComplexType()) {
String type = StringUtil.capitalizeFirstLetter(t.name().toLowerCase());
get.add("return java.util.Collections.unmodifiable", type, "(", f.getName(), ");");
} else {
get.add("return ", f.getName(), ";");
}
// HAS
CodegenMethod has = c.addMethod("public boolean has", beanPrefix, "()");
has.add("return ", f.getName(), " != null;");
// SETTER
CodegenMethod set;
if (t == BaseType.LIST || t == BaseType.SET) {
JavaGenType element = new JavaGenType(((ListOrSetType) f.getType()).getElementType());
String name = f.getName();
set = generateComplexAccessor(c, f);
set.add("java.util.Objects.requireNonNull(", name, ", \"", name, " is null\");");
set.add("this.", f.getName(), ".add(", name, ");");
set.add("return this;");
String beanPrefix2 = StringUtil.capitalizeFirstLetter(name);
set = generateComplexAccessorAll(c, f);
set.add("for (", element.render(c, file) + " e : ", name, ") {");
set.add("add", beanPrefix2, "(e);");
set.add("}");
} else if (t == BaseType.MAP) {
set = generateComplexAccessor(c, f);
set.add("java.util.Objects.requireNonNull(key, \"key is null\");");
set.add("java.util.Objects.requireNonNull(value, \"value is null\");");
set.add("this.", f.getName(), ".put(key, value);");
} else {
set = c.addMethod("public ", c.getSimpleName(), " set", beanPrefix, "(", r, " ", f.getName(), ")");
set.add("this.", f.getName(), " = ", f.getName(), ";");
}
set.add("return this;");
}
// if (f.getComment() != null && f.getComment().getCommentUncapitalized() != null) {
// m.addJavadoc("Returns " + f.getComment().getCommentUncapitalized());
// }
// m.add("return ", f.getName(), ";");
//
//
// public SomeTest putMd(Integer key, Map, String> value) {
// Map, String> $value = new HashMap<>();
// for (Map.Entry, String> $e : value.entrySet()) {
// List $res = new ArrayList<>();
// for (SomeTest $st : $e.getKey()) {
// $res.add(requireNonNull($st));
// }
// if (!$res.isEmpty()) {
// $value.put($res, requireNonNull($e.getValue()));
// }
// }
//
// return this;
// }
//
// public SomeTest putMpd(Integer key, List value) {
// java.util.Objects.requireNonNull(key, "key is null");
// java.util.Objects.requireNonNull(value, "value is null");
// List $res = new ArrayList<>();
// for (SomeTest $st : value) {
// $res.add(requireNonNull($st));
// }
// if (!$res.isEmpty()) {
// this.mpd.put(key, $res);
// } else {
// this.mpd.remove(key);
// }
// return this;
// }
}
void generateClass() {
String mType;
if (this instanceof JavaGenBroadcastMessageGenerator) {
// c.imports().addExplicitImport(ClassDefinitions.BROADCAST_MESSAGE_CLASS);
c.addImport(BroadcastMessage.class);
mType = BroadcastMessage.class.getSimpleName();
} else {
c.addImport(Message.class);
mType = Message.class.getSimpleName();
}
String imple = "";
if (plugin.isImplementsSerializable()) {
imple += ", " + Serializable.class.getSimpleName();
c.addImport(Serializable.class);
}
if (anno.isAnnotationPresent(JavaImplements.class)) {
for (String s : anno.getAnnotation(JavaImplements.class).value()) {
imple += ", " + s;
}
}
if (parent == null) {
c.setDefinition("public class ", name, " implements ", mType, imple);
} else {
String isPublic = anno instanceof EndpointDefinition ? "" : "public ";
c.setDefinition(isPublic, "static class ", name, " implements ", mType, imple);
}
// String fullName = file.getNamespace() + "." + name;
c.addFieldWithJavadoc("The full name of this message.", "public static final String NAME = \"", fullName, "\";");
c.addImport(MessageSerializer.class);
c.addFieldWithJavadoc("A message serializer that can read and write instances of this class.",
"public static final ", MessageSerializer.class, "<", name, "> SERIALIZER = new ", serializerName,
"();");
}
CodegenMethod generateComplexAccessor(CodegenClass clazz, FieldOrParameter f) {
String name = f.getName();
String beanPrefix2 = StringUtil.capitalizeFirstLetter(f.getName());
if (f.getType().getBaseType() == BaseType.MAP) {
JavaGenType key = new JavaGenType(((MapType) f.getType()).getKeyType());
JavaGenType value = new JavaGenType(((MapType) f.getType()).getValueType());
return clazz.addMethod("public ", c.getSimpleName(), " put", beanPrefix2, "(", key.render(c, file),
" key, ", value.render(c, file), " value)");
} else {
JavaGenType element = new JavaGenType(((ListOrSetType) f.getType()).getElementType());
return clazz.addMethod("public ", c.getSimpleName(), " add", beanPrefix2, "(", element.render(c, file),
" ", name, ")");
}
}
CodegenMethod generateComplexAccessorAll(CodegenClass clazz, FieldOrParameter f) {
String name = f.getName();
String beanPrefix2 = StringUtil.capitalizeFirstLetter(f.getName());
if (f.getType().getBaseType() == BaseType.MAP) {
JavaGenType key = new JavaGenType(((MapType) f.getType()).getKeyType());
JavaGenType value = new JavaGenType(((MapType) f.getType()).getValueType());
return clazz.addMethod("public ", c.getSimpleName(), " putAll", beanPrefix2, "(Map extends ",
key.render(c, file), ", ? extends ", value.render(c, file), "> ", name, ")");
} else {
clazz.addImport(Collection.class);
JavaGenType element = new JavaGenType(((ListOrSetType) f.getType()).getElementType());
return clazz.addMethod("public ", c.getSimpleName(), " addAll", beanPrefix2, "(Collection extends ",
element.render(c, file), "> ", name, ")");
}
}
void generateConstructorEmpty() {
CodegenMethod m = c.addMethod("public ", c.getSimpleName(), "()");
m.addJavadoc("Creates a new ", c.getSimpleName(), ".");
for (FieldOrParameter f : fields) {
BaseType t = f.getType().getBaseType();
if (t == BaseType.LIST) {
m.add(f.getName(), " = new java.util.ArrayList<>();");
} else if (t == BaseType.SET) {
m.add(f.getName(), " = new java.util.LinkedHashSet<>();");
} else if (t == BaseType.MAP) {
m.add(f.getName(), " = new java.util.LinkedHashMap<>();");
}
}
}
void generateConstructorImmutable() {
CodegenMethod m = c.addMethod(c.getSimpleName(), "(", c.getSimpleName(), " instance)");
m.addJavadoc("Creates a new ", c.getSimpleName(), " by copying an existing.");
m.addJavadocParameter("instance", "the instance to copy all fields from");
for (FieldOrParameter f : fields) {
BaseType t = f.getType().getBaseType();
if (t == BaseType.LIST || t == BaseType.SET || t == BaseType.MAP) {
c.addImport(MessageHelper.class);
m.add("this.", f.getName(), " = ", MessageHelper.class, ".immutableCopy(instance." + f.getName(), ");");
} else if (t == BaseType.MESSAGE) {
c.addImport(MessageHelper.class);
m.add("this.", f.getName(), " = ", MessageHelper.class, ".immutable(instance." + f.getName(), ");");
} else {
m.add("this.", f.getName(), " = instance." + f.getName(), ";");
}
}
}
void generateConstructorParser() {
CodegenMethod m = c.addMethod(c.getSimpleName(), "(", MessageReader.class, " reader) throws IOException");
m.addJavadoc("Creates a new ", c.getSimpleName(), " by reading from a message reader.");
m.addJavadocParameter("reader", "the message reader");
// TODO replace with generateParseMethod
for (FieldOrParameter f : fields) {
JavaGenType ty = new JavaGenType(f.getType());
BaseType type = f.getType().getBaseType();
String tagName = "(" + f.getTag() + ", \"" + f.getName() + "\"";
if (type.isPrimitive()) {
m.add("this.", f.getName(), " = reader.read", ty.writeReadName(), tagName, ", null);");
} else if (type == BaseType.ENUM) {
m.add("this.", f.getName(), " = reader.readEnum", tagName, ", ", ty.render(c, file), ".SERIALIZER);");
} else if (type == BaseType.MESSAGE) {
m.add("this.", f.getName(), " = reader.readMessage", tagName, ", ", ty.render(c, file), ".SERIALIZER);");
} else if (type == BaseType.LIST) { // Complex type
ListOrSetType los = (ListOrSetType) f.getType();
c.addImport(MessageHelper.class);
m.add("this.", f.getName(), " = ", MessageHelper.class, ".readList", tagName, ", reader, ",
complexParser(c, los.getElementType(), file), ");");
} else if (type == BaseType.SET) { // Complex type
c.addImport(MessageHelper.class);
ListOrSetType los = (ListOrSetType) f.getType();
m.add("this.", f.getName(), " = ", MessageHelper.class, ".readSet", tagName, ", reader, ",
complexParser(c, los.getElementType(), file), ");");
} else { // Complex type
c.addImport(MessageHelper.class);
MapType los = (MapType) f.getType();
m.add("this.", f.getName(), " = ", MessageHelper.class, ".readMap", tagName, ", reader, ",
complexParser(c, los.getKeyType(), file), ", ", complexParser(c, los.getValueType(), file),
");");
}
}
}
void generateEquals() {
CodegenMethod m = c.addMethod("public boolean equals(Object other)");
m.addJavadoc("{@inheritDoc}").addAnnotation(Override.class);
if (fields.isEmpty()) {
m.add("return other == this || other instanceof ", c.getSimpleName(), ";");
return;
}
m.add("if (other == this) {");
m.add("return true;");
m.add("} else if (other instanceof ", c.getSimpleName(), ") {");
m.add(c.getSimpleName(), " o = (", c.getSimpleName(), ") other;");
for (int i = 0; i < fields.size(); i++) {
StringBuilder b = i == 0 ? new StringBuilder("return ") : new StringBuilder(" ");
FieldOrParameter f = fields.get(i);
// JavaGenType t = new JavaGenType(f.getType());
// Check data typen
// if (false /* fields.get(i).getType().getBaseType().isPrimitive() */) {
// b.append("this." + f.getName() + " == o." + f.getName());
// } else {
c.addImport(Objects.class);
b.append("Objects.equals(" + f.getName() + ", o." + f.getName() + ")");
// }
b.append(i == fields.size() - 1 ? ";" : " &&");
m.add(b);
}
m.add("}");
m.add("return false;");
}
void generateFields() {
for (FieldOrParameter f : fields) {
JavaGenType ty = new JavaGenType(f.getType());
ty.addImports(c);
String init = "private " + (f.getType().getBaseType().isComplexType() ? "final " : "");
// MSDLBaseType t = f.getType().getBaseType();
// if (t == MSDLBaseType.LIST) {
// init = " = new java.util.ArrayList<>()";
// } else if (t == MSDLBaseType.SET) {
// init = " = new java.util.LinkedHashSet<>()";
// } else if (t == MSDLBaseType.MAP) {
// init = " = new java.util.LinkedHashMap<>()";
// }
c.addFieldWithJavadoc("Field definition.", init, ty.render(c, file), " ", f.getName(), ";");
ty.getMsgType(c, file);
}
}
void generateHashCode() {
CodegenMethod m = c.addMethod("public int hashCode()");
m.addAnnotation(Override.class).addJavadoc("{@inheritDoc}");
if (fields.size() == 0) {
m.add("return ", c.getSimpleName().hashCode(), ";");
} else if (fields.size() == 1) {
m.add("return ", generateHashCode(fields.get(0)), ";");
} else {
m.add("int result = 31 + ", generateHashCode(fields.get(0)), ";");
for (int i = 1; i < fields.size() - 1; i++) {
m.add("result = 31 * result + ", generateHashCode(fields.get(i)), ";");
}
m.add("return 31 * result + ", generateHashCode(fields.get(fields.size() - 1)), ";");
}
}
//
// public static HelloWorld fromJSON(CharSequence c) {
// return MessageSerializer.readFromJSON(PARSER, c);
// }
String generateHashCode(FieldOrParameter f) {
// Comment in again if we get primitive datatypes
// if (f.getType().getBaseType() == MSDLBaseType.INT32) {
// return f.getName();
// } else {
c.addImport(Hashing.class);
return "Hashing.hashcode(this." + f.getName() + ")";
// }
}
void generateParser() {
CodegenClass c = this.c.addInnerClass();
c.setDefinition("static class ", serializerName, " extends ", MessageSerializer.class, "<",
this.c.getSimpleName(), ">");
c.addJavadoc("A serializer for reading and writing instances of ", this.c.getSimpleName(), ".");
// Reader
CodegenMethod m = c.addMethod("public ", this.c.getSimpleName(), " read(", MessageReader.class,
" reader) throws ", IOException.class);
m.addImport(MessageReader.class, IOException.class);
m.addAnnotation(Override.class).addJavadoc("{@inheritDoc}");
m.add("return new ", this.c.getSimpleName(), "(" + (fields.isEmpty() ? "" : "reader") + ");");
// Writer
m = c.addMethod("public void write(", this.c.getSimpleName(), " message, ", MessageWriter.class,
" writer) throws ", IOException.class);
m.addImport(MessageWriter.class);
m.addAnnotation(Override.class).addJavadoc("{@inheritDoc}");
if (!fields.isEmpty()) {
m.add("message.writeTo(writer);");
}
}
void generateToFrom() {
CodegenMethod m = c.addMethod("public String toJSON()");
m.addJavadoc("Returns a JSON representation of this message");
if (isMessage) {
c.addImport(MessageSerializer.class);
m.add("return ", MessageSerializer.class, ".writeToJSON(this, SERIALIZER);");
CodegenMethod from = c.addMethod("public static ", c.getSimpleName(), " fromJSON(", CharSequence.class,
" c)");
from.addJavadoc("Creates a message of this type from a JSON throwing a runtime exception if the format of the message does not match");
from.add("return ", MessageSerializer.class, ".readFromJSON(SERIALIZER, c);");
} else {
m.throwNewUnsupportedOperationException("method not supported");
}
}
static String complexParser(CodegenClass c, Type type, MsdlFile file) {
if (type == null) {
return "null";
}
BaseType b = type.getBaseType();
if (b.isPrimitive()) {
c.addImport(ValueSerializer.class);
if (b == BaseType.BINARY) {
c.addImport(Binary.class);
}
return ValueSerializer.class.getSimpleName() + "." + b.name().toUpperCase();
} else if (b.isReferenceType()) {
JavaGenType ty = new JavaGenType(type);
return ty.render(c, file) + ".SERIALIZER";
} else if (b == BaseType.LIST) {
ListOrSetType los = (ListOrSetType) type;
return complexParser(c, los.getElementType(), file) + ".listOf()";
} else if (b == BaseType.SET) {
ListOrSetType los = (ListOrSetType) type;
return complexParser(c, los.getElementType(), file) + ".setOf()";
} else {
MapType los = (MapType) type;
return "MessageParser.ofMap(" + complexParser(c, los.getKeyType(), file) + ", "
+ complexParser(c, los.getValueType(), file) + ")";
}
}
static String generateParseMethod(String readerName, CodegenClass c, FieldOrParameter f, MsdlFile file) {
BaseType type = f.getType().getBaseType();
JavaGenType ty = new JavaGenType(f.getType());
ty.addImports(c);
if (type.isPrimitive()) {
return readerName + ".read" + ty.writeReadName() + "(" + f.getTag() + ", \"" + f.getName() + "\", null);";
} else if (type == BaseType.ENUM) {
return readerName + ".readEnum(" + f.getTag() + ", \"" + f.getName() + "\", " + ty.render(c, file)
+ ".SERIALIZER);";
} else if (type == BaseType.MESSAGE) {
return readerName + ".readMessage(" + f.getTag() + ", \"" + f.getName() + "\", " + ty.render(c, file)
+ ".SERIALIZER);";
} else if (type == BaseType.LIST) { // Complex type
ListOrSetType los = (ListOrSetType) f.getType();
c.addImport(MessageHelper.class);
return MessageHelper.class.getSimpleName() + ".readList(" + f.getTag() + ", \"" + f.getName() + "\", "
+ readerName + ", " + complexParser(c, los.getElementType(), file) + ");";
} else if (type == BaseType.SET) { // Complex type
ListOrSetType los = (ListOrSetType) f.getType();
c.addImport(MessageHelper.class);
return MessageHelper.class.getSimpleName() + ".readSet(" + f.getTag() + ", \"" + f.getName() + "\", "
+ readerName + ", " + complexParser(c, los.getElementType(), file) + ");";
} else { // Complex type
MapType los = (MapType) f.getType();
c.addImport(MessageHelper.class);
return MessageHelper.class.getSimpleName() + ".readMap(" + f.getTag() + ", \"" + f.getName() + "\", "
+ readerName + ", " + complexParser(c, los.getKeyType(), file) + ", "
+ complexParser(c, los.getValueType(), file) + ");";
}
}
static void generateWriteTo(CodegenClass c, Collection fields, MsdlFile file) {
if (fields.size() > 0) {
CodegenMethod m = c.addMethod("void writeTo(", MessageWriter.class, " w) throws IOException");
// m.addAnnotation(Override.class).addJavadoc("{@inheritDoc}");
m.addImport(IOException.class).addImport(MessageWriter.class);
for (FieldOrParameter f : fields) {
StringBuilder sb = new StringBuilder();
sb.append("w.write").append(new JavaGenType(f.getType()).writeReadName());
sb.append("(").append(f.getTag()).append(", \"").append(f.getName()).append("\", ");
sb.append(f.getName());
if (f.getType().getBaseType() == BaseType.MESSAGE) {
sb.append(", ").append(new JavaGenType(f.getType()).render(c, file) + ".SERIALIZER");
} else if (f.getType().getBaseType().isComplexType()) {
sb.append(", ");
if (f.getType() instanceof ListOrSetType) {
ListOrSetType lt = (ListOrSetType) f.getType();
sb.append(complexParser(c, lt.getElementType(), file));
} else {
MapType lt = (MapType) f.getType();
sb.append(complexParser(c, lt.getKeyType(), file));
sb.append(", ").append(complexParser(c, lt.getValueType(), file));
}
}
sb.append(");");
m.add(sb);
}
}
}
static enum MsgType {
BROADCAST, ENDPOINT_PARAMETERS, MESSAGE;
}
static class NameGenerator {
private final HashMap map = new HashMap<>();
String next(String prefix) {
Integer val = map.get(prefix);
map.put(prefix, val == null ? 1 : val + 1);
return "$" + prefix + (val == null ? "" : val);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy