in tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ModelWriterGeneratorMojo.java [182:600]
private JavaClass generateWriter(List<Class<?>> model, ClassLoader classLoader) {
JavaClass writer = new JavaClass(classLoader);
writer.setMaxImportPerPackage(4);
writer.setPackage(getWriterPackage());
writer.setName("ModelWriter");
writer.extendSuperType("BaseWriter");
writer.addImport(XmlModelWriterGeneratorMojo.MODEL_PACKAGE + ".OptionalIdentifiedDefinition");
writer.addImport(IOException.class);
writer.addImport(Array.class);
writer.addImport(List.class);
writer.addImport(ArrayList.class);
writer.addImport("org.apache.camel.Expression");
writer.addAnnotation(SuppressWarnings.class).setLiteralValue("\"all\"");
writer.addMethod()
.setConstructor(true)
.setPublic()
.setName("ModelWriter")
.addParameter(Writer.class, "writer")
.addParameter(String.class, "namespace")
.addThrows(IOException.class)
.setBody(
"super(writer, namespace);");
writer.addMethod()
.setConstructor(true)
.setPublic()
.setName("ModelWriter")
.addParameter(Writer.class, "writer")
.addThrows(IOException.class)
.setBody("super(writer, null);");
List<Class<?>> rootElements = model.stream().filter(this::isRootElement).toList();
for (Class<?> clazz : rootElements) {
String element = clazz.getAnnotation(XmlRootElement.class).name();
String name = clazz.getSimpleName();
writer.addMethod().setPublic()
.addParameter(clazz, "def")
.setReturnType(Void.TYPE)
.setName("write" + name)
.addThrows(IOException.class)
.setBody(
"doWrite" + name + "(\"" + element + "\", def);");
}
Set<Class<?>> elementRefs = new TreeSet<>(Comparator.comparing(Class::getName));
// Special case for OptionalIdentifiedDefinition
model.stream().filter(cl -> "OptionalIdentifiedDefinition".equals(cl.getSimpleName()))
.forEach(elementRefs::add);
writer.addMethod()
.setSignature(
"public void writeOptionalIdentifiedDefinitionRef(OptionalIdentifiedDefinition def) throws IOException")
.setBody("doWriteOptionalIdentifiedDefinitionRef(null, def);");
for (Class<?> clazz : model) {
if (clazz.getAnnotation(XmlEnum.class) != null || clazz.isInterface()) {
continue;
}
String name = clazz.getSimpleName();
String qname;
if (clazz.getDeclaringClass() != null) {
writer.addImport(clazz.getDeclaringClass());
qname = clazz.getDeclaringClass().getSimpleName() + "." + name;
} else {
writer.addImport(clazz);
qname = name;
}
boolean hasDerived = model.stream().anyMatch(cl -> cl.getSuperclass() == clazz);
List<Property> members = getProperties(clazz).toList();
// XmlAttribute
List<String> attributes = new ArrayList<>();
// call super class attributes writer
getClassAndSuper(clazz.getSuperclass())
.filter(c -> getProperties(c).anyMatch(Property::isAttribute))
.findFirst()
.ifPresent(cl -> attributes.add("doWrite" + cl.getSimpleName() + "Attributes(def);"));
// Add attributes
List<Property> attributeMembers = members.stream().filter(Property::isAttribute).toList();
attributeMembers.forEach(member -> {
Type pt = member.getType();
GenericType type = new GenericType(pt);
String an = member.getAnnotation(XmlAttribute.class).name();
if ("##default".equals(an)) {
an = member.getName();
}
String gn = member.getGetter();
attributes.add("doWriteAttribute(\"" + an + "\", "
+ conversion(writer, type, "def." + gn + "()", clazz.getName()) + ");");
});
// @XmlAnyAttribute
members.stream().filter(Property::isAnyAttribute).forEach(member -> {
throw new UnsupportedOperationException(
"Class " + clazz.getName() + " / member " + member + ": unsupported @XmlAnyAttribute");
});
List<String> elements = new ArrayList<>();
// Add super class element writer
getClassAndSuper(clazz.getSuperclass())
.filter(c -> getProperties(c).anyMatch(Property::isElement))
.findFirst()
.ifPresent(cl -> {
// special for namespace
if ("NamespaceAwareExpression".equals(cl.getSimpleName())) {
attributes.add("doWriteNamespaces(def);");
} else {
elements.add("doWrite" + cl.getSimpleName() + "Elements(def);");
}
});
// Loop through elements
List<Property> elementMembers = members.stream().filter(Property::isElement).toList();
elementMembers.forEach(member -> {
Type pt = member.getType();
GenericType type = new GenericType(pt);
boolean list = type.getRawClass() == List.class;
String gn = member.getGetter();
String pn = member.getName();
Class<?> root = list ? type.getActualTypeArgument(0).getRawClass() : type.getRawClass();
if (member.getAnnotation(XmlElementRefs.class) != null) {
elements.add("// TODO: @XmlElementRefs: " + member);
} else if (member.getAnnotation(XmlElementRef.class) != null) {
//elements.add("// @XmlElementRef: " + member);
if (list) {
Class<?> parent = new GenericType(member.getType()).getActualTypeArgument(0).getRawClass();
elementRefs.add(parent);
elements.add(
"doWriteList(null, null, def." + gn + "(), this::doWrite" + parent.getSimpleName() + "Ref);");
} else {
Class<?> parent = new GenericType(member.getType()).getRawClass();
elementRefs.add(parent);
elements.add("doWriteElement(null, def." + gn + "(), this::doWrite" + parent.getSimpleName() + "Ref);");
}
} else if (member.getAnnotation(XmlElements.class) != null) {
if (list) {
// elements.add("// @XmlElements: " + member);
elements.add("doWriteList(null, null, def." + gn + "(), (n, v) -> {");
elements.add(" switch (v.getClass().getSimpleName()) {");
for (XmlElement elem : member.getAnnotation(XmlElements.class).value()) {
String t = elem.type().getSimpleName();
String n = elem.name();
elements.add(" case \"" + t + "\" -> doWrite" + t + "(\"" + n + "\", (" + t + ") v);");
}
elements.add(" }");
elements.add("});");
} else {
// elements.add("// @XmlElements: " + member);
elements.add("doWriteElement(null, def." + gn + "(), (n, v) -> {");
elements.add(" switch (v.getClass().getSimpleName()) {");
for (XmlElement elem : member.getAnnotation(XmlElements.class).value()) {
String t = elem.type().getSimpleName();
String n = elem.name();
elements.add(" case \"" + t + "\" -> doWrite" + t + "(\"" + n + "\", (" + t + ") def." + gn
+ "());");
}
elements.add(" }");
elements.add("});");
}
} else if (member.getAnnotation(XmlElement.class) != null) {
String t = root.getSimpleName();
String n = member.getAnnotation(XmlElement.class).name();
if ("##default".equals(n)) {
n = pn;
}
// elements.add("// @XmlElement: " + member);
if (list) {
String w = member.getAnnotation(XmlElementWrapper.class) != null
? member.getAnnotation(XmlElementWrapper.class).name() : null;
elements.add("doWriteList(" + (w != null ? "\"" + w + "\"" : "null") + ", " +
"\"" + n + "\", def." + gn + "(), this::doWrite" + t + ");");
} else {
XmlJavaTypeAdapter adapter = member.getAnnotation(XmlJavaTypeAdapter.class);
if (adapter != null) {
Class<? extends XmlAdapter> cl = adapter.value();
Class<?> actualType = null;
for (Method m : cl.getDeclaredMethods()) {
if (m.getName().equals("marshal") && m.getReturnType() != Object.class) {
actualType = m.getReturnType();
break;
}
}
if (actualType == null) {
throw new IllegalArgumentException(
"Unable to determine property name for JAXB" +
" adapted member: " + member);
}
elements.add("doWriteElement(" +
"\"" + n + "\", new " + cl.getSimpleName() + "().marshal(def." + gn
+ "()), this::doWrite" + actualType.getSimpleName() + ");");
} else {
elements.add("doWriteElement(" +
"\"" + n + "\", def." + gn + "(), this::doWrite" + t + ");");
}
}
} else if (member.getAnnotation(XmlAnyElement.class) != null) {
elements.add("domElements(def." + gn + "());");
} else {
String t = root.getSimpleName();
String n = root.getAnnotation(XmlRootElement.class) != null
? root.getAnnotation(XmlRootElement.class).name() : "##default";
if ("##default".equals(n)) {
// TODO: handle default name
}
// elements.add("// " + member);
if (list) {
elements.add("doWriteList(\"" + pn + "\", " +
"\"" + n + "\", def." + gn + "(), this::doWrite" + t + ");");
} else {
elements.add("doWriteElement(\"" + pn + "\", def." + gn + "(), this::doWrite" + t + ");");
}
}
});
// @XmlValue
List<String> value = getClassAndSuper(clazz).flatMap(this::getProperties)
.filter(Property::isValue).findFirst()
.map(member -> "doWriteValue(def." + member.getGetter() + "());")
.stream().toList();
String qgname = qname;
if (clazz.getTypeParameters().length > 0) {
qgname = qname + "<" + Stream.of(clazz.getTypeParameters()).map(t -> "?")
.collect(Collectors.joining(", ")) + ">";
}
if (!attributeMembers.isEmpty() || !elementMembers.isEmpty() || isRootElement(clazz)
|| isReferenced(clazz, model)) {
List<String> statements = new ArrayList<>();
if ("ExpressionSubElementDefinition".equals(name)) {
statements.add("startExpressionElement(name);");
} else {
statements.add("startElement(name);");
}
// Attributes
if (hasDerived && !attributes.isEmpty()) {
writer.addMethod()
.setProtected()
.setReturnType(Void.TYPE)
.setName("doWrite" + name + "Attributes")
.addParameter(qgname, "def")
.addThrows(IOException.class)
.setBody(String.join("\n", attributes));
statements.add("doWrite" + name + "Attributes(def);");
} else {
statements.addAll(attributes);
}
// Value
statements.addAll(value);
// Elements
elements.sort((e1, e2) -> {
// sort so output is last
boolean l1 = e1.startsWith("doWriteList(null, null, def.getOutputs()");
boolean l2 = e2.startsWith("doWriteList(null, null, def.getOutputs()");
return Boolean.compare(l1, l2);
});
if (hasDerived && !elements.isEmpty()) {
writer.addMethod()
.setProtected()
.setReturnType(Void.TYPE)
.setName("doWrite" + name + "Elements")
.addParameter(qgname, "def")
.addThrows(IOException.class)
.setBody(String.join("\n", elements));
statements.add("doWrite" + name + "Elements(def);");
} else {
statements.addAll(elements);
}
if ("ExpressionSubElementDefinition".equals(name)) {
statements.add("endExpressionElement(name);");
} else {
statements.add("endElement(name);");
}
writer.addMethod()
.setProtected()
.setReturnType(Void.TYPE)
.setName("doWrite" + name)
.addParameter(String.class, "name")
.addParameter(qgname, "def")
.addThrows(IOException.class)
.setBody(String.join("\n", statements));
}
}
elementRefs.forEach(clazz -> {
List<String> elements = new ArrayList<>();
elements.add("if (v != null) {");
elements.add(" switch (v.getClass().getSimpleName()) {");
model.stream()
.filter(c -> c.getAnnotation(XmlRootElement.class) != null)
.filter(c -> getClassAndSuper(c).anyMatch(cl -> cl == clazz))
.forEach(cl -> {
String t = cl.getSimpleName();
String n = cl.getAnnotation(XmlRootElement.class).name();
if ("##default".equals(n)) {
n = lowercase(t);
}
elements.add(" case \"" + t + "\" -> doWrite" + t + "(\"" + n + "\", (" + t + ") v);");
});
elements.add(" }");
elements.add("}");
String qname = clazz.getSimpleName();
if (clazz.getTypeParameters().length > 0) {
qname = qname + "<" + Stream.of(clazz.getTypeParameters()).map(t -> "?")
.collect(Collectors.joining(", ")) + ">";
}
writer.addMethod()
.setProtected()
.setReturnType(Void.TYPE)
.setName("doWrite" + clazz.getSimpleName() + "Ref")
.addParameter("String", "n")
.addParameter(qname, "v")
.addThrows(IOException.class)
.setBody(String.join("\n", elements));
});
writer.addMethod()
.setProtected()
.setReturnType(Void.TYPE)
.setName("doWriteAttribute")
.addParameter(String.class, "attribute")
.addParameter(Object.class, "value")
.addThrows(IOException.class)
.setBody("if (value != null) {",
" attribute(attribute, value);",
"}");
writer.addMethod()
.setProtected()
.setReturnType(Void.TYPE)
.setName("doWriteValue")
.addParameter(String.class, "value")
.addThrows(IOException.class)
.setBody("if (value != null) {",
" value(value);",
"}");
writer.addMethod()
.setSignature(
"protected <T> void doWriteList(String wrapperName, String name, List<T> list, ElementSerializer<T> elementSerializer) throws IOException")
.setBody("""
if (list != null) {
if (wrapperName != null) {
startElement(wrapperName);
}
for (T v : list) {
elementSerializer.doWriteElement(name, v);
}
if (wrapperName != null) {
endElement(wrapperName);
}
}""");
writer.addMethod()
.setSignature(
"protected <T> void doWriteElement(String name, T v, ElementSerializer<T> elementSerializer) throws IOException")
.setBody("""
if (v != null) {
elementSerializer.doWriteElement(name, v);
}""");
writer.addNestedType()
.setClass(false)
.setAbstract(true)
.setName("ElementSerializer<T>")
.addMethod()
.setAbstract()
.setSignature("void doWriteElement(String name, T value) throws IOException");
writer.addMethod()
.setProtected()
.setReturnType(String.class)
.setName("toString")
.addParameter("Boolean", "b")
.setBody("return b != null ? b.toString() : null;");
writer.addMethod()
.setProtected()
.setReturnType(String.class)
.setName("toString")
.addParameter("Enum<?>", "e")
.setBody("return e != null ? e.name() : null;");
writer.addMethod()
.setProtected()
.setReturnType(String.class)
.setName("toString")
.addParameter("Number", "n")
.setBody("return n != null ? n.toString() : null;");
writer.addImport("java.util.Base64");
writer.addMethod()
.setProtected()
.setReturnType(String.class)
.setName("toString")
.addParameter("byte[]", "b")
.setBody("return b != null ? Base64.getEncoder().encodeToString(b) : null;");
writer.addMethod()
.setProtected()
.setReturnType(Void.TYPE)
.setName("doWriteString")
.addParameter(String.class, "name")
.addParameter(String.class, "value")
.addThrows(IOException.class)
.setBody("if (value != null) {",
" startElement(name);",
" text(name, value);",
" endElement(name);",
"}");
writer.addMethod()
.setProtected()
.setReturnType(Void.TYPE)
.setName("doWriteNamespaces")
.addParameter("NamespaceAwareExpression", "def")
.addThrows(IOException.class)
.setBody("if (def.getNamespaceAsMap() != null) {",
" for (var e : def.getNamespaceAsMap().entrySet()) {",
" doWriteAttribute(\"xmlns:\" + e.getKey(), e.getValue());",
" }",
"}");
return writer;
}