From 65359efd82a43e3cb7ad3ecde1fd57323352f020 Mon Sep 17 00:00:00 2001 From: "@fbiville" Date: Thu, 16 Mar 2017 22:23:46 +0100 Subject: [PATCH] Centralize main processing logic --- .../DuplicationAwareBaseProcessor.java | 138 ++++++++++++++++++ .../tooling/procedure/ProcedureProcessor.java | 91 ++---------- .../procedure/UserFunctionProcessor.java | 87 ++--------- .../DuplicatedExtensionValidator.java | 11 ++ 4 files changed, 171 insertions(+), 156 deletions(-) create mode 100644 community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/DuplicationAwareBaseProcessor.java diff --git a/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/DuplicationAwareBaseProcessor.java b/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/DuplicationAwareBaseProcessor.java new file mode 100644 index 000000000000..b7a9a63ffa3f --- /dev/null +++ b/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/DuplicationAwareBaseProcessor.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2002-2017 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.tooling.procedure; + +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Stream; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementVisitor; +import javax.lang.model.element.TypeElement; + +import org.neo4j.procedure.Procedure; +import org.neo4j.procedure.UserAggregationFunction; +import org.neo4j.procedure.UserAggregationResult; +import org.neo4j.procedure.UserAggregationUpdate; +import org.neo4j.procedure.UserFunction; +import org.neo4j.tooling.procedure.messages.CompilationMessage; +import org.neo4j.tooling.procedure.messages.MessagePrinter; +import org.neo4j.tooling.procedure.validators.DuplicatedExtensionValidator; + +import static org.neo4j.tooling.procedure.CompilerOptions.IGNORE_CONTEXT_WARNINGS_OPTION; + +/** + * Base processor that processes {@link Element} annotated with {@code T}. + * It also detects and reports duplicated elements (duplication can obviously be detected within a compilation unit and + * not globally per Neo4j instance, as explained in {@link DuplicatedExtensionValidator}. + * + * @param processed annotation type + */ +public class DuplicationAwareBaseProcessor extends AbstractProcessor +{ + private final Set visitedElements = new LinkedHashSet<>(); + private final Class supportedAnnotationType; + private final Function> customNameFunction; + private final Function,Void>> visitorSupplier; + + private Function,Stream> duplicationValidator; + private ElementVisitor,Void> visitor; + private MessagePrinter messagePrinter; + + /** + * Base initialization of Neo4j extension processor (where extension can be {@link Procedure}, {@link UserFunction}, + * {@link UserAggregationFunction}). + * + * @param supportedAnnotationType main annotation type supported by the processor. The main annotation may depend on + * other annotations (e.g. {@link UserAggregationFunction} works with {@link UserAggregationResult} and + * {@link UserAggregationUpdate}). + * However, by design, these auxiliary annotations are processed by traversing the + * element graph, rather than by standalone annotation processors. + * @param customNameFunction function allowing to extract the custom simple name of the annotated element + * @param visitorSupplier supplies the main {@link ElementVisitor} class in charge of traversing and validating the + * annotated elements + */ + public DuplicationAwareBaseProcessor( Class supportedAnnotationType, Function> customNameFunction, + Function,Void>> visitorSupplier) + { + this.supportedAnnotationType = supportedAnnotationType; + this.customNameFunction = customNameFunction; + this.visitorSupplier = visitorSupplier; + } + + @Override + public synchronized void init( ProcessingEnvironment processingEnv ) + { + super.init( processingEnv ); + + messagePrinter = new MessagePrinter( processingEnv.getMessager() ); + duplicationValidator = new DuplicatedExtensionValidator<>( processingEnv.getElementUtils(), supportedAnnotationType, customNameFunction ); + visitor = visitorSupplier.apply( processingEnv ); + } + + @Override + public Set getSupportedOptions() + { + return Collections.singleton( IGNORE_CONTEXT_WARNINGS_OPTION ); + } + + @Override + public Set getSupportedAnnotationTypes() + { + return Collections.singleton( supportedAnnotationType.getName() ); + } + + @Override + public SourceVersion getSupportedSourceVersion() + { + return SourceVersion.RELEASE_8; + } + + @Override + public boolean process( Set annotations, RoundEnvironment roundEnv ) + { + processElements( roundEnv ); + if ( roundEnv.processingOver() ) + { + duplicationValidator.apply( visitedElements ).forEach( messagePrinter::print ); + } + return false; + } + + private void processElements( RoundEnvironment roundEnv ) + { + Set functions = roundEnv.getElementsAnnotatedWith( supportedAnnotationType ); + visitedElements.addAll( functions ); + functions.stream().flatMap( this::validate ).forEachOrdered( messagePrinter::print ); + } + + private Stream validate( Element element ) + { + return visitor.visit( element ); + } +} diff --git a/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/ProcedureProcessor.java b/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/ProcedureProcessor.java index 41df6d9bc6e7..4d183b10fa3a 100644 --- a/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/ProcedureProcessor.java +++ b/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/ProcedureProcessor.java @@ -21,99 +21,30 @@ import com.google.auto.service.AutoService; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Stream; -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; -import javax.annotation.processing.RoundEnvironment; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementVisitor; -import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import org.neo4j.procedure.Procedure; import org.neo4j.tooling.procedure.compilerutils.CustomNameExtractor; -import org.neo4j.tooling.procedure.messages.CompilationMessage; -import org.neo4j.tooling.procedure.messages.MessagePrinter; -import org.neo4j.tooling.procedure.validators.DuplicatedExtensionValidator; import org.neo4j.tooling.procedure.visitors.ProcedureVisitor; import static org.neo4j.tooling.procedure.CompilerOptions.IGNORE_CONTEXT_WARNINGS_OPTION; @AutoService( Processor.class ) -public class ProcedureProcessor extends AbstractProcessor +public class ProcedureProcessor extends DuplicationAwareBaseProcessor { - private static final Class sprocType = Procedure.class; - private final Set visitedProcedures = new LinkedHashSet<>(); - - private Function,Stream> duplicationValidator; - private ElementVisitor,Void> visitor; - private MessagePrinter messagePrinter; - - @Override - public Set getSupportedOptions() - { - return Collections.singleton( IGNORE_CONTEXT_WARNINGS_OPTION ); - } - - @Override - public Set getSupportedAnnotationTypes() - { - return Collections.singleton( sprocType.getName() ); - } - - @Override - public SourceVersion getSupportedSourceVersion() - { - return SourceVersion.RELEASE_8; - } - - @Override - public synchronized void init( ProcessingEnvironment processingEnv ) + public ProcedureProcessor() { - super.init( processingEnv ); - Types typeUtils = processingEnv.getTypeUtils(); - Elements elementUtils = processingEnv.getElementUtils(); - - visitedProcedures.clear(); - messagePrinter = new MessagePrinter( processingEnv.getMessager() ); - visitor = new ProcedureVisitor( typeUtils, elementUtils, - processingEnv.getOptions().containsKey( IGNORE_CONTEXT_WARNINGS_OPTION ) ); - duplicationValidator = - new DuplicatedExtensionValidator<>( elementUtils, sprocType, - ( proc ) -> CustomNameExtractor.getName( proc::name, proc::value ) ); + super( Procedure.class, ( proc ) -> CustomNameExtractor.getName( proc::name, proc::value ), + processingEnvironment -> + { + Types typeUtils = processingEnvironment.getTypeUtils(); + Elements elementUtils = processingEnvironment.getElementUtils(); + + return new ProcedureVisitor( typeUtils, elementUtils, + processingEnvironment.getOptions().containsKey( IGNORE_CONTEXT_WARNINGS_OPTION ) ); + } ); } - - @Override - public boolean process( Set annotations, RoundEnvironment roundEnv ) - { - - processElements( roundEnv ); - if ( roundEnv.processingOver() ) - { - duplicationValidator.apply( visitedProcedures ).forEach( messagePrinter::print ); - } - return false; - } - - private void processElements( RoundEnvironment roundEnv ) - { - Set procedures = roundEnv.getElementsAnnotatedWith( sprocType ); - visitedProcedures.addAll( procedures ); - procedures.stream().flatMap( this::validate ).forEachOrdered( messagePrinter::print ); - } - - private Stream validate( Element element ) - { - return visitor.visit( element ); - } - } diff --git a/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/UserFunctionProcessor.java b/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/UserFunctionProcessor.java index 7128394f07b1..01049a0cb1ae 100644 --- a/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/UserFunctionProcessor.java +++ b/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/UserFunctionProcessor.java @@ -21,96 +21,31 @@ import com.google.auto.service.AutoService; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Stream; -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; -import javax.annotation.processing.RoundEnvironment; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementVisitor; -import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import org.neo4j.procedure.UserFunction; import org.neo4j.tooling.procedure.compilerutils.CustomNameExtractor; import org.neo4j.tooling.procedure.compilerutils.TypeMirrorUtils; -import org.neo4j.tooling.procedure.messages.CompilationMessage; -import org.neo4j.tooling.procedure.messages.MessagePrinter; -import org.neo4j.tooling.procedure.validators.DuplicatedExtensionValidator; import org.neo4j.tooling.procedure.visitors.UserFunctionVisitor; import static org.neo4j.tooling.procedure.CompilerOptions.IGNORE_CONTEXT_WARNINGS_OPTION; @AutoService( Processor.class ) -public class UserFunctionProcessor extends AbstractProcessor +public class UserFunctionProcessor extends DuplicationAwareBaseProcessor { - private static final Class userFunctionType = UserFunction.class; - private final Set visitedFunctions = new LinkedHashSet<>(); - private Function,Stream> duplicationValidator; - private ElementVisitor,Void> visitor; - private MessagePrinter messagePrinter; - - @Override - public Set getSupportedOptions() - { - return Collections.singleton( IGNORE_CONTEXT_WARNINGS_OPTION ); - } - - @Override - public Set getSupportedAnnotationTypes() - { - return Collections.singleton( userFunctionType.getName() ); - } - - @Override - public SourceVersion getSupportedSourceVersion() - { - return SourceVersion.RELEASE_8; - } - - @Override - public synchronized void init( ProcessingEnvironment processingEnv ) - { - super.init( processingEnv ); - Types typeUtils = processingEnv.getTypeUtils(); - Elements elementUtils = processingEnv.getElementUtils(); - - visitedFunctions.clear(); - messagePrinter = new MessagePrinter( processingEnv.getMessager() ); - visitor = new UserFunctionVisitor( typeUtils, elementUtils, new TypeMirrorUtils( typeUtils, elementUtils ), - processingEnv.getOptions().containsKey( IGNORE_CONTEXT_WARNINGS_OPTION ) ); - duplicationValidator = new DuplicatedExtensionValidator<>( elementUtils, userFunctionType, - ( function ) -> CustomNameExtractor.getName( function::name, function::value ) ); - } - - @Override - public boolean process( Set annotations, RoundEnvironment roundEnv ) - { - processElements( roundEnv ); - if ( roundEnv.processingOver() ) - { - duplicationValidator.apply( visitedFunctions ).forEach( messagePrinter::print ); - } - return false; - } - - private void processElements( RoundEnvironment roundEnv ) - { - Set functions = roundEnv.getElementsAnnotatedWith( userFunctionType ); - visitedFunctions.addAll( functions ); - functions.stream().flatMap( this::validate ).forEachOrdered( messagePrinter::print ); - } - - private Stream validate( Element element ) + public UserFunctionProcessor() { - return visitor.visit( element ); + super( UserFunction.class, ( function ) -> CustomNameExtractor.getName( function::name, function::value ), + processingEnvironment -> + { + Types typeUtils = processingEnvironment.getTypeUtils(); + Elements elementUtils = processingEnvironment.getElementUtils(); + return new UserFunctionVisitor( typeUtils, elementUtils, + new TypeMirrorUtils( typeUtils, elementUtils ), + processingEnvironment.getOptions().containsKey( IGNORE_CONTEXT_WARNINGS_OPTION ) ); + } ); } } diff --git a/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/validators/DuplicatedExtensionValidator.java b/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/validators/DuplicatedExtensionValidator.java index 82d5126bf50a..41720b9b3fc5 100644 --- a/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/validators/DuplicatedExtensionValidator.java +++ b/community/procedure-compiler/processor/src/main/java/org/neo4j/tooling/procedure/validators/DuplicatedExtensionValidator.java @@ -38,6 +38,17 @@ import static java.util.stream.Collectors.groupingBy; +/** + * Validates that a given extension name is not declared by multiple elements annotated with the same annotation of type + * {@code T}. + * This validation is done within an annotation processor. This means that the detection is detected only per + * compilation unit, not per Neo4j instance. + * + * Indeed, a Neo4j instance can aggregate several extension JARs and its duplication detection cannot be entirely + * replaced by this. + * + * @param annotation type + */ public class DuplicatedExtensionValidator implements Function,Stream> {