Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
de.knightsoftnet.gwtp.spring.annotation.processor.AbstractBackofficeCreator Maven / Gradle / Ivy
/*
* 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 de.knightsoftnet.gwtp.spring.annotation.processor;
import de.knightsoftnet.validators.annotation.processor.TypeUtils;
import de.knightsoftnet.validators.shared.data.FieldTypeEnum;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.processing.FilerException;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
/**
* Create class or interface.
*/
public abstract class AbstractBackofficeCreator {
protected static final String JAVAX_VALIDATION_PATH = "javax.validation.constraints.";
protected static final String JAKARTA_VALIDATION_PATH = "jakarta.validation.constraints.";
protected static final String MT_VALIDATION_PATH = "de.knightsoftnet.validators.shared.";
protected static final String WIDGET_PATH = "de.knightsoftnet.mtwidgets.client.ui.widget.";
private final List imports;
protected final String suffix;
protected AbstractBackofficeCreator(final String suffix) {
super();
imports = new ArrayList<>();
this.suffix = suffix;
}
public String getSuffix() {
return suffix;
}
/**
* write class or interface.
*
* @param element the element which represents the entity
* @param annotationInterface annotation interface
* @param processingEnv processing environment
*/
public void writeClassOrInterface(final Element element, final T annotationInterface,
final ProcessingEnvironment processingEnv) {
try {
final String serverPackage = processingEnv.getElementUtils()
.getPackageOf(element.getEnclosingElement()).getQualifiedName().toString();
final String entityType = getEntityNameOfElement(element);
final JavaFileObject builderFile =
processingEnv.getFiler().createSourceFile(serverPackage + "." + entityType + suffix);
try (PrintWriter out = new PrintWriter(builderFile.openWriter())) {
out.print("package ");
out.print(serverPackage);
out.println(";");
out.println();
addAdditionalImports(serverPackage, element, annotationInterface, processingEnv);
writeImports(out, serverPackage);
out.println();
writeBody(out, serverPackage, element, annotationInterface, processingEnv);
}
} catch (final FilerException e) {
// happens when trying to recreate an existing interface
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, e.getMessage());
} catch (final IOException e) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());
e.printStackTrace();
}
}
protected String getPackageOfElement(final Element element) {
final String entityName = element.asType().toString();
final int packagePos = entityName.lastIndexOf('.');
return entityName.substring(0, packagePos);
}
protected String getEntityNameOfElement(final Element element) {
final String entityName = element.asType().toString();
final int packagePos = entityName.lastIndexOf('.');
return entityName.substring(packagePos + 1);
}
/**
* add import line.
*
* @param classWithPaths class including pass to import
*/
protected void addImports(final String... classWithPaths) {
Stream.of(classWithPaths).forEach(this::addImport);
}
/**
* add import line.
*
* @param typeMirrors class including pass to import
*/
protected void addImports(final TypeMirror... typeMirrors) {
Stream.of(typeMirrors).forEach(this::addImport);
}
/**
* add import line.
*
* @param classWithPath class including pass to import
*/
protected void addImport(final String classWithPath) {
imports.add(classWithPath);
}
/**
* add import line.
*
* @param typeMirror type mirror of the class to add
*/
protected void addImport(final TypeMirror typeMirror) {
addImport(typeMirror.toString());
}
/**
* write import lines.
*
* @param out print writer
* @param classPath path of the class (or interface) to crate
*/
protected void writeImports(final PrintWriter out, final String classPath) {
String lastPrefix = firstTwoPartsOfPath(classPath);
String lastImport = StringUtils.EMPTY;
imports.sort(new ImportComparator(lastPrefix));
for (final String importValue : imports) {
if (!StringUtils.equals(importValue, lastImport)) {
lastImport = importValue;
if (!importValue.startsWith(lastPrefix)) {
out.println();
lastPrefix = firstPartOfPath(importValue);
}
out.print("import ");
out.print(importValue);
out.println(";");
}
}
}
protected String firstPartOfPath(final String classPath) {
final int pos1 = classPath.indexOf('.');
return classPath.substring(0, pos1);
}
protected String firstTwoPartsOfPath(final String classPath) {
final int pos1 = classPath.indexOf('.');
final int pos2 = classPath.indexOf('.', pos1 + 1);
return classPath.substring(0, pos2);
}
protected List getFields(final Element element) {
return TypeUtils.getFields(element.asType()).stream().sorted((o1, o2) -> {
// sort id on top, keep the rest unchanged
if ("id".equals(o1.getSimpleName().toString())) {
return "id".equals(o2.getSimpleName().toString()) ? 0 : -1;
}
return 0;
}).collect(Collectors.toList());
}
protected boolean hasEnum(final Element element) {
return getFields(element).stream().map(Element::asType).anyMatch(this::isEnum);
}
protected boolean isEnum(final TypeMirror typeMirror) {
return StringUtils.isNoneEmpty(getEnumName(typeMirror));
}
protected String getEnumName(final TypeMirror typeMirror) {
if ("java.lang.Enum".equals(TypeUtils.getClassName(typeMirror))) {
return TypeUtils.getClassName(((DeclaredType) typeMirror).getTypeArguments().get(0));
}
if (typeMirror.getKind() == TypeKind.DECLARED) {
final TypeMirror superclass =
((TypeElement) ((DeclaredType) typeMirror).asElement()).getSuperclass();
if (superclass != null) {
return getEnumName(superclass);
}
}
return null;
}
protected String getEnumNameWithoutPackage(final TypeMirror typeMirror) {
final String[] splittedName = StringUtils.split(getEnumName(typeMirror), '.');
return splittedName[splittedName.length - 1];
}
/**
* read all values of a enumeration.
*
* @param enumTypeElement type element of the enumeration
* @return list of values
*/
protected List getEnumValues(final TypeElement enumTypeElement) {
return enumTypeElement.getEnclosedElements().stream()
.filter(element -> element.getKind().equals(ElementKind.ENUM_CONSTANT))
.map(Object::toString).collect(Collectors.toList());
}
/**
* map given field to FieldTypeEnum.
*
* @param field the field to detect
* @param elementType declared type of the structure
* @param processingEnv progressing environment
* @return the detected field type enum
*/
protected FieldTypeEnum mapElementToSearchType(final Element field,
final DeclaredType elementType, final ProcessingEnvironment processingEnv) {
// TODO incomplete implementation, handle additional stuff
switch (TypeUtils.getClassName(processingEnv.getTypeUtils().asMemberOf(elementType, field))) {
case "byte":
case "short":
case "int":
case "long":
case "float":
case "double":
case "java.lang.Byte":
case "java.lang.Short":
case "java.lang.Integer":
case "java.lang.Long":
case "java.lang.Double":
case "java.lang.Float":
case "java.lang.Number":
case "java.math.BigDecimal":
case "java.math.BigInteger":
return FieldTypeEnum.NUMERIC;
case "boolean":
case "java.lang.Boolean":
return FieldTypeEnum.BOOLEAN;
case "java.time.LocalDate":
return FieldTypeEnum.DATE;
case "java.util.Calendar":
case "java.util.Date":
case "java.time.LocalDateTime":
return FieldTypeEnum.DATETIME;
case "java.time.LocalTime":
return FieldTypeEnum.TIME;
case "java.lang.Enum":
return FieldTypeEnum.ENUM_FIXED;
case "java.lang.String":
return FieldTypeEnum.STRING;
default:
if (isEnum(processingEnv.getTypeUtils().asMemberOf(elementType, field))) {
return FieldTypeEnum.ENUM_FIXED;
}
return FieldTypeEnum.STRING;
}
}
/**
* detect the matching widget for given field.
*
* @param field the field to detect
* @param elementType declared type of the structure
* @param processingEnv progressing environment
* @return BackofficeWidget
*/
protected BackofficeWidget detectBackofficeWidgetOfField(final Element field,
final DeclaredType elementType, final ProcessingEnvironment processingEnv) {
final String fieldName = field.getSimpleName().toString();
final List imports = new ArrayList<>();
final String widgetName;
final List widgetParameter = new ArrayList<>();
boolean provided = false;
String providedConstructor = StringUtils.EMPTY;
final List extends AnnotationMirror> annotations = field.getAnnotationMirrors();
final List annotationNames = annotations.stream()
.map(annotationMirror -> annotationMirror.getAnnotationType().toString())
.collect(Collectors.toList());
if ("id".equals(fieldName)) {
widgetParameter.add(BackofficeWidgetParameter.of("enabled", "false", true));
}
switch (TypeUtils.getClassName(processingEnv.getTypeUtils().asMemberOf(elementType, field))) {
case "double":
case "java.lang.Double":
widgetName = "DecimalDoubleBox";
imports.add(WIDGET_PATH + widgetName);
notNullRequiredCheck(widgetParameter, annotationNames);
break;
case "java.math.BigDecimal":
widgetName = "DecimalBigDecimalBox";
imports.add(WIDGET_PATH + widgetName);
notNullRequiredCheck(widgetParameter, annotationNames);
break;
case "long":
case "java.lang.Long":
widgetName = "LongBox";
imports.add(WIDGET_PATH + widgetName);
notNullRequiredCheck(widgetParameter, annotationNames);
break;
case "boolean":
case "java.lang.Boolean":
widgetName = "CheckBox";
imports.add(WIDGET_PATH + widgetName);
break;
case "java.time.LocalDate":
widgetName = "DateBoxLocalDate";
imports.add(WIDGET_PATH + widgetName);
futurePastMinMaxCheck(imports, widgetParameter, annotationNames, "java.time.LocalDate",
"LocalDate", "minusDays(1L)", "plusDays(1L)");
notNullRequiredCheck(widgetParameter, annotationNames);
break;
case "java.util.Date":
widgetName = "DateTimeLocalBox";
imports.add(WIDGET_PATH + widgetName);
notNullRequiredCheck(widgetParameter, annotationNames);
break;
case "java.time.LocalDateTime":
widgetName = "DateTimeLocalBoxLocalDateTime";
imports.add(WIDGET_PATH + widgetName);
futurePastMinMaxCheck(imports, widgetParameter, annotationNames, "java.time.LocalDateTime",
"LocalDateTime", "minusMinutes(1L)", "plusMinutes(1L)");
notNullRequiredCheck(widgetParameter, annotationNames);
break;
case "java.time.LocalTime":
widgetName = "TimeBoxLocalTime";
imports.add(WIDGET_PATH + widgetName);
notNullRequiredCheck(widgetParameter, annotationNames);
break;
case "de.knightsoftnet.validators.shared.data.CountryEnum":
widgetName = "CountryListBox";
imports.add(WIDGET_PATH + widgetName);
widgetParameter.add(BackofficeWidgetParameter.of("sort", "NAME_ASC", true));
break;
case "de.knightsoftnet.gwtp.spring.shared.db.LocalizedEntity":
widgetName = "MultiLanguageTextBox";
imports.add(WIDGET_PATH + widgetName);
notNullNotBlankNotEmptyRequiredCheck(widgetParameter, annotationNames);
break;
default:
if (isEnum(processingEnv.getTypeUtils().asMemberOf(elementType, field))) {
final List enumValues = getEnumValues((TypeElement) ((DeclaredType) processingEnv
.getTypeUtils().asMemberOf(elementType, field)).asElement());
if (enumValues.size() < 4) {
widgetName =
"SortableIdAndNameRadioButton<" + getEnumNameWithoutPackage(field.asType()) + ">";
imports.add(WIDGET_PATH + "SortableIdAndNameRadioButton");
providedConstructor = "new SortableIdAndNameRadioButton<"
+ getEnumNameWithoutPackage(field.asType()) + ">(\"" + fieldName + "\",\n" //
+ " Stream.of(" + getEnumNameWithoutPackage(field.asType()) + ".values())\n"
+ " .map(entry -> new IdAndNameBean<"
+ getEnumNameWithoutPackage(field.asType()) + ">(entry, " + "messages." + fieldName
+ "(entry)))\n" //
+ " .collect(Collectors.toList()));";
} else {
widgetName = "IdAndNameListBox<" + getEnumNameWithoutPackage(field.asType()) + ">";
imports.add(WIDGET_PATH + "IdAndNameListBox");
providedConstructor =
"new IdAndNameListBox<" + getEnumNameWithoutPackage(field.asType()) + ">(Stream.of("
+ getEnumNameWithoutPackage(field.asType()) + ".values())\n" //
+ " .map(entry -> new IdAndNameBean<"
+ getEnumNameWithoutPackage(field.asType()) + ">(entry, " + "messages."
+ fieldName + "(entry)))\n" //
+ " .collect(Collectors.toList()));";
}
provided = true;
imports.add(WIDGET_PATH + "helper.IdAndNameBean");
imports.add("java.util.stream.Collectors");
imports.add("java.util.stream.Stream");
imports.add(getEnumName(field.asType()));
} else if (annotationNames.contains(MT_VALIDATION_PATH + "Email")
|| annotationNames.contains(JAVAX_VALIDATION_PATH + "Email")
|| annotationNames.contains(JAKARTA_VALIDATION_PATH + "Email")) {
widgetName = "EmailTextBox";
imports.add(WIDGET_PATH + widgetName);
notNullNotBlankNotEmptyRequiredCheck(widgetParameter, annotationNames);
} else if (annotationNames.contains(MT_VALIDATION_PATH + "Bic")
|| annotationNames.contains(MT_VALIDATION_PATH + "BicValue")) {
widgetName = "BicSuggestBox";
imports.add(WIDGET_PATH + widgetName);
notNullNotBlankNotEmptyRequiredCheck(widgetParameter, annotationNames);
} else if (annotationNames.contains(MT_VALIDATION_PATH + "Iban")) {
widgetName = "IbanTextBox";
imports.add(WIDGET_PATH + widgetName);
notNullNotBlankNotEmptyRequiredCheck(widgetParameter, annotationNames);
} else if (annotationNames.contains(MT_VALIDATION_PATH + "PhoneNumber")) {
widgetName = detectPhoneWidget(annotations);
imports.add(WIDGET_PATH + widgetName);
notNullNotBlankNotEmptyRequiredCheck(widgetParameter, annotationNames);
} else {
widgetName = "TextBox";
imports.add(WIDGET_PATH + widgetName);
notNullNotBlankNotEmptyRequiredCheck(widgetParameter, annotationNames);
sizeMaxLengthCheck(widgetParameter, annotations, annotationNames);
}
}
return BackofficeWidget.of(fieldName, imports, widgetName, widgetParameter, provided,
providedConstructor);
}
private String detectPhoneWidget(final List extends AnnotationMirror> annotations) {
final Optional extends AnnotationMirror> phoneNumberValidator =
annotations.stream().filter(annotationMirror -> (MT_VALIDATION_PATH + "PhoneNumber")
.equals(annotationMirror.getAnnotationType().toString())).findFirst();
if (phoneNumberValidator.isPresent()) {
if (BooleanUtils.isTrue(phoneNumberValidator.get().getElementValues().entrySet().stream()
.filter(
elementValue -> "allowDin5008".contentEquals(elementValue.getKey().getSimpleName()))
.map(elementValue -> (Boolean) elementValue.getValue().getValue()).findAny()
.orElse(Boolean.FALSE))) {
return "PhoneNumberDin5008InterSuggestBox";
}
if (BooleanUtils.isTrue(phoneNumberValidator.get().getElementValues().entrySet().stream()
.filter(elementValue -> "allowE123".contentEquals(elementValue.getKey().getSimpleName()))
.map(elementValue -> (Boolean) elementValue.getValue().getValue()).findAny()
.orElse(Boolean.FALSE))) {
return "PhoneNumberE123InterSuggestBox";
}
if (BooleanUtils.isTrue(phoneNumberValidator.get().getElementValues().entrySet().stream()
.filter(elementValue -> "allowUri".contentEquals(elementValue.getKey().getSimpleName()))
.map(elementValue -> (Boolean) elementValue.getValue().getValue()).findAny()
.orElse(Boolean.FALSE))) {
return "PhoneNumberUriSuggestBox";
}
if (BooleanUtils.isTrue(phoneNumberValidator.get().getElementValues().entrySet().stream()
.filter(
elementValue -> "allowCommon".contentEquals(elementValue.getKey().getSimpleName()))
.map(elementValue -> (Boolean) elementValue.getValue().getValue()).findAny()
.orElse(Boolean.FALSE))) {
return "PhoneNumberCommonInterSuggestBox";
}
}
return "PhoneNumberMsSuggestBox";
}
private void futurePastMinMaxCheck(final List imports,
final List widgetParameter, final List annotationNames,
final String importString, final String classString, final String minusMethodString,
final String plusMethodString) {
if (annotationNames.contains(JAVAX_VALIDATION_PATH + "PastOrPresent")
|| annotationNames.contains(JAKARTA_VALIDATION_PATH + "PastOrPresent")) {
imports.add(importString);
widgetParameter.add(BackofficeWidgetParameter.of("max", classString + ".now()", false));
}
if (annotationNames.contains(JAVAX_VALIDATION_PATH + "Past")
|| annotationNames.contains(JAKARTA_VALIDATION_PATH + "Past")) {
imports.add(importString);
widgetParameter.add(
BackofficeWidgetParameter.of("max", classString + ".now()." + minusMethodString, false));
}
if (annotationNames.contains(JAVAX_VALIDATION_PATH + "FutureOrPresent")
|| annotationNames.contains(JAKARTA_VALIDATION_PATH + "FutureOrPresent")) {
imports.add(importString);
widgetParameter.add(BackofficeWidgetParameter.of("min", classString + ".now()", false));
}
if (annotationNames.contains(JAVAX_VALIDATION_PATH + "Future")
|| annotationNames.contains(JAKARTA_VALIDATION_PATH + "Future")) {
imports.add(importString);
widgetParameter.add(
BackofficeWidgetParameter.of("min", classString + ".now()." + plusMethodString, false));
}
}
private void sizeMaxLengthCheck(final List widgetParameter,
final List extends AnnotationMirror> annotations, final List annotationNames) {
if (annotationNames.contains(JAVAX_VALIDATION_PATH + "Size")
|| annotationNames.contains(JAKARTA_VALIDATION_PATH + "Size")) {
final Optional extends AnnotationMirror> sizeValidator = annotations.stream()
.filter(annotationMirror -> StringUtils.equals(JAKARTA_VALIDATION_PATH + "Size",
annotationMirror.getAnnotationType().toString())
|| StringUtils.equals(JAVAX_VALIDATION_PATH + "Size",
annotationMirror.getAnnotationType().toString()))
.findFirst();
if (sizeValidator.isPresent()) {
final Optional maxSize = sizeValidator.get().getElementValues().entrySet().stream()
.filter(elementValue -> "max".contentEquals(elementValue.getKey().getSimpleName()))
.map(elementValue -> Objects.toString(elementValue.getValue().getValue(), null))
.findAny();
if (maxSize.isPresent()) {
widgetParameter.add(BackofficeWidgetParameter.of("maxLength", maxSize.get(), true));
}
}
}
}
private void notNullNotBlankNotEmptyRequiredCheck(
final List widgetParameter, final List annotationNames) {
if (annotationNames.contains(JAVAX_VALIDATION_PATH + "NotNull")
|| annotationNames.contains(JAKARTA_VALIDATION_PATH + "NotNull")
|| annotationNames.contains(JAVAX_VALIDATION_PATH + "NotBlank")
|| annotationNames.contains(JAKARTA_VALIDATION_PATH + "NotBlank")
|| annotationNames.contains(JAVAX_VALIDATION_PATH + "NotEmpty")
|| annotationNames.contains(JAKARTA_VALIDATION_PATH + "NotEmpty")) {
widgetParameter.add(BackofficeWidgetParameter.of("required", "true", true));
}
}
private void notNullRequiredCheck(final List widgetParameter,
final List annotationNames) {
if (annotationNames.contains(JAVAX_VALIDATION_PATH + "NotNull")
|| annotationNames.contains(JAKARTA_VALIDATION_PATH + "NotNull")) {
widgetParameter.add(BackofficeWidgetParameter.of("required", "true", true));
}
}
/**
* write additional imports.
*
* @param element the element which represents the entity
*/
protected abstract void addAdditionalImports(final String serverPackage, final Element element,
final T annotationInterface, final ProcessingEnvironment processingEnv);
/**
* write class or interface body.
*
* @param out print writer
* @param serverPackage package to generate stuff in
* @param element the element which represents the entity
* @param annotationInterface annotation interface
* @param processingEnv processing environment
*/
protected abstract void writeBody(final PrintWriter out, final String serverPackage,
final Element element, final T annotationInterface, ProcessingEnvironment processingEnv);
}