Template inheritance
Template Group inheritance overview
A template group may derive from other template groups, thus inheriting all of the features (templates and maps) from the supergroup. The descendant template group can then add more templates and maps, override inherited templates and maps or modify inherited templates in a finer-grained manner using the template regions feature.
This article probably should be merged in with Group Files. Also, on some points it seemed out-of-date - I've made annotations in red where I think this occurs. - Graham Wideman 2009-05-21
Template Group inheritance
Recall that a StringTemplateGroup is a collection of related templates such as all templates associated with the look of a web site. If you want to design a second similar look for that site (such as for premium users), you don't really want to cut-n-paste the original template files for use in the new look. Subsequent changes to the original template files would not be propagated to the new look.
Just like you would do with a class definition in other languages, a template group may inherit features (templates and maps) from another template group, the supergroup. If template t is not found in a group, it is looked up in the supergroup, if present. This works regardless of whether you use a group file format or load templates from the disk via a StringTemplateGroup
object. Currently you cannot use the group file syntax to specify a supergroup. I am investigating how this should work. In the meantime, you must explicitly set the supergroup in code.
I think this is now handled by the syntax discussed in Group Files. - GW
group mygroup : supergroup; ...
From the unit tests, here is a simple inheritance of a template, bold
:
Java |
StringTemplateGroup supergroup = new StringTemplateGroup("super"); StringTemplateGroup subgroup = new StringTemplateGroup("sub"); supergroup.defineTemplate("bold", "<b>$it$</b>"); subgroup.setSuperGroup(supergroup); StringTemplate st = new StringTemplate(subgroup, "$name:bold()$"); st.setAttribute("name", "Terence"); String expecting = "<b>Terence</b>"; |
---|---|
C# |
StringTemplateGroup supergroup = new StringTemplateGroup("super"); StringTemplateGroup subgroup = new StringTemplateGroup("sub"); supergroup.DefineTemplate("bold", "<b>$it$</b>"); subgroup.SuperGroup = supergroup; StringTemplate st = new StringTemplate(subgroup, "$name:bold()$"); st.SetAttribute("name", "Terence"); string expecting = "<b>Terence</b>"; |
Python |
supergroup = stringtemplate3.StringTemplateGroup("super") subgroup = stringtemplate3.StringTemplateGroup("sub", superGroup=group) supergroup.defineTemplate("bold", "<b>$it$</b>") st = stringtemplate3.StringTemplate("$name:bold()$", group=subgroup) st["name"] = "Terence" expecting = "<b>Terence</b>" |
The supergroup has a bold definition but the subgroup does not. Referencing $name:bold()$
from a template in the subgroup works because StringTemplate looks into the supergroup if a referenced template is not found in the subgroup..
A template in a subgroup may override a template inhererited from a supergroup:
Java |
supergroup.defineTemplate("bold", "<b>$it$</b>"); subgroup.defineTemplate("bold", "<strong>$it$</strong>"); |
---|---|
C# |
supergroup.DefineTemplate("bold", "<b>$it$</b>"); subgroup.DefineTemplate("bold", "<strong>$it$</strong>"); |
Python |
supergroup.defineTemplate("bold", "<b>$it$</b>"); subgroup.defineTemplate("bold", "<strong>$it$</strong>"); |
And a template in a subgroup may refer to a template in a supergroup via super.
template()
:
Java |
StringTemplateGroup group = new StringTemplateGroup(...); StringTemplateGroup subGroup = new StringTemplateGroup(...); subGroup.setSuperGroup(group); group.defineTemplate("page", "$font()$:text"); group.defineTemplate("font", "Helvetica"); subGroup.defineTemplate("font", "$super.font()$ and Times"); StringTemplate st = subGroup.getInstanceOf("page"); |
---|---|
C# |
StringTemplateGroup group = new StringTemplateGroup(...); StringTemplateGroup subGroup = new StringTemplateGroup(...); subGroup.SuperGroup = group; group.DefineTemplate("page", "$font()$:text"); group.DefineTemplate("font", "Helvetica"); subGroup.DefineTemplate("font", "$super.font()$ and Times"); StringTemplate st = subGroup.GetInstanceOf("page"); |
Python |
group = stringtemplate3.StringTemplateGroup(...) subGroup = stringtemplate3.StringTemplateGroup(...) subGroup.setSuperGroup(group) group.defineTemplate("page", "$font()$:text") group.defineTemplate("font", "Helvetica") subGroup.defineTemplate("font", "$super.font()$ and Times") st = subGroup.getInstanceOf("page") |
The expression st.ToString()
results in "Helvetica and Times:text
".
Just like object-oriented programming languages, StringTemplate
has polymorphism. That is, template names are looked up dynamically relative to the invoking template's group.
The classic demonstration of dynamic message sends, for example, would be the following example (this catches my students all the time):
Java
class A { public void page() {bold();} public void bold() {System.out.println("A.bold");} } class B extends A { public void bold() {System.out.println("B.bold");} } ... A a = new B(); a.page();C#
class A { public void page() {bold();} override public void bold() {Console.Out.WriteLine("A.bold");} } class B : A { virtual public void bold() {Console.Out.WriteLine("B.bold");} } ... ... A a = new B(); a.page();This prints "
B.bold
" not "A.bold
" because the receiver determines how to answer a message not the type of the variable. So, I have created aB
object meaning that any message, such asbold()
, invoked will first look in classB
forbold()
.
Similarly, a template's group determines where it starts looking for a template. In this case, both super and sub groups define a bold
template mirroring the code above. Because I create template st
as a member of subGroup
, any reference to bold
(say while processing st.ToString()
) prompts StringTemplate to start looking for the bold
template in subGroup
, even though bold
is referenced via the page
template which is a member of the supergroup..
Java |
StringTemplateGroup group = new StringTemplateGroup("super"); StringTemplateGroup subGroup = new StringTemplateGroup("sub"); subGroup.setSuperGroup(group); group.defineTemplate("bold", "<b>$it$</b>"); group.defineTemplate("page", "$name:bold()$"); subGroup.defineTemplate("bold", "<strong>$it$</strong>"); StringTemplate st = subGroup.getInstanceOf("page"); st.setAttribute("name", "Ter"); String expecting = "<strong>Ter</strong>"; |
---|---|
C# |
StringTemplateGroup group = new StringTemplateGroup("super"); StringTemplateGroup subGroup = new StringTemplateGroup("sub"); subGroup.SuperGroup = group; group.DefineTemplate("bold", "<b>$it$</b>"); group.DefineTemplate("page", "$name:bold()$"); subGroup.DefineTemplate("bold", "<strong>$it$</strong>"); StringTemplate st = subGroup.GetInstanceOf("page"); st.SetAttribute("name", "Ter"); string expecting = "<strong>Ter</strong>"; |
Python |
group = stringtemplate3.StringTemplateGroup("super") subGroup = stringtemplate3.StringTemplateGroup("sub", superGroup=group) group.defineTemplate("bold", "<b>$it$</b>") group.defineTemplate("page", "$name:bold()$") subGroup.defineTemplate("bold", "<strong>$it$</strong>") st = subGroup.getInstanceOf("page") st["name"] = "Ter" expecting = "<strong>Ter</strong>" |
StringTemplate
group maps also inherit. If an attribute reference is not found, StringTemplate
looks for a map in its group with that name. If not found, the super group is checked.
See more extensive details regarding template and attribute lookup here: Template and attribute lookup rules