Skip to content

Commit

Permalink
Merge pull request #191 from kasiaMarek/record-support
Browse files Browse the repository at this point in the history
Add support for java records
  • Loading branch information
paul-hammant authored Dec 18, 2023
2 parents 6ba542e + 3b59a88 commit e99f82b
Show file tree
Hide file tree
Showing 11 changed files with 312 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/grammar/lexer.flex
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ JavadocEnd = "*"+ "/"
"record" / {WhiteSpace}+ {Id} {
markAnnotatedElementLine();
classDepth++;
braceMode = CODEBLOCK;
braceMode = TYPE;
pushState(NAME);
return Parser.RECORD;
}
Expand Down
92 changes: 87 additions & 5 deletions src/grammar/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -711,11 +711,46 @@ EnumBodyDeclarations_opt:

// RecordDeclaration:
// {ClassModifier} record TypeIdentifier [TypeParameters] RecordHeader [ClassImplements] RecordBody
RecordDeclaration: Modifiers_opt RECORD IDENTIFIER TypeParameters_opt RecordHeader ClassImplements_opt RecordBody
RecordDeclaration: Modifiers_opt RECORD IDENTIFIER
{
cls.setLineNumber(lexer.getLine());
cls.getModifiers().addAll(modifiers);
cls.setName( $3 );
cls.setType(ClassDef.RECORD);
}
TypeParameters_opt
{
cls.setTypeParameters(typeParams);
builder.beginClass(cls);
}
RecordHeader ClassImplements_opt
{
builder.addImplements(cls.getImplements());
cls = new ClassDef();
}
RecordBody
{
builder.endRecord(recordHeaderStack.removeFirst());
}
;

// RecordHeader:
// ( [RecordComponentList] )
RecordHeader: PARENOPEN RecordComponentList_opt PARENCLOSE
RecordHeader: PARENOPEN
{
builder.beginConstructor();
mth.setLineNumber(lexer.getLine());
mth.setConstructor(true);
mth.setName(cls.getName());
recordHeaderStack.addFirst(new RecordFieldsDef());
}
RecordComponentList_opt
{
builder.endConstructor(mth);
mth = new MethodDef();
}
PARENCLOSE
;

// RecordComponentList:
// RecordComponent {, RecordComponent}
Expand All @@ -730,16 +765,61 @@ RecordComponentList_opt:
// {RecordComponentModifier} UnannType Identifier
// VariableArityRecordComponent
RecordComponent: Annotations_opt /* ={RecordComponentModifier} */ Type /* =UnannType */ IDENTIFIER
{
param.setType($2);
param.setName($3);
param.setDimensions(0);
param.setVarArgs(false);
builder.addParameter(param);
recordHeaderStack.getFirst().addField(param);
param = new FieldDef();
}
| VariableArityRecordComponent
;

// VariableArityRecordComponent:
// {RecordComponentModifier} UnannType {Annotation} ... Identifier
VariableArityRecordComponent: Annotations_opt /* ={RecordComponentModifier} */ Type /* =UnannType */ DOTDOTDOT IDENTIFIER
{
param.setType($2);
param.setName($4);
param.setDimensions(0);
param.setVarArgs(true);
builder.addParameter(param);
recordHeaderStack.getFirst().addField(param);
param = new FieldDef();
}
;

// RecordBody:
// { { RecordBodyDeclaration } }
RecordBody: BRACEOPEN RecordBodyDeclarations_opt BRACECLOSE

// RecordBody:
// { {RecordBodyDeclaration} }
RecordBody: CODEBLOCK
// RecordBodyDeclaration:
// ClassBodyDeclaration
// CompactConstructorDeclaration
RecordBodyDeclaration: ClassBodyDeclaration
| CompactConstructorDeclaration
;
RecordBodyDeclarations_opt:
| RecordBodyDeclarations_opt
{
line = lexer.getLine();
}
RecordBodyDeclaration
;

// CompactConstructorDeclaration:
// {ConstructorModifier} SimpleTypeName ConstructorBody
CompactConstructorDeclaration: Modifiers_opt IDENTIFIER MethodBody /* =ConstructorBody */
{
compactConstructor.setModifiers(modifiers); modifiers.clear();
compactConstructor.setBody($3);
compactConstructor.setLineNumber(lexer.getLine());
builder.addCompactConstructor(compactConstructor);
compactConstructor = new CompactConstructorDef();
}
;

