Skip to content

Commit

Permalink
ZOOKEEPER-4880: Generate comments from zookeeper.jute into code.
Browse files Browse the repository at this point in the history
  • Loading branch information
luozongle01 committed Nov 28, 2024
1 parent 837f86c commit 99d15ec
Show file tree
Hide file tree
Showing 4 changed files with 409 additions and 29 deletions.
42 changes: 42 additions & 0 deletions zookeeper-jute/src/main/java/org/apache/jute/compiler/JField.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,31 @@

package org.apache.jute.compiler;

import org.apache.jute.compiler.generated.RccConstants;
import org.apache.jute.compiler.generated.Token;

/**
*
*/
public class JField {
private JType mType;
private String mName;

/**
* {@link #mType} of token.
*/
private Token mTypeToken;

private Token previousToken;

/**
* Since we can only get the comments before the token through the {@link Token#specialToken},
* we need to save the next token to get the end-of-line comment.
*
* <p>It may be the type of the next field, or it may be {@link RccConstants#RBRACE_TKN} of the class.
*/
private Token nextToken;

/**
* Creates a new instance of JField.
*/
Expand All @@ -33,6 +51,30 @@ public JField(JType type, String name) {
mName = name;
}

public Token getTypeToken() {
return mTypeToken;
}

public void setTypeToken(Token typeToken) {
this.mTypeToken = typeToken;
}

public Token getNextToken() {
return nextToken;
}

public void setNextToken(Token nextToken) {
this.nextToken = nextToken;
}

public Token getPreviousToken() {
return previousToken;
}

public void setPreviousToken(Token previousToken) {
this.previousToken = previousToken;
}

public String getSignature() {
return mType.getSignature();
}
Expand Down
161 changes: 160 additions & 1 deletion zookeeper-jute/src/main/java/org/apache/jute/compiler/JRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.jute.compiler.generated.RccConstants;
import org.apache.jute.compiler.generated.Token;

/**
*
Expand All @@ -36,24 +39,30 @@ public class JRecord extends JCompType {
private String mName;
private String mModule;
private List<JField> mFields;
private Token mRecordToken;

/**
* Creates a new instance of JRecord.
*/
public JRecord(String name, ArrayList<JField> flist) {
public JRecord(String name, ArrayList<JField> flist, Token recordToken) {
super("struct " + name.substring(name.lastIndexOf('.') + 1),
name.replaceAll("\\.", "::"), getCsharpFQName(name), name, "Record", name, getCsharpFQName("IRecord"));
mFQName = name;
int idx = name.lastIndexOf('.');
mName = name.substring(idx + 1);
mModule = name.substring(0, idx);
mFields = flist;
mRecordToken = recordToken;
}

public String getName() {
return mName;
}

public Token getRecordToken() {
return mRecordToken;
}

public String getCsharpName() {
return "Id".equals(mName) ? "ZKId" : mName;
}
Expand Down Expand Up @@ -208,8 +217,18 @@ public void genCCode(FileWriter h, FileWriter c) throws IOException {
}
}
String recName = getName();

String recordComments = getRecordComments();
if (recordComments != null && !recordComments.isEmpty()) {
h.write(recordComments);
}
h.write("struct " + recName + " {\n");
for (JField f : mFields) {

String fieldComments = getCFieldComments(f);
if (fieldComments != null && !fieldComments.isEmpty()) {
h.write(fieldComments);
}
h.write(f.genCDecl());
}
h.write("};\n");
Expand Down Expand Up @@ -436,10 +455,19 @@ public void genJavaCode(File outputDirectory) throws IOException {
jj.write("import org.apache.jute.*;\n");
jj.write("import org.apache.jute.Record; // JDK14 needs explicit import due to clash with java.lang.Record\n");
jj.write("import org.apache.yetus.audience.InterfaceAudience;\n");
String recordComments = getRecordComments();
if (recordComments != null && !recordComments.isEmpty()) {
jj.write(recordComments);
}
jj.write("@InterfaceAudience.Public\n");
jj.write("public class " + getName() + " implements Record {\n");
for (Iterator<JField> i = mFields.iterator(); i.hasNext(); ) {
JField jf = i.next();

String fieldComments = getJavaFieldComments(jf);
if (fieldComments != null && !fieldComments.isEmpty()) {
jj.write(fieldComments);
}
jj.write(jf.genJavaDecl());
}
jj.write(" public " + getName() + "() {\n");
Expand Down Expand Up @@ -767,4 +795,135 @@ public static String getCsharpFQName(String name) {
}
return fQName.toString();
}

public String getJavaFieldComments(JField jField) {
return getFieldComments(jField, " ");
}

public String getCFieldComments(JField jField) {
return getFieldComments(jField, " ");
}

private String getFieldComments(JField jField, String indent) {
if (jField == null || jField.getTypeToken() == null || jField.getNextToken() == null) {
return "";
}

// get the comment before the line
Token beforeTheLineCommentToken = getCommentToken(jField.getPreviousToken(), jField.getTypeToken());
List<String> comments = extractLeadingComments(beforeTheLineCommentToken, null);

Token endOfLineCommentToken = getCommentToken(null, jField.getNextToken());
if (endOfLineCommentToken != null && jField.getTypeToken().beginLine == endOfLineCommentToken.beginLine) {

comments.addAll(extractLeadingComments(endOfLineCommentToken, endOfLineCommentToken.next));
}

return formatComments(indent, comments);
}

private Token getCommentToken(Token previousToken, Token token) {
if (token == null || token.specialToken == null || belongsToThePreviousToken(previousToken, token.specialToken)) {
return null;
}

Token tmpToken = token.specialToken;
while (tmpToken.specialToken != null) {

if (belongsToThePreviousToken(previousToken, tmpToken.specialToken)) {
return tmpToken;
}
tmpToken = tmpToken.specialToken;
}
return tmpToken;
}

/**
* Determine whether the current commentToken belongs to the previousToken.
*
* @return true: If the current commentToken should belong to the previousToken.
*/
private boolean belongsToThePreviousToken(Token previousToken, Token commentToken) {
if (previousToken == null || commentToken == null) {
return false;
}

return previousToken.beginLine == commentToken.beginLine;
}

public String getRecordComments() {
if (getRecordToken() == null || getRecordToken().specialToken == null) {
return "";
}

// get the comments before the class
Token beforeTheClassToken = getCommentToken(null, getRecordToken());
return formatComments("", extractLeadingComments(beforeTheClassToken, null));
}

private static String formatComments(String indent, List<String> commentLines) {
if (commentLines == null || commentLines.isEmpty()) {
return "";
}

StringBuilder builder = new StringBuilder();
for (String line : commentLines) {
if (!line.isEmpty()) {
builder.append(indent).append(line);
}
builder.append(System.lineSeparator());
}

return builder.toString();
}

/**
* Extracts leading comments with indentation and line separator trimmed.
*
* <p>Empty line is represented as empty string.
*/
private static List<String> extractLeadingComments(Token token, Token sentinelToken) {
List<String> comments = new ArrayList<>();

if (token == null) {
return comments;
}

int nextLine = token.beginLine;
while (token != null && token != sentinelToken) {
while (nextLine < token.beginLine) {
comments.add("");
nextLine++;
}
nextLine = token.endLine + 1;
switch (token.kind) {
case RccConstants.ONE_LINE_COMMENT:
comments.add(token.image);
break;
case RccConstants.MULTI_LINE_COMMENT: {
List<String> lines = Arrays.asList(token.image.split("\r|\n|\r\n"));
// First line captures no indentation.
comments.add(lines.get(0));
int indentSpaces = token.beginColumn - 1;
for (int i = 1; i < lines.size(); i++) {
String line = lines.get(i);
int j = 0;
while (j < indentSpaces && j < line.length()) {
if (line.charAt(j) != ' ') {
break;
}
j++;
}
comments.add(line.substring(j));
}
}
break;
default:
throw new IllegalStateException("expect comment token, but get token kind " + token.kind);
}
token = token.next;
}

return comments;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,32 +111,8 @@ SKIP :

SPECIAL_TOKEN :
{
"//" : WithinOneLineComment
}

<WithinOneLineComment> SPECIAL_TOKEN :
{
<("\n" | "\r" | "\r\n" )> : DEFAULT
}

<WithinOneLineComment> MORE :
{
<~[]>
}

SPECIAL_TOKEN :
{
"/*" : WithinMultiLineComment
}

<WithinMultiLineComment> SPECIAL_TOKEN :
{
"*/" : DEFAULT
}

<WithinMultiLineComment> MORE :
{
<~[]>
<ONE_LINE_COMMENT: "//" (~["\n","\r"])*>
| <MULTI_LINE_COMMENT: "/*" (~["*"])* "*" (~["*","/"] (~["*"])* "*" | "*")* "/">
}

TOKEN :
Expand Down Expand Up @@ -274,21 +250,33 @@ JRecord Record() :
ArrayList<JField> flist = new ArrayList<JField>();
Token t;
JField f;
// Get the comments on the class token
Token recordTkn;
Token typeTkn;
Token previousToken = null;
}
{
<RECORD_TKN>
recordTkn = <RECORD_TKN>
t = <IDENT_TKN>
{ rname = t.image; }
<LBRACE_TKN>
(
{typeTkn = getToken(1);}
f = Field()
{ flist.add(f); }
<SEMICOLON_TKN>
{
f.setTypeToken(typeTkn);
f.setPreviousToken(previousToken);
f.setNextToken(getToken(1));
previousToken = typeTkn;
}
)+
<RBRACE_TKN>
{
previousToken = null;
String fqn = curModuleName + "." + rname;
JRecord r = new JRecord(fqn, flist);
JRecord r = new JRecord(fqn, flist, recordTkn);
recTab.put(fqn, r);
return r;
}
Expand Down
Loading

0 comments on commit 99d15ec

Please sign in to comment.