kezhuw commented on code in PR #2206: URL: https://github.com/apache/zookeeper/pull/2206#discussion_r1865098118
########## zookeeper-jute/src/main/java/org/apache/jute/compiler/JRecord.java: ########## @@ -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) { + Review Comment: ```suggestion ``` ########## zookeeper-jute/src/main/java/org/apache/jute/compiler/JRecord.java: ########## @@ -767,4 +794,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 indent) { + if (jField == null || jField.getTypeToken() == null || jField.getNextToken() == null) { + return ""; + } + + // get the comment before the line + Token beforeTheLineCommentToken = getCommentToken(jField.getTypeToken(), jField.getPreviousToken()); + List<String> comments = extractComments(beforeTheLineCommentToken, null); Review Comment: ```suggestion List<String> comments = extractComments(beforeTheLineCommentToken, Integer.MAX_VALUE); ``` ########## zookeeper-jute/src/test/java/org/apache/jute/compiler/JRecordTest.java: ########## @@ -0,0 +1,191 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jute.compiler; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.StringReader; +import java.lang.reflect.Field; +import java.util.List; +import org.apache.jute.compiler.generated.ParseException; +import org.apache.jute.compiler.generated.Rcc; +import org.junit.jupiter.api.Test; + +@SuppressWarnings({"unchecked", "SameParameterValue"}) +public class JRecordTest { + + @Test + public void testEndOfLineComments() throws ParseException, NoSuchFieldException, IllegalAccessException { + String juteStr = "module org.apache.zookeeper.data {\n" + + " // information explicitly stored by the server persistently\n" + + " class StatPersisted {\n" + + " long czxid; // created zxid\n" + + " long mzxid; // last modified zxid\n" + + " long ctime; // created\n" + + " long mtime; // last modified\n" + + " }\n" + + "}"; + + try (StringReader stringReader = new StringReader(juteStr)) { + Rcc parser = new Rcc(stringReader); + JFile jFile = parser.Input(); + List<JRecord> mRecords = getField(jFile, "mRecords", List.class); + assertEquals(1, mRecords.size()); + + JRecord jRecord = mRecords.get(0); + assertEquals("StatPersisted", jRecord.getName()); + List<JField> fields = jRecord.getFields(); + assertFiled(fields); + + assertEquals("// information explicitly stored by the server persistently\n", jRecord.getRecordComments()); + assertEquals(" // created zxid\n", jRecord.getJavaFieldComments(fields.get(0))); + assertEquals(" // last modified zxid\n", jRecord.getJavaFieldComments(fields.get(1))); + assertEquals(" // created\n", jRecord.getJavaFieldComments(fields.get(2))); + assertEquals(" // last modified\n", jRecord.getJavaFieldComments(fields.get(3))); + } + } + + @Test + public void testCommentBeforeLineAndEndOfLine() throws ParseException, NoSuchFieldException, IllegalAccessException { + String juteStr = "module org.apache.zookeeper.data {\n" + + " // information explicitly stored by the server persistently\n" + + " class StatPersisted {\n" + + " // created zxid\n" + + " long czxid; // created zxid comment2\n" + + " // last modified zxid\n" + + " long mzxid; // last modified zxid comment2\n" + + " // created\n" + + " long ctime; // created comment2\n" + + " // last modified\n" + + " long mtime; // last modified comment2\n" + + " }\n" + + "}"; + try (StringReader stringReader = new StringReader(juteStr)) { + Rcc parser = new Rcc(stringReader); + JFile jFile = parser.Input(); + List<JRecord> mRecords = getField(jFile, "mRecords", List.class); + assertEquals(1, mRecords.size()); + + JRecord jRecord = mRecords.get(0); + assertEquals("StatPersisted", jRecord.getName()); + List<JField> fields = jRecord.getFields(); + assertFiled(fields); + + assertEquals("// information explicitly stored by the server persistently\n", jRecord.getRecordComments()); + assertEquals(" // created zxid\n // created zxid comment2\n", jRecord.getJavaFieldComments(fields.get(0))); + assertEquals(" // last modified zxid\n // last modified zxid comment2\n", jRecord.getJavaFieldComments(fields.get(1))); + assertEquals(" // created\n // created comment2\n", jRecord.getJavaFieldComments(fields.get(2))); + assertEquals(" // last modified\n // last modified comment2\n", jRecord.getJavaFieldComments(fields.get(3))); + } + } + + @Test + public void testCommentBeforeLine() throws ParseException, NoSuchFieldException, IllegalAccessException { + String juteStr = "module org.apache.zookeeper.data {\n" + + " // information explicitly stored by the server persistently\n" + + " class StatPersisted {\n" + + " // created zxid\n" + + " long czxid;\n" + + " // last modified zxid\n" + + " long mzxid;\n" + + " // created\n" + + " long ctime;\n" + + " // last modified\n" + + " long mtime;\n" + + " }\n" + + "}"; + try (StringReader stringReader = new StringReader(juteStr)) { + Rcc parser = new Rcc(stringReader); + JFile jFile = parser.Input(); + List<JRecord> mRecords = getField(jFile, "mRecords", List.class); + assertEquals(1, mRecords.size()); + + JRecord jRecord = mRecords.get(0); + assertEquals("StatPersisted", jRecord.getName()); + List<JField> fields = jRecord.getFields(); + assertFiled(fields); + + assertEquals("// information explicitly stored by the server persistently\n", jRecord.getRecordComments()); + assertEquals(" // created zxid\n", jRecord.getJavaFieldComments(fields.get(0))); + assertEquals(" // last modified zxid\n", jRecord.getJavaFieldComments(fields.get(1))); + assertEquals(" // created\n", jRecord.getJavaFieldComments(fields.get(2))); + assertEquals(" // last modified\n", jRecord.getJavaFieldComments(fields.get(3))); + } + } + + @Test + public void testMultiLineComments() throws ParseException, NoSuchFieldException, IllegalAccessException { + String juteStr = "module org.apache.zookeeper.data {\n" + + " // information explicitly stored by the server persistently\n" + + " class StatPersisted {\n" + + " /**\n" + + " * created zxid\n" + + " */\n" + + " long czxid;\n" + + " /* last modified zxid */\n" + + " long mzxid;\n" + + " /*\n" + + " * created\n" + + " */\n" + + " long ctime;\n" + + " /*\n" + + " last modified\n" + + " */" Review Comment: How about adding more cases to verify more multi-line captures ? ``` /** ** Consecutive star signs. **/ int version; /* multi-line end of line */ int cversion; /* A multi-line end of line comment. */ /* Another multi-line end of line commnet. */ /* Yet another end of line comment. */ /* Comment belong to new field */ long ephemeralOwner; ``` ########## zookeeper-jute/src/main/java/org/apache/jute/compiler/JRecord.java: ########## @@ -767,4 +794,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 indent) { + if (jField == null || jField.getTypeToken() == null || jField.getNextToken() == null) { + return ""; + } + + // get the comment before the line + Token beforeTheLineCommentToken = getCommentToken(jField.getTypeToken(), jField.getPreviousToken()); + List<String> comments = extractComments(beforeTheLineCommentToken, null); + + Token endOfLineCommentToken = getCommentToken(jField.getNextToken(), null); + if (endOfLineCommentToken != null && jField.getTypeToken().beginLine == endOfLineCommentToken.beginLine) { + + comments.addAll(extractComments(endOfLineCommentToken, endOfLineCommentToken.next)); + } + + return formatComments(indent, comments); + } + + private Token getCommentToken(Token token, Token previousToken) { + if (token == null || token.specialToken == null) { + return null; + } + + Token commentToken = token.specialToken; + while (commentToken.specialToken != null) { + commentToken = commentToken.specialToken; + } + // Skip end of line comment belong to previous token. + if (previousToken != null && commentToken.beginLine == previousToken.endLine) { + commentToken = commentToken.next; + } + return commentToken; + } + + public String getRecordComments() { + if (getRecordToken() == null || getRecordToken().specialToken == null) { + return ""; + } + + // get the comments before the class + Token commentToken = getCommentToken(getRecordToken(), null); + return formatComments("", extractComments(commentToken, 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 comments with indentation and line separator trimmed. + * + * <p>Empty line is represented as empty string. + */ + private static List<String> extractComments(Token token, Token sentinelToken) { Review Comment: ```suggestion private static List<String> extractComments(Token token, int endLine) { ``` ########## zookeeper-jute/src/main/java/org/apache/jute/compiler/JRecord.java: ########## @@ -767,4 +794,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 indent) { + if (jField == null || jField.getTypeToken() == null || jField.getNextToken() == null) { + return ""; + } + + // get the comment before the line + Token beforeTheLineCommentToken = getCommentToken(jField.getTypeToken(), jField.getPreviousToken()); + List<String> comments = extractComments(beforeTheLineCommentToken, null); + + Token endOfLineCommentToken = getCommentToken(jField.getNextToken(), null); + if (endOfLineCommentToken != null && jField.getTypeToken().beginLine == endOfLineCommentToken.beginLine) { + + comments.addAll(extractComments(endOfLineCommentToken, endOfLineCommentToken.next)); + } + + return formatComments(indent, comments); + } + + private Token getCommentToken(Token token, Token previousToken) { + if (token == null || token.specialToken == null) { + return null; + } + + Token commentToken = token.specialToken; + while (commentToken.specialToken != null) { + commentToken = commentToken.specialToken; + } + // Skip end of line comment belong to previous token. + if (previousToken != null && commentToken.beginLine == previousToken.endLine) { + commentToken = commentToken.next; + } + return commentToken; + } + + public String getRecordComments() { + if (getRecordToken() == null || getRecordToken().specialToken == null) { + return ""; + } + + // get the comments before the class + Token commentToken = getCommentToken(getRecordToken(), null); + return formatComments("", extractComments(commentToken, 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 comments with indentation and line separator trimmed. + * + * <p>Empty line is represented as empty string. + */ + private static List<String> extractComments(Token token, Token sentinelToken) { + List<String> comments = new ArrayList<>(); + + if (token == null) { + return comments; + } + + int nextLine = token.beginLine; + while (token != null && token != sentinelToken) { Review Comment: ```suggestion while (token != null && token.beginLine <= endLine) { ``` ########## zookeeper-jute/src/main/java/org/apache/jute/compiler/generated/rcc.jj: ########## @@ -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> + previousToken = <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; Review Comment: ```suggestion ``` ########## zookeeper-jute/src/main/java/org/apache/jute/compiler/JRecord.java: ########## @@ -767,4 +794,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 indent) { + if (jField == null || jField.getTypeToken() == null || jField.getNextToken() == null) { + return ""; + } + + // get the comment before the line + Token beforeTheLineCommentToken = getCommentToken(jField.getTypeToken(), jField.getPreviousToken()); + List<String> comments = extractComments(beforeTheLineCommentToken, null); + + Token endOfLineCommentToken = getCommentToken(jField.getNextToken(), null); + if (endOfLineCommentToken != null && jField.getTypeToken().beginLine == endOfLineCommentToken.beginLine) { + + comments.addAll(extractComments(endOfLineCommentToken, endOfLineCommentToken.next)); + } + + return formatComments(indent, comments); + } + + private Token getCommentToken(Token token, Token previousToken) { + if (token == null || token.specialToken == null) { + return null; + } + + Token commentToken = token.specialToken; + while (commentToken.specialToken != null) { + commentToken = commentToken.specialToken; + } + // Skip end of line comment belong to previous token. + if (previousToken != null && commentToken.beginLine == previousToken.endLine) { Review Comment: ```suggestion while (previousToken != null && commentToken != null && commentToken.beginLine == previousToken.endLine) { ``` It is possible that multiple multi-line comments could resides in one line. ########## zookeeper-jute/src/main/java/org/apache/jute/compiler/JRecord.java: ########## @@ -767,4 +794,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 indent) { + if (jField == null || jField.getTypeToken() == null || jField.getNextToken() == null) { + return ""; + } + + // get the comment before the line + Token beforeTheLineCommentToken = getCommentToken(jField.getTypeToken(), jField.getPreviousToken()); + List<String> comments = extractComments(beforeTheLineCommentToken, null); + + Token endOfLineCommentToken = getCommentToken(jField.getNextToken(), null); + if (endOfLineCommentToken != null && jField.getTypeToken().beginLine == endOfLineCommentToken.beginLine) { + + comments.addAll(extractComments(endOfLineCommentToken, endOfLineCommentToken.next)); + } + + return formatComments(indent, comments); + } + + private Token getCommentToken(Token token, Token previousToken) { + if (token == null || token.specialToken == null) { + return null; + } + + Token commentToken = token.specialToken; + while (commentToken.specialToken != null) { + commentToken = commentToken.specialToken; + } + // Skip end of line comment belong to previous token. + if (previousToken != null && commentToken.beginLine == previousToken.endLine) { + commentToken = commentToken.next; + } + return commentToken; + } + + public String getRecordComments() { + if (getRecordToken() == null || getRecordToken().specialToken == null) { + return ""; + } + + // get the comments before the class + Token commentToken = getCommentToken(getRecordToken(), null); + return formatComments("", extractComments(commentToken, null)); Review Comment: ```suggestion return formatComments("", extractComments(commentToken, Integer.MAX_VALUE)); ``` ########## zookeeper-jute/src/test/java/org/apache/jute/compiler/JRecordTest.java: ########## @@ -0,0 +1,191 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jute.compiler; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.StringReader; +import java.lang.reflect.Field; +import java.util.List; +import org.apache.jute.compiler.generated.ParseException; +import org.apache.jute.compiler.generated.Rcc; +import org.junit.jupiter.api.Test; + +@SuppressWarnings({"unchecked", "SameParameterValue"}) +public class JRecordTest { + + @Test + public void testEndOfLineComments() throws ParseException, NoSuchFieldException, IllegalAccessException { + String juteStr = "module org.apache.zookeeper.data {\n" + + " // information explicitly stored by the server persistently\n" + + " class StatPersisted {\n" + + " long czxid; // created zxid\n" + + " long mzxid; // last modified zxid\n" + + " long ctime; // created\n" + + " long mtime; // last modified\n" + + " }\n" + + "}"; + + try (StringReader stringReader = new StringReader(juteStr)) { + Rcc parser = new Rcc(stringReader); + JFile jFile = parser.Input(); + List<JRecord> mRecords = getField(jFile, "mRecords", List.class); + assertEquals(1, mRecords.size()); + + JRecord jRecord = mRecords.get(0); + assertEquals("StatPersisted", jRecord.getName()); + List<JField> fields = jRecord.getFields(); + assertFiled(fields); + + assertEquals("// information explicitly stored by the server persistently\n", jRecord.getRecordComments()); + assertEquals(" // created zxid\n", jRecord.getJavaFieldComments(fields.get(0))); + assertEquals(" // last modified zxid\n", jRecord.getJavaFieldComments(fields.get(1))); + assertEquals(" // created\n", jRecord.getJavaFieldComments(fields.get(2))); + assertEquals(" // last modified\n", jRecord.getJavaFieldComments(fields.get(3))); + } + } + + @Test + public void testCommentBeforeLineAndEndOfLine() throws ParseException, NoSuchFieldException, IllegalAccessException { + String juteStr = "module org.apache.zookeeper.data {\n" + + " // information explicitly stored by the server persistently\n" + + " class StatPersisted {\n" + + " // created zxid\n" + + " long czxid; // created zxid comment2\n" + + " // last modified zxid\n" + + " long mzxid; // last modified zxid comment2\n" + + " // created\n" + + " long ctime; // created comment2\n" + + " // last modified\n" + + " long mtime; // last modified comment2\n" + + " }\n" + + "}"; + try (StringReader stringReader = new StringReader(juteStr)) { + Rcc parser = new Rcc(stringReader); + JFile jFile = parser.Input(); + List<JRecord> mRecords = getField(jFile, "mRecords", List.class); + assertEquals(1, mRecords.size()); + + JRecord jRecord = mRecords.get(0); + assertEquals("StatPersisted", jRecord.getName()); + List<JField> fields = jRecord.getFields(); + assertFiled(fields); + + assertEquals("// information explicitly stored by the server persistently\n", jRecord.getRecordComments()); + assertEquals(" // created zxid\n // created zxid comment2\n", jRecord.getJavaFieldComments(fields.get(0))); + assertEquals(" // last modified zxid\n // last modified zxid comment2\n", jRecord.getJavaFieldComments(fields.get(1))); + assertEquals(" // created\n // created comment2\n", jRecord.getJavaFieldComments(fields.get(2))); + assertEquals(" // last modified\n // last modified comment2\n", jRecord.getJavaFieldComments(fields.get(3))); + } + } + + @Test + public void testCommentBeforeLine() throws ParseException, NoSuchFieldException, IllegalAccessException { + String juteStr = "module org.apache.zookeeper.data {\n" + + " // information explicitly stored by the server persistently\n" + + " class StatPersisted {\n" + + " // created zxid\n" + + " long czxid;\n" + + " // last modified zxid\n" + + " long mzxid;\n" + + " // created\n" + + " long ctime;\n" + + " // last modified\n" + + " long mtime;\n" + + " }\n" + + "}"; + try (StringReader stringReader = new StringReader(juteStr)) { + Rcc parser = new Rcc(stringReader); + JFile jFile = parser.Input(); + List<JRecord> mRecords = getField(jFile, "mRecords", List.class); + assertEquals(1, mRecords.size()); + + JRecord jRecord = mRecords.get(0); + assertEquals("StatPersisted", jRecord.getName()); + List<JField> fields = jRecord.getFields(); + assertFiled(fields); + + assertEquals("// information explicitly stored by the server persistently\n", jRecord.getRecordComments()); + assertEquals(" // created zxid\n", jRecord.getJavaFieldComments(fields.get(0))); + assertEquals(" // last modified zxid\n", jRecord.getJavaFieldComments(fields.get(1))); + assertEquals(" // created\n", jRecord.getJavaFieldComments(fields.get(2))); + assertEquals(" // last modified\n", jRecord.getJavaFieldComments(fields.get(3))); + } + } + + @Test + public void testMultiLineComments() throws ParseException, NoSuchFieldException, IllegalAccessException { + String juteStr = "module org.apache.zookeeper.data {\n" + + " // information explicitly stored by the server persistently\n" Review Comment: How about changing this to multi-line comment ? There is no test for multi-line comment for `class`. ########## zookeeper-jute/src/main/java/org/apache/jute/compiler/JRecord.java: ########## @@ -767,4 +794,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 indent) { + if (jField == null || jField.getTypeToken() == null || jField.getNextToken() == null) { + return ""; + } + + // get the comment before the line + Token beforeTheLineCommentToken = getCommentToken(jField.getTypeToken(), jField.getPreviousToken()); + List<String> comments = extractComments(beforeTheLineCommentToken, null); + + Token endOfLineCommentToken = getCommentToken(jField.getNextToken(), null); + if (endOfLineCommentToken != null && jField.getTypeToken().beginLine == endOfLineCommentToken.beginLine) { + + comments.addAll(extractComments(endOfLineCommentToken, endOfLineCommentToken.next)); Review Comment: ```suggestion comments.addAll(extractComments(endOfLineCommentToken, endOfLineCommentToken.beginLine)); ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: notifications-unsubscr...@zookeeper.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org