Beispiel #1
0
 internal AXmlAttribute(AXmlObject parent, int startOffset, InternalAttribute internalObject)
     : base(parent, startOffset, internalObject)
 {
 }
		/// <summary>
		/// Context: name or "=\'\""
		/// </summary>
		void ReadAttribute()
		{
			AssertHasMoreData();
			
			InternalAttribute attr = new InternalAttribute();
			var frame = BeginInternalObject(attr);
			
			// Read name
			string name;
			if (TryReadName(out name)) {
				if (!IsValidName(name)) {
					OnSyntaxError(this.CurrentLocation - name.Length, this.CurrentLocation, "The name '{0}' is invalid", name);
				}
			} else {
				OnSyntaxError("Attribute name expected");
			}
			attr.Name = name;
			
			// Read equals sign and surrounding whitespace
			int checkpoint = this.CurrentLocation;
			TryMoveToNonWhiteSpace();
			if (TryRead('=')) {
				int chk2 = this.CurrentLocation;
				TryMoveToNonWhiteSpace();
				if (!TryPeek('"') && !TryPeek('\'')) {
					// Do not read whitespace if quote does not follow
					GoBack(chk2);
				}
				attr.EqualsSignLength = this.CurrentLocation - checkpoint;
			} else {
				GoBack(checkpoint);
				OnSyntaxError("'=' expected");
				attr.EqualsSignLength = 0;
			}
			
			// Read attribute value
			int start = this.CurrentLocation;
			char quoteChar = TryPeek('"') ? '"' : '\'';
			bool startsWithQuote;
			if (TryRead(quoteChar)) {
				startsWithQuote = true;
				int valueStart = this.CurrentLocation;
				TryMoveToAnyOf(quoteChar, '<');
				if (TryRead(quoteChar)) {
					if (!TryPeekAnyOf(' ', '\t', '\n', '\r', '/', '>', '?')) {
						if (TryPeekPrevious('=', 2) || (TryPeekPrevious('=', 3) && TryPeekPrevious(' ', 2))) {
							// This actually most likely means that we are in the next attribute value
							GoBack(valueStart);
							ReadAttributeValue(quoteChar);
							if (TryRead(quoteChar)) {
								OnSyntaxError("White space or end of tag expected");
							} else {
								OnSyntaxError("Quote {0} expected (or add whitespace after the following one)", quoteChar);
							}
						} else {
							OnSyntaxError("White space or end of tag expected");
						}
					}
				} else {
					// '<' or end of file
					GoBack(valueStart);
					ReadAttributeValue(quoteChar);
					OnSyntaxError("Quote {0} expected", quoteChar);
				}
			} else {
				startsWithQuote = false;
				int valueStart = this.CurrentLocation;
				ReadAttributeValue(null);
				TryRead('\"');
				TryRead('\'');
				if (valueStart == this.CurrentLocation) {
					OnSyntaxError("Attribute value expected");
				} else {
					OnSyntaxError(valueStart, this.CurrentLocation, "Attribute value must be quoted");
				}
			}
			string val = GetText(start, this.CurrentLocation);
			val = Unquote(val);
			attr.Value = Dereference(val, startsWithQuote ? start + 1 : start);
			
			EndInternalObject(frame);
		}
        /// <summary>
        /// Context: name or "=\'\""
        /// </summary>
        void ReadAttribute()
        {
            AssertHasMoreData();

            InternalAttribute attr = new InternalAttribute();
            var frame = BeginInternalObject(attr);

            // Read name
            string name;

            if (TryReadName(out name))
            {
                if (!IsValidName(name))
                {
                    OnSyntaxError(this.CurrentLocation - name.Length, this.CurrentLocation, "The name '{0}' is invalid", name);
                }
            }
            else
            {
                OnSyntaxError("Attribute name expected");
            }
            attr.Name = name;

            // Read equals sign and surrounding whitespace
            int checkpoint = this.CurrentLocation;

            TryMoveToNonWhiteSpace();
            if (TryRead('='))
            {
                int chk2 = this.CurrentLocation;
                TryMoveToNonWhiteSpace();
                if (!TryPeek('"') && !TryPeek('\''))
                {
                    // Do not read whitespace if quote does not follow
                    GoBack(chk2);
                }
                attr.EqualsSignLength = this.CurrentLocation - checkpoint;
            }
            else
            {
                GoBack(checkpoint);
                OnSyntaxError("'=' expected");
                attr.EqualsSignLength = 0;
            }

            // Read attribute value
            int  start     = this.CurrentLocation;
            char quoteChar = TryPeek('"') ? '"' : '\'';
            bool startsWithQuote;

            if (TryRead(quoteChar))
            {
                startsWithQuote = true;
                int valueStart = this.CurrentLocation;
                TryMoveToAnyOf(quoteChar, '<');
                if (TryRead(quoteChar))
                {
                    if (!TryPeekAnyOf(' ', '\t', '\n', '\r', '/', '>', '?'))
                    {
                        if (TryPeekPrevious('=', 2) || (TryPeekPrevious('=', 3) && TryPeekPrevious(' ', 2)))
                        {
                            // This actually most likely means that we are in the next attribute value
                            GoBack(valueStart);
                            ReadAttributeValue(quoteChar);
                            if (TryRead(quoteChar))
                            {
                                OnSyntaxError("White space or end of tag expected");
                            }
                            else
                            {
                                OnSyntaxError("Quote {0} expected (or add whitespace after the following one)", quoteChar);
                            }
                        }
                        else
                        {
                            OnSyntaxError("White space or end of tag expected");
                        }
                    }
                }
                else
                {
                    // '<' or end of file
                    GoBack(valueStart);
                    ReadAttributeValue(quoteChar);
                    OnSyntaxError("Quote {0} expected", quoteChar);
                }
            }
            else
            {
                startsWithQuote = false;
                int valueStart = this.CurrentLocation;
                ReadAttributeValue(null);
                TryRead('\"');
                TryRead('\'');
                if (valueStart == this.CurrentLocation)
                {
                    OnSyntaxError("Attribute value expected");
                }
                else
                {
                    OnSyntaxError(valueStart, this.CurrentLocation, "Attribute value must be quoted");
                }
            }
            string val = GetText(start, this.CurrentLocation);

            val        = Unquote(val);
            attr.Value = Dereference(val, startsWithQuote ? start + 1 : start);

            EndInternalObject(frame);
        }
		internal AXmlAttribute(AXmlObject parent, int startOffset, InternalAttribute internalObject)
			: base(parent, startOffset, internalObject)
		{
		}
        /// <summary>
        /// Context: "&lt;"
        /// </summary>
        void ReadTag()
        {
            AssertHasMoreData();

            int         tagStart = this.CurrentLocation;
            InternalTag tag      = new InternalTag();
            var         frame    = BeginInternalObject(tag);

            // Read the opening bracket
            // It identifies the type of tag and parsing behavior for the rest of it
            tag.OpeningBracket = ReadOpeningBracket();

            if (tag.IsUnknownBang && !TryPeekWhiteSpace())
            {
                OnSyntaxError(tagStart, this.CurrentLocation, "Unknown tag");
            }

            if (tag.IsStartOrEmptyTag || tag.IsEndTag || tag.IsProcessingInstruction)
            {
                // Read the name
                TryMoveToNonWhiteSpace();
                tag.RelativeNameStart = this.CurrentRelativeLocation;
                string name;
                if (TryReadName(out name))
                {
                    if (!IsValidName(name))
                    {
                        OnSyntaxError(this.CurrentLocation - name.Length, this.CurrentLocation, "The name '{0}' is invalid", name);
                    }
                }
                else
                {
                    OnSyntaxError("Element name expected");
                }
                tag.Name = name;
            }
            else
            {
                tag.Name = string.Empty;
            }

            bool isXmlDeclr     = tag.Name == "xml" && tag.IsProcessingInstruction;
            int  oldObjectCount = objects.Count;

            if (tag.IsStartOrEmptyTag || tag.IsEndTag || isXmlDeclr)
            {
                // Read attributes for the tag
                while (HasMoreData())
                {
                    // Chech for all forbiden 'name' characters first - see ReadName
                    TryMoveToNonWhiteSpace();
                    if (TryPeek('<'))
                    {
                        break;
                    }
                    string endBr;
                    int    endBrStart = this.CurrentLocation;                  // Just peek
                    if (TryReadClosingBracket(out endBr))                      // End tag
                    {
                        GoBack(endBrStart);
                        break;
                    }

                    // We have "=\'\"" or name - read attribute
                    int attrStartOffset = this.CurrentLocation;
                    ReadAttribute();
                    if (tag.IsEndTag)
                    {
                        OnSyntaxError(attrStartOffset, this.CurrentLocation, "Attribute not allowed in end tag.");
                    }
                }
            }
            else if (tag.IsDocumentType)
            {
                ReadContentOfDTD();
            }
            else
            {
                int start = this.CurrentLocation;
                if (tag.IsComment)
                {
                    ReadText(TextType.Comment);
                }
                else if (tag.IsCData)
                {
                    ReadText(TextType.CData);
                }
                else if (tag.IsProcessingInstruction)
                {
                    ReadText(TextType.ProcessingInstruction);
                }
                else if (tag.IsUnknownBang)
                {
                    ReadText(TextType.UnknownBang);
                }
                else
                {
                    throw new InternalException(string.Format(CultureInfo.InvariantCulture, "Unknown opening bracket '{0}'", tag.OpeningBracket));
                }
                // Backtrack at complete start
                if (IsEndOfFile() || (tag.IsUnknownBang && TryPeek('<')))
                {
                    GoBack(start);
                    objects.RemoveRange(oldObjectCount, objects.Count - oldObjectCount);
                }
            }

            // Read closing bracket
            string bracket;

            TryReadClosingBracket(out bracket);
            tag.ClosingBracket = bracket;

            // Error check
            int brStart = this.CurrentLocation - (tag.ClosingBracket ?? string.Empty).Length;
            int brEnd   = this.CurrentLocation;

            if (tag.Name == null)
            {
                // One error was reported already
            }
            else if (tag.IsStartOrEmptyTag)
            {
                if (tag.ClosingBracket != ">" && tag.ClosingBracket != "/>")
                {
                    OnSyntaxError(brStart, brEnd, "'>' or '/>' expected");
                }
            }
            else if (tag.IsEndTag)
            {
                if (tag.ClosingBracket != ">")
                {
                    OnSyntaxError(brStart, brEnd, "'>' expected");
                }
            }
            else if (tag.IsComment)
            {
                if (tag.ClosingBracket != "-->")
                {
                    OnSyntaxError(brStart, brEnd, "'-->' expected");
                }
            }
            else if (tag.IsCData)
            {
                if (tag.ClosingBracket != "]]>")
                {
                    OnSyntaxError(brStart, brEnd, "']]>' expected");
                }
            }
            else if (tag.IsProcessingInstruction)
            {
                if (tag.ClosingBracket != "?>")
                {
                    OnSyntaxError(brStart, brEnd, "'?>' expected");
                }
            }
            else if (tag.IsUnknownBang)
            {
                if (tag.ClosingBracket != ">")
                {
                    OnSyntaxError(brStart, brEnd, "'>' expected");
                }
            }
            else if (tag.IsDocumentType)
            {
                if (tag.ClosingBracket != ">")
                {
                    OnSyntaxError(brStart, brEnd, "'>' expected");
                }
            }
            else
            {
                throw new InternalException(string.Format(CultureInfo.InvariantCulture, "Unknown opening bracket '{0}'", tag.OpeningBracket));
            }

            // Attribute name may not apper multiple times
            if (objects.Count > oldObjectCount)
            {
                // Move nested objects into tag.NestedObjects:
                tag.NestedObjects = new InternalObject[objects.Count - oldObjectCount];
                objects.CopyTo(oldObjectCount, tag.NestedObjects, 0, tag.NestedObjects.Length);
                objects.RemoveRange(oldObjectCount, objects.Count - oldObjectCount);

                // Look for duplicate attributes:
                HashSet <string> attributeNames = new HashSet <string>();
                foreach (var obj in tag.NestedObjects)
                {
                    InternalAttribute attr = obj as InternalAttribute;
                    if (attr != null && !attributeNames.Add(attr.Name))
                    {
                        int attrStart = tagStart + attr.StartRelativeToParent;
                        OnSyntaxError(attrStart, attrStart + attr.Name.Length, "Attribute with name '{0}' already exists", attr.Name);
                    }
                }
            }

            EndInternalObject(frame);
        }