diff --git a/documentation/documentation.gradle b/documentation/documentation.gradle index 15ee582734..fadcfc9412 100644 --- a/documentation/documentation.gradle +++ b/documentation/documentation.gradle @@ -17,7 +17,6 @@ apply from: rootProject.file( 'gradle/releasable.gradle' ) apply plugin: 'org.hibernate.matrix-test' apply plugin: 'org.hibernate.orm.build.reports' -apply plugin: 'org.hibernate.orm.build.properties' tasks.build.dependsOn 'buildDocs' defaultTasks 'buildDocs' @@ -106,24 +105,6 @@ asciidoctorj { 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ task renderTopicalGuides(type: AsciidoctorTask, group: 'Documentation') {task-> @@ -178,7 +159,9 @@ task renderUserGuide(type: AsciidoctorTask, group: 'Documentation') {task-> tasks.buildDocs.dependsOn task tasks.buildDocsForPublishing.dependsOn task - dependsOn tasks.collectConfigProperties + dependsOn ':hibernate-core:collectConfigProperties' + dependsOn ':hibernate-envers:collectConfigProperties' + dependsOn ':hibernate-jcache:collectConfigProperties' sourceDir = file( 'src/main/asciidoc/userguide' ) sources { diff --git a/documentation/src/main/asciidoc/userguide/ConfigPropertyList.adoc b/documentation/src/main/asciidoc/userguide/ConfigPropertyList.adoc index 0cdee70fc7..34ccba7510 100644 --- a/documentation/src/main/asciidoc/userguide/ConfigPropertyList.adoc +++ b/documentation/src/main/asciidoc/userguide/ConfigPropertyList.adoc @@ -1,3 +1,5 @@ == List of all available configuration properties -include::../../../../target/configs.asciidoc[opts=optional] \ No newline at end of file +include::../../../../target/config-properties/hibernate-core.asciidoc[opts=optional] +include::../../../../target/config-properties/hibernate-envers.asciidoc[opts=optional] +include::../../../../target/config-properties/hibernate-jcache.asciidoc[opts=optional] \ No newline at end of file diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index 4156191218..e516027fb9 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -15,7 +15,7 @@ description = 'Hibernate\'s core ORM functionality' apply from: rootProject.file( 'gradle/published-java-module.gradle' ) apply plugin: 'org.hibernate.orm.antlr' apply plugin: 'org.hibernate.matrix-test' - +apply plugin: 'org.hibernate.orm.build.properties' configurations { tests { @@ -307,4 +307,12 @@ javadoc { overview = 'src/main/javadoc/overview.html' stylesheetFile = new File(projectDir, 'src/main/javadoc/stylesheet.css') } -} \ No newline at end of file +} + +task collectConfigProperties { task -> + description 'Collect config properties' + + tasks.generateConfigsProperties.javadocsBaseLink = 'https://docs.jboss.org/hibernate/orm/' + rootProject.ormVersion.family + '/javadocs/' + + dependsOn tasks.generateConfigsProperties +} diff --git a/hibernate-envers/hibernate-envers.gradle b/hibernate-envers/hibernate-envers.gradle index e09a6151a6..1d11e99954 100644 --- a/hibernate-envers/hibernate-envers.gradle +++ b/hibernate-envers/hibernate-envers.gradle @@ -9,6 +9,7 @@ description = 'Hibernate\'s entity version (audit/history) support' apply from: rootProject.file( 'gradle/published-java-module.gradle' ) apply plugin: 'org.hibernate.matrix-test' +apply plugin: 'org.hibernate.orm.build.properties' dependencies { api project( ':hibernate-core' ) @@ -69,3 +70,11 @@ tasks."matrix_mariadb" { println "Starting test: " + descriptor } } + +task collectConfigProperties { task -> + description 'Collect config properties' + + tasks.generateConfigsProperties.javadocsBaseLink = 'https://docs.jboss.org/hibernate/orm/' + rootProject.ormVersion.family + '/javadocs/' + + dependsOn tasks.generateConfigsProperties +} diff --git a/hibernate-jcache/hibernate-jcache.gradle b/hibernate-jcache/hibernate-jcache.gradle index 690c896b56..78862cfeeb 100644 --- a/hibernate-jcache/hibernate-jcache.gradle +++ b/hibernate-jcache/hibernate-jcache.gradle @@ -1,7 +1,7 @@ description = 'Integration for javax.cache into Hibernate as a second-level caching service' apply from: rootProject.file( 'gradle/published-java-module.gradle' ) - +apply plugin: 'org.hibernate.orm.build.properties' dependencies { api project( ':hibernate-core' ) @@ -15,3 +15,11 @@ dependencies { } } } + +task collectConfigProperties { task -> + description 'Collect config properties' + + tasks.generateConfigsProperties.javadocsBaseLink = 'https://docs.jboss.org/hibernate/orm/' + rootProject.ormVersion.family + '/javadocs/' + + dependsOn tasks.generateConfigsProperties +} diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/AsciiDocWriter.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/AsciiDocWriter.java new file mode 100644 index 0000000000..ff724e06b6 --- /dev/null +++ b/local-build-plugins/src/main/java/org/hibernate/orm/properties/AsciiDocWriter.java @@ -0,0 +1,71 @@ +/* + * 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.IOException; +import java.io.Writer; +import java.util.Set; +import java.util.function.BiConsumer; + +public class AsciiDocWriter implements BiConsumer, Writer> { + + private final String anchor; + private final String title; + + public AsciiDocWriter(String anchor, String title) { + this.anchor = anchor; + this.title = title; + } + + @Override + public void accept(Set properties, Writer writer) { + try { + tryToWriteLine( writer, "[[configuration-properties-aggregated-", anchor, "]]" ); + tryToWriteLine( writer, "=== ", title ); + writer.write( '\n' ); + for ( ConfigurationProperty el : properties ) { + String key = el.key(); + writer.write( "[[" ); + writer.write( "configuration-properties-aggregated-" ); + writer.write( el.anchorPrefix() ); + writer.write( key.replaceAll( "[^\\w-.]", "-" ) ); + writer.write( "]] " ); + + writer.write( '`' ); + writer.write( key ); + writer.write( '`' ); + writer.write( "::\n" ); + + // using inline passthrough for javadocs to not render HTML. + writer.write( "+++ " ); + writer.write( el.javadoc() ); + writer.write( " +++ " ); + + writer.write( '\n' ); + } + writer.write( '\n' ); + } + catch (IOException e) { + throw new RuntimeException( "Unable to create asciidoc output", e ); + } + } + + 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( "Unable to create asciidoc output", e ); + } + } +} diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyCollectorPlugin.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyCollectorPlugin.java index f596805bcb..2b019dd9ca 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyCollectorPlugin.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyCollectorPlugin.java @@ -6,8 +6,6 @@ */ 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; @@ -20,20 +18,15 @@ public class ConfigPropertyCollectorPlugin implements Plugin { 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 ); + Task javadoc = project.getTasks().maybeCreate( "javadoc" ); + groupingTask.dependsOn( javadoc ); - final ConfigPropertyWriterTask configPropertyWriterTask = project.getTasks().create( - "writeConfigPropertiesMap", - ConfigPropertyWriterTask.class, - propertyHolder + + final ConfigPropertyCollectorTask configPropertyCollectorTask = project.getTasks().create( + "generateConfigsProperties", + ConfigPropertyCollectorTask.class ); - groupingTask.dependsOn( configPropertyWriterTask ); - configPropertyWriterTask.dependsOn( configPropertyCollectorTask ); + configPropertyCollectorTask.dependsOn( javadoc ); + groupingTask.dependsOn( configPropertyCollectorTask ); } } diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyCollectorTask.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyCollectorTask.java index e7c3ed2f32..7e7d067456 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyCollectorTask.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyCollectorTask.java @@ -1,178 +1,68 @@ /* * 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 + * 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.FileWriter; import java.io.IOException; -import java.nio.file.FileVisitResult; +import java.io.Writer; 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.provider.Property; +import org.gradle.api.tasks.Internal; 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; + private final Path javadocsLocation; + private final Property javadocsBaseLink; + private final String anchor; + private final String moduleName; + private final ConfigPropertyHolder propertyHolder = new ConfigPropertyHolder(); + + private final Path output; + private final String fileName; + @Inject - public ConfigPropertyCollectorTask(ConfigPropertyHolder properties, Project project) { - this.project = project; - this.properties = properties; - this.target = project.getBuildDir().toPath(); + public ConfigPropertyCollectorTask(Project project) { + this.javadocsLocation = project.getBuildDir().toPath().resolve( "docs/javadoc" ); + this.javadocsBaseLink = project.getObjects().property( String.class ); + this.anchor = project.getName() + "-"; + this.moduleName = project.getDescription(); + this.output = project.getRootProject().project( ":documentation" ).getBuildDir().toPath() + .resolve( "config-properties" ); + this.fileName = project.getName() + ".asciidoc"; } + @Internal + public Property getJavadocsBaseLink() { + return javadocsBaseLink; + } @TaskAction - public void collectProperties() { - for ( Map.Entry 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 sources, Collection classpath) { - List 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 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 - ); + public void generateConfigProperties() { + new ConfigurationPropertyCollector( + propertyHolder, getLogger(), javadocsLocation, javadocsBaseLink.get(), anchor, moduleName + ).processClasses(); + try{ + Files.createDirectories( output ); } catch (IOException e) { - throw new RuntimeException( e ); + throw new RuntimeException( "Unable to prepare output directory structure", e ); + } + try ( Writer writer = new FileWriter( output.resolve( fileName ).toFile() ) ) { + propertyHolder.write( new AsciiDocWriter( anchor, moduleName ), writer ); + } + catch (IOException e) { + throw new RuntimeException( "Failed to produce asciidoc output for collected properties", e ); } - - List options = new ArrayList<>(); - options.add( "-proc:only" ); - 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(); } - - } diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/ConfigPropertyHolder.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyHolder.java similarity index 51% rename from local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/ConfigPropertyHolder.java rename to local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyHolder.java index 16a9685c17..53bab0d4e3 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/ConfigPropertyHolder.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyHolder.java @@ -4,36 +4,36 @@ * 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; +package org.hibernate.orm.properties; import java.io.Writer; -import java.util.Map; -import java.util.TreeMap; +import java.util.Set; +import java.util.TreeSet; import java.util.function.BiConsumer; import java.util.function.Predicate; public class ConfigPropertyHolder { - private final Map properties = new TreeMap<>(); + private final Set properties = new TreeSet<>(); public boolean isEmpty() { return properties.isEmpty(); } - public void write(BiConsumer, Writer> transformer, Writer writer) { + public void write(BiConsumer, Writer> transformer, Writer writer) { transformer.accept( this.properties, writer ); } - public void put(String key, ConfigurationProperty property) { - properties.put( key, property ); + public void add(ConfigurationProperty property) { + properties.add( property ); } public boolean hasProperties() { return !properties.isEmpty(); } - public boolean hasProperties(Predicate> filter) { - return properties.entrySet().stream().anyMatch( filter ); + public boolean hasProperties(Predicate filter) { + return properties.stream().anyMatch( filter ); } } diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyWriterTask.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyWriterTask.java deleted file mode 100644 index 1d3ba136a2..0000000000 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigPropertyWriterTask.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 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.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 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() ) { - writeProperties( - fileName + ".asciidoc", - new AsciiDocWriter() - ); - } - } - - private void writeProperties(String fileName, BiConsumer, Writer> transformer) { - try ( Writer writer = new FileWriter( project.getBuildDir().toPath().resolve( fileName ).toFile() ) - ) { - properties.write( transformer, writer ); - } - catch (IOException e) { - throw new RuntimeException( e ); - } - } - -} diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/ConfigurationProperty.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigurationProperty.java similarity index 75% rename from local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/ConfigurationProperty.java rename to local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigurationProperty.java index 86aa8d7a82..ba18ba8c49 100644 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/ConfigurationProperty.java +++ b/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigurationProperty.java @@ -4,29 +4,26 @@ * 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; +package org.hibernate.orm.properties; -import java.util.Collections; import java.util.Comparator; -import java.util.List; import java.util.Objects; -import java.util.regex.Pattern; public class ConfigurationProperty implements Comparable { private static final Comparator CONFIGURATION_PROPERTY_COMPARATOR = Comparator.comparing( - c -> c.key().key ); - private Key key; + ConfigurationProperty::key ); + private String key; private String javadoc; private String sourceClass; private String anchorPrefix; private String moduleName; - public Key key() { + public String key() { return key; } - public ConfigurationProperty key(Key key) { + public ConfigurationProperty key(String key) { this.key = key; return this; } @@ -53,7 +50,7 @@ public class ConfigurationProperty implements Comparable return anchorPrefix; } - public ConfigurationProperty withAnchorPrefix(String anchorPrefix) { + public ConfigurationProperty anchorPrefix(String anchorPrefix) { this.anchorPrefix = anchorPrefix.replaceAll( "[^\\w-.]", "_" ); return this; } @@ -62,7 +59,7 @@ public class ConfigurationProperty implements Comparable return moduleName; } - public ConfigurationProperty withModuleName(String moduleName) { + public ConfigurationProperty moduleName(String moduleName) { this.moduleName = moduleName; return this; } @@ -103,25 +100,4 @@ public class ConfigurationProperty implements Comparable public int hashCode() { return Objects.hash( key, javadoc, sourceClass, anchorPrefix, moduleName ); } - - public static class Key { - private final String key; - - public Key(String key) { - this.key = key; - } - - public boolean matches(Pattern pattern) { - return pattern.matcher( key ).matches(); - } - - public List resolvedKeys() { - return Collections.singletonList( key ); - } - - @Override - public String toString() { - return key; - } - } } diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigurationPropertyCollector.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigurationPropertyCollector.java new file mode 100644 index 0000000000..8c27e72898 --- /dev/null +++ b/local-build-plugins/src/main/java/org/hibernate/orm/properties/ConfigurationPropertyCollector.java @@ -0,0 +1,150 @@ +/* + * 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.Path; +import java.util.Optional; + +import org.gradle.api.logging.Logger; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; + +public class ConfigurationPropertyCollector { + + private final ConfigPropertyHolder propertyHolder; + private final Logger logger; + + // configs: + private final Path javadocsLocation; + private final String javadocsBaseLink; + private final String anchor; + private final String moduleName; + + public ConfigurationPropertyCollector(ConfigPropertyHolder propertyHolder, Logger logger, Path javadocsLocation, + String javadocsBaseLink, String anchor, String moduleName) { + this.propertyHolder = propertyHolder; + this.logger = logger; + this.javadocsLocation = javadocsLocation; + this.javadocsBaseLink = javadocsBaseLink; + this.anchor = anchor; + this.moduleName = moduleName; + + } + + public void processClasses() { + processClasses( locateConstants() ); + } + + private void processClasses(Document constants) { + for ( Element table : constants.select( "table.constantsSummary" ) ) { + String className = table.selectFirst( "caption" ).text(); + if ( className.endsWith( "Settings" ) && !className.contains( ".impl." ) && !className.contains( ".internal." ) ) { + // assume that such class is a config class and we want to collect properties from it. + Optional javadoc = obtainJavadoc( className ); + javadoc.ifPresent( doc -> { + // go through constants: + for ( Element row : table.select( "tr" ) ) { + if ( row.hasClass( "altColor" ) || row.hasClass( "rowColor" ) ) { + propertyHolder.add( + new ConfigurationProperty() + .key( stripQuotes( row.selectFirst( ".colLast" ).text() ) ) + .javadoc( + extractJavadoc( + doc, + className, + withoutPackagePrefix( row.selectFirst( ".colFirst a" ).id() ) + ) + ) + .sourceClass( className ) + .anchorPrefix( anchor ) + .moduleName( moduleName ) + ); + } + } + } ); + } + } + } + + private String stripQuotes(String value) { + if ( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) { + return value.substring( 1, value.length() - 1 ); + } + return value; + } + + private String extractJavadoc(Document javadoc, String className, String constant) { + org.jsoup.nodes.Element block = javadoc.selectFirst( "#" + constant + " + 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 = withoutPackagePrefix( className ) + ".html" + href; + } + String packagePath = packagePrefix(className).replace( ".", File.separator ); + 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(); + } + return ""; + } + + private String withoutPackagePrefix(String className) { + return className.substring( className.lastIndexOf( '.' ) + 1 ); + } + + private String packagePrefix(String className) { + return className.substring( 0, className.lastIndexOf( '.' ) ); + } + + private Optional obtainJavadoc(String enclosingClass) { + try { + Path docs = javadocsLocation.resolve( + enclosingClass.replace( ".", File.separator ) + ".html" + ); + + return Optional.of( Jsoup.parse( docs.toFile() ) ); + } + catch (IOException e) { + logger.error( "Unable to access javadocs for " + enclosingClass, e ); + } + return Optional.empty(); + } + + private Document locateConstants() { + try { + Path docs = javadocsLocation.resolve( "constant-values.html" ); + + return Jsoup.parse( docs.toFile() ); + } + catch (IOException e) { + logger.error( "Unable to access javadocs `constant-values.html`", e ); + throw new IllegalStateException( e ); + } + } +} diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/AsciiDocWriter.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/AsciiDocWriter.java deleted file mode 100644 index f153271cd6..0000000000 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/AsciiDocWriter.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.TreeMap; -import java.util.TreeSet; -import java.util.function.BiConsumer; -import java.util.stream.Collectors; - -public class AsciiDocWriter implements BiConsumer, Writer> { - - @Override - public void accept(Map propertyMap, Writer writer) { - Map> groups = propertyMap.entrySet().stream() - .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> 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 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( " +++ " ); - - writer.write( '\n' ); - - printOtherKeyVariants( writer, keys ); - } - } - writer.write( '\n' ); - } - catch (IOException e) { - throw new RuntimeException( e ); - } - } - - private void printOtherKeyVariants(Writer writer, Iterator 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 ); - } - } -} diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/Configuration.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/Configuration.java deleted file mode 100644 index 6e634a89b3..0000000000 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/Configuration.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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"; -} diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/ConfigurationPropertyCollector.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/ConfigurationPropertyCollector.java deleted file mode 100644 index 3ddcaf498b..0000000000 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/ConfigurationPropertyCollector.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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.File; -import java.io.IOException; -import java.nio.file.Path; -import java.util.HashSet; -import java.util.Objects; -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 { - - private final Set 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 ); - - for ( Element inner : elementUtils.getAllMembers( element ) ) { - if ( inner.getKind().equals( ElementKind.FIELD ) && inner instanceof VariableElement ) { - processConstant( ( (VariableElement) inner ) ); - } - } - } - } - - private void processConstant(VariableElement constant) { - ConfigurationProperty.Key key = extractKey( constant ); - if ( !key.matches( ignoreKeys ) ) { - properties.put( - constant.getEnclosingElement().toString() + "#" + constant.getSimpleName().toString(), - new ConfigurationProperty() - .javadoc( extractJavadoc( constant ) ) - .key( key ) - .sourceClass( constant.getEnclosingElement().toString() ) - .withModuleName( this.title ) - .withAnchorPrefix( this.anchor ) - ); - } - } - - - private ConfigurationProperty.Key extractKey(VariableElement constant) { - return new ConfigurationProperty.Key( - Objects.toString( constant.getConstantValue(), "NOT_FOUND#" + constant.getSimpleName() ) - ); - } - - 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 ); - } - } - - 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; - } - -} diff --git a/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/ConfigurationPropertyProcessor.java b/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/ConfigurationPropertyProcessor.java deleted file mode 100644 index 447640d39b..0000000000 --- a/local-build-plugins/src/main/java/org/hibernate/orm/properties/processor/ConfigurationPropertyProcessor.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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.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 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 annotations, RoundEnvironment roundEnv) { - Set 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 ); - } - } - - 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 ( !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" ); - } -}