// -----------------------------
// Productions from �9 (Interfaces)
Expand Down Expand Up @@ -1967,6 +2047,8 @@ private List<TypeVariableDef> typeParams = new LinkedList<TypeVariableDef>(); //
private LinkedList<AnnoDef> annotationStack = new LinkedList<AnnoDef>(); // Use LinkedList instead of Stack because it is unsynchronized
private List<List<ElemValueDef>> annoValueListStack = new LinkedList<List<ElemValueDef>>(); // Use LinkedList instead of Stack because it is unsynchronized
private List<ElemValueDef> annoValueList = null;
private LinkedList<RecordFieldsDef> recordHeaderStack = new LinkedList<RecordFieldsDef>();
private CompactConstructorDef compactConstructor = new CompactConstructorDef();
private FieldDef param = new FieldDef();
private java.util.Set<String> modifiers = new java.util.LinkedHashSet<String>();
private TypeDef fieldType;
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/com/thoughtworks/qdox/builder/Builder.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,22 @@
*/

import java.net.URL;
import java.util.Set;

import com.thoughtworks.qdox.model.JavaModule;
import com.thoughtworks.qdox.model.JavaSource;
import com.thoughtworks.qdox.parser.expression.ExpressionDef;
import com.thoughtworks.qdox.parser.structs.AnnoDef;
import com.thoughtworks.qdox.parser.structs.ClassDef;
import com.thoughtworks.qdox.parser.structs.CompactConstructorDef;
import com.thoughtworks.qdox.parser.structs.FieldDef;
import com.thoughtworks.qdox.parser.structs.InitDef;
import com.thoughtworks.qdox.parser.structs.MethodDef;
import com.thoughtworks.qdox.parser.structs.ModuleDef;
import com.thoughtworks.qdox.parser.structs.PackageDef;
import com.thoughtworks.qdox.parser.structs.RecordFieldsDef;
import com.thoughtworks.qdox.parser.structs.TagDef;
import com.thoughtworks.qdox.parser.structs.TypeDef;
import com.thoughtworks.qdox.writer.ModelWriterFactory;

public interface Builder
Expand All @@ -57,11 +61,14 @@ public interface Builder

void beginClass( ClassDef def );
void endClass();
void endRecord( RecordFieldsDef def );

void addImplements( Set<TypeDef> implementSet );
void addInitializer( InitDef def );

void beginConstructor();
void endConstructor( MethodDef def );
void addCompactConstructor( CompactConstructorDef def );

void beginMethod();
void endMethod( MethodDef def );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import com.thoughtworks.qdox.parser.expression.ExpressionDef;
import com.thoughtworks.qdox.parser.structs.AnnoDef;
import com.thoughtworks.qdox.parser.structs.ClassDef;
import com.thoughtworks.qdox.parser.structs.CompactConstructorDef;
import com.thoughtworks.qdox.parser.structs.FieldDef;
import com.thoughtworks.qdox.parser.structs.InitDef;
import com.thoughtworks.qdox.parser.structs.MethodDef;
Expand All @@ -74,6 +75,7 @@
import com.thoughtworks.qdox.parser.structs.ModuleDef.RequiresDef;
import com.thoughtworks.qdox.parser.structs.ModuleDef.UsesDef;
import com.thoughtworks.qdox.parser.structs.PackageDef;
import com.thoughtworks.qdox.parser.structs.RecordFieldsDef;
import com.thoughtworks.qdox.parser.structs.TagDef;
import com.thoughtworks.qdox.parser.structs.TypeDef;
import com.thoughtworks.qdox.parser.structs.TypeVariableDef;
Expand All @@ -94,6 +96,8 @@ public class ModelBuilder implements Builder {

private LinkedList<DefaultJavaClass> classStack = new LinkedList<DefaultJavaClass>();

private LinkedList<DefaultJavaConstructor> recordHeaderStack = new LinkedList<DefaultJavaConstructor>();

private List<DefaultJavaParameter> parameterList = new LinkedList<DefaultJavaParameter>();

private DefaultJavaConstructor currentConstructor;
Expand Down Expand Up @@ -226,6 +230,16 @@ public void addImport( String importName )
source.addImport( importName );
}

public void addImplements( Set<TypeDef> implementSet )
{
List<JavaClass> implementz = new LinkedList<JavaClass>();
for ( TypeDef implementType : implementSet )
{
implementz.add( createType( implementType, 0 ) );
}
classStack.getFirst().setImplementz( implementz );
}

/** {@inheritDoc} */
public void addJavaDoc( String text )
{
Expand All @@ -249,6 +263,7 @@ public void beginClass(ClassDef def)
newClass.setName( def.getName() );
newClass.setInterface( ClassDef.INTERFACE.equals( def.getType() ) );
newClass.setEnum( ClassDef.ENUM.equals( def.getType() ) );
newClass.setRecord( ClassDef.RECORD.equals( def.getType() ) );
newClass.setAnnotation( ClassDef.ANNOTATION_TYPE.equals( def.getType() ) );

// superclass
Expand Down Expand Up @@ -325,6 +340,48 @@ public void endClass()
classStack.removeFirst();
}

