Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ target
**/.settings/
**/.project
**/.classpath
**/.idea
pom.xml.versionsBackup
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ public enum EClassType
CLASS ("class"),
INTERFACE ("interface"),
ANNOTATION_TYPE_DECL ("@interface"),
ENUM ("enum");
ENUM ("enum"),
RECORD ("record");

/**
* The keyword used to declare this type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,40 @@ default CLASSTYPE _enum (final int nMods, @NonNull final String sName) throws JC
return _class (nMods, sName, EClassType.ENUM);
}

/**
* Add a public record to this package/class.
*
* @param sName
* Name of the record to be added
* @return newly created record
* @throws JCodeModelException
* If another class/interface/... with the same name already exists
* @since 4.2.0
*/
@NonNull
default CLASSTYPE _record (@NonNull final String sName) throws JCodeModelException
{
return _record (JMod.PUBLIC, sName);
}

/**
* Add a record to this package/class.
*
* @param nMods
* Modifiers for this record declaration
* @param sName
* Name of the record to be added
* @return newly created record
* @throws JCodeModelException
* If another class/interface/... with the same name already exists
* @since 4.2.0
*/
@NonNull
default CLASSTYPE _record (final int nMods, @NonNull final String sName) throws JCodeModelException
{
return _class (nMods, sName, EClassType.RECORD);
}

/**
* @return A collection with all nested classes defined in this class. Never <code>null</code>.
*/
Expand Down
181 changes: 177 additions & 4 deletions jcodemodel/src/main/java/com/helger/jcodemodel/JDefinedClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ public class JDefinedClass extends AbstractJClassContainer <JDefinedClass> imple
*/
private final Map <String, JEnumConstant> m_aEnumConstantsByName = new LinkedHashMap <> ();

/**
* Record components for record types. Order is significant.
*/
private final List <JRecordComponent> m_aRecordComponents = new ArrayList <> ();

/**
* Compact constructor for record types, if defined.
*/
private JMethod m_aCompactConstructor;

/**
* Annotations on this variable. Lazily created.
*/
Expand Down Expand Up @@ -342,7 +352,7 @@ public JEnumConstant enumConstant (@NonNull final String sName)

/**
* Check if a specific enum constant name is already in use
*
*
* @param sName
* Enum constant name to check. May not be <code>null</code>.
* @return <code>true</code> if it is contained, <code>false</code> otherwise
Expand All @@ -352,6 +362,136 @@ public boolean containsEnumConstant (@NonNull final String sName)
return m_aEnumConstantsByName.containsKey (sName);
}

/**
* Adds a component to this record.
*
* @param aType
* Type of the component. May not be <code>null</code>.
* @param sName
* Name of the component. May not be <code>null</code>.
* @return The newly created record component.
* @throws IllegalStateException
* If this is not a record type.
* @since 4.2.0
*/
@NonNull
public JRecordComponent recordComponent (@NonNull final AbstractJType aType, @NonNull final String sName)
{
if (!isRecord())
throw new IllegalStateException("recordComponent() is only valid for record types");
ValueEnforcer.notNull (aType, "Type");
ValueEnforcer.notNull (sName, "Name");

final JRecordComponent comp = new JRecordComponent (this, aType, sName, false);
m_aRecordComponents.add (comp);
return comp;
}

/**
* Adds a component to this record using a Class reference.
*
* @param aType
* Type of the component. May not be <code>null</code>.
* @param sName
* Name of the component. May not be <code>null</code>.
* @return The newly created record component.
* @throws IllegalStateException
* If this is not a record type.
* @since 4.2.0
*/
@NonNull
public JRecordComponent recordComponent (@NonNull final Class <?> aType, @NonNull final String sName)
{
return recordComponent (owner ().ref (aType), sName);
}

/**
* Adds a varargs component to this record. This must be the last component.
*
* @param aType
* The component type of the varargs array. May not be <code>null</code>.
* @param sName
* Name of the component. May not be <code>null</code>.
* @return The newly created record component.
* @throws IllegalStateException
* If this is not a record type.
* @since 4.2.0
*/
@NonNull
public JRecordComponent recordComponentVararg (@NonNull final AbstractJType aType, @NonNull final String sName)
{
if (!isRecord())
throw new IllegalStateException("recordComponentVararg() is only valid for record types");
ValueEnforcer.notNull (aType, "Type");
ValueEnforcer.notNull (sName, "Name");

final JRecordComponent comp = new JRecordComponent (this, aType.array (), sName, true);
m_aRecordComponents.add (comp);
return comp;
}

