[stringtemplate-interest] Problems with List and Hashes revisited
Kunle Odutola
Kunle_Odutola at hotmail.com
Mon May 29 09:28:54 PDT 2006
> Hello all!
Hi Ulf,
> I am trying to read (more or less arbitrary) xml data
> from disc and feed them to a 'root template' which
> in turn calls other user-defined subtemplates.
>
> As my mail is rather long (again) the problem in short:
> I think (not entirely sure) ST# somtimes treats single
> values (which are aggregates (hashmap)) as lists.
As Ter mentioned in Jeremy's thread on the list, ST# (and ST) enumerates any
enumerable attributes by default.
> (It enumerates my hash)
> "Sometimes" meaning whenever I call a subtemplate
> that is defined without formal parameters OR
> I call a subtemplate (with or without formal parameters defined)
> in the <single value>:subtemplate() syntax.
> ??
> What do I have to do so that ST# can deal with
> <DataStruct>:subtemplate()
> whether DataStruct is a single value (or aggregate)
> or a List?
Nothing. If an attribute is enumerable it will be treated as such. Else it
won't be.
If you don't want an enumerable attribute to be treated as enumerable,
define a formal parameter and use the alternate syntax:
subtemplate(<parameterName>=<DataStruct>)
Which leads me to a rather relevant point, "template application and
template include have different semantics:
1. Template include (single-parameter case)
$bold(paragraph)$ ==> includes (i.e. invokes) 'boot' template setting it's
only parameter to $paragraph$
2. Template aplication
$paragraph:bold()$ ==>
IF $paragraph$ IS enumerable
FOREACH item in $paragraph$
applies (i.e. invokes) 'bold' template
setting it's only parameter to $item$
ELSE
applies (i.e. invokes) 'bold' template
setting it's only parameter to $paragraph$
> Sample xml-file:
>
> <?xml version="1.0"?>
> <root>
> <DataBlock1>
> <DataA>1_DataA</DataA>
> <DataB>1_DataB</DataB>
> <DataC>
> <SubC1>1_SubC1Content1</SubC1>
> <SubC1>1_SubC1Content2</SubC1>
> <SubC2>1_SubC2Content</SubC2>
> </DataC>
> </DataBlock1>
> <DataBlock1>
> <DataA>2_DataA</DataA>
> <DataB>2_DataB</DataB>
> <DataC>
> <SubC1>2_SubC1Content1</SubC1>
> <SubC1>2_SubC1Content2</SubC1>
> <SubC2>2_SubC2Content</SubC2>
> </DataC>
> </DataBlock1>
> <DataBlock2>
> <ItemB>ItemBContent</ItemB>
> </DataBlock2>
> </root>
So you have a toplevel Hashtable that contains ("key, [elem1,...elemN]"
means entry has key "key" and is a list of elem1...elemN):
DataBlock1, [DataA, DataB, DataC]
DataBlock2, ItemB where ItemB is not a list right?
{{
I presume that the extra DataBlock1 entry is a typo right?. If not, then
DataBlock1 is as below:
DataBlock1, [DataA, DataB, DataC, DataA, DataB, DataC]
}}
Looking at DataBlock1's value
DataA is [1_DataA] (or [1_DataA, 2_DataA] if both DataBlock1 entries are
valid - same for others)
DataB is [1_DataB]
DataC is [SubC1, SubC1, SubC3] where each SubC is a Hashtable right (or is
it *could be* a hashtable)?
> Sample TemplateGroup:
>
> group behaviourDemo;
>
> subsubTemplate(Data) ::= <<
> Input is : $Data$
> SubC1 is '$Data.SubC1$'
> SubC2 is '$Data.SubC2$'
>
> >>
>
> subsubTemplateB() ::= <<
> Input is : $it$
> SubC1 is '$it.SubC1$'
> SubC2 is '$it.SubC2$'
>
> >>
>
> subTemplate() ::= <<
> ------------------------------------------------------
> A=$it.DataA$
> B=$it.DataB$
>
> // both blocks SHOULD return the same:
>
> subsubTemplate(it.DataC):
> $subsubTemplate(it.DataC)$
> ----------------------------
> it.DataC:subsubTemplate():
> $it.DataC:subsubTemplate()$
> ------------------------------------------------------
>
> >>
Nope. As I mentioned above, they have different semantics.
$subsubTemplate(it.DataC)$ ==> invokes 'subsubTemplate' setting it's only
parameter to '$it.DataC$'
$it.DataC:subsubTemplate()$ ==>
IF '$it.DataC$' IS enumerable
FOREACH element in '$it.DataC$'
invokes 'subsubTemplate' setting it's
only parameter to '$element$'
ELSE
invokes 'subsubTemplate' setting it's
only parameter to '$it.DataC$'
>
> rootTemplate(DataBlock1,DataBlock2) ::= <<
> BEGIN (rootTemplate)
> Applying the subtemplate to DataBlock1:
>
> $DataBlock1:subTemplate()$
> ======================================================
> Accessing part of DataBlock1 directly (multi-valued):
> DataBlock1.DataA = $DataBlock1.DataA$
>
> Accessing part of DataBlock2 directly (single-valued):
> DataBlock2.ItemB = $DataBlock2.ItemB$
> END (rootTemplate)
> >>
>
> To archive my goal I have written a data-structure consisting
> of ArrayLists and Hashmaps (alternating).
> A hash being the root and containing Lists(of hashes) for multi-valued
> attributes or attributes (Strings) for single-values.
>
> DataBlock2.ItemB = $DataBlock2.ItemB$ from the rootTemplate always works.
(good!)
>
> DataBlock1.DataA = $DataBlock1.DataA$ does not normally work because the
"answer"
> would be multi-valued.
> I got this to work too by writing a List which implements a this[string]
accessor that
> iterates over the list and constructs a new list from the matching
attributes of the
> hashmaps in the list. (Essentially skipping one "layer" of the data
structure)
> @Kunle: This is how I solved the last problem I mailed you about.
> In the example my list implementation returns [1_DataA,2_DataA] if called
as
> myList_DataBlock1["DataA"] (Not necessarily stings but whatever the hashes
contain at key "DataA").
Since they are indexed by (and accessed by) a string key (not uniquely I now
understand), why not just use an IDictionary?. The separate entries for keys
like "DataA" can then held in a list.
If preserving insertion order is important, you can develop a specialized
IDictionary. ST#'s HashList may provide pointers there.
> One last problem which I feel is related to the " question regarding
specialcasingof IList"
> previously on the mailinlist.
> The call to the subsubTemplate!
>
> As you see from the code I have given two implementations.
> The one with the named parameter works IF (and only if) I call it
> $subsubTemplate(it.DataC)$
> My preferred version of
> $it.DataC:subsubTemplate()$
> does NOT work.
It does work as advertised. If the attribute is multi-valued (i.e.
enumerable), it will be treated as such. Using explicit formal parameters
affords you some control over the process. You may choose to never enumerate
by only accessing properties directly for instance.
Incidentally special-casing of IList simply means that IList is *always*
regarded as enumerable.
Given two IList instances list1 = {1,2,3} and list2 = {"a", "b", "c"}:
st.SetAttribute("data", list1);
st.SetAttribute("data", list2);
would result in:
$data$ -> [1, 2, 3, "a", "b", "c"]
If list1 and list2 were not ILists, you would get:
$data$ -> [list1, list2].
> Neither does the subsubTemplateB in any version
It can't. The template application logic controls the enumeration in this
case not the template.
> From the output below one can see that $it.DataC:subsubTemplate()$
> gives a list to subsubTemplate() when it SHOULD be a Hash containing
> a list (of 2 items) and a single value.
> Any idea why this is the case?
IDictionary objects are enumerable so that is the expected behaviour. You
should pass your hashtable as a formal parameter if you don't want it
enumerated by the template application logic.
Good luck.
Kunle
More information about the stringtemplate-interest
mailing list