Donnerstag, 11. Juni 2015

FormGenerator: Automatic html forms from java objects

Purpose

One of Ruby on Rails' features I quite liked but missed in other frameworks, is code generation. The scaffolding produces forms and controllers for a given object automatically and saves the files in appropriate folders. This is mainly convention driven.

With Java, we have a nice type system, so why do I have to write forms over and over again? I used reflection to automatically generate an html form for a given object.

Algorithm

Basically, the FormGenerator gets an arbitrary object attached. On this object, first all fields and inherited fields have to be obtained. Since it would be useless to just process public fields, I had to use a small piece of utility code I found here.



public Iterable<Field> getFieldsUpTo(@Nonnull Class<?> startClass,
                                     @Nullable Class<?> exclusiveParent) {

  List<Field> currentClassFields = Lists.newArrayList(startClass.getDeclaredFields());
  Class<?> parentClass = startClass.getSuperclass();

  if (parentClass != null &&
     (exclusiveParent == null || !(parentClass.equals(exclusiveParent)))) {
    List<Field> parentClassFields =
                (List<Field>) getFieldsUpTo(parentClass, exclusiveParent);
    currentClassFields.addAll(parentClassFields);
  }

  return currentClassFields;
}


After the information about the object is obtained, the form fields are wrapped by a form begin/end pair. The private fields have to be made accassible - note the exception handling.


try {
  field.setAccessible(true);
} catch (SecurityException e) {
  Logger.getGlobal().info(String.format("Field %s can't be accessed, so no input for this field.", field.getName()));
  return result;
}


Depending on the type of the field, inputs should be generated. The types are determined at runtime, so a switch is needed for the value extraction.



if(type.equals(String.class)) {
  result += generate(formGenerator.getFieldName(field.getName()), (String) field.get(object));
} else if(type.equals(Boolean.class)) {
result += generate(formGenerator.getFieldName(field.getName()), (Boolean) field.get(object));
} else if(type.equals(boolean.class)) {
  result += generate(formGenerator.getFieldName(field.getName()), field.getBoolean(object));
} else if(type.equals(Integer.class)) {
  result += generate(formGenerator.getFieldName(field.getName()), (Integer) field.get(object));
} else if(type.equals(int.class)) {
  result += generate(formGenerator.getFieldName(field.getName()), field.getInt(object));
} else if(type.equals(Float.class)) {
  result += generate(formGenerator.getFieldName(field.getName()), (Float) field.get(object));
} else if(type.equals(float.class)) {
  result += generate(formGenerator.getFieldName(field.getName()), field.getFloat(object));
} else if(type.equals(List.class)) {
  result += "<div class=\"well\">" + newLine;
  result += String.format("<div id=\"%s\">%s", formGenerator.getFieldName(field.getName()), newLine);
  result += generate(formGenerator.getFieldName(field.getName()), (List) field.get(object), (ParameterizedType) field.getGenericType());
  result += "</div>" + newLine;
  result += "</div>" + newLine;
}

After all primitive types and lists/collections/iterables or whatever are treated, this method can be called recursively to treat arbitrary classes for fields again. It's probably not the best idea to hardcode css classes into this methods, but for my purposes and right now, bootstrap is the only ui framework I satisfy.

Attention has to be paid for generics. For lists, I implemented a treatment in the following way.



static String generate(String fieldName, List value, ParameterizedType type) {
    StringBuilder builder = new StringBuilder();

    int counter = 0;
    for (Object listItem : value) {
        Class<?> componentClass = (Class<?>) type.getActualTypeArguments()[0];
        String listItemFieldName = fieldName + "_" + counter;
        try {
            Method InputGeneratorMethod = InputGenerator.class.getDeclaredMethod("generate", String.class, componentClass);
            String generatedFormElements = (String) InputGeneratorMethod.invoke(null, listItemFieldName, listItem);
            builder.append(generatedFormElements);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        counter++;
    }

    return builder.toString();
}

The method invocation can be done via reflection again. With the given type, the correct overloaded method is chosen at runtime. However, this could lead to exceptions that one has to handle properly *cough*.

Results

The following class definition is used in my tests.


class MyClass {
    private String testString = "testStringValue";
    private Boolean testBoolean = true;
    private Boolean testBooleanObject = false;
    private int testInt = 12;
    private Integer testInteger = 14;
    private float testFloat = 12.0f;
    private Float testFloatObject = 14.0f;
    private List<String> testStringList = new ArrayList() {{
        add("a0");
        add("a1");
    }};
    private List<Boolean> testBooleanList = new ArrayList() {{
        add(true);
        add(false);
    }};
}

And the generated form looks like this.


It's just an early version yet, there is plenty of stuff left to do. For example the recursive generation for arbirtary objects. Or an injector for style classes. Or field annotations for named fields and non-exported or disabled fields. After this, I'll try to write a reflective argument extractor for ninja, that is capable of parsing request data from generated forms and propagate it back.

Mittwoch, 10. Juni 2015

Ninja framework: Argument extractors

The last post introduced a simple way to automatically extract a collection of objects from a form and inject it into a controller's action. However, when classes get more complex, this option is not the best one because of two reasons: Method signatures get bloated and the collections have to somehow get attached to the corresponding object (injected into the action as well) by hand. If one writes a new action and uses the built in functionality, it's possible that he forgets to update one of the instance's fields....saves...and boom: the object's data is gone.

The functionality can be gathered into an argument extractor. Sadly, the official documentation only shows an example where the session is used to extract a simple session cookie. But what if you have to get complex form data? Ideally, one wants a clean action method signature, where the instance is injected correctly. This can be done with simply with an annotation:


public Result saveTrip(Context context, @argumentextractors.Trip Trip trip) {

It's important to note, that you can't use other built-in extractors (Param, Params) any more, after you parsed the request. Additionally, your own extractor has to be the first extracting paramter in the signature.

The marker interface specifies the extractor class:

@WithArgumentExtractor(TripExtractor.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface Trip {
}

The magic then has to be implemented by yourself. Therefore, extend a BodyAsExtractorwhere T is the return type you want to extract. There are three methods to be overriden. I don't have a clue what the third one (getFieldName()) does, but the important one is

public Trip extract(Context context) {}

Your form data has to be gathered now. Took me some time to find out how to do this - actually, you can do


String body = "";
while (context.getReader().ready()) {
  body += context.getReader().readLine();
}

and that's it. I was'n able to use the InputStream the context provides directly. Now the ugly part. the params string of the form http://example.org/path/to/file?a=1&b=2&c=3 should result in a list of a=1, b=2, c=3. Since this is a common task, it's implemented in the apache commons htmlUtils - nice wordplay. I extracted some single methods from their library, because I only use a few ones. Now, you have to apply the parsed values by hand. To mention would be, that this can only work, if the keys you use to extract all the stuff don't change between forms. Otherwise, you would have to implement another extractor.


trip.getStops().clear();
for (int x = 2; x < params.size(); x++) {
  trip.getStops().add(params.get(x).getName());
}
return trip;

The nice thing is now, that everyone who uses this object class, can use the extractor and afterwards just has to save the instance regularly in the controller action:
manager.merge(trip);

I'm curious if this is the intended way to extract stuff from forms. It's a pity that such an important requirement isn't documented better.

Ninja framework: collection extration from forms

Ninja quickly became one of my favorite web frameworks. For REST, MVC, dependency injection, database and other basic stuff, it mostly is very convenient. But what about the more complicated things web development often demands? Because documentation is rather sparse for it, here's how you can use built in functionality to extract a collection of objects from a form.

My example has a simple edit form for a trip model.A trip can have multiple stops, for simplicity represented by a String. With a POST route in the TripsController, Ninja can automatically parse the request, extract your form data and inject the Trip instance into the method call - one has to add a Trip reference the controller's signature and it just works, how great is that:


public Result saveTrip(Context context, Trip trip) {

However, the documentation states, that the extraction only works with primitives and arrays of them. This means no other collections, like Lists, can be extracted automatically. But no one uses plain arrays as fields... So, an easy way to circumvent this limitation, is to add the given items within the collection to the form and provide the same name attribute for all of them:
<#list trip.stops as stop>
  <tr>
    <td><input type="text" class="form-control" id="stops[${stop_index}]" name="stops" value="${stop}" ></td>
  </tr>
</#list>

Then, add the String[] stops parameter to your signature and you're done.


public Result saveTrip(Context context, @Params("stops") String[] stops, Trip trip) {

In my case, I updated all of the trip instance's stops with the stops automatically injected and saved the objet. Can't get any easier, I think.

I'm not yet sure if this would work for more complex (means no-primitive type) objects. For this purpose, argument extractors were introduced. The documentation is again a bit sparse about them - a first try seemed that argument extractors that try to parse the request data for object extraction tend to be a bit hacky. Will be continued.