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
commit 0a88d1366ebde37bb5973704dd88ee723c005209 Author: Guillaume Nodet <gno...@gmail.com> AuthorDate: Mon Jun 25 15:22:46 2018 +0200 [CAMEL-12688] Generate a fallback converter containing all core converters for performances --- .../apache/camel/tools/apt/ConverterProcessor.java | 198 +++++++++++++++++++++ .../services/javax.annotation.processing.Processor | 1 + 2 files changed, 199 insertions(+) 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 new file mode 100644 index 0000000..ac372e1 --- /dev/null +++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/ConverterProcessor.java @@ -0,0 +1,198 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.tools.apt; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import javax.annotation.processing.AbstractProcessor; +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.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic; +import javax.tools.Diagnostic.Kind; +import javax.tools.JavaFileObject; + +@SupportedAnnotationTypes({"org.apache.camel.Converter"}) +@SupportedSourceVersion(SourceVersion.RELEASE_8) +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) { + return false; + } + + if (roundEnv.processingOver()) { + 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)) { + if (element.getKind() == ElementKind.METHOD) { + ExecutableElement ee = (ExecutableElement) element; + TypeMirror to = ee.getReturnType(); + TypeMirror from = ee.getParameters().get(0).asType(); + String fromStr = toString(from); + if (!fromStr.endsWith("[]")) { + TypeElement e = this.processingEnv.getElementUtils().getTypeElement(fromStr); + if (e != null) { + from = e.asType(); + } else { + processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Could not retrieve type element for " + fromStr); + } + + } + 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; + } + + String p = "org.apache.camel.impl.converter"; + String c = "CoreFallbackConverter"; + 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("\n"); + writer.append("@SuppressWarnings(\"unchecked\")\n"); + writer.append("public class ").append(c).append(" extends TypeConverterSupport {\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(" }\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"); + 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"); + } + writer.append(" return ").append(toJava(from.getValue(), converterClasses)).append(";\n"); + writer.append(" }\n"); + } + writer.append(" break;\n"); + writer.append(" }\n"); + } + writer.append(" }\n"); + writer.append(" return null;\n"); + writer.append(" }\n"); + + for (String f : converterClasses) { + String s = f.substring(f.lastIndexOf('.') + 1); + String v = s.substring(0, 1).toLowerCase() + s.substring(1); + writer.append(" private volatile ").append(f).append(" ").append(v).append(";\n"); + writer.append(" private ").append(f).append(" get").append(s).append("() {\n"); + writer.append(" if (").append(v).append(" == null) {\n"); + writer.append(" synchronized (this) {\n"); + writer.append(" if (").append(v).append(" == null) {\n"); + writer.append(" ").append(v).append(" = new ").append(f).append("();\n"); + writer.append(" }\n"); + writer.append(" }\n"); + writer.append(" }\n"); + writer.append(" return ").append(v).append(";\n"); + writer.append(" }\n"); + } + + writer.append("}\n"); + writer.flush(); + } + + } catch (Throwable e) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Unable to process elements annotated with @UriEndpoint: " + e.getMessage()); + dumpExceptionToErrorFile("camel-apt-error.log", "Error processing @Converter", e); + } + return false; + } + + private String toString(TypeMirror type) { + return type.toString().replaceAll("<.*>", ""); + } + + private String toJava(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(0).asType()); + String cast = type.equals("java.lang.Object") ? "" : "(" + type + ") "; + return pfx + "(" + cast + "value" + (converter.getParameters().size() == 2 ? ", exchange" : "") + ")"; + } + + public static void dumpExceptionToErrorFile(String fileName, String message, Throwable e) { + File file = new File(fileName); + try { + FileOutputStream fos = new FileOutputStream(file); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + fos.write(message.getBytes()); + fos.write("\n\n".getBytes()); + fos.write(sw.toString().getBytes()); + pw.close(); + sw.close(); + fos.close(); + } catch (Throwable t) { + // ignore + } + } + +} diff --git a/tooling/apt/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/tooling/apt/src/main/resources/META-INF/services/javax.annotation.processing.Processor index 065c7b0..85b35af 100644 --- a/tooling/apt/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ b/tooling/apt/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -18,3 +18,4 @@ ### only specify ModelAnnotationProcessor as it delegates to CoreEip or Spring accordingly org.apache.camel.tools.apt.ModelAnnotationProcessor org.apache.camel.tools.apt.EndpointAnnotationProcessor +org.apache.camel.tools.apt.ConverterProcessor