private void EndElement()
{
MethodInfo facMethod = null;
object[] facArgs;
QilList list;
QilNode nd;
ReaderAnnotation ann;
list = _stk.Pop();
ann = (ReaderAnnotation)list.Annotation;
// Special case certain element names
string s = _r.LocalName;
switch (_r.LocalName)
{
case "QilExpression":
{
Debug.Assert(list.Count > 0, "QilExpression node requires a Root expression");
QilExpression qil = _f.QilExpression(list[list.Count - 1]);
// Be flexible on order and presence of QilExpression children
for (int i = 0; i < list.Count - 1; i++)
{
switch (list[i].NodeType)
{
case QilNodeType.True:
case QilNodeType.False:
qil.IsDebug = list[i].NodeType == QilNodeType.True;
break;
case QilNodeType.FunctionList:
qil.FunctionList = (QilList)list[i];
break;
case QilNodeType.GlobalVariableList:
qil.GlobalVariableList = (QilList)list[i];
break;
case QilNodeType.GlobalParameterList:
qil.GlobalParameterList = (QilList)list[i];
break;
}
}
nd = qil;
break;
}
case "ForwardDecls":
_inFwdDecls = false;
return;
case "Parameter":
case "Let":
case "For":
case "Function":
{
string id = ann.Id;
QilName name = ann.Name;
Debug.Assert(id != null, _r.LocalName + " must have an id attribute");
Debug.Assert(!_inFwdDecls || ann.XmlType != null, "Forward decl for " + _r.LocalName + " '" + id + "' must have an xmlType attribute");
// Create node (may be discarded later if it was already declared in forward declarations section)
switch (_r.LocalName)
{
case "Parameter":
Debug.Assert(list.Count <= (_inFwdDecls ? 0 : 1), "Parameter '" + id + "' must have 0 or 1 arguments");
Debug.Assert(ann.XmlType != null, "Parameter '" + id + "' must have an xmlType attribute");
if (_inFwdDecls || list.Count == 0)
nd = _f.Parameter(null, name, ann.XmlType);
else
nd = _f.Parameter(list[0], name, ann.XmlType);
break;
case "Let":
Debug.Assert(list.Count == (_inFwdDecls ? 0 : 1), "Let '" + id + "' must have 0 or 1 arguments");
if (_inFwdDecls)
nd = _f.Let(_f.Unknown(ann.XmlType));
else
nd = _f.Let(list[0]);
break;
case "For":
Debug.Assert(list.Count == 1, "For '" + id + "' must have 1 argument");
nd = _f.For(list[0]);
break;
default:
Debug.Assert(list.Count == (_inFwdDecls ? 2 : 3), "Function '" + id + "' must have 2 or 3 arguments");
if (_inFwdDecls)
nd = _f.Function(list[0], list[1], ann.XmlType);
else
nd = _f.Function(list[0], list[1], list[2], ann.XmlType != null ? ann.XmlType : list[1].XmlType);
break;
}
// Set DebugName
if (name != null)
((QilReference)nd).DebugName = name.ToString();
if (_inFwdDecls)
{
Debug.Assert(!_scope.ContainsKey(id), "Multiple nodes have id '" + id + "'");
_fwdDecls[id] = nd;
_scope[id] = nd;
}
else
{
if (_fwdDecls.ContainsKey(id))
{
// Replace forward declaration
Debug.Assert(_r.LocalName == Enum.GetName(typeof(QilNodeType), nd.NodeType), "Id '" + id + "' is not not bound to a " + _r.LocalName + " forward decl");
nd = _fwdDecls[id];
_fwdDecls.Remove(id);
if (list.Count > 0) nd[0] = list[0];
if (list.Count > 1) nd[1] = list[1];
}
else
{
// Put reference in scope
Debug.Assert(!_scope.ContainsKey(id), "Id '" + id + "' is already in scope");
_scope[id] = nd;
}
}
nd.Annotation = ann;
break;
}
case "RefTo":
{
// Lookup reference
string id = ann.Id;
Debug.Assert(id != null, _r.LocalName + " must have an id attribute");
Debug.Assert(_scope.ContainsKey(id), "Id '" + id + "' is not in scope");
_stk.Peek().Add(_scope[id]);
return;
}
case "Sequence":
nd = _f.Sequence(list);
break;
case "FunctionList":
nd = _f.FunctionList(list);
break;
case "GlobalVariableList":
nd = _f.GlobalVariableList(list);
break;
case "GlobalParameterList":
nd = _f.GlobalParameterList(list);
break;
case "ActualParameterList":
nd = _f.ActualParameterList(list);
break;
case "FormalParameterList":
nd = _f.FormalParameterList(list);
break;
case "SortKeyList":
nd = _f.SortKeyList(list);
break;
case "BranchList":
nd = _f.BranchList(list);
break;
case "XsltInvokeEarlyBound":
{
Debug.Assert(ann.ClrNamespace != null, "XsltInvokeEarlyBound must have a clrNamespace attribute");
Debug.Assert(list.Count == 2, "XsltInvokeEarlyBound must have exactly 2 arguments");
Debug.Assert(list.XmlType != null, "XsltInvokeEarlyBound must have an xmlType attribute");
MethodInfo mi = null;
QilName name = (QilName)list[0];
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
{
Type t = asm.GetType(ann.ClrNamespace);
if (t != null)
{
mi = t.GetMethod(name.LocalName);
break;
}
}
Debug.Assert(mi != null, "Cannot find method " + ann.ClrNamespace + "." + name.ToString());
nd = _f.XsltInvokeEarlyBound(name, _f.LiteralObject(mi), list[1], ann.XmlType);
break;
}
default:
{
// Find factory method which will be used to construct the Qil node
Debug.Assert(s_nameToFactoryMethod.ContainsKey(_r.LocalName), "Method " + _r.LocalName + " could not be found on QilFactory");
facMethod = s_nameToFactoryMethod[_r.LocalName];
Debug.Assert(facMethod.GetParameters().Length == list.Count, "NodeType " + _r.LocalName + " does not allow " + list.Count + " parameters");
// Create factory method arguments
facArgs = new object[list.Count];
for (int i = 0; i < facArgs.Length; i++)
facArgs[i] = list[i];
// Create node and set its properties
nd = (QilNode)facMethod.Invoke(_f, facArgs);
break;
}
}
nd.SourceLine = list.SourceLine;
// Add node to its parent's list
_stk.Peek().Add(nd);
}