/** {@inheritDoc} */
public void endRecord( RecordFieldsDef def )
{
DefaultJavaClass cls = classStack.getFirst();
for ( FieldDef param : def.getFields() )
{
int dimensions =
param.isVarArgs()
? param.getDimensions() + 1
: param.getDimensions();

FieldDef field = new FieldDef();
field.setName(param.getName());
field.setType(param.getType());
field.setDimensions(dimensions);
field.setEnumConstant(false);
field.getModifiers().addAll(param.getModifiers());
field.getModifiers().add("private");
field.getModifiers().add("final");
field.setLineNumber(param.getLineNumber());
beginField(field);
endField();

if( cls.getMethod( param.getName(), new LinkedList(), false ) == null )
{
MethodDef mth = new MethodDef();
mth.setName(param.getName());
mth.setLineNumber(param.getLineNumber());
mth.setReturnType(param.getType());
mth.getModifiers().add("public");
mth.setDimensions(dimensions);
mth.setTypeParams(new LinkedList());
mth.setLineNumber(param.getLineNumber());
beginMethod();
endMethod(mth);
}
}

recordHeaderStack.removeFirst();
classStack.removeFirst();
}

/**
* this one is specific for those cases where dimensions can be part of both the type and identifier
* i.e. private String[] matrix[]; //field
Expand Down Expand Up @@ -393,7 +450,14 @@ public void beginConstructor()
addJavaDoc( currentConstructor );
setAnnotations( currentConstructor );

classStack.getFirst().addConstructor( currentConstructor );
DefaultJavaClass cls = classStack.getFirst();

if( cls.isRecord() && cls.getConstructors().isEmpty() )
{
recordHeaderStack.addFirst( currentConstructor );
}

cls.addConstructor( currentConstructor );
}

/** {@inheritDoc} */
Expand Down Expand Up @@ -436,6 +500,15 @@ public void endConstructor( MethodDef def )
currentConstructor.setSourceCode( def.getBody() );
}

/** {@inheritDoc} */
public void addCompactConstructor( CompactConstructorDef def )
{
DefaultJavaConstructor javaConstructor = recordHeaderStack.getFirst();
javaConstructor.setModifiers( new LinkedList<String>( def.getModifiers() ) );
javaConstructor.setSourceCode( def.getBody() );
javaConstructor.setLineNumber( def.getLineNumber() );
}

/** {@inheritDoc} */
public void beginMethod()
{
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/thoughtworks/qdox/model/JavaClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ public interface JavaClass extends JavaModel, JavaType, JavaAnnotatedElement, Ja
*/
boolean isEnum();

/**
* (API description of {@link java.lang.Class#isRecord()})
* <p>
* Returns <code>true</code> if and only if this class was declared as a record in the source code.
* </p>
*
* @return <code>true</code> if this object represents a record, otherwise <code>false</code>
*/
boolean isRecord();

/**
* (API description of {@link java.lang.Class#isAnnotation()})
* <p>Returns true if this <code>Class</code> object represents an annotation type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public class DefaultJavaClass

private boolean anEnum;

private boolean aRecord;

private boolean anAnnotation;

private JavaType superClass;
Expand Down Expand Up @@ -115,6 +117,12 @@ public boolean isEnum()
return anEnum;
}

/** {@inheritDoc} */
public boolean isRecord()
{
return aRecord;
}

/** {@inheritDoc} */
public boolean isAnnotation()
{
Expand Down Expand Up @@ -208,6 +216,11 @@ public void setEnum( boolean anEnum )
this.anEnum = anEnum;
}

public void setRecord( boolean aRecord )
{
this.aRecord = aRecord;
}

public void setAnnotation( boolean anAnnotation )
{
this.anAnnotation = anAnnotation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,12 @@ public boolean isEnum()
return resolveRealClass().isEnum();
}

/** {@inheritDoc} */
public boolean isRecord()
{
return resolveRealClass().isRecord();
}

/** {@inheritDoc} */
public String getComment()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class ClassDef extends LocatedDef {
public static final String CLASS = "class";
public static final String INTERFACE = "interface";
public static final String ENUM = "enum";
public static final String RECORD = "record";
public static final String ANNOTATION_TYPE = "@interface";

private String name = "";
Expand Down
Loading

0 comments on commit e99f82b

Please sign in to comment.