[jOOQ/jOOQ#9728] Add <comments/> configuration to allow for generating

custom Javadoc on generated code

This includes:

- [jOOQ/jOOQ#11189] Improve generated Javadoc formatting
This commit is contained in:
Lukas Eder 2021-01-05 16:26:59 +01:00
parent d9fd96e30b
commit 1f9f3dc269
10 changed files with 224 additions and 45 deletions

View File

@ -566,6 +566,7 @@ public class GenerationTool {
database.setConfiguredEnumTypes(d.getEnumTypes());
database.setConfiguredForcedTypes(d.getForcedTypes());
database.setConfiguredEmbeddables(d.getEmbeddables());
database.setConfiguredComments(d.getComments());
database.setConfiguredSyntheticObjects(d.getSyntheticObjects());
database.setEmbeddablePrimaryKeys(d.getEmbeddablePrimaryKeys());
database.setEmbeddableUniqueKeys(d.getEmbeddableUniqueKeys());

View File

@ -84,6 +84,7 @@ public abstract class GeneratorWriter<W extends GeneratorWriter<W>> {
private String tabString = " ";
private String newlineString = "\n";
private boolean newline = true;
private boolean blockComment = false;
protected GeneratorWriter(File file) {
this(file, null, null);
@ -157,23 +158,38 @@ public abstract class GeneratorWriter<W extends GeneratorWriter<W>> {
// the character is interpreted erroneously as being semantic.
if (string.startsWith("}") || string.startsWith("]") || string.startsWith(")"))
indentTabsAllLines--;
else if (string.endsWith("*/"))
blockComment = false;
if (indentTabsAllLines < 0 && !Boolean.getBoolean("mute-indentation-error"))
new IllegalStateException("A formatting error has been produced by https://github.com/jOOQ/jOOQ/issues/10196").printStackTrace(System.err);
int indentTabsThisLine0 = indentTabsThisLine;
if (newline && indentTabsThisLine + indentTabsAllLines > 0) {
for (int i = 0; i < indentTabsThisLine + indentTabsAllLines; i++)
sb.append(tabString);
StringBuilder indent = new StringBuilder();
if (newline) {
newline = false;
indentTabsThisLine = 0;
if (indentTabsThisLine + indentTabsAllLines > 0) {
for (int i = 0; i < indentTabsThisLine + indentTabsAllLines; i++)
indent.append(tabString);
indentTabsThisLine = 0;
}
if (blockComment)
indent.append(" * ");
sb.append(indent);
indent.insert(0, newlineString);
}
if (string.endsWith("{") || string.endsWith("[") || string.endsWith("("))
indentTabsAllLines++;
else if (string.startsWith("if") || string.startsWith("else") || string.startsWith("for") || string.startsWith("while"))
indentTabsThisLine = indentTabsThisLine0 + 1;
else if (string.startsWith("/*"))
blockComment = true;
if (args.length > 0) {
List<Object> originals = Arrays.asList(args);
@ -185,9 +201,8 @@ public abstract class GeneratorWriter<W extends GeneratorWriter<W>> {
translated.add(ref((Class<?>) arg));
}
else if (arg instanceof Object[] || arg instanceof Collection) {
if (arg instanceof Collection) {
if (arg instanceof Collection)
arg = ((Collection<?>) arg).toArray();
}
int start = string.indexOf("[[");
int end = string.indexOf("]]");
@ -213,30 +228,26 @@ public abstract class GeneratorWriter<W extends GeneratorWriter<W>> {
separator = gSeparator;
}
if (((Object[]) arg).length > 0) {
if (((Object[]) arg).length > 0)
replacement.append(gAfter);
}
string = string.substring(0, start) + replacement + string.substring(end + 2);
}
else {
else
translated.add(arg);
}
}
if (!string.contains("[[")) {
if (!string.contains("[["))
break;
}
originals = translated;
translated = new ArrayList<>();
}
sb.append(String.format(string, translated.toArray()));
}
else {
sb.append(string);
sb.append(String.format(string, translated.toArray()).replace(newlineString, indent));
}
else
sb.append(string.replace(newlineString, indent));
return (W) this;
}

View File

@ -6885,20 +6885,21 @@ public class JavaGenerator extends AbstractGenerator {
out.println();
if (scala) {
out.println("%soverride def get%ss: %s[%s%s] = {", visibilityPublic(), type.getSimpleName(), List.class, type, generic);
if (definitions.size() > maxMembersPerInitialiser()) {
out.println("%soverride def get%ss: %s[%s%s] = {", visibilityPublic(), type.getSimpleName(), List.class, type, generic);
out.println("val result = new %s[%s%s]", ArrayList.class, type, generic);
for (int i = 0; i < definitions.size(); i += maxMembersPerInitialiser())
out.println("result.addAll(get%ss%s)", type.getSimpleName(), i / maxMembersPerInitialiser());
out.println("result");
out.println("}");
}
else {
out.println("%soverride def get%ss: %s[%s%s] = %s.asList[%s%s](", visibilityPublic(), type.getSimpleName(), List.class, type, generic, Arrays.class, type, generic);
out.println("[[separator=,\n][%s]]", references);
out.println(")");
}
else
out.println("return %s.asList[%s%s]([[before=\n\t\t\t][separator=,\n\t\t\t][%s]])", Arrays.class, type, generic, references);
out.println("}");
}
else if (kotlin) {
if (definitions.size() > maxMembersPerInitialiser()) {
@ -6913,7 +6914,7 @@ public class JavaGenerator extends AbstractGenerator {
}
else {
out.println("%soverride fun get%ss(): %s<%s%s> = listOf(", visibilityPublic(), type.getSimpleName(), out.ref(KLIST), type, generic);
out.println("[[separator=,\n\t\t][%s]]", references);
out.println("[[separator=,\n][%s]]", references);
out.println(")");
}
}
@ -6931,7 +6932,9 @@ public class JavaGenerator extends AbstractGenerator {
out.println("return result;");
}
else {
out.println("return %s.<%s%s>asList([[before=\n\t\t\t][separator=,\n\t\t\t][%s]]);", Arrays.class, type, generic, references);
out.println("return %s.<%s%s>asList(", Arrays.class, type, generic);
out.println("[[separator=,\n][%s]]", references);
out.println(");");
}
out.println("}");
@ -6943,17 +6946,19 @@ public class JavaGenerator extends AbstractGenerator {
if (scala) {
out.println("private def get%ss%s(): %s[%s%s] = %s.asList[%s%s](", type.getSimpleName(), i / maxMembersPerInitialiser(), List.class, type, generic, Arrays.class, type, generic);
out.println("[[before=\n\t\t\t][separator=,\n\t\t\t][%s]]", references.subList(i, Math.min(i + maxMembersPerInitialiser(), references.size())));
out.println("[[separator=,\n][%s]]", references.subList(i, Math.min(i + maxMembersPerInitialiser(), references.size())));
out.println(")");
}
else if (kotlin) {
out.println("private fun get%ss%s(): %s<%s%s> = listOf(", type.getSimpleName(), i / maxMembersPerInitialiser(), out.ref(KLIST), type, generic);
out.println("[[before=\t][separator=,\n\t\t\t][%s]]", references.subList(i, Math.min(i + maxMembersPerInitialiser(), references.size())));
out.println("[[separator=,\n][%s]]", references.subList(i, Math.min(i + maxMembersPerInitialiser(), references.size())));
out.println(")");
}
else {
out.println("private final %s<%s%s> get%ss%s() {", List.class, type, generic, type.getSimpleName(), i / maxMembersPerInitialiser());
out.println("return %s.<%s%s>asList([[before=\n\t\t\t][separator=,\n\t\t\t][%s]]);", Arrays.class, type, generic, references.subList(i, Math.min(i + maxMembersPerInitialiser(), references.size())));
out.println("return %s.<%s%s>asList(", Arrays.class, type, generic);
out.println("[[separator=,\n][%s]]", references.subList(i, Math.min(i + maxMembersPerInitialiser(), references.size())));
out.println(");");
out.println("}");
}
}
@ -8048,9 +8053,9 @@ public class JavaGenerator extends AbstractGenerator {
out.println("/**");
if (comment != null && comment.length() > 0)
printJavadocParagraph(out, comment, "");
out.println(JavaWriter.escapeJavadoc(comment));
else
out.println(" * This class is generated by jOOQ.");
out.println("This class is generated by jOOQ.");
out.println(" */");
}
@ -8199,16 +8204,6 @@ public class JavaGenerator extends AbstractGenerator {
return result;
}
/**
* This method is used to add line breaks in lengthy javadocs
*/
protected void printJavadocParagraph(JavaWriter out, String comment, String indent) {
// [#3450] [#4880] [#7693] Must not print */ inside Javadoc
String escaped = JavaWriter.escapeJavadoc(comment);
printParagraph(out, escaped, indent + " * ");
}
protected void printParagraph(GeneratorWriter<?> out, String comment, String indent) {
boolean newLine = true;
int lineLength = 0;
@ -8266,7 +8261,7 @@ public class JavaGenerator extends AbstractGenerator {
if (!StringUtils.isBlank(header)) {
out.println("/*");
printJavadocParagraph(out, header, "");
out.println(JavaWriter.escapeJavadoc(header));
out.println(" */");
}
}
@ -8276,7 +8271,7 @@ public class JavaGenerator extends AbstractGenerator {
if (!StringUtils.isBlank(header)) {
out.println("/*");
printJavadocParagraph(out, header, "");
out.println(JavaWriter.escapeJavadoc(header));
out.println(" */");
}
}

View File

@ -101,7 +101,7 @@ public class JavaWriter extends GeneratorWriter<JavaWriter> {
escapedArgs[i] = escapeJavadoc(escapedArgs[i]);
println("/**");
println(" * " + escaped, escapedArgs);
println(escaped, escapedArgs);
println(" */");
}

View File

@ -102,6 +102,7 @@ import org.jooq.impl.DefaultExecuteListenerProvider;
import org.jooq.impl.F;
import org.jooq.impl.SQLDataType;
import org.jooq.meta.jaxb.CatalogMappingType;
import org.jooq.meta.jaxb.CommentType;
import org.jooq.meta.jaxb.CustomType;
import org.jooq.meta.jaxb.EmbeddableDefinitionType;
import org.jooq.meta.jaxb.EmbeddableField;
@ -190,6 +191,8 @@ public abstract class AbstractDatabase implements Database {
private Set<ForcedType> unusedForcedTypes = new HashSet<>();
private List<EmbeddableDefinitionType> configuredEmbeddables = new ArrayList<>();
private Set<EmbeddableDefinitionType> unusedEmbeddables = new HashSet<>();
private List<CommentType> configuredComments = new ArrayList<>();
private Set<CommentType> unusedComments = new HashSet<>();
private List<SyntheticIdentityType> configuredSyntheticIdentities = new ArrayList<>();
private Set<SyntheticIdentityType> unusedSyntheticIdentities = new HashSet<>();
private List<SyntheticPrimaryKeyType> configuredSyntheticPrimaryKeys = new ArrayList<>();
@ -2855,6 +2858,41 @@ public abstract class AbstractDatabase implements Database {
return fetched.size() + " (" + included.size() + " included, " + (fetched.size() - included.size()) + " excluded)";
}
@SuppressWarnings("unused")
@Override
public void setConfiguredComments(List<CommentType> configuredComments) {
if (configuredComments != null) {
getConfiguredComments().addAll(configuredComments);
unusedComments.addAll(configuredComments);
if (!configuredComments.isEmpty())
log.info("Commercial feature", "Comments are a commercial only feature. Please upgrade to the jOOQ Professional Edition");
}
}
@Override
public List<CommentType> getConfiguredComments() {
if (configuredComments == null)
configuredComments = new ArrayList<>();
return configuredComments;
}
@Override
public void markUsed(CommentType object) {
unusedComments.remove(object);
}
@Override
public List<CommentType> getUnusedComments() {
return new ArrayList<>(unusedComments);
}
@SuppressWarnings("unused")
@Override
public void setConfiguredSyntheticObjects(SyntheticObjectsType configuredSyntheticObjects) {

View File

@ -38,6 +38,9 @@
package org.jooq.meta;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static java.util.Arrays.asList;
import static org.jooq.impl.DSL.name;
import java.sql.Connection;
@ -47,6 +50,7 @@ import java.util.List;
import org.jooq.DSLContext;
import org.jooq.Name;
import org.jooq.SQLDialect;
import org.jooq.meta.jaxb.CommentType;
import org.jooq.tools.StringUtils;
/**
@ -60,7 +64,10 @@ public abstract class AbstractDefinition implements Definition {
private final SchemaDefinition schema;
private final PackageDefinition pkg;
private final String name;
private final String comment;
private final String schemaComment;
private final String overload;
private final String source;
@ -96,9 +103,9 @@ public abstract class AbstractDefinition implements Definition {
: schema;
this.pkg = pkg;
this.name = name;
this.comment = comment;
this.overload = overload;
this.source = source;
this.schemaComment = comment;
}
@Override
@ -155,7 +162,28 @@ public abstract class AbstractDefinition implements Definition {
@Override
public final String getComment() {
return comment;
return schemaComment;
}
@Override

View File

@ -49,6 +49,7 @@ import org.jooq.SQLDialect;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.meta.jaxb.CatalogMappingType;
import org.jooq.meta.jaxb.CommentType;
import org.jooq.meta.jaxb.CustomType;
import org.jooq.meta.jaxb.EmbeddableDefinitionType;
import org.jooq.meta.jaxb.EnumType;
@ -1026,6 +1027,26 @@ public interface Database extends AutoCloseable {
*/
void setEmbeddableDomains(String embeddableDomains);
/**
* Configure the comments.
*/
void setConfiguredComments(List<CommentType> configuredComments);
/**
* Get the configured comments.
*/
List<CommentType> getConfiguredComments();
/**
* Mark a comment as used.
*/
void markUsed(CommentType comment);
/**
* Retrieve the not-yet used comments.
*/
List<CommentType> getUnusedComments();
/**
* Configure the synthetic objects.
*/

View File

@ -156,6 +156,9 @@ public class Database implements Serializable, XMLAppendable
@XmlElementWrapper(name = "properties")
@XmlElement(name = "property")
protected List<Property> properties;
@XmlElementWrapper(name = "comments")
@XmlElement(name = "comment")
protected List<CommentType> comments;
@XmlElementWrapper(name = "catalogs")
@XmlElement(name = "catalog")
protected List<CatalogMappingType> catalogs;
@ -1603,6 +1606,17 @@ public class Database implements Serializable, XMLAppendable
this.properties = properties;
}
public List<CommentType> getComments() {
if (comments == null) {
comments = new ArrayList<CommentType>();
}
return comments;
}
public void setComments(List<CommentType> comments) {
this.comments = comments;
}
public List<CatalogMappingType> getCatalogs() {
if (catalogs == null) {
catalogs = new ArrayList<CatalogMappingType>();
@ -2238,6 +2252,27 @@ public class Database implements Serializable, XMLAppendable
return this;
}
public Database withComments(CommentType... values) {
if (values!= null) {
for (CommentType value: values) {
getComments().add(value);
}
}
return this;
}
public Database withComments(Collection<CommentType> values) {
if (values!= null) {
getComments().addAll(values);
}
return this;
}
public Database withComments(List<CommentType> comments) {
setComments(comments);
return this;
}
public Database withCatalogs(CatalogMappingType... values) {
if (values!= null) {
for (CatalogMappingType value: values) {
@ -2427,6 +2462,7 @@ public class Database implements Serializable, XMLAppendable
builder.append("logSlowQueriesAfterSeconds", logSlowQueriesAfterSeconds);
builder.append("logSlowResultsAfterSeconds", logSlowResultsAfterSeconds);
builder.append("properties", "property", properties);
builder.append("comments", "comment", comments);
builder.append("catalogs", "catalog", catalogs);
builder.append("schemata", "schema", schemata);
builder.append("embeddables", "embeddable", embeddables);
@ -2949,6 +2985,15 @@ public class Database implements Serializable, XMLAppendable
return false;
}
}
if (comments == null) {
if (other.comments!= null) {
return false;
}
} else {
if (!comments.equals(other.comments)) {
return false;
}
}
if (catalogs == null) {
if (other.catalogs!= null) {
return false;
@ -3065,6 +3110,7 @@ public class Database implements Serializable, XMLAppendable
result = ((prime*result)+((logSlowQueriesAfterSeconds == null)? 0 :logSlowQueriesAfterSeconds.hashCode()));
result = ((prime*result)+((logSlowResultsAfterSeconds == null)? 0 :logSlowResultsAfterSeconds.hashCode()));
result = ((prime*result)+((properties == null)? 0 :properties.hashCode()));
result = ((prime*result)+((comments == null)? 0 :comments.hashCode()));
result = ((prime*result)+((catalogs == null)? 0 :catalogs.hashCode()));
result = ((prime*result)+((schemata == null)? 0 :schemata.hashCode()));
result = ((prime*result)+((embeddables == null)? 0 :embeddables.hashCode()));

View File

@ -157,6 +157,14 @@ public class ObjectFactory {
return new Database();
}
/**
* Create an instance of {@link CommentType }
*
*/
public CommentType createCommentType() {
return new CommentType();
}
/**
* Create an instance of {@link SyntheticObjectsType }
*

View File

@ -678,6 +678,10 @@ See {@link org.jooq.UpdatableRecord#store()} and {@link org.jooq.UpdatableRecord
This is a Java regular expression. Use the pipe to separate several expressions.
See {@link org.jooq.UpdatableRecord#store()} and {@link org.jooq.UpdatableRecord#delete()} for details about optimistic locking.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="comments" type="tns:CommentsType" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The documentation configuration.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="syntheticObjects" type="tns:SyntheticObjectsType" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The synthetic objects configuration.]]></jxb:javadoc></jxb:property></appinfo></annotation>
@ -923,6 +927,33 @@ for Oracle.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</all>
</complexType>
<complexType name="CommentsType">
<sequence>
<element name="comment" type="tns:CommentType" minOccurs="1" maxOccurs="unbounded"/>
</sequence>
</complexType>
<complexType name="CommentType">
<annotation><appinfo><jxb:class><jxb:javadoc><![CDATA[The documentation configuration.]]></jxb:javadoc></jxb:class></appinfo></annotation>
<all>
<element name="expression" type="string" minOccurs="1" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[A regular expression matching all objects that should be commented.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="message" type="string" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[A comment that should be added to objects matched by this configuration.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="deprecated" type="boolean" minOccurs="0" maxOccurs="1" default="false">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether the comment is a deprecation notice.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="includeSchemaComment" type="boolean" minOccurs="0" maxOccurs="1" default="true">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether the schema comment (if available) should be included and prepended to the message.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
</all>
</complexType>
<complexType name="SyntheticObjectsType">
<annotation><appinfo><jxb:class><jxb:javadoc><![CDATA[Synthetic objects configuration.]]></jxb:javadoc></jxb:class></appinfo></annotation>
<all>