antlr / stringtemplate4

StringTemplate 4
http://www.stringtemplate.org
Other
955 stars 231 forks source link

Support for Java records #313

Open kjsmita6 opened 1 year ago

kjsmita6 commented 1 year ago

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.

vorburger commented 11 months ago

https://github.com/antlr/stringtemplate4/blob/master/doc/adaptors.md may be useful for you? Best of luck.

starksm64 commented 5 months ago

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);
    }
}