This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push: new 5f4530c [CAMEL-12688] Fix generation and improve the generated code 5f4530c is described below commit 5f4530c7077e9871f56b49a59ea8f11f2cdd03eb Author: Guillaume Nodet <gno...@gmail.com> AuthorDate: Wed Nov 21 18:05:10 2018 +0100 [CAMEL-12688] Fix generation and improve the generated code --- .../apache/camel/tools/apt/ConverterProcessor.java | 161 ++++++++++++++++----- 1 file changed, 127 insertions(+), 34 deletions(-) diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java index ac372e1..1757f21 100644 --- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java @@ -21,9 +21,12 @@ import java.io.FileOutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; +import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -33,6 +36,8 @@ import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -50,20 +55,25 @@ public class ConverterProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { try { - if (this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.impl.converter.CoreFallbackConverter") != null) { + if (roundEnv.processingOver()) { return false; } - if (roundEnv.processingOver()) { + if (this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.impl.converter.CoreStaticTypeConverterLoader") != null) { + return false; + } + + // We're in tests, do not generate anything + if (this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.converter.ObjectConverter") == null) { return false; } Comparator<TypeMirror> comparator = (o1, o2) -> processingEnv.getTypeUtils().isAssignable(o1, o2) ? -1 : processingEnv.getTypeUtils().isAssignable(o2, o1) ? +1 : o1.toString().compareTo(o2.toString()); - Map<String, Map<TypeMirror, ExecutableElement>> converters = new HashMap<>(); - TypeElement annotationType = this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.Converter"); - for (Element element : roundEnv.getElementsAnnotatedWith(annotationType)) { + Map<String, Map<TypeMirror, ExecutableElement>> converters = new TreeMap<>(); + TypeElement converterAnnotationType = this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.Converter"); + for (Element element : roundEnv.getElementsAnnotatedWith(converterAnnotationType)) { if (element.getKind() == ElementKind.METHOD) { ExecutableElement ee = (ExecutableElement) element; TypeMirror to = ee.getReturnType(); @@ -81,57 +91,127 @@ public class ConverterProcessor extends AbstractProcessor { converters.computeIfAbsent(toString(to), c -> new TreeMap<>(comparator)).put(from, ee); } } - - // We're in tests, do not generate anything - if (this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.converter.ObjectConverter") == null) { - return false; + TypeElement fallbackAnnotationType = this.processingEnv.getElementUtils().getTypeElement("org.apache.camel.FallbackConverter"); + List<ExecutableElement> fallbackConverters = new ArrayList<>(); + for (Element element : roundEnv.getElementsAnnotatedWith(fallbackAnnotationType)) { + if (element.getKind() == ElementKind.METHOD) { + ExecutableElement ee = (ExecutableElement) element; + fallbackConverters.add(ee); + } } String p = "org.apache.camel.impl.converter"; - String c = "CoreFallbackConverter"; + String c = "CoreStaticTypeConverterLoader"; JavaFileObject jfo = processingEnv.getFiler().createSourceFile(p + "." + c); Set<String> converterClasses = new LinkedHashSet<>(); try (Writer writer = jfo.openWriter()) { writer.append("package ").append(p).append(";\n"); writer.append("\n"); - writer.append("import org.apache.camel.support.TypeConverterSupport;\n"); writer.append("import org.apache.camel.Exchange;\n"); writer.append("import org.apache.camel.TypeConversionException;\n"); + writer.append("import org.apache.camel.TypeConverterLoaderException;\n"); + writer.append("import org.apache.camel.spi.TypeConverterLoader;\n"); + writer.append("import org.apache.camel.spi.TypeConverterRegistry;\n"); + writer.append("import org.apache.camel.support.TypeConverterSupport;\n"); writer.append("\n"); writer.append("@SuppressWarnings(\"unchecked\")\n"); - writer.append("public class ").append(c).append(" extends TypeConverterSupport {\n"); + writer.append("public class ").append(c).append(" implements TypeConverterLoader {\n"); + writer.append("\n"); + writer.append(" static abstract class SimpleTypeConverter extends TypeConverterSupport {\n"); + writer.append(" private final boolean allowNull;\n"); writer.append("\n"); - writer.append(" public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException {\n"); - writer.append(" try {\n"); - writer.append(" return (T) doConvert(type, exchange, value);\n"); - writer.append(" } catch (TypeConversionException e) {\n"); - writer.append(" throw e;\n"); - writer.append(" } catch (Exception e) {\n"); - writer.append(" throw new TypeConversionException(value, type, e);\n"); + writer.append(" public SimpleTypeConverter(boolean allowNull) {\n"); + writer.append(" this.allowNull = allowNull;\n"); writer.append(" }\n"); - writer.append(" }\n"); writer.append("\n"); - writer.append(" private Object doConvert(Class<?> type, Exchange exchange, Object value) throws Exception {\n"); - writer.append(" switch (type.getName()) {\n"); + writer.append(" @Override\n"); + writer.append(" public boolean allowNull() {\n"); + writer.append(" return allowNull;\n"); + writer.append(" }\n"); + writer.append("\n"); + writer.append(" @Override\n"); + writer.append(" public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException {\n"); + writer.append(" try {\n"); + writer.append(" return (T) doConvert(exchange, value);\n"); + writer.append(" } catch (TypeConversionException e) {\n"); + writer.append(" throw e;\n"); + writer.append(" } catch (Exception e) {\n"); + writer.append(" throw new TypeConversionException(value, type, e);\n"); + writer.append(" }\n"); + writer.append(" }\n"); + writer.append(" protected abstract Object doConvert(Exchange exchange, Object value) throws Exception;\n"); + writer.append(" };\n"); + writer.append("\n"); + writer.append(" @Override\n"); + writer.append(" public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {\n"); + for (Map.Entry<String, Map<TypeMirror, ExecutableElement>> to : converters.entrySet()) { - writer.append(" case \"").append(to.getKey()).append("\": {\n"); for (Map.Entry<TypeMirror, ExecutableElement> from : to.getValue().entrySet()) { - String name = toString(from.getKey()); - if ("java.lang.Object".equals(name)) { - writer.append(" if (value != null) {\n"); - } else { - writer.append(" if (value instanceof ").append(name).append(") {\n"); + boolean allowNull = false; + for (AnnotationMirror ann : from.getValue().getAnnotationMirrors()) { + if (ann.getAnnotationType().asElement() == converterAnnotationType) { + for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : ann.getElementValues().entrySet()) { + switch (entry.getKey().getSimpleName().toString()) { + case "allowNull": + allowNull = (Boolean) entry.getValue().getValue(); + break; + default: + throw new IllegalStateException(); + } + } + } + } + writer.append(" registry.addTypeConverter(").append(to.getKey()).append(".class").append(", ") + .append(toString(from.getKey())).append(".class, new SimpleTypeConverter(") + .append(Boolean.toString(allowNull)).append(") {\n"); + writer.append(" @Override\n"); + writer.append(" public Object doConvert(Exchange exchange, Object value) throws Exception {\n"); + writer.append(" return ").append(toJava(from.getValue(), converterClasses)).append(";\n"); + writer.append(" }\n"); + writer.append(" });\n"); + } + } + + for (ExecutableElement ee : fallbackConverters) { + boolean allowNull = false; + boolean canPromote = false; + for (AnnotationMirror ann : ee.getAnnotationMirrors()) { + if (ann.getAnnotationType().asElement() == fallbackAnnotationType) { + for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : ann.getElementValues().entrySet()) { + switch (entry.getKey().getSimpleName().toString()) { + case "allowNull": + allowNull = (Boolean) entry.getValue().getValue(); + break; + case "canPromote": + canPromote = (Boolean) entry.getValue().getValue(); + break; + default: + throw new IllegalStateException(); + } + } } - writer.append(" return ").append(toJava(from.getValue(), converterClasses)).append(";\n"); - writer.append(" }\n"); } - writer.append(" break;\n"); + writer.append(" registry.addFallbackTypeConverter(new TypeConverterSupport() {\n"); + writer.append(" @Override\n"); + writer.append(" public boolean allowNull() {\n"); + writer.append(" return ").append(Boolean.toString(allowNull)).append(";\n"); writer.append(" }\n"); + writer.append(" @Override\n"); + writer.append(" public <T> T convertTo(Class<T> type, Exchange exchange, Object value) throws TypeConversionException {\n"); + writer.append(" try {\n"); + writer.append(" return (T) ").append(toJavaFallback(ee, converterClasses)).append(";\n"); + writer.append(" } catch (TypeConversionException e) {\n"); + writer.append(" throw e;\n"); + writer.append(" } catch (Exception e) {\n"); + writer.append(" throw new TypeConversionException(value, type, e);\n"); + writer.append(" }\n"); + writer.append(" }\n"); + writer.append(" }, ").append(Boolean.toString(canPromote)).append(");\n"); } - writer.append(" }\n"); - writer.append(" return null;\n"); + writer.append("\n"); writer.append(" }\n"); + writer.append("\n"); for (String f : converterClasses) { String s = f.substring(f.lastIndexOf('.') + 1); @@ -154,7 +234,7 @@ public class ConverterProcessor extends AbstractProcessor { } } catch (Throwable e) { - processingEnv.getMessager().printMessage(Kind.ERROR, "Unable to process elements annotated with @UriEndpoint: " + e.getMessage()); + processingEnv.getMessager().printMessage(Kind.ERROR, "Unable to process elements annotated with @Converter: " + e.getMessage()); dumpExceptionToErrorFile("camel-apt-error.log", "Error processing @Converter", e); } return false; @@ -177,6 +257,19 @@ public class ConverterProcessor extends AbstractProcessor { return pfx + "(" + cast + "value" + (converter.getParameters().size() == 2 ? ", exchange" : "") + ")"; } + private String toJavaFallback(ExecutableElement converter, Set<String> converterClasses) { + String pfx; + if (converter.getModifiers().contains(Modifier.STATIC)) { + pfx = converter.getEnclosingElement().toString() + "." + converter.getSimpleName(); + } else { + converterClasses.add(converter.getEnclosingElement().toString()); + pfx = "get" + converter.getEnclosingElement().getSimpleName() + "()." + converter.getSimpleName(); + } + String type = toString(converter.getParameters().get(converter.getParameters().size() - 2).asType()); + String cast = type.equals("java.lang.Object") ? "" : "(" + type + ") "; + return pfx + "(type, " + (converter.getParameters().size() == 4 ? "exchange, " : "") + cast + "value" + ", registry)"; + } + public static void dumpExceptionToErrorFile(String fileName, String message, Throwable e) { File file = new File(fileName); try {