Visitar URL original
Support for Java records · Issue #313 · antlr/stringtemplate4 · GitHub
Skip to content

Support for Java records #313

Open
Open

Description

@kjsmita6

Java 14 introduced the record keyword, which allows the creation of a lightweight "class" which really only holds data, similar to a struct in C for example. StringTemplate does not support records because the way records define fields/method is different.

Say I have a record Person (with the equivalent class after it) defined as

/*
 * Lightweight Person record, only one line.
 */
public record Person(String name, int age) {
    // Nothing inside
}

/*
 * This class is equivalent to the record above, but much more code needs to be written.
 */
public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String name() {
        return this.name;
    }

    public int age() {
        return this.age;
    }
}

The record will have two methods, name(), and age(), for getting the name and age fields.

Person person = new Person("John Doe", 30);
assert "John Doe".equals(person.name()) && person.age() == 30;

Related to #302, the fields of a record are private, and the methods do not follow StringTemplate's findMember method search pattern of getField, isField, and hasField:

// try getXXX and isXXX properties, look up using reflection
String methodSuffix = Character.toUpperCase(memberName.charAt(0)) +
memberName.substring(1, memberName.length());
            
member = tryGetMethod(clazz, "get" + methodSuffix);
if (member == null) {
    member = tryGetMethod(clazz, "is" + methodSuffix);
    if (member == null) {
        member = tryGetMethod(clazz, "has" + methodSuffix);
    }
}

if (member == null) {
    // try for a visible field
    member = tryGetField(clazz, memberName);
}

The addition of a tryGetMethod(clazz, memberName) would solve this because the methods of a record are public. Or, fix the issue of #302, but doing this would help as well.

Activity

  1. vorburger commented on Dec 10, 2023

    @vorburger
  2. starksm64 commented on Jun 27, 2024

    @starksm64

    Here is a generic adaptor for records that I have used.

    import org.stringtemplate.v4.Interpreter;
    import org.stringtemplate.v4.ST;
    import org.stringtemplate.v4.misc.ObjectModelAdaptor;
    import org.stringtemplate.v4.misc.STNoSuchPropertyException;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.RecordComponent;
    
    public class RecordAdaptor<R extends Record> extends ObjectModelAdaptor<R> {
        public Object getProperty(Interpreter interpreter, ST self, R rtype, Object property, String propertyName)
                throws STNoSuchPropertyException {
            RecordComponent[] components = rtype.getClass().getRecordComponents();
            for (RecordComponent component : components) {
                if (component.getName().equals(propertyName)) {
                    try {
                        return component.getAccessor().invoke(rtype);
                    } catch (IllegalAccessException|InvocationTargetException e) {
                        throw new STNoSuchPropertyException(e, rtype, propertyName);
                    }
                }
            }
            return super.getProperty(interpreter,self,rtype,property,propertyName);
        }
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions