I was probably spouting off a bit too abstractly, but here's an
example of what I was getting at. I hope it shows how component trees
can be constructed efficiently and how bindings can be compiled
rather than reflected.
I think it's somewhat interesting that this pushes the controller out
of the component hierarchy. If all of the components know how to
reset securely at the end of a request cycle, then all that's
necessary is to have the component null out the controller, and
reinstantiate it the next request cycle. So the component tree can be
reused, and the controller can be ignorant.
I'm sure all this is nonsense, but it's what I'd like to see…
===== mydogs_en.html =====
01 <html>
02 <body>
03 <table>
04 <tr>
05 <th>Name</th>
06 <th>Breed</th>
07 </tr>
08 <tbody jwcid="dogLoop"><tr>
09 <td><span jwcid="dogName">Name</span></td>
10 <td><span jwcid="dogBreed">breed</span></td>
11 </tr></tbody>
12 </table>
13 </body>
14 </html>
===== mydogs.page =====
01 <page-specification>
02 <component id="dogLoop" type="For">
03 <binding name="source">dogs</binding>
04 <binding name="value">dog</binding>
05 </component>
06 <component id="dogName" type="Insert">
07 <binding name="value">dog.name</binding>
08 </component>
09 <component id="dogBreed" type="Insert">
10 <binding name="value">dog.breed</binding>
11 </component>
12 </page-specification>
===== mydogs.java =====
01 public class mydogs {
02 List<Dog> dogs = new ArrayList<Dog>();
03 public Example() {
04 dogs.add(new Dog(1, "Tammy", "Sheltie"));
05 dogs.add(new Dog(2, "Hagar", "Yellow Lab"));
06 dogs.add(new Dog(3, "Conrad", "Lab Mix"));
07 }
08 public List<Dog> getDogs() { return dogs; }
09 public Dog getDog() { return dog; }
10 public void setDog(Dog value) { dog = value; }
11 }
===== Runtime Codegen =====
// This is a translation of the component specification.
abstract class _mydogs_page extends Component {
mydogs controller;
For dogLoop;
Insert dogName;
Insert dogBreed;
public _mydogs_page() {
controller = new Dogs();
nameHeader = new Insert();
nameHeader.setMessage("Name"); // Presumably to be read from
the messages file…
breedHeader = new Insert();
breedHeader.setMessage("Breed"); // Presumably read from the
messages file…
dogLoop = new For();
dogName = new Insert();
dogBreed = new Insert();
// There are a bunch of obvious fiddly little details to
this that I'm glossing over.
// But the point is that bindings can be built without
using reflection.
dogLoop.addBinding(new Binding(
public void read() {
// #line 03 Dogs.page
dogLoop.setSource(controller.getDogs());
}
public void write() {
// #line 03 Dogs.page
controller.setDogs(dogLoop.getSource());
}
));
dogLoop.getContent().addBinding(new Binding(
public void read() {
// #line 04 Dogs.page
controller.setDog(dogLoop.value());
}
public void write() {
// #line 03 Dogs.page
dogLoop.setValue(controller.getDog());
}
));
dogName.addBinding(new Binding{
public void read() {
// #line 07 Dogs.page
dogName.setValue(controller.getDog().getName());
}
public void write() {
// #line 07 Dogs.page
controller.getDog().setName((String) dogName.getValue
());
}
});
dogBreed.addBinding(new Binding{
public void read() {
// #line 10 Dogs.page
dogBreed.setValue(controller.getDog().getBreed());
}
public void write() {
// #line 10 Dogs.page
controller.getDog().setBreed((String)
dogBreed.getValue());
}
});
}
}
// And this is a translation of the template.
class _mydogs_en_html extends _mydogs_page {
public _mydogs_en_html() {
// Here, we build literals for all the static text from the
template
Literal _c1 = new Literal("<html>\r\n<body>\r\n<table>\r\n
<tr>\r\n <th>Name</th>\r\n <th>Breed</th>\r\n </tr>\r\n ");
Literal _c2 = new Literal("<tr>\r\n <td>");
Literal _c3 = new Literal("</td>\r\n <td>");
Literal _c4 = new Literal("</td>\r\n </tr>");
Literal _c5 = new Literal("\r\n</table>\r\n</body>\r\n</html>
\r\n");
// Then we interleave them with the component specification
according to the order in the file.
setChildren(new Component[]{_c1, dogLoop, _c5});
dogLoop.getContent().setChildren(new Component[]{_c2,
dogName, _c3, dogBreed, _c4});
}
}
— G