/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
*
* 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 org.kie.workbench.common.stunner.core.processors;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import org.uberfire.annotations.processors.exceptions.GenerationException;
public class GeneratorUtils extends org.uberfire.annotations.processors.GeneratorUtils {
private static final String[] NO_PARAMS = new String[0];
private static final String[] ANY_PARAMS = new String[0];
public static String getTypedMethodName(final TypeElement classElement,
final String annName,
final String returnTypeName,
final ProcessingEnvironment processingEnvironment) {
final Elements elementUtils = processingEnvironment.getElementUtils();
return getMethodName(classElement,
processingEnvironment,
elementUtils.getTypeElement(returnTypeName).asType(),
annName);
}
public static String getStringMethodName(final TypeElement classElement,
final String annName,
final ProcessingEnvironment processingEnvironment) {
final Elements elementUtils = processingEnvironment.getElementUtils();
return getMethodName(classElement,
processingEnvironment,
elementUtils.getTypeElement(String.class.getName()).asType(),
annName);
}
private static String getMethodName(final TypeElement classElement,
final ProcessingEnvironment processingEnvironment,
final TypeMirror mirror,
final String annotationName) {
ExecutableElement match = getUniqueAnnotatedMethod(classElement,
processingEnvironment,
annotationName,
mirror,
NO_PARAMS);
if (match == null) {
return null;
}
return match.getSimpleName().toString();
}
/*
classElem=MyDiagram
annotClassNamee=IsProperty
returnClassName=MyPropery
*/
public static ExecutableElement getExecutableElementMethodName(final TypeElement classElement,
final String returnClassName,
final String annotClassName,
final ProcessingEnvironment processingEnvironment) throws GenerationException {
return getExecutableElementMethodName(classElement,
processingEnvironment,
returnClassName,
annotClassName);
}
private static ExecutableElement getExecutableElementMethodName(final TypeElement originalClassElement,
final ProcessingEnvironment processingEnvironment,
final String returnClassName,
final String annotationName) throws GenerationException {
final Elements elementUtils = processingEnvironment.getElementUtils();
return getUniqueAnnotatedMethod(originalClassElement,
processingEnvironment,
annotationName,
// elementUtils.getTypeElement( "com.google.gwt.user.client.ui.IsWidget" ).asType(),
elementUtils.getTypeElement(returnClassName).asType(),
NO_PARAMS);
}
private static ExecutableElement getUniqueAnnotatedMethod(final TypeElement originalClassElement,
final ProcessingEnvironment processingEnvironment,
final String annotationName,
final TypeMirror requiredReturnType,
final String[] requiredParameterTypes) {
List<ExecutableElement> matches = getAnnotatedMethods(originalClassElement,
processingEnvironment,
annotationName,
requiredReturnType,
requiredParameterTypes);
if (matches.size() == 1) {
return matches.get(0);
} else if (matches.size() > 1) {
for (ExecutableElement match : matches) {
processingEnvironment.getMessager().printMessage(
Diagnostic.Kind.ERROR,
"Found multiple methods annotated with @" + fqcnToSimpleName(annotationName) + ". There should only be one.",
match);
}
}
return null;
}
public static List<ExecutableElement> getAnnotatedMethods(final TypeElement originalClassElement,
final ProcessingEnvironment processingEnvironment,
final String annotationName,
final TypeMirror requiredReturnType,
final String[] requiredParameterTypes) {
final Types typeUtils = processingEnvironment.getTypeUtils();
final Elements elementUtils = processingEnvironment.getElementUtils();
TypeElement classElement = originalClassElement;
while (true) {
final List<ExecutableElement> methods = ElementFilter.methodsIn(classElement.getEnclosedElements());
List<ExecutableElement> matches = new ArrayList<ExecutableElement>();
for (ExecutableElement e : methods) {
final TypeMirror actualReturnType = e.getReturnType();
if (getAnnotation(elementUtils,
e,
annotationName) == null) {
continue;
}
List<String> problems = new ArrayList<String>();
if (!typeUtils.isAssignable(actualReturnType,
requiredReturnType)) {
problems.add("return " + requiredReturnType);
}
if (!doParametersMatch(typeUtils,
elementUtils,
e,
requiredParameterTypes)) {
if (requiredParameterTypes.length == 0) {
problems.add("take no parameters");
} else {
StringBuilder sb = new StringBuilder();
sb.append("take ")
.append(requiredParameterTypes.length)
.append(" parameters of type (");
boolean first = true;
for (String p : requiredParameterTypes) {
if (!first) {
sb.append(", ");
}
sb.append(p);
first = false;
}
sb.append(")");
problems.add(sb.toString());
}
}
if (e.getModifiers().contains(Modifier.STATIC)) {
problems.add("be non-static");
}
if (e.getModifiers().contains(Modifier.PRIVATE)) {
problems.add("be non-private");
}
if (problems.isEmpty()) {
matches.add(e);
} else {
processingEnvironment.getMessager().printMessage(Diagnostic.Kind.ERROR,
formatProblemsList(annotationName,
problems),
e);
}
}
if (!matches.isEmpty()) {
return matches;
}
TypeMirror superclass = classElement.getSuperclass();
if (superclass instanceof DeclaredType) {
classElement = (TypeElement) ((DeclaredType) superclass).asElement();
} else {
break;
}
}
return Collections.emptyList();
}
public static AnnotationMirror getAnnotation(final Elements elementUtils,
final Element annotationTarget,
final String annotationName) {
Iterator i$ = elementUtils.getAllAnnotationMirrors(annotationTarget).iterator();
AnnotationMirror annotation;
do {
if (!i$.hasNext()) {
return null;
}
annotation = (AnnotationMirror) i$.next();
} while (!annotationName.contentEquals(getQualifiedName(annotation)));
return annotation;
}
private static boolean doParametersMatch(final Types typeUtils,
final Elements elementUtils,
final ExecutableElement e,
final String[] requiredParameterTypes) {
if (requiredParameterTypes == ANY_PARAMS) {
return true;
} else if (e.getParameters().size() != requiredParameterTypes.length) {
return false;
} else {
ArrayList requiredTypes = new ArrayList();
String[] i = requiredParameterTypes;
int actualType = requiredParameterTypes.length;
for (int requiredType = 0; requiredType < actualType; ++requiredType) {
String parameterType = i[requiredType];
requiredTypes.add(elementUtils.getTypeElement(parameterType).asType());
}
for (int var9 = 0; var9 < requiredTypes.size(); ++var9) {
TypeMirror var10 = ((VariableElement) e.getParameters().get(var9)).asType();
TypeMirror var11 = (TypeMirror) requiredTypes.get(var9);
if (!typeUtils.isAssignable(var10,
var11)) {
return false;
}
}
return true;
}
}
public static String getTypeMirrorDeclaredName(final TypeMirror typeMirror) {
TypeKind returnKind = typeMirror.getKind();
if (returnKind == TypeKind.DECLARED) {
DeclaredType declaredReturnType = (DeclaredType) typeMirror;
return declaredReturnType.toString();
}
return null;
}
private static String fqcnToSimpleName(final String fqcn) {
int lastIndexOfDot = fqcn.lastIndexOf(46);
return lastIndexOfDot != -1 ? fqcn.substring(lastIndexOfDot + 1) : fqcn;
}
static String formatProblemsList(final String annotationFqcn,
final List<String> problems) {
StringBuilder msg = new StringBuilder();
msg.append("Methods annotated with @").append(fqcnToSimpleName(annotationFqcn)).append(" must ");
for (int i = 0; i < problems.size(); ++i) {
if (problems.size() > 2 && i > 0) {
msg.append(", ");
}
if (problems.size() == 2 && i == 1) {
msg.append(" and ");
}
if (problems.size() > 2 && i == problems.size() - 1) {
msg.append("and ");
}
msg.append(problems.get(i));
}
return msg.toString();
}
}