Skip to content
Ravi Chodavarapu edited this page Jun 9, 2016 · 4 revisions

One of the primary utilities in datamill is an API for performing reflection on your Java classes. A core concept in this reflection API is an outline. An outline provides an easy way for you to get the names of the various properties and methods of your classes. For example, let's build an outline for a simple entity:

import foundation.stack.datamill.reflection.Outline;
import foundation.stack.datamill.reflection.OutlineBuilder;

public class Main {
    public class SystemUser {
        private String firstName;

        public String getFirstName() { return firstName; }
    }

    public static void main(String[] arguments) {
        Outline<SystemUser> userOutline = new OutlineBuilder(SystemUser.class).defaultSnakeCased().build();
    }
}

Using this outline, we can now get the name of the class itself, as well as the property:

String entityName = userOutline.name(); // returns "system_user"
String propertyName = userOutline.name(userOutline.members().getFirstName()); // returns "first_name"

The name methods return snake_cased names. This is because we built an outline, calling defaultSnakeCased() on the builder. We could have defaulted to having camelCased names by calling defaultcamelCased() on the builder instead. The outline also has specific methods for obtaining camelCased, snake_cased, and pluralized names (using an English inflector library).

Take a closer look at the way property names are obtained. Calling the members() method on an outline returns a special proxy instance of the entity being outlined. In this case, we have a special proxy instance of SystemUser. Calling the getFirstName getter on this proxy instance does not return anything meaningful. Instead, when you make the getter call, a record is kept of the getter call. When the name method is subsequently called on the outline, we take a look at the record of the call we made to the outline proxy's getter method. The name method returns the name of the property whose getter or setter was last invoked. This is the reason why userOutline.name(userOutline.members().getFirstName()) returns "first_name".

Note that this mechanism is thread-safe so that an outline can be safely re-used in multiple threads and still return the correct name. Note also that this mechanism means that we are not resorting to using strings.

Here is another example showing outlines at work:

import foundation.stack.datamill.reflection.Outline;
import foundation.stack.datamill.reflection.OutlineBuilder;

public class Main {
    public class NewsStream {
        private String title;
        private String description;
        private String imageUrl;

        public String getTitle() { return title; }
        public void setTitle(String title) { this.title = title; }
        
        public String getDescription() { return description; }
        public void setDescription(String description) { this.description = description; }
        
        public String getImageUrl() { return description; }
        public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
    }

    public static void main(String[] arguments) {
        Outline<NewsStream> streamOutline = new OutlineBuilder(NewsStream.class).defaultCamelCased().build();
        
        String entityName = streamOutline.name(); // returns "NewsStream"
        String titleName = streamOutline.name(streamOutline.members().getTitle()); // returns "title"
        String imageUrlName = streamOutline.name(streamOutline.members().getImageUrl()); // returns "imageUrl"
        String descriptionName = streamOutline.name(members -> members.setDescription("")); // returns "description"
    }
}

Clone this wiki locally