Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Java records #313

Open
kjsmita6 opened this issue Sep 19, 2023 · 2 comments
Open

Support for Java records #313

kjsmita6 opened this issue Sep 19, 2023 · 2 comments

Comments

@kjsmita6
Copy link

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
Copy link

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

@starksm64
Copy link

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
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants