protected void addTag(Tag tag, Dictionary<string, string> cleanAttributes, Dictionary<string, object> tagProperties) { // Used to add a tag to the stack. Various properties can be passed in the dictionary // as being information required by the tag. // Currently supported properties are: // 'command' - The (command,args) tuple associated with this command // 'originalAtts' - The original attributes that include any metal/tal attributes // 'endTagSymbol' - The symbol associated with the end tag for this element // 'popFunctionList' - A list of functions to execute when this tag is popped // 'singletonTag' - A boolean to indicate that this is a singleton flag tag.Attributes = cleanAttributes; // Add the tag to the tagStack (list of tuples (tag, properties, useMacroLocation)) TALCommand command = (TALCommand)(tagProperties.ContainsKey("command") ? tagProperties["command"] : null); Dictionary<string, string> originalAtts = (Dictionary<string, string>)(tagProperties.ContainsKey("originalAtts") ? tagProperties["originalAtts"] : null); int singletonTag = (int)(tagProperties.ContainsKey("singletonTag") ? tagProperties["singletonTag"] : 0); TagInfo ti = new TagInfo(); if (command != null) { if (command.ID == Constants.METAL_USE_MACRO) { ti.Tag = tag; ti.Properties = tagProperties; ti.UseMacroLocation = this.commandList.Count + 1; } else { ti.Tag = tag; ti.Properties = tagProperties; ti.UseMacroLocation = -1; } } else { ti.Tag = tag; ti.Properties = tagProperties; ti.UseMacroLocation = -1; } this.tagStack.Add(ti); if (command != null) { // All tags that have a TAL attribute on them start with a 'start scope' TALCommand cmd = new TALCommand(); cmd.Tag = this.currentStartTag; cmd.ID = Constants.TAL_START_SCOPE; cmd.Attributes = new List<object>(); cmd.Attributes.Add(originalAtts); cmd.Attributes.Add(cleanAttributes); this.AddCommand(cmd); // Now we add the TAL command this.AddCommand(command); } else { // It's just a straight output, so create an output command and append it TALCommand cmd = new TALCommand(); cmd.Tag = this.currentStartTag; cmd.ID = Constants.TAL_OUTPUT; cmd.Attributes = new List<object>(); cmd.Attributes.Add(this.tagAsText(tag, singletonTag)); this.AddCommand(cmd); } }
public TemplateParseException(Tag tag, string errorDescription) : base(errorDescription) { this.m_Tag = tag; this.m_ErrorDescription = errorDescription; }
public TALProgram Compile(string source, string sourcePath) { // Initialise a template compiler. this.commandList = new List<TALCommand>(); this.tagStack = new List<TagInfo>(); this.symbolLocationTable = new Dictionary<int, int>(); this.macroMap = new Dictionary<string, TALSubProgram>(); this.imports = new HashSet<string>(); this.endTagSymbol = 1; this.currentStartTag = null; XmlReader reader = null; StringReader inputReader = null; try { inputReader = new StringReader(source); XmlTextReader xmlTextReader = new XmlTextReader(inputReader); xmlTextReader.XmlResolver = null; xmlTextReader.Namespaces = false; XmlReaderSettings readerSettings = new XmlReaderSettings(); readerSettings.ProhibitDtd = false; reader = XmlReader.Create(xmlTextReader, readerSettings); while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element) { Tag tag = new Tag(); tag.SourcePath = sourcePath; IXmlLineInfo li = reader as IXmlLineInfo; if (li != null && li.HasLineInfo()) { tag.LineNumber = li.LineNumber; tag.LinePosition = li.LinePosition; } tag.Name = reader.Name; tag.Attributes = new Dictionary<string, string>(); string atttribs = ""; for (int i = 0; i < reader.AttributeCount; i++) { reader.MoveToAttribute(i); atttribs += string.Format(" {0}=\"{1}\"", reader.Name, reader.Value); tag.Attributes.Add(reader.Name, reader.Value); } reader.MoveToElement(); if (reader.IsEmptyElement) { handle_startendtag(tag); } else { handle_starttag(tag); } } else if (reader.NodeType == XmlNodeType.EndElement) { Tag tag = new Tag(); tag.SourcePath = sourcePath; IXmlLineInfo li = reader as IXmlLineInfo; if (li != null && li.HasLineInfo()) { tag.LineNumber = li.LineNumber; tag.LinePosition = li.LinePosition; } tag.Name = reader.Name; handle_endtag(tag); } else if (reader.NodeType == XmlNodeType.DocumentType) { handle_doctype(reader); } else if (reader.NodeType == XmlNodeType.Comment) { handle_comment(reader.Value); } else if (reader.NodeType == XmlNodeType.CDATA) { handle_cdata(reader.Value); } else if (reader.NodeType == XmlNodeType.XmlDeclaration) { handle_xmldecl(reader.Name, reader.Value); } else if (reader.NodeType == XmlNodeType.Whitespace || reader.NodeType == XmlNodeType.SignificantWhitespace || reader.NodeType == XmlNodeType.Text) { handle_data(reader.Value); } else if (reader.NodeType == XmlNodeType.ProcessingInstruction) { handle_pi(reader.Name, reader.Value); } else if (reader.NodeType == XmlNodeType.EntityReference) { handle_entityref(reader.Name); } } } finally { if (reader != null) reader.Close(); if (inputReader != null) inputReader.Close(); } TALProgram template = new TALProgram(source, sourcePath, this.commandList, this.macroMap, this.imports, this.symbolLocationTable); return template; }
public string tagAsText(Tag tag, int singletonFlag) { // This returns a tag as text. // string result = "<"; result += tag.Name; foreach (KeyValuePair<string, string> att in tag.Attributes) { result += " "; result += att.Key; result += "=\""; result += Utils.EscapeXml(att.Value, true); result += "\""; } if (singletonFlag == 1) result += " />"; else result += ">"; return result; }
protected void popTag(Tag tag) { popTag(tag, 0); }
protected void popTag(Tag tag, int omitTagFlag) { // omitTagFlag is used to control whether the end tag should be included in the // output or not. In HTML 4.01 there are several tags which should never have // end tags, this flag allows the template compiler to specify that these // should not be output. while (this.tagStack.Count > 0) { TagInfo ti = this.tagStack[this.tagStack.Count - 1]; this.tagStack.RemoveAt(this.tagStack.Count - 1); Tag oldTag = ti.Tag; Dictionary<string, object> tagProperties = ti.Properties; int? endTagSymbol = (int?)(tagProperties.ContainsKey("endTagSymbol") ? tagProperties["endTagSymbol"] : null); List<VoidFuncDelegate> popCommandList = (List<VoidFuncDelegate>)(tagProperties.ContainsKey("popCommandList") ? tagProperties["popCommandList"] : null); int singletonTag = (int)(tagProperties.ContainsKey("singletonTag") ? tagProperties["singletonTag"] : 0); if (popCommandList != null) { foreach (VoidFuncDelegate func in popCommandList) { func(); } } if (oldTag.Name == tag.Name) { // We've found the right tag, now check to see if we have any TAL commands on it if (endTagSymbol != null) { // We have a command (it's a TAL tag) // Note where the end tag symbol should point (i.e. the next command) this.symbolLocationTable[(int)endTagSymbol] = this.commandList.Count; // We need a "close scope and tag" command TALCommand cmd = new TALCommand(); cmd.Tag = this.currentStartTag; cmd.ID = Constants.TAL_ENDTAG_ENDSCOPE; cmd.Attributes = new List<object>(); cmd.Attributes.Add(tag.Name); cmd.Attributes.Add(omitTagFlag); cmd.Attributes.Add(singletonTag); this.AddCommand(cmd); return; } else if (omitTagFlag == 0 && singletonTag == 0) { // We are popping off an un-interesting tag, just add the close as text // We need a "close scope and tag" command TALCommand cmd = new TALCommand(); cmd.Tag = this.currentStartTag; cmd.ID = Constants.TAL_OUTPUT; cmd.Attributes = new List<object>(); cmd.Attributes.Add("</" + tag.Name + ">"); this.AddCommand(cmd); return; } else { // We are suppressing the output of this tag, so just return return; } } else { // We have a different tag, which means something like <br> which never closes is in // between us and the real tag. // If the tag that we did pop off has a command though it means un-balanced TAL tags! if (endTagSymbol != null) { // ERROR string msg = string.Format("TAL/METAL Elements must be balanced - found close tag {0} expecting {1}", tag.Name, oldTag.Name); throw new TemplateParseException(oldTag, msg); } } } throw new TemplateParseException(null, string.Format("</{0}> {1}", tag.Name, "Close tag encountered with no corresponding open tag.")); }
protected void parseStartTag(Tag tag, Dictionary<string, string> attributes) { parseStartTag(tag, attributes, 0); }
protected void parseStartTag(Tag tag, Dictionary<string, string> attributes, int singletonElement) { // Note down the tag we are handling, it will be used for error handling during // compilation this.currentStartTag = new Tag(); this.currentStartTag.Name = tag.Name; this.currentStartTag.Attributes = attributes; this.currentStartTag.SourcePath = tag.SourcePath; this.currentStartTag.LineNumber = tag.LineNumber; this.currentStartTag.LinePosition = tag.LinePosition; // Look for tal/metal attributes List<int> foundTALAtts = new List<int>(); List<int> foundMETALAtts = new List<int>(); Dictionary<int, string> foundCommandsArgs = new Dictionary<int, string>(); Dictionary<string, string> cleanAttributes = new Dictionary<string, string>(); Dictionary<string, string> originalAttributes = new Dictionary<string, string>(); Dictionary<string, object> tagProperties = new Dictionary<string, object>(); List<VoidFuncDelegate> popTagFuncList = new List<VoidFuncDelegate>(); bool isTALElementNameSpace = false; string prefixToAdd = ""; tagProperties.Add("singletonTag", singletonElement); // Determine whether this element is in either the METAL or TAL namespace if (tag.Name.IndexOf(':') > 0) { // We have a namespace involved, so let's look to see if its one of ours string _namespace = tag.Name.Substring(0, tag.Name.IndexOf(':')); if (_namespace == this.metal_namespace_prefix) { isTALElementNameSpace = true; prefixToAdd = this.metal_namespace_prefix + ":"; } else if (_namespace == this.tal_namespace_prefix) { // This tag has not his own scope if (tag.Name == tal_namespace_omitscope) { tag.OmitTagScope = true; this.currentStartTag.OmitTagScope = true; } isTALElementNameSpace = true; prefixToAdd = this.tal_namespace_prefix + ":"; } if (isTALElementNameSpace) { // We should treat this an implicit omit-tag foundTALAtts.Add(Constants.TAL_OMITTAG); // Will go to default, i.e. yes foundCommandsArgs[Constants.TAL_OMITTAG] = ""; } } foreach (KeyValuePair<string, string> attr in attributes) { string att = attr.Key; string value = attr.Value; string commandAttName = ""; originalAttributes.Add(att, value); if (isTALElementNameSpace && !(att.IndexOf(':') > 0)) { // This means that the attribute name does not have a namespace, so use the prefix for this tag. commandAttName = prefixToAdd + att; } else { commandAttName = att; } if (att.Length > 4 && att.Substring(0, 5) == "xmlns") { // We have a namespace declaration. string prefix = att.Length > 5 ? att.Substring(6) : ""; if (value == Constants.METAL_NAME_URI) { // It's a METAL namespace declaration if (prefix.Length > 0) { this.metal_namespace_prefix_stack.Add(this.metal_namespace_prefix); this.setMETALPrefix(prefix); // We want this function called when the scope ends popTagFuncList.Add(this.popMETALNamespace); } else { // We don't allow METAL/TAL to be declared as a default string msg = "Can not use METAL name space by default, a prefix must be provided."; throw new TemplateParseException(this.currentStartTag, msg); } } else if (value == Constants.TAL_NAME_URI) { // TAL this time if (prefix.Length > 0) { this.tal_namespace_prefix_stack.Add(this.tal_namespace_prefix); this.setTALPrefix(prefix); // We want this function called when the scope ends popTagFuncList.Add(this.popTALNamespace); } else { // We don't allow METAL/TAL to be declared as a default string msg = "Can not use TAL name space by default, a prefix must be provided."; throw new TemplateParseException(this.currentStartTag, msg); } } else { // It's nothing special, just an ordinary namespace declaration cleanAttributes.Add(att, value); } } else if (this.tal_attribute_map.ContainsKey(commandAttName)) { // It's a TAL attribute int cmnd = this.tal_attribute_map[commandAttName]; if (cmnd == Constants.TAL_OMITTAG && isTALElementNameSpace) { //this.log.warn("Supressing omit-tag command present on TAL or METAL element"); } else { foundCommandsArgs.Add(cmnd, value); foundTALAtts.Add(cmnd); } } else if (this.metal_attribute_map.ContainsKey(commandAttName)) { // It's a METAL attribute int cmnd = this.metal_attribute_map[commandAttName]; foundCommandsArgs.Add(cmnd, value); foundMETALAtts.Add(cmnd); } else { cleanAttributes.Add(att, value); } } tagProperties.Add("popFunctionList", popTagFuncList); // This might be just content if ((foundTALAtts.Count + foundMETALAtts.Count) == 0) { // Just content, add it to the various stacks this.addTag(tag, cleanAttributes, tagProperties); return; } // Create a symbol for the end of the tag - we don't know what the offset is yet this.endTagSymbol += 1; tagProperties.Add("endTagSymbol", this.endTagSymbol); // Sort the METAL commands by priority. Priority is defined by opcode number, see Constants.METAL_* opcodes. foundMETALAtts.Sort(); // Sort the TAL commands by priority. Priority is defined by opcode number, see Constants.TAL_* opcodes. foundTALAtts.Sort(); // We handle the METAL before the TAL List<int> allCommands = new List<int>(); allCommands.AddRange(foundMETALAtts); allCommands.AddRange(foundTALAtts); int firstTag = 1; foreach (int talAtt in allCommands) { // Parse and create a command for each TALCommand cmnd = this.commandHandler[talAtt](foundCommandsArgs[talAtt]); if (cmnd != null) { if (firstTag == 1) { // The first one needs to add the tag firstTag = 0; tagProperties["originalAtts"] = originalAttributes; tagProperties["command"] = cmnd; this.addTag(tag, cleanAttributes, tagProperties); } else { // All others just append this.AddCommand(cmnd); } } } TALCommand cmd = new TALCommand(); cmd.Tag = this.currentStartTag; cmd.ID = Constants.TAL_STARTTAG; cmd.Attributes = new List<object>(); cmd.Attributes.Add(tag); cmd.Attributes.Add(singletonElement); if (firstTag == 1) { tagProperties["originalAtts"] = originalAttributes; tagProperties["command"] = cmd; this.addTag(tag, cleanAttributes, tagProperties); } else { // Add the start tag command in as a child of the last TAL command this.AddCommand(cmd); } }
protected void parseEndTag(Tag tag) { // Just pop the tag and related commands off the stack. this.popTag(tag); }
protected void handle_starttag(Tag tag, int singletonElement) { Dictionary<string, string> atts = new Dictionary<string, string>(); foreach (KeyValuePair<string, string> attInfo in tag.Attributes) { string att = attInfo.Key; string attValue = attInfo.Value; // We need to spot empty tal:omit-tags if (attValue == null) { if (att == this.tal_namespace_omittag) atts.Add(att, ""); else atts.Add(att, att); } else { // Expand any SGML entity references if (attValue.IndexOf('&') != -1 && attValue.IndexOf('&') < attValue.LastIndexOf(';')) { foreach (string entity in SGMLEntityNames.htmlNameToUnicodeNumber.Keys) { if (attValue.Contains(entity)) { attValue = attValue.Replace(entity, ((char)SGMLEntityNames.htmlNameToUnicodeNumber[entity]).ToString()); } } } atts.Add(att, attValue); } } if (Constants.HTML_FORBIDDEN_ENDTAG.ContainsKey(tag.Name.ToUpper())) { // This should have no end tag, so we just do the start and suppress the end this.parseStartTag(tag, atts, singletonElement); this.popTag(tag, 1); } else this.parseStartTag(tag, atts, singletonElement); }
protected void handle_starttag(Tag tag) { handle_starttag(tag, 0); }
protected void handle_startendtag(Tag tag) { this.handle_starttag(tag, 1); if (!Constants.HTML_FORBIDDEN_ENDTAG.ContainsKey(tag.Name.ToUpper())) this.handle_endtag(tag); }
protected void handle_endtag(Tag tag) { if (Constants.HTML_FORBIDDEN_ENDTAG.ContainsKey(tag.Name.ToUpper())) { //this.log.warn(string.Format("HTML 4.01 forbids end tags for the {0} element", tag)); } else { // Normal end tag this.popTag(tag); } }