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 extends TypeElement> annotations, RoundEnvironment roundEnv )
+ {
+ processElements( roundEnv );
+ if ( roundEnv.processingOver() )
+ {
+ duplicationValidator.apply( visitedElements ).forEach( messagePrinter::print );
+ }
+ return false;
+ }
+
+ private void processElements( RoundEnvironment roundEnv )
+ {
+ Set extends Element> 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 extends TypeElement> annotations, RoundEnvironment roundEnv )
- {
-
- processElements( roundEnv );
- if ( roundEnv.processingOver() )
- {
- duplicationValidator.apply( visitedProcedures ).forEach( messagePrinter::print );
- }
- return false;
- }
-
- private void processElements( RoundEnvironment roundEnv )
- {
- Set extends Element> 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 extends TypeElement> annotations, RoundEnvironment roundEnv )
- {
- processElements( roundEnv );
- if ( roundEnv.processingOver() )
- {
- duplicationValidator.apply( visitedFunctions ).forEach( messagePrinter::print );
- }
- return false;
- }
-
- private void processElements( RoundEnvironment roundEnv )
- {
- Set extends Element> 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>
{