Add tasks to process sources and collect config properties
This commit is contained in:
parent
e27dc5bc47
commit
8fa8b23d63
|
@ -17,6 +17,7 @@ apply from: rootProject.file( 'gradle/releasable.gradle' )
|
||||||
|
|
||||||
apply plugin: 'org.hibernate.matrix-test'
|
apply plugin: 'org.hibernate.matrix-test'
|
||||||
apply plugin: 'org.hibernate.orm.build.reports'
|
apply plugin: 'org.hibernate.orm.build.reports'
|
||||||
|
apply plugin: 'org.hibernate.orm.build.properties'
|
||||||
|
|
||||||
tasks.build.dependsOn 'buildDocs'
|
tasks.build.dependsOn 'buildDocs'
|
||||||
defaultTasks 'buildDocs'
|
defaultTasks 'buildDocs'
|
||||||
|
@ -105,6 +106,23 @@ asciidoctorj {
|
||||||
options logDocuments: true
|
options logDocuments: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect config properties ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
task collectConfigProperties { task ->
|
||||||
|
group 'Documentation'
|
||||||
|
description 'Collect config properties'
|
||||||
|
|
||||||
|
// make sure that the javadocs are generated prior to collecting properties.
|
||||||
|
dependsOn ':hibernate-core:javadoc'
|
||||||
|
dependsOn ':hibernate-envers:javadoc'
|
||||||
|
dependsOn ':hibernate-jcache:javadoc'
|
||||||
|
|
||||||
|
dependsOn tasks.generateConfigPropertiesMap
|
||||||
|
dependsOn tasks.writeConfigPropertiesMap
|
||||||
|
|
||||||
|
tasks.buildDocs.dependsOn task
|
||||||
|
tasks.buildDocsForPublishing.dependsOn task
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Topical Guides ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// Topical Guides ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -160,6 +178,8 @@ task renderUserGuide(type: AsciidoctorTask, group: 'Documentation') {task->
|
||||||
tasks.buildDocs.dependsOn task
|
tasks.buildDocs.dependsOn task
|
||||||
tasks.buildDocsForPublishing.dependsOn task
|
tasks.buildDocsForPublishing.dependsOn task
|
||||||
|
|
||||||
|
dependsOn tasks.collectConfigProperties
|
||||||
|
|
||||||
sourceDir = file( 'src/main/asciidoc/userguide' )
|
sourceDir = file( 'src/main/asciidoc/userguide' )
|
||||||
sources {
|
sources {
|
||||||
include 'Hibernate_User_Guide.adoc'
|
include 'Hibernate_User_Guide.adoc'
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
== List of all available configuration properties
|
||||||
|
|
||||||
|
include::../../../../target/configs.asciidoc[opts=optional]
|
|
@ -42,5 +42,6 @@ include::appendices/Legacy_DomainModel.adoc[]
|
||||||
include::appendices/LegacyBasicTypeResolution.adoc[]
|
include::appendices/LegacyBasicTypeResolution.adoc[]
|
||||||
include::appendices/Legacy_Native_Queries.adoc[]
|
include::appendices/Legacy_Native_Queries.adoc[]
|
||||||
|
|
||||||
|
include::ConfigPropertyList.adoc[]
|
||||||
include::Bibliography.adoc[]
|
include::Bibliography.adoc[]
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ dependencies {
|
||||||
implementation 'jakarta.json.bind:jakarta.json.bind-api:2.0.0'
|
implementation 'jakarta.json.bind:jakarta.json.bind-api:2.0.0'
|
||||||
implementation 'jakarta.json:jakarta.json-api:2.0.1'
|
implementation 'jakarta.json:jakarta.json-api:2.0.1'
|
||||||
implementation 'org.eclipse:yasson:2.0.4'
|
implementation 'org.eclipse:yasson:2.0.4'
|
||||||
|
implementation 'org.jsoup:jsoup:1.15.3'
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.compileJava {
|
tasks.compileJava {
|
||||||
|
@ -63,6 +64,10 @@ gradlePlugin {
|
||||||
id = 'org.hibernate.orm.build.env-project'
|
id = 'org.hibernate.orm.build.env-project'
|
||||||
implementationClass = 'org.hibernate.orm.env.EnvironmentProjectPlugin'
|
implementationClass = 'org.hibernate.orm.env.EnvironmentProjectPlugin'
|
||||||
}
|
}
|
||||||
|
configPropertiesCollectorPlugin {
|
||||||
|
id = 'org.hibernate.orm.build.properties'
|
||||||
|
implementationClass = 'org.hibernate.orm.properties.ConfigPropertyCollectorPlugin'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.properties;
|
||||||
|
|
||||||
|
import org.hibernate.orm.properties.processor.ConfigPropertyHolder;
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
|
||||||
|
public class ConfigPropertyCollectorPlugin implements Plugin<Project> {
|
||||||
|
public static final String TASK_GROUP_NAME = "hibernate-properties";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project project) {
|
||||||
|
final Task groupingTask = project.getTasks().maybeCreate( "generateHibernateConfigProperties" );
|
||||||
|
groupingTask.setGroup( TASK_GROUP_NAME );
|
||||||
|
|
||||||
|
ConfigPropertyHolder propertyHolder = new ConfigPropertyHolder();
|
||||||
|
final ConfigPropertyCollectorTask configPropertyCollectorTask = project.getTasks().create(
|
||||||
|
"generateConfigPropertiesMap",
|
||||||
|
ConfigPropertyCollectorTask.class,
|
||||||
|
propertyHolder
|
||||||
|
);
|
||||||
|
groupingTask.dependsOn( configPropertyCollectorTask );
|
||||||
|
|
||||||
|
final ConfigPropertyWriterTask configPropertyWriterTask = project.getTasks().create(
|
||||||
|
"writeConfigPropertiesMap",
|
||||||
|
ConfigPropertyWriterTask.class,
|
||||||
|
propertyHolder
|
||||||
|
);
|
||||||
|
groupingTask.dependsOn( configPropertyWriterTask );
|
||||||
|
configPropertyWriterTask.dependsOn( configPropertyCollectorTask );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.properties;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileVisitResult;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.SimpleFileVisitor;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.tools.JavaCompiler;
|
||||||
|
import javax.tools.JavaFileObject;
|
||||||
|
import javax.tools.StandardJavaFileManager;
|
||||||
|
import javax.tools.StandardLocation;
|
||||||
|
import javax.tools.ToolProvider;
|
||||||
|
|
||||||
|
import org.hibernate.orm.properties.processor.ConfigPropertyHolder;
|
||||||
|
import org.hibernate.orm.properties.processor.Configuration;
|
||||||
|
import org.hibernate.orm.properties.processor.ConfigurationPropertyProcessor;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.UnknownDomainObjectException;
|
||||||
|
import org.gradle.api.tasks.SourceSetContainer;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task that goes to the root project and then sends all the sources from children projects trough annotation processing
|
||||||
|
* collecting all the config properties into a map. See {@link ConfigurationPropertyProcessor}
|
||||||
|
*/
|
||||||
|
public class ConfigPropertyCollectorTask extends DefaultTask {
|
||||||
|
|
||||||
|
private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||||
|
private final Project project;
|
||||||
|
private final ConfigPropertyHolder properties;
|
||||||
|
private final Path target;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ConfigPropertyCollectorTask(ConfigPropertyHolder properties, Project project) {
|
||||||
|
this.project = project;
|
||||||
|
this.properties = properties;
|
||||||
|
this.target = project.getBuildDir().toPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void collectProperties() {
|
||||||
|
for ( Map.Entry<String, Project> projectEntry : project.getRootProject().getChildProjects().entrySet() ) {
|
||||||
|
try {
|
||||||
|
// we don't need to look at testing projects as these aren't for public configurations.
|
||||||
|
if ( projectEntry.getKey().contains( "test" ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SourceSetContainer sources = projectEntry.getValue().getExtensions().getByType(
|
||||||
|
SourceSetContainer.class );
|
||||||
|
|
||||||
|
sources.all( s -> {
|
||||||
|
// no need to compile/process test sources:
|
||||||
|
if ( !"test".equals( s.getName() ) ) {
|
||||||
|
compile(
|
||||||
|
projectEntry.getValue(),
|
||||||
|
s.getAllJava().getSourceDirectories().getFiles(),
|
||||||
|
s.getCompileClasspath().getFiles()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
catch (UnknownDomainObjectException e) {
|
||||||
|
getLogger().info( "Ignoring " + projectEntry.getKey() + " because of " + e.getMessage(), e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean compile(Project project, Collection<File> sources, Collection<File> classpath) {
|
||||||
|
List<File> classes = new ArrayList<>();
|
||||||
|
for ( File sourceFile : sources ) {
|
||||||
|
try {
|
||||||
|
// need to find all java files to be later converted to compilation units:
|
||||||
|
Files.walkFileTree( sourceFile.toPath(), new SimpleFileVisitor<>() {
|
||||||
|
@Override
|
||||||
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
|
if ( file.getFileName().toString().endsWith( ".java" ) ) {
|
||||||
|
classes.add( file.toFile() );
|
||||||
|
}
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
getLogger().debug( "Failed to process " + sourceFile.getAbsolutePath(), e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( classes.isEmpty() ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
|
||||||
|
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(
|
||||||
|
classes.stream().toArray( File[]::new ) );
|
||||||
|
|
||||||
|
try {
|
||||||
|
// we don't really need the compiled classes, so we just dump them somewhere to not mess with the rest of the
|
||||||
|
// classes:
|
||||||
|
fileManager.setLocation(
|
||||||
|
StandardLocation.CLASS_OUTPUT,
|
||||||
|
Arrays.asList(
|
||||||
|
Files.createDirectories( target.resolve( "config-property-collector-compiled-classes" ) )
|
||||||
|
.toFile() )
|
||||||
|
);
|
||||||
|
fileManager.setLocation(
|
||||||
|
StandardLocation.CLASS_PATH,
|
||||||
|
classpath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new RuntimeException( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> options = new ArrayList<>();
|
||||||
|
options.add(
|
||||||
|
String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"-A%s=%s",
|
||||||
|
Configuration.MODULE_TITLE,
|
||||||
|
project.getDescription()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
options.add(
|
||||||
|
String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"-A%s=%s",
|
||||||
|
Configuration.MODULE_LINK_ANCHOR,
|
||||||
|
project.getName() + "-"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// todo: this should come from some plugin/task config rather than be hardcoded:
|
||||||
|
options.add(
|
||||||
|
String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"-A%s=%s",
|
||||||
|
Configuration.JAVADOC_LINK,
|
||||||
|
"https://docs.jboss.org/hibernate/orm/6.2/javadocs/"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
JavaCompiler.CompilationTask task = compiler.getTask(
|
||||||
|
null,
|
||||||
|
fileManager,
|
||||||
|
null,
|
||||||
|
options,
|
||||||
|
null,
|
||||||
|
compilationUnits
|
||||||
|
);
|
||||||
|
|
||||||
|
task.setProcessors( Arrays.asList( new ConfigurationPropertyProcessor(
|
||||||
|
project.getBuildDir().toPath().resolve( "docs/javadoc" ),
|
||||||
|
properties
|
||||||
|
) ) );
|
||||||
|
|
||||||
|
return task.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.properties;
|
||||||
|
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.hibernate.orm.properties.processor.AsciiDocWriter;
|
||||||
|
import org.hibernate.orm.properties.processor.ConfigPropertyHolder;
|
||||||
|
import org.hibernate.orm.properties.processor.ConfigurationProperty;
|
||||||
|
import org.hibernate.orm.properties.processor.HibernateOrmConfiguration;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task that writes two asciidoc files from the collected config properties. One is for public configurations, another - for SPI.
|
||||||
|
*/
|
||||||
|
public class ConfigPropertyWriterTask extends DefaultTask {
|
||||||
|
|
||||||
|
private static final Predicate<Map.Entry<String, ConfigurationProperty>> API_FILTER = entry -> HibernateOrmConfiguration.Type.API.equals(
|
||||||
|
entry.getValue().type() );
|
||||||
|
private static final Predicate<Map.Entry<String, ConfigurationProperty>> SPI_FILTER = entry -> HibernateOrmConfiguration.Type.SPI.equals(
|
||||||
|
entry.getValue().type() );
|
||||||
|
|
||||||
|
private final Project project;
|
||||||
|
private final ConfigPropertyHolder properties;
|
||||||
|
private final String fileName = "configs";
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ConfigPropertyWriterTask(Project project, ConfigPropertyHolder properties) {
|
||||||
|
this.project = project;
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
public void writeProperties() {
|
||||||
|
if ( properties.hasProperties() ) {
|
||||||
|
if ( properties.hasProperties( API_FILTER ) ) {
|
||||||
|
writeProperties(
|
||||||
|
fileName + ".asciidoc",
|
||||||
|
new AsciiDocWriter(
|
||||||
|
API_FILTER
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( properties.hasProperties( SPI_FILTER ) ) {
|
||||||
|
writeProperties(
|
||||||
|
fileName + "-spi.asciidoc",
|
||||||
|
new AsciiDocWriter(
|
||||||
|
SPI_FILTER
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeProperties(String fileName, BiConsumer<Map<String, ConfigurationProperty>, Writer> transformer) {
|
||||||
|
try ( Writer writer = new FileWriter( project.getBuildDir().toPath().resolve( fileName ).toFile() )
|
||||||
|
) {
|
||||||
|
properties.write( transformer, writer );
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new RuntimeException( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.properties.processor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
|
import javax.lang.model.element.AnnotationValue;
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
|
|
||||||
|
|
||||||
|
public final class AnnotationUtils {
|
||||||
|
|
||||||
|
private AnnotationUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isIgnored(Element element) {
|
||||||
|
return findAnnotation( element, HibernateOrmConfiguration.class )
|
||||||
|
.flatMap( a -> a.attribute( "ignore", Boolean.class ) )
|
||||||
|
.orElse( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<AnnotationAttributeHolder> findAnnotation(Element element, Class<?> annotation) {
|
||||||
|
for ( AnnotationMirror mirror : element.getAnnotationMirrors() ) {
|
||||||
|
if ( mirror.getAnnotationType().toString().equals( annotation.getName() ) ) {
|
||||||
|
return Optional.of( new AnnotationAttributeHolder( mirror ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AnnotationAttributeHolder {
|
||||||
|
private final AnnotationMirror annotationMirror;
|
||||||
|
|
||||||
|
private AnnotationAttributeHolder(AnnotationMirror annotationMirror) {
|
||||||
|
this.annotationMirror = annotationMirror;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Optional<T> attribute(String name, Class<T> klass) {
|
||||||
|
return annotationMirror.getElementValues().entrySet().stream()
|
||||||
|
.filter( entry -> entry.getKey().getSimpleName().contentEquals( name ) )
|
||||||
|
.map( entry -> klass.cast( entry.getValue().getValue() ) )
|
||||||
|
.findAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Optional<List<T>> multiAttribute(String name, Class<T> klass) {
|
||||||
|
return annotationMirror.getElementValues().entrySet().stream()
|
||||||
|
.filter( entry -> entry.getKey().getSimpleName().contentEquals( name ) )
|
||||||
|
.map( entry -> entry.getValue().getValue() )
|
||||||
|
.map( obj -> ( (List<?>) List.class.cast( obj ) ) )
|
||||||
|
.map( list -> list.stream().map( AnnotationValue.class::cast ).map( AnnotationValue::getValue )
|
||||||
|
.map( klass::cast ).collect( Collectors.toList() ) )
|
||||||
|
.findAny();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.properties.processor;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class AsciiDocWriter implements BiConsumer<Map<String, ConfigurationProperty>, Writer> {
|
||||||
|
|
||||||
|
private final Predicate<Map.Entry<String, ConfigurationProperty>> filter;
|
||||||
|
|
||||||
|
public AsciiDocWriter(Predicate<Map.Entry<String, ConfigurationProperty>> filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Map<String, ConfigurationProperty> propertyMap, Writer writer) {
|
||||||
|
Map<String, Collection<ConfigurationProperty>> groups = propertyMap.entrySet().stream()
|
||||||
|
.filter( filter )
|
||||||
|
.map( Map.Entry::getValue )
|
||||||
|
.collect(
|
||||||
|
Collectors.groupingBy(
|
||||||
|
ConfigurationProperty::moduleName,
|
||||||
|
TreeMap::new,
|
||||||
|
Collectors.toCollection( TreeSet::new )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( groups.isEmpty() ) {
|
||||||
|
// nothing to write - return fast.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for ( Map.Entry<String, Collection<ConfigurationProperty>> entry : groups.entrySet() ) {
|
||||||
|
tryToWriteLine( writer, "[[configuration-properties-aggregated-", entry.getValue().iterator().next().anchorPrefix(), "]]" );
|
||||||
|
tryToWriteLine( writer, "=== ", entry.getKey() );
|
||||||
|
writer.write( '\n' );
|
||||||
|
for ( ConfigurationProperty el : entry.getValue() ) {
|
||||||
|
Iterator<String> keys = el.key().resolvedKeys().iterator();
|
||||||
|
String firstKey = keys.next();
|
||||||
|
writer.write( "[[" );
|
||||||
|
writer.write( "configuration-properties-aggregated-" );
|
||||||
|
writer.write( el.anchorPrefix() );
|
||||||
|
writer.write( firstKey.replaceAll( "[^\\w-.]", "_" ) );
|
||||||
|
writer.write( "]] " );
|
||||||
|
|
||||||
|
writer.write( '`' );
|
||||||
|
writer.write( firstKey );
|
||||||
|
writer.write( '`' );
|
||||||
|
writer.write( "::\n" );
|
||||||
|
|
||||||
|
// using inline passthrough for javadocs to not render HTML.
|
||||||
|
writer.write( "+++ " );
|
||||||
|
writer.write( el.javadoc() );
|
||||||
|
writer.write( " +++ " );
|
||||||
|
|
||||||
|
String defaultValue = Objects.toString( el.defaultValue(), "" );
|
||||||
|
if ( !defaultValue.trim().isEmpty() ) {
|
||||||
|
writer.write( "\n+\n" );
|
||||||
|
writer.write( "Default value: `" );
|
||||||
|
writer.write( defaultValue );
|
||||||
|
writer.write( '`' );
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write( '\n' );
|
||||||
|
|
||||||
|
printOtherKeyVariants( writer, keys );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer.write( '\n' );
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new RuntimeException( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printOtherKeyVariants(Writer writer, Iterator<String> keys) throws IOException {
|
||||||
|
boolean hasMultipleKeys = false;
|
||||||
|
if ( keys.hasNext() ) {
|
||||||
|
hasMultipleKeys = true;
|
||||||
|
writer.write( "+\n" );
|
||||||
|
writer.write( ".Variants of this configuration property (Click here):\n" );
|
||||||
|
writer.write( "[%collapsible]\n" );
|
||||||
|
writer.write( "====\n" );
|
||||||
|
}
|
||||||
|
while ( keys.hasNext() ) {
|
||||||
|
writer.write( '`' );
|
||||||
|
writer.write( keys.next() );
|
||||||
|
writer.write( '`' );
|
||||||
|
if ( keys.hasNext() ) {
|
||||||
|
writer.write( ", " );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hasMultipleKeys ) {
|
||||||
|
writer.write( "\n====\n" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryToWriteLine(Writer writer, String prefix, String value, String... other) {
|
||||||
|
try {
|
||||||
|
writer.write( prefix );
|
||||||
|
writer.write( value );
|
||||||
|
for ( String s : other ) {
|
||||||
|
writer.write( s );
|
||||||
|
}
|
||||||
|
writer.write( "\n" );
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new RuntimeException( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.properties.processor;
|
||||||
|
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public class ConfigPropertyHolder {
|
||||||
|
|
||||||
|
private final Map<String, ConfigurationProperty> properties = new TreeMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return properties.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(BiConsumer<Map<String, ConfigurationProperty>, Writer> transformer, Writer writer) {
|
||||||
|
transformer.accept( this.properties, writer );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String key, ConfigurationProperty property) {
|
||||||
|
properties.put( key, property );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasProperties() {
|
||||||
|
return !properties.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasProperties(Predicate<Map.Entry<String, ConfigurationProperty>> filter) {
|
||||||
|
return properties.entrySet().stream().anyMatch( filter );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.properties.processor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the config parameters that can be passed to this annotation processor via {@code -A.....}.
|
||||||
|
*/
|
||||||
|
public final class Configuration {
|
||||||
|
private Configuration() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String HIBERNATE_ORM_CPCAP_PREFIX = "org.hibernate.orm.cpcap.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use to define a base URL for Hibernate ORM Javadoc. As we are getting parts of Javadoc links in it should
|
||||||
|
* be adjusted to point to somewhere where the docs actually live.
|
||||||
|
*/
|
||||||
|
public static final String JAVADOC_LINK = HIBERNATE_ORM_CPCAP_PREFIX + "javadoc.link";
|
||||||
|
/**
|
||||||
|
* Use to define a pattern for classes to be ignored by this collector. We can have some {@code *Settings} classes
|
||||||
|
* in {@code impl} packages. And we don't need to collect properties from those.
|
||||||
|
*/
|
||||||
|
public static final String IGNORE_PATTERN = HIBERNATE_ORM_CPCAP_PREFIX + "ignore.pattern";
|
||||||
|
/**
|
||||||
|
* Use to define a pattern for property key values that should be ignored. By default, we will ignore keys that end
|
||||||
|
* with a dot {@code '.'}.
|
||||||
|
*/
|
||||||
|
public static final String IGNORE_KEY_VALUE_PATTERN = HIBERNATE_ORM_CPCAP_PREFIX + "ignore.key.value.pattern";
|
||||||
|
/**
|
||||||
|
* Used to group properties in sections and as a title of that section.
|
||||||
|
*/
|
||||||
|
public static final String MODULE_TITLE = HIBERNATE_ORM_CPCAP_PREFIX + "module.title";
|
||||||
|
/**
|
||||||
|
* Used to group properties in sections and as a title of that section.
|
||||||
|
*/
|
||||||
|
public static final String MODULE_LINK_ANCHOR = HIBERNATE_ORM_CPCAP_PREFIX + "module.link.anchor";
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.properties.processor;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ConfigurationProperty implements Comparable<ConfigurationProperty> {
|
||||||
|
|
||||||
|
private static final Comparator<ConfigurationProperty> CONFIGURATION_PROPERTY_COMPARATOR = Comparator.comparing(
|
||||||
|
c -> c.key().key );
|
||||||
|
private Key key;
|
||||||
|
private String javadoc;
|
||||||
|
private String sourceClass;
|
||||||
|
|
||||||
|
private HibernateOrmConfiguration.Type type;
|
||||||
|
|
||||||
|
private Object defaultValue;
|
||||||
|
|
||||||
|
private String anchorPrefix;
|
||||||
|
private String moduleName;
|
||||||
|
|
||||||
|
public Key key() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationProperty key(Key key) {
|
||||||
|
this.key = key;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String javadoc() {
|
||||||
|
return javadoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationProperty javadoc(String javadoc) {
|
||||||
|
this.javadoc = javadoc == null ? "" : javadoc;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String sourceClass() {
|
||||||
|
return sourceClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationProperty sourceClass(String sourceClass) {
|
||||||
|
this.sourceClass = sourceClass;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HibernateOrmConfiguration.Type type() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationProperty type(HibernateOrmConfiguration.Type type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object defaultValue() {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationProperty defaultValue(Object defaultValue) {
|
||||||
|
this.defaultValue = defaultValue == null ? "" : defaultValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String anchorPrefix() {
|
||||||
|
return anchorPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationProperty withAnchorPrefix(String anchorPrefix) {
|
||||||
|
this.anchorPrefix = anchorPrefix.replaceAll( "[^\\w-.]", "_" );
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String moduleName() {
|
||||||
|
return moduleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationProperty withModuleName(String moduleName) {
|
||||||
|
this.moduleName = moduleName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ConfigurationProperty{" +
|
||||||
|
"key='" + key + '\'' +
|
||||||
|
", javadoc='" + javadoc + '\'' +
|
||||||
|
", sourceClass='" + sourceClass + '\'' +
|
||||||
|
", type='" + type + '\'' +
|
||||||
|
", default='" + defaultValue + '\'' +
|
||||||
|
", anchorPrefix='" + anchorPrefix + '\'' +
|
||||||
|
", moduleName='" + moduleName + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(ConfigurationProperty o) {
|
||||||
|
return CONFIGURATION_PROPERTY_COMPARATOR.compare( this, o );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if ( this == o ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( o == null || getClass() != o.getClass() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ConfigurationProperty that = (ConfigurationProperty) o;
|
||||||
|
return Objects.equals( key, that.key ) &&
|
||||||
|
Objects.equals( javadoc, that.javadoc ) &&
|
||||||
|
Objects.equals( sourceClass, that.sourceClass ) &&
|
||||||
|
type == that.type &&
|
||||||
|
Objects.equals( defaultValue, that.defaultValue ) &&
|
||||||
|
Objects.equals( anchorPrefix, that.anchorPrefix ) &&
|
||||||
|
Objects.equals( moduleName, that.moduleName );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash( key, javadoc, sourceClass, type, defaultValue, anchorPrefix, moduleName );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Key {
|
||||||
|
private final List<String> prefixes;
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
public Key(List<String> prefixes, String key) {
|
||||||
|
this.key = key;
|
||||||
|
this.prefixes = prefixes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void overridePrefixes(String... prefixes) {
|
||||||
|
overridePrefixes( Arrays.asList( prefixes ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void overridePrefixes(List<String> prefixes) {
|
||||||
|
this.prefixes.clear();
|
||||||
|
this.prefixes.addAll( prefixes );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matches(Pattern pattern) {
|
||||||
|
return pattern.matcher( key ).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> resolvedKeys() {
|
||||||
|
if ( prefixes.isEmpty() ) {
|
||||||
|
return Collections.singletonList( key );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return prefixes.stream()
|
||||||
|
.map( p -> p + key )
|
||||||
|
.collect( Collectors.toList() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return toString( "/" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toString(String delimiter) {
|
||||||
|
if ( prefixes.isEmpty() ) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return prefixes.stream()
|
||||||
|
.map( p -> p + key )
|
||||||
|
.collect( Collectors.joining( delimiter ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,226 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.properties.processor;
|
||||||
|
|
||||||
|
import static org.hibernate.orm.properties.processor.AnnotationUtils.findAnnotation;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import javax.annotation.processing.Messager;
|
||||||
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.lang.model.element.ElementKind;
|
||||||
|
import javax.lang.model.element.Name;
|
||||||
|
import javax.lang.model.element.PackageElement;
|
||||||
|
import javax.lang.model.element.TypeElement;
|
||||||
|
import javax.lang.model.element.VariableElement;
|
||||||
|
import javax.lang.model.util.Elements;
|
||||||
|
import javax.tools.Diagnostic;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
|
||||||
|
public class ConfigurationPropertyCollector {
|
||||||
|
|
||||||
|
// assume that spi/impl/internal packages are not for public use and consider all of them as SPI:
|
||||||
|
private static final Pattern SPI_PATTERN = Pattern.compile(
|
||||||
|
"(.*\\.spi$)|(.*\\.spi\\..*)|(.*\\.impl$)|(.*\\.impl\\..*)|(.*\\.internal$)|(.*\\.internal\\..*)" );
|
||||||
|
|
||||||
|
private final Set<Name> processedTypes = new HashSet<>();
|
||||||
|
private final ConfigPropertyHolder properties;
|
||||||
|
private final Elements elementUtils;
|
||||||
|
private final String title;
|
||||||
|
private final String anchor;
|
||||||
|
private final Path javadocsLocation;
|
||||||
|
private final String javadocsBaseLink;
|
||||||
|
private final Pattern ignoreKeys;
|
||||||
|
private final Messager messager;
|
||||||
|
|
||||||
|
public ConfigurationPropertyCollector(ConfigPropertyHolder properties, ProcessingEnvironment processingEnvironment,
|
||||||
|
String title,
|
||||||
|
String anchor, Path javadocsLocation, Pattern ignoreKeys,
|
||||||
|
String javadocsBaseLink) {
|
||||||
|
this.properties = properties;
|
||||||
|
this.elementUtils = processingEnvironment.getElementUtils();
|
||||||
|
this.title = title;
|
||||||
|
this.anchor = anchor;
|
||||||
|
this.javadocsLocation = javadocsLocation;
|
||||||
|
this.javadocsBaseLink = javadocsBaseLink;
|
||||||
|
this.ignoreKeys = ignoreKeys;
|
||||||
|
this.messager = processingEnvironment.getMessager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitType(TypeElement element) {
|
||||||
|
Name qualifiedName = element.getQualifiedName();
|
||||||
|
if ( !processedTypes.contains( qualifiedName ) ) {
|
||||||
|
processedTypes.add( qualifiedName );
|
||||||
|
|
||||||
|
Optional<AnnotationUtils.AnnotationAttributeHolder> annotation = findAnnotation(
|
||||||
|
element, HibernateOrmConfiguration.class );
|
||||||
|
Optional<List<String>> classPrefix = annotation
|
||||||
|
.flatMap( a -> a.multiAttribute( "prefix", String.class ) );
|
||||||
|
Optional<String> title = annotation.flatMap( a -> a.attribute( "title", String.class ) );
|
||||||
|
Optional<String> anchorPrefix = annotation.flatMap( a -> a.attribute( "anchorPrefix", String.class ) );
|
||||||
|
|
||||||
|
for ( Element inner : elementUtils.getAllMembers( element ) ) {
|
||||||
|
if ( inner.getKind().equals( ElementKind.FIELD ) && inner instanceof VariableElement ) {
|
||||||
|
processConstant( ( (VariableElement) inner ), classPrefix, title, anchorPrefix );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processConstant(VariableElement constant, Optional<List<String>> classPrefix,
|
||||||
|
Optional<String> classTitle,
|
||||||
|
Optional<String> classAnchorPrefix) {
|
||||||
|
Optional<AnnotationUtils.AnnotationAttributeHolder> annotation = findAnnotation(
|
||||||
|
constant, HibernateOrmConfiguration.class );
|
||||||
|
if ( annotation.flatMap( a -> a.attribute( "ignore", Boolean.class ) ).orElse( false ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<String> title = annotation.flatMap( a -> a.attribute( "title", String.class ) );
|
||||||
|
Optional<String> anchorPrefix = annotation.flatMap( a -> a.attribute( "anchorPrefix", String.class ) );
|
||||||
|
|
||||||
|
ConfigurationProperty.Key key = extractKey(
|
||||||
|
constant,
|
||||||
|
classPrefix,
|
||||||
|
annotation.flatMap( a -> a.multiAttribute( "prefix", String.class ) )
|
||||||
|
);
|
||||||
|
if ( !key.matches( ignoreKeys ) ) {
|
||||||
|
// Try to find a default value. Assumption is that the settings class has an inner class called "Defaults" and
|
||||||
|
// the key for the default value is exactly the same as the config constant name:
|
||||||
|
Object value = findDefault( constant );
|
||||||
|
|
||||||
|
properties.put(
|
||||||
|
constant.getEnclosingElement().toString() + "#" + constant.getSimpleName().toString(),
|
||||||
|
new ConfigurationProperty()
|
||||||
|
.javadoc( extractJavadoc( constant ) )
|
||||||
|
.key( key )
|
||||||
|
.sourceClass( constant.getEnclosingElement().toString() )
|
||||||
|
.type( extractType( constant ) )
|
||||||
|
.defaultValue( value )
|
||||||
|
.withModuleName( title.orElse( classTitle.orElse( this.title ) ) )
|
||||||
|
.withAnchorPrefix( anchorPrefix.orElse( classAnchorPrefix.orElse( this.anchor ) ) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ConfigurationProperty.Key extractKey(VariableElement constant, Optional<List<String>> classPrefix,
|
||||||
|
Optional<List<String>> constantPrefix) {
|
||||||
|
List<String> prefix;
|
||||||
|
if ( constantPrefix.isPresent() ) {
|
||||||
|
prefix = constantPrefix.get();
|
||||||
|
}
|
||||||
|
else if ( classPrefix.isPresent() ) {
|
||||||
|
prefix = classPrefix.get();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
prefix = Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConfigurationProperty.Key(
|
||||||
|
prefix,
|
||||||
|
Objects.toString( constant.getConstantValue(), "NOT_FOUND#" + constant.getSimpleName() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HibernateOrmConfiguration.Type extractType(VariableElement constant) {
|
||||||
|
String packageName = packageElement( constant ).getQualifiedName().toString();
|
||||||
|
return SPI_PATTERN.matcher( packageName ).matches() ?
|
||||||
|
HibernateOrmConfiguration.Type.SPI :
|
||||||
|
HibernateOrmConfiguration.Type.API;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractJavadoc(VariableElement constant) {
|
||||||
|
try {
|
||||||
|
Element enclosingClass = constant.getEnclosingElement();
|
||||||
|
Path docs = javadocsLocation.resolve(
|
||||||
|
enclosingClass.toString().replace( ".", File.separator ) + ".html"
|
||||||
|
);
|
||||||
|
|
||||||
|
String packagePath = packageElement( enclosingClass ).getQualifiedName().toString().replace( ".", File.separator );
|
||||||
|
|
||||||
|
Document javadoc = Jsoup.parse( docs.toFile() );
|
||||||
|
|
||||||
|
org.jsoup.nodes.Element block = javadoc.selectFirst( "#" + constant.getSimpleName() + " + ul li.blockList");
|
||||||
|
if ( block != null ) {
|
||||||
|
for ( org.jsoup.nodes.Element link : block.getElementsByTag( "a" ) ) {
|
||||||
|
String href = link.attr( "href" );
|
||||||
|
// only update links if they are not external:
|
||||||
|
if ( !link.hasClass( "external-link" ) ) {
|
||||||
|
if ( href.startsWith( "#" ) ) {
|
||||||
|
href = enclosingClass.getSimpleName().toString() + ".html" + href;
|
||||||
|
}
|
||||||
|
href = javadocsBaseLink + packagePath + "/" + href;
|
||||||
|
}
|
||||||
|
else if ( href.contains( "/build/parents/" ) && href.contains( "/apidocs" ) ) {
|
||||||
|
// means a link was to a class from other module and javadoc plugin generated some external link
|
||||||
|
// that won't work. So we replace it:
|
||||||
|
href = javadocsBaseLink + href.substring( href.indexOf( "/apidocs" ) + "/apidocs".length() );
|
||||||
|
}
|
||||||
|
link.attr( "href", href );
|
||||||
|
}
|
||||||
|
|
||||||
|
org.jsoup.nodes.Element result = new org.jsoup.nodes.Element( "div" );
|
||||||
|
for ( org.jsoup.nodes.Element child : block.children() ) {
|
||||||
|
if ( "h4".equalsIgnoreCase( child.tagName() ) || "pre".equalsIgnoreCase( child.tagName() ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.appendChild( child );
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return elementUtils.getDocComment( constant );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
messager.printMessage( Diagnostic.Kind.NOTE, "Wasn't able to find rendered javadocs for " + constant + ". Trying to read plain javadoc comment." );
|
||||||
|
return elementUtils.getDocComment( constant );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This really works only for string/primitive constants ... other types would just get null returned.
|
||||||
|
*/
|
||||||
|
private Object findDefault(VariableElement constant) {
|
||||||
|
if ( constant.getEnclosingElement() instanceof TypeElement ) {
|
||||||
|
for ( Element element : elementUtils.getAllMembers( (TypeElement) constant.getEnclosingElement() ) ) {
|
||||||
|
if ( ElementKind.CLASS.equals( element.getKind() )
|
||||||
|
&& element.getSimpleName().contentEquals( "Defaults" ) ) {
|
||||||
|
for ( Element enclosedElement : element.getEnclosedElements() ) {
|
||||||
|
if ( enclosedElement.getSimpleName().equals( constant.getSimpleName() ) ) {
|
||||||
|
return ( (VariableElement) enclosedElement ).getConstantValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PackageElement packageElement(Element element) {
|
||||||
|
Element packageElement = element;
|
||||||
|
while ( !( packageElement instanceof PackageElement ) && packageElement.getEnclosingElement() != null ) {
|
||||||
|
packageElement = packageElement.getEnclosingElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return packageElement instanceof PackageElement ? (PackageElement) packageElement : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.properties.processor;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.hibernate.orm.properties.processor.AnnotationUtils.isIgnored;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import javax.annotation.processing.AbstractProcessor;
|
||||||
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
|
import javax.annotation.processing.RoundEnvironment;
|
||||||
|
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||||
|
import javax.annotation.processing.SupportedOptions;
|
||||||
|
import javax.annotation.processing.SupportedSourceVersion;
|
||||||
|
import javax.lang.model.SourceVersion;
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.lang.model.element.ElementKind;
|
||||||
|
import javax.lang.model.element.TypeElement;
|
||||||
|
|
||||||
|
|
||||||
|
@SupportedAnnotationTypes("*")
|
||||||
|
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||||
|
@SupportedOptions({
|
||||||
|
Configuration.JAVADOC_LINK,
|
||||||
|
Configuration.IGNORE_PATTERN,
|
||||||
|
Configuration.IGNORE_KEY_VALUE_PATTERN,
|
||||||
|
Configuration.MODULE_TITLE,
|
||||||
|
Configuration.MODULE_LINK_ANCHOR
|
||||||
|
})
|
||||||
|
public class ConfigurationPropertyProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
|
private ConfigurationPropertyCollector propertyCollector;
|
||||||
|
private Optional<Pattern> ignore;
|
||||||
|
private final Path javadocFolder;
|
||||||
|
private final ConfigPropertyHolder properties;
|
||||||
|
|
||||||
|
public ConfigurationPropertyProcessor(Path javadocFolder, ConfigPropertyHolder properties) {
|
||||||
|
this.javadocFolder = javadocFolder;
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void init(ProcessingEnvironment processingEnv) {
|
||||||
|
super.init( processingEnv );
|
||||||
|
|
||||||
|
String pattern = processingEnv.getOptions().get( Configuration.IGNORE_PATTERN );
|
||||||
|
this.ignore = Optional.ofNullable( pattern ).map( Pattern::compile );
|
||||||
|
String title = processingEnv.getOptions().getOrDefault( Configuration.MODULE_TITLE, "Unknown" );
|
||||||
|
String anchor = processingEnv.getOptions().getOrDefault( Configuration.MODULE_LINK_ANCHOR, "hibernate-orm-" );
|
||||||
|
|
||||||
|
String javadocsBaseLink = processingEnv.getOptions().getOrDefault( Configuration.JAVADOC_LINK, "" );
|
||||||
|
|
||||||
|
String keyPattern = processingEnv.getOptions().getOrDefault( Configuration.IGNORE_KEY_VALUE_PATTERN, ".*\\.$" );
|
||||||
|
Pattern ignoreKeys = Pattern.compile( keyPattern );
|
||||||
|
|
||||||
|
this.propertyCollector = new ConfigurationPropertyCollector(
|
||||||
|
properties,
|
||||||
|
processingEnv,
|
||||||
|
title,
|
||||||
|
anchor,
|
||||||
|
javadocFolder,
|
||||||
|
ignoreKeys,
|
||||||
|
javadocsBaseLink
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||||
|
Set<? extends Element> rootElements = roundEnv.getRootElements();
|
||||||
|
|
||||||
|
// first let's go through all root elements and see if we can find *Settings classes:
|
||||||
|
for ( Element element : rootElements ) {
|
||||||
|
if ( isSettingsClass( element ) ) {
|
||||||
|
process( propertyCollector, element );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// means we might have some inner classes that we also wanted to consider for config property processing
|
||||||
|
// so let's see if we need to process any:
|
||||||
|
for ( TypeElement annotation : annotations ) {
|
||||||
|
if ( annotation.getQualifiedName().contentEquals( HibernateOrmConfiguration.class.getName() ) ) {
|
||||||
|
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith( annotation );
|
||||||
|
for ( Element element : elements ) {
|
||||||
|
if ( isTypeElement( element ) ) {
|
||||||
|
process( propertyCollector, element );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( roundEnv.processingOver() ) {
|
||||||
|
beforeExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void beforeExit() {
|
||||||
|
// processor won't generate anything another gradle task would create an asciidoc file.
|
||||||
|
}
|
||||||
|
|
||||||
|
private void process(ConfigurationPropertyCollector propertyCollector, Element element) {
|
||||||
|
if ( !isIgnored( element ) && !ignore.map( p -> p.matcher( element.toString() ).matches() ).orElse(
|
||||||
|
Boolean.FALSE ) ) {
|
||||||
|
propertyCollector.visitType( (TypeElement) element );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSettingsClass(Element element) {
|
||||||
|
return ( element.getKind().equals( ElementKind.CLASS ) || element.getKind().equals( ElementKind.INTERFACE ) ) &&
|
||||||
|
element.getSimpleName().toString().endsWith( "Settings" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTypeElement(Element element) {
|
||||||
|
return element instanceof TypeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.properties.processor;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marker annotation to define overrides configured by annotation processor configuration.
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@Target({ ElementType.TYPE, ElementType.FIELD })
|
||||||
|
public @interface HibernateOrmConfiguration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes to which type the configuration property belongs to - API/SPI.
|
||||||
|
*/
|
||||||
|
enum Type {
|
||||||
|
/**
|
||||||
|
* Configuration property type API/SPI will be determined by inspecting the package in which a class is located.
|
||||||
|
* In case package contains {@code spi} package at any upper levels the type will be {@code SPI}, otherwise - {@code API}
|
||||||
|
*/
|
||||||
|
API,
|
||||||
|
SPI
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to {@code true} in case we have a {@code *Settings} class that we want to ignore in config processing.
|
||||||
|
* Also works on a field leve. Setting it to {@code true} on field level will not include that particular constant.
|
||||||
|
* Can be useful to skip prefix definitions etc.
|
||||||
|
*/
|
||||||
|
boolean ignore() default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides a prefix provided by annotation processor configuration. If set on class level - all constants from that class will
|
||||||
|
* use this prefix. If set on field level - that particular constant will use the configured prefix and will ignore the
|
||||||
|
* one set by annotation processor configuration or at class level.
|
||||||
|
*/
|
||||||
|
String[] prefix() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to group properties in sections and as a title of that grouped section.
|
||||||
|
*/
|
||||||
|
String title() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used as part of generated anchor links to provide uniqueness.
|
||||||
|
*/
|
||||||
|
String anchorPrefix() default "";
|
||||||
|
}
|
Loading…
Reference in New Issue