[stringtemplate-interest] Why an == operator is sometimes necessary, and how to implement it (ST3/C#)

Harald Mueller harald_m_mueller at gmx.de
Fri Mar 12 11:15:22 PST 2010


Hi -

as we all know, ST does not have any expression operators in its language, and that is almost always a good thing. However, in a well-worn code generator we use to create triggers in an SQL database, we finally got to the point where we needed an equals operator in $if(...)$. Here is why; and how we implemented it.

The problem is as follows: Input to the generation is (a) a list of SQL tables; (b) the FROM part of an SQL statement, which is a tree of join nodes; each join node points to a table (and of course has information about the joined columns and inner/outer etc., but that's not important); and (c) a replacement string (typically "INSERTED" or "DELETED"; but there are more).
The job to do is to write a trigger for each table in the join tree, such that the table itself is replaced with the replacement string.

A small example:
(a) T1, T2
(b) ... FROM T1 INNER JOIN T2 INNER JOIN T3
(c) "INSERTED"
Result:
... FROM INSERTED INNER JOIN T2 INNER JOIN T3
... FROM T1 INNER JOIN INSERTED INNER JOIN T3

The example below is a simplified version of the above, however, its ST template marks nodes in a tree with an arrow (<--) if the node's "table name" is equal to the "replacement table".

Here is the model, together with the "equals operator":

    public class Table {
        public string Name;
    }

    public class Join {
        public readonly string Name;
        public readonly Table JoinedTable;
        public readonly List<Join> Children;
        public Join(string name, Table joinedTable, params Join[] children) {
            Name = name;
            JoinedTable = joinedTable;
            Children = new List<Join>(children);
        }
    }

Here are the templates - first the Main template, then the recursive one for printing the tree.

Main(Target, Tables) ::= <<
    $Tables:PrintTreeReplacingTable(Target=Target,Replace=it)$
>>

PrintTreeReplacingTable(Target,Replace) ::= <<
    $Target.Name$ $if(Target.NameIsReplace.(Replace.Name))$ <--	$endif$
	$Target.Children:PrintTreeReplacingTable(Target=it,Replace=Replace)$

>>

The important idea here is the expression

    Target.NameIsReplace.(Replace.Name)

where .NameEquals is a property that returns an IDictionary. The (Replacement.Name) then passes in a value which is in effect the parameter of a single-arg function which implements "Target.Name == Replace.Name" - that's the whole trick.

So we extend Join with the following additional property

    public IDictionary NameEquals {
        get {
            return new Equals(JoinedTable.Name);
        }
    }

and the class Equals, which is implemented as follows:

    class Equals : DictionaryBase, IDictionary {
        private readonly string _tn;

        public Equals(string tn) {
            _tn = tn;
        }

        bool IDictionary.Contains(object key) {
            return key.ToString() == _tn;
        }

        object IDictionary.this[object key] {
            get {
                return key.ToString() == _tn;
            }
            set { throw new NotImplementedException(); }
        }
    }

That's it. Maybe it helps someone.
And maybe someone's got an idea how this works without that IDictionary trick - but we didn't see how!

Regards
Harald M.

-- 
GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT!
Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01


More information about the stringtemplate-interest mailing list