/**
* @return The list of record components. Never <code>null</code>.
* @since 4.2.0
*/
@NonNull
public List <JRecordComponent> recordComponents ()
{
return Collections.unmodifiableList (m_aRecordComponents);
}

/**
* Creates a compact constructor for this record. A compact constructor has no
* parameter list and is used for validation or normalization of record components.
* <p>
* Example:
* <pre>
* public record Range(int lo, int hi) {
* public Range {
* if (lo &gt; hi) throw new IllegalArgumentException();
* }
* }
* </pre>
*
* @param nMods
* Modifiers for this constructor (typically {@link JMod#PUBLIC}).
* @return The newly created compact constructor.
* @throws IllegalStateException
* If this is not a record type or a compact constructor already exists.
* @since 4.2.0
*/
@NonNull
public JMethod compactConstructor (final int nMods)
{
if (!isRecord())
throw new IllegalStateException("compactConstructor() is only valid for record types");

if (m_aCompactConstructor != null)
throw new IllegalStateException("A compact constructor has already been defined");

m_aCompactConstructor = new JMethod (nMods, this);
return m_aCompactConstructor;
}

/**
* @return The compact constructor if defined, <code>null</code> otherwise.
* @since 4.2.0
*/
@Nullable
public JMethod compactConstructor ()
{
return m_aCompactConstructor;
}

/**
* @return <code>true</code> if this is a record type.
* @since 4.2.0
*/
public boolean isRecord ()
{
return getClassType () == EClassType.RECORD;
}

@Override
public String binaryName ()
{
Expand Down Expand Up @@ -665,14 +805,31 @@ public void declare (@NonNull final IJFormatter f)
f.generable (aAnnotation).newline ();

// Modifiers (private, protected, public)
// Type of class (class, interface, enum, @interface)
// Type of class (class, interface, enum, @interface, record)
// Name of the class
// Class wildcards
f.generable (m_aMods).print (getClassType ().declarationToken ()).id (name ()).declaration (m_aGenerifiable);

// For records, output the component list
if (isRecord ())
{
f.print ('(');
boolean bFirst = true;
for (final JRecordComponent comp : m_aRecordComponents)
{
if (bFirst)
bFirst = false;
else
f.print (',');
f.generable (comp);
}
f.print (')');
}

// If a super class is defined and is not "Object"
// Note: Records cannot extend classes (they implicitly extend java.lang.Record)
boolean bHasSuperClass = false;
if (m_aSuperClass != null && m_aSuperClass != owner ().ref (Object.class))
if (!isRecord () && m_aSuperClass != null && m_aSuperClass != owner ().ref (Object.class))
{
bHasSuperClass = true;
f.newline ().indent ().print ("extends").generable (m_aSuperClass).newline ().outdent ();
Expand Down Expand Up @@ -727,7 +884,23 @@ protected void declareBody (@NonNull final IJFormatter f)
if (m_aInstanceInit != null)
f.newline ().statement (m_aInstanceInit);

// All constructors
// Compact constructor for records (special syntax without parameter list)
if (m_aCompactConstructor != null)
{
f.newline ();
// Output javadoc if present
if (m_aCompactConstructor.hasJavadoc ())
f.generable (m_aCompactConstructor.javadoc ());
// Output annotations
for (final JAnnotationUse annotation : m_aCompactConstructor.annotations ())
f.generable (annotation).newline ();
// Output modifiers and name only (no parentheses)
f.generable (m_aCompactConstructor.mods ()).id (name ());
// Output body
f.statement (m_aCompactConstructor.body ());
}

// All regular constructors
for (final JMethod m : m_aConstructors)
f.newline ().declaration (m);

Expand Down
13 changes: 13 additions & 0 deletions jcodemodel/src/main/java/com/helger/jcodemodel/JDocComment.java
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,19 @@ public JCommentPart addParam (@NonNull final JVar sParam)
return addParam (sParam.name ());
}

/**
* Append a text to an @param tag for a record component.
*
* @param aComponent
* Record component to be added
* @return The created {@link JCommentPart}
* @since 4.2.0
*/
public JCommentPart addParam (@NonNull final JRecordComponent aComponent)
{
return addParam (aComponent.name ());
}

@Nullable
public JCommentPart removeParam (@Nullable final String sParam)
{
Expand Down
9 changes: 9 additions & 0 deletions jcodemodel/src/main/java/com/helger/jcodemodel/JMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,15 @@ public JDocComment javadoc ()
return m_aJDoc;
}

/**
* @return <code>true</code> if a javadoc comment has been set on this method.
* @since 4.2.0
*/
public boolean hasJavadoc ()
{
return m_aJDoc != null;
}

@Override
public void declare (@NonNull final IJFormatter f)
{
Expand Down
Loading