Java tutorial
/******************************************************************************* * Copyright (c) 2015 Red Hat. All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which accompanies this * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: Red Hat - Initial Contribution *******************************************************************************/ package org.lambdamatic.mongodb.apt.template; import java.util.List; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import org.apache.commons.lang3.ClassUtils; import org.lambdamatic.mongodb.annotations.EmbeddedDocument; import org.lambdamatic.mongodb.annotations.TransientField; import org.lambdamatic.mongodb.apt.BaseAnnotationProcessor; import org.lambdamatic.mongodb.metadata.ProjectionMetadata; import org.lambdamatic.mongodb.metadata.QueryMetadata; import org.lambdamatic.mongodb.metadata.UpdateMetadata; /** * Template Context builder for {@link QueryMetadata}, {@link ProjectionMetadata} and * {@link UpdateMetadata} generated classes. */ public class MetadataTemplateContext extends BaseTemplateContext { /** * {@link Function} to generate the simple class name for the {@link QueryMetadata} implementation * of a given {@link TypeElement}. */ public static final Function<DeclaredType, String> elementToQuerySimpleClassNameFunction = t -> "Q" + ClassUtils.getShortClassName(t.toString()); /** * {@link Function} to generate the simple class name for the {@link QueryMetadata} implementation * of a given {@link TypeElement}. */ public static final Function<DeclaredType, String> elementToProjectionSimpleClassName = t -> "P" + ClassUtils.getShortClassName(t.toString()); /** * {@link Function} to generate the simple class name for the {@link QueryMetadata} implementation * of a given {@link TypeElement}. */ public static final Function<DeclaredType, String> elementToUpdateSimpleClassNameFunction = t -> "U" + ClassUtils.getShortClassName(t.toString()); /** * {@link MetadataTemplateContext} constructor for a {@link QueryMetadata} implementation. * * @param domainType the type of the annotated document * @param annotationProcessor the annotation processor used to generate the {@link QueryMetadata} * implementation * @return a {@link MetadataTemplateContext} with all the data to generate the source code */ public static MetadataTemplateContext createQueryMetadataTemplateContext(final TypeElement domainType, final BaseAnnotationProcessor annotationProcessor) { return new MetadataTemplateContext(domainType, annotationProcessor, TemplateField.createQueryFieldFunction, elementToQuerySimpleClassNameFunction, "query_metadata_template.mustache"); } /** * {@link MetadataTemplateContext} constructor for a {@link ProjectionMetadata} implementation. * * @param domainType the type of the annotated document * @param annotationProcessor the annotation processor used to generate the * {@link ProjectionMetadata} implementation * @return a {@link MetadataTemplateContext} with all the data to generate the source code */ public static MetadataTemplateContext createProjectionMetadataTemplateContext(final TypeElement domainType, final BaseAnnotationProcessor annotationProcessor) { return new MetadataTemplateContext(domainType, annotationProcessor, TemplateField.createProjectionFieldFunction, elementToProjectionSimpleClassName, "projection_metadata_template.mustache"); } /** * {@link MetadataTemplateContext} constructor for a {@link UpdateMetadata} implementation. * * @param domainType the type of the annotated document * @param annotationProcessor the annotation processor used to generate the {@link UpdateMetadata} * implementation * @return a {@link MetadataTemplateContext} with all the data to generate the source code */ public static MetadataTemplateContext createUpdateMetadataTemplateContext(final TypeElement domainType, final BaseAnnotationProcessor annotationProcessor) { return new MetadataTemplateContext(domainType, annotationProcessor, TemplateField.createUpdateFieldFunction, elementToUpdateSimpleClassNameFunction, "update_metadata_template.mustache"); } /** The list of relevant fields to process. */ private final List<VariableElement> domainTypeFields; /** * the list of {@link TemplateField} that will be output in the generated source code. */ private final List<TemplateField> templateFields; /** the simple name of the Java class to generate. */ private final String simpleClassName; /** the fully qualified name of the Java class to generate. */ private final String fullyQualifiedClassName; /** the name of the template to use to generate the source code. */ private final String templateFileName; /** The annotations to include on the generated type. */ private List<TemplateAnnotation> annotations; /** * Full constructor * * @param domainElement the {@link TypeElement} to work on. * @param templateFieldBuildFunction the {@link Function} used to generate a single * {@link TemplateField} from a given relevant {@link VariableElement} in the given * {@link TypeElement}. * @param templateMethodsBuildFunction the {@link Function} used to generate zero or more * {@link TemplateMethods} from a given relevant {@link VariableElement} in the given * {@link TypeElement}. * */ private MetadataTemplateContext(final TypeElement domainElement, final BaseAnnotationProcessor annotationProcessor, final BiFunction<VariableElement, ProcessingEnvironment, TemplateField> templateFieldBuildFunction, final Function<DeclaredType, String> simpleClassNameBuilder, final String templateFileName) { super((DeclaredType) domainElement.asType(), annotationProcessor); this.domainTypeFields = domainElement.getEnclosedElements().stream() .filter(e -> e.getKind() == ElementKind.FIELD) .filter(e -> e.getAnnotation(TransientField.class) == null).map(e -> (VariableElement) e) .collect(Collectors.toList()); this.templateFields = this.domainTypeFields.stream().map(f -> { return templateFieldBuildFunction.apply(f, annotationProcessor.getProcessingEnvironment()); }).collect(Collectors.toList()); this.simpleClassName = simpleClassNameBuilder.apply(this.domainType); this.fullyQualifiedClassName = getPackageName() + '.' + this.simpleClassName; this.templateFileName = templateFileName; this.annotations = Stream.of(domainElement.getAnnotationsByType(EmbeddedDocument.class)) .map(a -> TemplateAnnotation.Builder.type(EmbeddedDocument.class).build()) .collect(Collectors.toList()); } @Override public String getFullyQualifiedClassName() { return this.fullyQualifiedClassName; } @Override public String getSimpleClassName() { return this.simpleClassName; } @Override public String getTemplateFileName() { return this.templateFileName; } /** * @return the {@link List} of {@link TemplateField} for the associated domain type. */ public List<TemplateField> getFields() { return this.templateFields; } /** * Finds all required import statements that need to be included in the target template. * * @return the {@link List} of class imports (excluding those in the same package) */ public List<String> getRequiredImports() { // combining required types on annotations and fields final List<String> requiredImports = Stream .concat(this.templateFields.stream().flatMap(f -> f.getRequiredJavaTypes().stream()), this.annotations.stream().flatMap(a -> a.getRequiredJavaTypes().stream())) .filter(i -> { final String packageCanonicalName = ClassUtils.getPackageCanonicalName(i); return !packageCanonicalName.equals(getPackageName()) && !packageCanonicalName.equals("java.lang"); }).distinct().sorted().collect(Collectors.toList()); return requiredImports; } }