Upgrade custom Control rendering code to use new
render
method. This method allows improved rendering performance, because all controls can be
rendered from a single buffer that is created when rendering starts.
If your created custom controls override toString(), please adapt it to the following pattern. From this:
public class HtmlTable extends AbstractControl {
...
public String toString() {
int estimatedControlSize = 1000;
HtmlStringBuffer buffer = new HtmlStringBuffer(estimatedControlSize);
// Rendering Start
buffer.elementStart("table");
appendAttributes(buffer);
buffer.elementClose();
renderRows(buffer);
buffer.closeElement("table");
// Rendering End
return buffer.toString();
}
}
to this:
public class HtmlTable extends AbstractControl {
...
public void render(HtmlStringBuffer buffer) {
// Rendering Start
buffer.elementStart("table");
appendAttributes(buffer);
buffer.elementClose();
renderRows(buffer);
buffer.closeElement("table");
// Rendering End
}
public String toString() {
int estimatedControlSize = 1000;
HtmlStringBuffer buffer = new HtmlStringBuffer(estimatedControlSize);
render(buffer);
return buffer.toString();
}
}
Note, the code between the commented section, was moved from toString to the render method.
Also note that invoking a Control's toString()
method still outputs the same HTML representation, as toString() delegates to the
render
method.
Please note a common problem when overriding render in custom components, is
invoking super.toString() in order to render the Control's default
markup:
public class CustomField extends Field {
...
public void render(HtmlStringBuffer buffer) {
String field = super.toString(); // BEWARE this line will cause StackOverflowError
...
}
public String toString() {
HtmlStringBuffer buffer = new HtmlStringBuffer();
render(buffer);
return buffer.toString();
}
}
The highlighted line above will cause a StackOverflowError, meaning an infinite
loop was encountered. The reason for the error will become obvious when
tracing the sequence of calls:
- The first statement in the method CustomField.render invokes
super.toString. In this example the super class is Field, thus
the actual method invoked is Field.toString
- Field.toString performs its own rendering by invoking Field.render.
However since CustomField overrides Field.render, Field.toString
will invoke CustomField.render instead.
- This leads us back to step 1. where CustomField.render invokes
super.toString. This creates the infinite loop and leads to the StackOverflowError.
The fix is straight forward. If you override a Control's render
method, but still want to render the Control's default markup, invoke
super.render instead of super.toString.
Here is the correct version:
public class CustomField extends Field {
...
public void render(HtmlStringBuffer buffer) {
super.render(buffer); // NOTE StackOverflowError won't occur
...
}
public String toString() {
HtmlStringBuffer buffer = new HtmlStringBuffer();
render(buffer);
return buffer.toString();
}
}