public override void Visit (TagNode node)
		{
			debugString.Append ('\t', indent);
			debugString.Append (node.ToString ());
			debugString.AppendLine ();
			indent++;
		}
		public override void Visit (TagNode node)
		{
			if (0 == String.Compare (node.TagName, "asp:ContentPlaceHolder", StringComparison.OrdinalIgnoreCase)
			    && node.Attributes.IsRunAtServer ())
			{
				string id = node.Attributes["id"] as string;
				if (!String.IsNullOrEmpty (id))
					placeholders.Add (id);
			}
		}
		public OutlineNode (TagNode node)
		{
			Name = node.TagName;
			string att = null;
			if (node.Attributes != null)
				att = node.Attributes["id"] as string;
			if (att != null)
				Name = "<" + Name + "#" + att + ">";
			else
				Name = "<" + Name + ">";

			ILocation start = node.Location, end;
			if (node.EndLocation != null)
				end = node.EndLocation;
			else
				end = start;
			Location = new DomRegion (start.BeginLine, start.BeginColumn + 1, end.EndLine, end.EndColumn + 1);
		}
		public override void Visit (TagNode node)
		{
			if (!node.Attributes.IsRunAtServer ())
				return;
			
			string id = node.Attributes ["id"] as string;
			
			if (id == null)
				return;
			
			if (Members.ContainsKey (id)) {
				AddError (ErrorType.Error, node.Location, GettextCatalog.GetString ("Tag ID must be unique within the document: '{0}'.", id));
				return;
			}
			
			string [] s = node.TagName.Split (':');
			string prefix = (s.Length == 1)? "" : s[0];
			string name = (s.Length == 1)? s[0] : s[1];
			if (s.Length > 2) {
				AddError (ErrorType.Error, node.Location, GettextCatalog.GetString ("Malformed tag name: '{0}'.", node.TagName));
				return;
			}
			
			IType type = null;
			try {
				type = refMan.GetType (prefix, name, node.Attributes ["type"] as string);
			} catch (Exception e) {
				AddError (ErrorType.Error, node.Location, "Unknown parser error:" + e.ToString ());
				return;
			}
			
			if (type == null) {
				AddError (ErrorType.Error, node.Location, GettextCatalog.GetString ("The tag type '{0}{1}{2}' has not been registered.", prefix, string.IsNullOrEmpty(prefix)? string.Empty:":", name));
				return;
			}
			
			Members [id] = new CodeBehindMember (id, type, new TextLocation (node.Location.BeginLine, node.Location.BeginColumn));
		}
		void BuildRegion (TagNode node)
		{
			if (!IsMultiLine (node.Location, node.EndLocation))
			    return;
			
			string id = null;
			if (node.Attributes != null)
				id = (string) node.Attributes["id"];
			
			string name;
			if (id != null)
				name = string.Concat ("<", node.TagName, "#", id);
			else
				name = string.Concat ("<", node.TagName);
				
			
			if (node.EndLocation != null)
				name = string.Concat (name, ">...</", node.TagName, ">");
			else
				name = string.Concat (name, " />");
			
			AddRegion (name, node.Location, node.EndLocation);
		}
			public override void Visit (TagNode node)
			{
				//as soon as tags are declared, doctypes and directives must been set
				QuickExit = true;
			}
			public override void Visit (TagNode node)
			{
				if (node.TagName == "script" && (string)node.Attributes["runat"] == "server")
					parent.ScriptBlocks.Add (node);
			}
			public override void Visit (TagNode node)
			{
				QuickExit = true;
			}
		public override void Visit (TagNode node)
		{
			BuildRegion (node);
		}
		void TagParsed (ILocation location, TagType tagtype, string tagId, TagAttributes attributes)
		{
			switch (tagtype)
			{
			case TagType.Close:
				TagNode tn = currentNode as TagNode;
				if (tn == null) {
					errors.Add (new ParseException (location, "Closing tag '" + tagId +"' does not match an opening tag."));
				} else {
					if (tn.TagName == tagId) {
						tn.EndLocation = location;
						currentNode = currentNode.Parent;
						tn.Close ();
					} else {
						errors.Add (new ParseException (location, "Closing tag '" + tagId +"' does not match opening tag '" + tn.TagName + "'."));
						currentNode = currentNode.Parent;
						TagParsed (location, TagType.Close, tagId, null);
					}
				}
				break;
				
			case TagType.CodeRender:
			case TagType.CodeRenderExpression:
			case TagType.DataBinding:
				try {
					AddtoCurrent (location, new ExpressionNode (location, tagId, tagtype == TagType.CodeRenderExpression));
				} catch (ParseException ex) {
					errors.Add (ex);
				}
				break;
				
			case TagType.Directive:
				try {
					AddtoCurrent (location, new DirectiveNode (location, tagId, attributes));
				} catch (ParseException ex) {
					errors.Add (ex);
				}	
				break;
				
			case TagType.Include:
				throw new NotImplementedException ("Server-side includes have not yet been implemented: " + location.PlainText);
				
			case TagType.ServerComment:
				//FIXME: the parser doesn't actually return these
				throw new NotImplementedException ("Server comments have not yet been implemented: " + location.PlainText);
				
			case TagType.SelfClosing:
				try {
					tn = new TagNode (location, tagId, attributes);
					AddtoCurrent (location, tn);
					tn.Close ();
				} catch (ParseException ex) {
					errors.Add (ex);
				}
				break;
				
			case TagType.Tag:
				try {
					//HACK: implicit close on block level in HTML4
					TagNode prevTag = currentNode as TagNode;
					if (prevTag != null) {
						if (Array.IndexOf (implicitCloseOnBlock, prevTag.TagName.ToLowerInvariant ()) > -1
						    && Array.IndexOf (blockLevel, tagId.ToLowerInvariant ()) > -1) {
							errors.Add (new ParseException (location, "Unclosed " + prevTag.TagName + " tag. Assuming implicitly closed by block level tag."));
							prevTag.Close ();
							currentNode = currentNode.Parent;
						}
					}
					
					//create and add the new tag
					TagNode child = new TagNode (location, tagId, attributes);
					AddtoCurrent (location, child);
					
					//HACK: implicitly closing tags in HTML4
					if (Array.IndexOf (implicitSelfClosing, tagId.ToLowerInvariant ()) > -1) {
						errors.Add (new ParseException (location, "Unclosed " + tagId + " tag. Assuming implicitly closed."));
						child.Close ();
					} else {
						currentNode = child;
					}
					
				} catch (ParseException ex) {
					errors.Add (ex);
				}
				break;
				
			case TagType.Text:
				//FIXME: the parser doesn't actually return these
				throw new NotImplementedException("Text tagtypes have not yet been implemented: " + location.PlainText);
			}
		}
		public virtual void Visit (TagNode node)
		{
		}