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 27, 2024
1 parent 837f86c commit 582c32c
Show file tree
Hide file tree
Showing 4 changed files with 406 additions and 56 deletions.
32 changes: 32 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,29 @@

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;

/**
* 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 +49,22 @@ 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 String getSignature() {
return mType.getSignature();
}
Expand Down
148 changes: 147 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,122 @@ 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 space) {
if (jField == null || jField.getTypeToken() == null || jField.getNextToken() == null) {
return "";
}

// get the comment before the line
List<String> comments = extractLeadingComments(jField.getTypeToken().specialToken);

// get the end-of-line comments of fields
// If the current field and the next field are on the same line,
// the leading field comment of the next field should be discarded
Token tmpToken = jField.getNextToken();
while (tmpToken.specialToken != null) {

if (jField.getTypeToken().beginLine == tmpToken.specialToken.beginLine) {
Token endLineComments = tmpToken.specialToken;
tmpToken.specialToken = null;
comments.addAll(extractLeadingComments(endLineComments));
break;
}

tmpToken = tmpToken.specialToken;
}

return formatComments(space, comments);
}

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

// get the comments before the class
return formatComments("", extractLeadingComments(getRecordToken().specialToken));
}

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) {
List<String> comments = new ArrayList<>();

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

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

int nextLine = tmpToken.beginLine;
while (tmpToken != null) {
while (nextLine < tmpToken.beginLine) {
comments.add("");
nextLine++;
}
nextLine = tmpToken.endLine + 1;
switch (tmpToken.kind) {
case RccConstants.ONE_LINE_COMMENT:
comments.add(tmpToken.image);
break;
case RccConstants.MULTI_LINE_COMMENT: {
List<String> lines = Arrays.asList(tmpToken.image.split("\r|\n|\r\n"));
// First line captures no indentation.
comments.add(lines.get(0));
int indentSpaces = tmpToken.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 " + tmpToken.kind);
}
if (tmpToken == token) {
break;
}
tmpToken = tmpToken.next;
}

return comments;
}
}
Loading

0 comments on commit 582c32c

Please sign in to comment.