25 changed files with 2143 additions and 21 deletions
@ -0,0 +1,61 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Name-value pair in a tag
|
||||||
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")] |
||||||
|
public class AXmlAttribute : AXmlObject |
||||||
|
{ |
||||||
|
internal AXmlAttribute(AXmlObject parent, int startOffset, InternalAttribute internalObject) |
||||||
|
: base(parent, startOffset, internalObject) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
internal InternalAttribute InternalAttribute { |
||||||
|
get { return (InternalAttribute)internalObject; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> Name with namespace prefix - exactly as in source file </summary>
|
||||||
|
public string Name { get { return InternalAttribute.Name; } } |
||||||
|
|
||||||
|
/// <summary> Unquoted and dereferenced value of the attribute </summary>
|
||||||
|
public string Value { get { return InternalAttribute.Value; } } |
||||||
|
|
||||||
|
/// <summary>Gets the segment for the attribute name</summary>
|
||||||
|
public ISegment NameSegment { |
||||||
|
get { return new XmlSegment(startOffset, startOffset + Name.Length); } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>Gets the segment for the attribute value, including the quotes</summary>
|
||||||
|
public ISegment ValueSegment { |
||||||
|
get { return new XmlSegment(startOffset + Name.Length + InternalAttribute.EqualsSignLength, this.EndOffset); } |
||||||
|
} |
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void AcceptVisitor(IAXmlVisitor visitor) |
||||||
|
{ |
||||||
|
visitor.VisitAttribute(this); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,163 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
using ICSharpCode.NRefactory.Utils; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// XML object.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class AXmlObject : ISegment |
||||||
|
{ |
||||||
|
readonly AXmlObject parent; |
||||||
|
internal readonly int startOffset; |
||||||
|
internal readonly InternalObject internalObject; |
||||||
|
IList<AXmlObject> children; |
||||||
|
|
||||||
|
internal AXmlObject(AXmlObject parent, int startOffset, InternalObject internalObject) |
||||||
|
{ |
||||||
|
this.parent = parent; |
||||||
|
this.startOffset = startOffset; |
||||||
|
this.internalObject = internalObject; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parent node.
|
||||||
|
/// </summary>
|
||||||
|
public AXmlObject Parent { |
||||||
|
get { return parent; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of child objects.
|
||||||
|
/// </summary>
|
||||||
|
public IList<AXmlObject> Children { |
||||||
|
get { |
||||||
|
var result = this.children; |
||||||
|
if (result != null) { |
||||||
|
LazyInit.ReadBarrier(); |
||||||
|
return result; |
||||||
|
} else { |
||||||
|
if (internalObject.NestedObjects != null) { |
||||||
|
var array = new AXmlObject[internalObject.NestedObjects.Length]; |
||||||
|
for (int i = 0; i < array.Length; i++) { |
||||||
|
array[i] = internalObject.NestedObjects[i].CreatePublicObject(this, startOffset); |
||||||
|
} |
||||||
|
result = Array.AsReadOnly(array); |
||||||
|
} else { |
||||||
|
result = EmptyList<AXmlObject>.Instance; |
||||||
|
} |
||||||
|
return LazyInit.GetOrSet(ref this.children, result); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The error that occured in the context of this node (excluding nested nodes)
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<SyntaxError> MySyntaxErrors { |
||||||
|
get { |
||||||
|
if (internalObject.SyntaxErrors != null) { |
||||||
|
return internalObject.SyntaxErrors.Select(e => new SyntaxError(startOffset + e.RelativeStart, startOffset + e.RelativeEnd, e.Description)); |
||||||
|
} else { |
||||||
|
return EmptyList<SyntaxError>.Instance; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The error that occured in the context of this node and all nested nodes.
|
||||||
|
/// It has O(n) cost.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<SyntaxError> SyntaxErrors { |
||||||
|
get { |
||||||
|
return TreeTraversal.PreOrder(this, n => n.Children).SelectMany(obj => obj.MySyntaxErrors); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> Get all ancestors of this node </summary>
|
||||||
|
public IEnumerable<AXmlObject> Ancestors { |
||||||
|
get { |
||||||
|
AXmlObject curr = this.Parent; |
||||||
|
while(curr != null) { |
||||||
|
yield return curr; |
||||||
|
curr = curr.Parent; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#region Helper methods
|
||||||
|
|
||||||
|
/// <summary> The part of name before ":" </summary>
|
||||||
|
/// <returns> Empty string if not found </returns>
|
||||||
|
protected static string GetNamespacePrefix(string name) |
||||||
|
{ |
||||||
|
if (string.IsNullOrEmpty(name)) return string.Empty; |
||||||
|
int colonIndex = name.IndexOf(':'); |
||||||
|
if (colonIndex != -1) { |
||||||
|
return name.Substring(0, colonIndex); |
||||||
|
} else { |
||||||
|
return string.Empty; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> The part of name after ":" </summary>
|
||||||
|
/// <returns> Whole name if ":" not found </returns>
|
||||||
|
protected static string GetLocalName(string name) |
||||||
|
{ |
||||||
|
if (string.IsNullOrEmpty(name)) return string.Empty; |
||||||
|
int colonIndex = name.IndexOf(':'); |
||||||
|
if (colonIndex != -1) { |
||||||
|
return name.Remove(0, colonIndex + 1); |
||||||
|
} else { |
||||||
|
return name ?? string.Empty; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary> Call appropriate visit method on the given visitor </summary>
|
||||||
|
public abstract void AcceptVisitor(IAXmlVisitor visitor); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the start offset of the segment.
|
||||||
|
/// </summary>
|
||||||
|
public int StartOffset { |
||||||
|
get { return startOffset; } |
||||||
|
} |
||||||
|
|
||||||
|
int ISegment.Offset { |
||||||
|
get { return startOffset; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int Length { |
||||||
|
get { return internalObject.Length; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int EndOffset { |
||||||
|
get { return startOffset + internalObject.Length; } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,91 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.ObjectModel; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Represents any markup starting with "<" and (hopefully) ending with ">"
|
||||||
|
/// </summary>
|
||||||
|
public class AXmlTag : AXmlObject |
||||||
|
{ |
||||||
|
/// <summary> These identify the start of DTD elements </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification="ReadOnlyCollection is immutable")] |
||||||
|
public static readonly ReadOnlyCollection<string> DtdNames = new ReadOnlyCollection<string>( |
||||||
|
new string[] {"<!DOCTYPE", "<!NOTATION", "<!ELEMENT", "<!ATTLIST", "<!ENTITY" } ); |
||||||
|
|
||||||
|
new readonly InternalTag internalObject; |
||||||
|
|
||||||
|
internal AXmlTag(AXmlObject parent, int startOffset, InternalTag internalObject) |
||||||
|
: base(parent, startOffset, internalObject) |
||||||
|
{ |
||||||
|
this.internalObject = internalObject; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> Opening bracket - usually "<" </summary>
|
||||||
|
public string OpeningBracket { |
||||||
|
get { return internalObject.OpeningBracket; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> Name following the opening bracket </summary>
|
||||||
|
public string Name { |
||||||
|
get { return internalObject.Name; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> Gets the segment containing the tag name </summary>
|
||||||
|
public ISegment NameSegment { |
||||||
|
get { |
||||||
|
int start = startOffset + internalObject.RelativeNameStart; |
||||||
|
return new XmlSegment(start, start + internalObject.Name.Length); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> Closing bracket - usually ">" </summary>
|
||||||
|
public string ClosingBracket { |
||||||
|
get { return internalObject.ClosingBracket; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> True if tag starts with "<" </summary>
|
||||||
|
public bool IsStartOrEmptyTag { get { return internalObject.IsStartOrEmptyTag; } } |
||||||
|
/// <summary> True if tag starts with "<" and ends with ">" </summary>
|
||||||
|
public bool IsStartTag { get { return internalObject.IsStartOrEmptyTag && ClosingBracket == ">"; } } |
||||||
|
/// <summary> True if tag starts with "<" and does not end with ">" </summary>
|
||||||
|
public bool IsEmptyTag { get { return internalObject.IsStartOrEmptyTag && ClosingBracket != ">" ; } } |
||||||
|
/// <summary> True if tag starts with "</" </summary>
|
||||||
|
public bool IsEndTag { get { return internalObject.IsEndTag; } } |
||||||
|
/// <summary> True if tag starts with "<?" </summary>
|
||||||
|
public bool IsProcessingInstruction { get { return internalObject.IsProcessingInstruction; } } |
||||||
|
/// <summary> True if tag starts with "<!--" </summary>
|
||||||
|
public bool IsComment { get { return internalObject.IsComment; } } |
||||||
|
/// <summary> True if tag starts with "<![CDATA[" </summary>
|
||||||
|
public bool IsCData { get { return internalObject.IsCData; } } |
||||||
|
/// <summary> True if tag starts with one of the DTD starts </summary>
|
||||||
|
public bool IsDocumentType { get { return internalObject.IsDocumentType; } } |
||||||
|
/// <summary> True if tag starts with "<!" </summary>
|
||||||
|
public bool IsUnknownBang { get { return internalObject.IsUnknownBang; } } |
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void AcceptVisitor(IAXmlVisitor visitor) |
||||||
|
{ |
||||||
|
visitor.VisitTag(this); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,51 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Whitespace or character data
|
||||||
|
/// </summary>
|
||||||
|
public class AXmlText : AXmlObject |
||||||
|
{ |
||||||
|
internal AXmlText(AXmlObject parent, int startOffset, InternalText internalObject) |
||||||
|
: base(parent, startOffset, internalObject) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> The text with all entity references resloved </summary>
|
||||||
|
public string Value { |
||||||
|
get { return ((InternalText)internalObject).Value; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> True if the text contains only whitespace characters </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace", |
||||||
|
Justification = "System.Xml also uses 'Whitespace'")] |
||||||
|
public bool ContainsOnlyWhitespace { |
||||||
|
get { return ((InternalText)internalObject).ContainsOnlyWhitespace; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void AcceptVisitor(IAXmlVisitor visitor) |
||||||
|
{ |
||||||
|
visitor.VisitText(this); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,37 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Visitor for the XML tree
|
||||||
|
/// </summary>
|
||||||
|
public interface IAXmlVisitor |
||||||
|
{ |
||||||
|
/// <summary> Visit tag </summary>
|
||||||
|
void VisitTag(AXmlTag tag); |
||||||
|
|
||||||
|
/// <summary> Visit attribute </summary>
|
||||||
|
void VisitAttribute(AXmlAttribute attribute); |
||||||
|
|
||||||
|
/// <summary> Visit text </summary>
|
||||||
|
void VisitText(AXmlText text); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,79 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> |
||||||
|
<PropertyGroup> |
||||||
|
<ProjectGuid>{DC393B66-92ED-4CAD-AB25-CFEF23F3D7C6}</ProjectGuid> |
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||||
|
<OutputType>Library</OutputType> |
||||||
|
<RootNamespace>ICSharpCode.NRefactory.Xml</RootNamespace> |
||||||
|
<AssemblyName>ICSharpCode.NRefactory.Xml</AssemblyName> |
||||||
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> |
||||||
|
<TargetFrameworkProfile>Client</TargetFrameworkProfile> |
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder> |
||||||
|
<AllowUnsafeBlocks>False</AllowUnsafeBlocks> |
||||||
|
<NoStdLib>False</NoStdLib> |
||||||
|
<WarningLevel>4</WarningLevel> |
||||||
|
<TreatWarningsAsErrors>false</TreatWarningsAsErrors> |
||||||
|
<DocumentationFile>..\ICSharpCode.NRefactory\bin\$(Configuration)\ICSharpCode.NRefactory.Xml.xml</DocumentationFile> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' "> |
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget> |
||||||
|
<RegisterForComInterop>False</RegisterForComInterop> |
||||||
|
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies> |
||||||
|
<BaseAddress>4194304</BaseAddress> |
||||||
|
<FileAlignment>4096</FileAlignment> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> |
||||||
|
<OutputPath>..\ICSharpCode.NRefactory\bin\Debug\</OutputPath> |
||||||
|
<DebugSymbols>true</DebugSymbols> |
||||||
|
<DebugType>Full</DebugType> |
||||||
|
<Optimize>False</Optimize> |
||||||
|
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow> |
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> |
||||||
|
<OutputPath>..\ICSharpCode.NRefactory\bin\Release\</OutputPath> |
||||||
|
<DebugSymbols>false</DebugSymbols> |
||||||
|
<DebugType>None</DebugType> |
||||||
|
<Optimize>True</Optimize> |
||||||
|
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow> |
||||||
|
<DefineConstants>TRACE</DefineConstants> |
||||||
|
</PropertyGroup> |
||||||
|
<ItemGroup> |
||||||
|
<Reference Include="System" /> |
||||||
|
<Reference Include="System.Core"> |
||||||
|
<RequiredTargetFramework>3.5</RequiredTargetFramework> |
||||||
|
</Reference> |
||||||
|
<Reference Include="System.Xml" /> |
||||||
|
<Reference Include="System.Xml.Linq"> |
||||||
|
<RequiredTargetFramework>3.5</RequiredTargetFramework> |
||||||
|
</Reference> |
||||||
|
</ItemGroup> |
||||||
|
<ItemGroup> |
||||||
|
<Compile Include="..\ICSharpCode.NRefactory\Properties\GlobalAssemblyInfo.cs"> |
||||||
|
<Link>Properties\GlobalAssemblyInfo.cs</Link> |
||||||
|
</Compile> |
||||||
|
<Compile Include="AXmlAttribute.cs" /> |
||||||
|
<Compile Include="AXmlObject.cs" /> |
||||||
|
<Compile Include="AXmlTag.cs" /> |
||||||
|
<Compile Include="AXmlText.cs" /> |
||||||
|
<Compile Include="IAXmlVisitor.cs" /> |
||||||
|
<Compile Include="IncrementalParserState.cs" /> |
||||||
|
<Compile Include="InternalDocument.cs" /> |
||||||
|
<Compile Include="SyntaxError.cs" /> |
||||||
|
<Compile Include="TextType.cs" /> |
||||||
|
<Compile Include="Log.cs" /> |
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||||
|
<Compile Include="TagReader.cs" /> |
||||||
|
<Compile Include="TagSoupParser.cs" /> |
||||||
|
<Compile Include="TokenReader.cs" /> |
||||||
|
<Compile Include="XmlSegment.cs" /> |
||||||
|
</ItemGroup> |
||||||
|
<ItemGroup> |
||||||
|
<ProjectReference Include="..\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj"> |
||||||
|
<Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project> |
||||||
|
<Name>ICSharpCode.NRefactory</Name> |
||||||
|
</ProjectReference> |
||||||
|
</ItemGroup> |
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> |
||||||
|
</Project> |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Encapsulates the state of the incremental tag soup parser.
|
||||||
|
/// </summary>
|
||||||
|
public class IncrementalParserState |
||||||
|
{ |
||||||
|
internal IncrementalParserState() |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,107 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
abstract class InternalObject |
||||||
|
{ |
||||||
|
internal int StartRelativeToParent; |
||||||
|
internal int Length; |
||||||
|
internal InternalSyntaxError[] SyntaxErrors; |
||||||
|
internal InternalObject[] NestedObjects; |
||||||
|
|
||||||
|
internal InternalObject SetStartRelativeToParent(int newStartRelativeToParent) |
||||||
|
{ |
||||||
|
if (newStartRelativeToParent == StartRelativeToParent) |
||||||
|
return this; |
||||||
|
InternalObject obj = (InternalObject)MemberwiseClone(); |
||||||
|
obj.StartRelativeToParent = newStartRelativeToParent; |
||||||
|
return obj; |
||||||
|
} |
||||||
|
|
||||||
|
public abstract AXmlObject CreatePublicObject(AXmlObject parent, int parentStartOffset); |
||||||
|
} |
||||||
|
|
||||||
|
sealed class InternalText : InternalObject |
||||||
|
{ |
||||||
|
internal TextType Type; |
||||||
|
internal bool ContainsOnlyWhitespace; |
||||||
|
internal string Value; |
||||||
|
|
||||||
|
public override AXmlObject CreatePublicObject(AXmlObject parent, int parentStartOffset) |
||||||
|
{ |
||||||
|
return new AXmlText(parent, parentStartOffset + StartRelativeToParent, this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sealed class InternalTag : InternalObject |
||||||
|
{ |
||||||
|
internal string OpeningBracket; |
||||||
|
internal int RelativeNameStart; |
||||||
|
internal string Name; |
||||||
|
internal string ClosingBracket; |
||||||
|
|
||||||
|
/// <summary> True if tag starts with "<" </summary>
|
||||||
|
public bool IsStartOrEmptyTag { get { return OpeningBracket == "<"; } } |
||||||
|
/// <summary> True if tag starts with "</" </summary>
|
||||||
|
public bool IsEndTag { get { return OpeningBracket == "</"; } } |
||||||
|
/// <summary> True if tag starts with "<?" </summary>
|
||||||
|
public bool IsProcessingInstruction { get { return OpeningBracket == "<?"; } } |
||||||
|
/// <summary> True if tag starts with "<!--" </summary>
|
||||||
|
public bool IsComment { get { return OpeningBracket == "<!--"; } } |
||||||
|
/// <summary> True if tag starts with "<![CDATA[" </summary>
|
||||||
|
public bool IsCData { get { return OpeningBracket == "<![CDATA["; } } |
||||||
|
/// <summary> True if tag starts with one of the DTD starts </summary>
|
||||||
|
public bool IsDocumentType { get { return AXmlTag.DtdNames.Contains(OpeningBracket); } } |
||||||
|
/// <summary> True if tag starts with "<!" </summary>
|
||||||
|
public bool IsUnknownBang { get { return OpeningBracket == "<!"; } } |
||||||
|
|
||||||
|
public override AXmlObject CreatePublicObject(AXmlObject parent, int parentStartOffset) |
||||||
|
{ |
||||||
|
return new AXmlTag(parent, parentStartOffset + StartRelativeToParent, this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct InternalSyntaxError |
||||||
|
{ |
||||||
|
internal readonly int RelativeStart; |
||||||
|
internal readonly int RelativeEnd; |
||||||
|
internal readonly string Description; |
||||||
|
|
||||||
|
public InternalSyntaxError(int relativeStart, int relativeEnd, string description) |
||||||
|
{ |
||||||
|
this.RelativeStart = relativeStart; |
||||||
|
this.RelativeEnd = relativeEnd; |
||||||
|
this.Description = description; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class InternalAttribute : InternalObject |
||||||
|
{ |
||||||
|
internal string Name; |
||||||
|
internal int EqualsSignLength; // length of equals sign including the surrounding whitespace
|
||||||
|
internal string Value; |
||||||
|
|
||||||
|
public override AXmlObject CreatePublicObject(AXmlObject parent, int parentStartOffset) |
||||||
|
{ |
||||||
|
return new AXmlAttribute(parent, parentStartOffset + StartRelativeToParent, this); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,88 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Diagnostics; |
||||||
|
using System.Runtime.Serialization; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
static class Log |
||||||
|
{ |
||||||
|
/// <summary> Throws exception if condition is false </summary>
|
||||||
|
internal static void Assert(bool condition, string message) |
||||||
|
{ |
||||||
|
if (!condition) { |
||||||
|
throw new InternalException("Assertion failed: " + message); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> Throws exception if condition is false </summary>
|
||||||
|
[Conditional("DEBUG")] |
||||||
|
internal static void DebugAssert(bool condition, string message) |
||||||
|
{ |
||||||
|
if (!condition) { |
||||||
|
throw new InternalException("Assertion failed: " + message); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[Conditional("DEBUG")] |
||||||
|
internal static void WriteLine(string text, params object[] pars) |
||||||
|
{ |
||||||
|
//System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "XML: " + text, pars));
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exception used for internal errors in XML parser.
|
||||||
|
/// This exception indicates a bug in NRefactory.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable] |
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic", Justification = "This exception is not public because it is not supposed to be caught by user code - it indicates a bug in AvalonEdit.")] |
||||||
|
class InternalException : Exception |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Creates a new InternalException instance.
|
||||||
|
/// </summary>
|
||||||
|
public InternalException() : base() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new InternalException instance.
|
||||||
|
/// </summary>
|
||||||
|
public InternalException(string message) : base(message) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new InternalException instance.
|
||||||
|
/// </summary>
|
||||||
|
public InternalException(string message, Exception innerException) : base(message, innerException) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new InternalException instance.
|
||||||
|
/// </summary>
|
||||||
|
protected InternalException(SerializationInfo info, StreamingContext context) : base(info, context) |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
#region Using directives
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Reflection; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("ICSharpCode.NRefactory.Xml")] |
||||||
|
[assembly: AssemblyDescription("Error-tolerant XML parser")] |
||||||
|
|
||||||
|
[assembly: CLSCompliant(true)] |
||||||
@ -0,0 +1,63 @@ |
|||||||
|
/* |
||||||
|
* Created by SharpDevelop. |
||||||
|
* User: Daniel |
||||||
|
* Date: 2/20/2012 |
||||||
|
* Time: 17:46 |
||||||
|
* |
||||||
|
* To change this template use Tools | Options | Coding | Edit Standard Headers. |
||||||
|
*/ |
||||||
|
using System; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// A syntax error.
|
||||||
|
/// </summary>
|
||||||
|
public class SyntaxError : ISegment |
||||||
|
{ |
||||||
|
readonly int startOffset; |
||||||
|
readonly int endOffset; |
||||||
|
readonly string description; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new syntax error.
|
||||||
|
/// </summary>
|
||||||
|
public SyntaxError(int startOffset, int endOffset, string description) |
||||||
|
{ |
||||||
|
if (description == null) |
||||||
|
throw new ArgumentNullException("description"); |
||||||
|
this.startOffset = startOffset; |
||||||
|
this.endOffset = endOffset; |
||||||
|
this.description = description; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a description of the syntax error.
|
||||||
|
/// </summary>
|
||||||
|
public string Description { |
||||||
|
get { return description; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the start offset of the segment.
|
||||||
|
/// </summary>
|
||||||
|
public int StartOffset { |
||||||
|
get { return startOffset; } |
||||||
|
} |
||||||
|
|
||||||
|
int ISegment.Offset { |
||||||
|
get { return startOffset; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int Length { |
||||||
|
get { return endOffset - startOffset; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int EndOffset { |
||||||
|
get { return endOffset; } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,755 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Globalization; |
||||||
|
using System.Linq; |
||||||
|
using System.Text; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
class TagReader : TokenReader |
||||||
|
{ |
||||||
|
readonly TagSoupParser tagSoupParser; |
||||||
|
|
||||||
|
public TagReader(TagSoupParser tagSoupParser, ITextSource input) : base(input) |
||||||
|
{ |
||||||
|
this.tagSoupParser = tagSoupParser; |
||||||
|
} |
||||||
|
|
||||||
|
public InternalObject[] ReadAllObjects() |
||||||
|
{ |
||||||
|
while (HasMoreData()) { |
||||||
|
ReadObject(); |
||||||
|
} |
||||||
|
var arr = objects.ToArray(); |
||||||
|
objects.Clear(); |
||||||
|
return arr; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads one or more objects.
|
||||||
|
/// </summary>
|
||||||
|
void ReadObject() |
||||||
|
{ |
||||||
|
if (TryPeek('<')) { |
||||||
|
ReadTag(); |
||||||
|
} else { |
||||||
|
ReadText(TextType.CharacterData); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#region BeginInternalObject / EndInternalObject
|
||||||
|
List<InternalObject> objects = new List<InternalObject>(); |
||||||
|
int internalObjectStartPosition; |
||||||
|
|
||||||
|
int CurrentRelativeLocation { |
||||||
|
get { return CurrentLocation - internalObjectStartPosition; } |
||||||
|
} |
||||||
|
|
||||||
|
struct InternalObjectFrame |
||||||
|
{ |
||||||
|
public readonly InternalObject InternalObject; |
||||||
|
public readonly int ParentStartPosition; |
||||||
|
|
||||||
|
public InternalObjectFrame(InternalObject internalObject, int parentStartPosition) |
||||||
|
{ |
||||||
|
this.InternalObject = internalObject; |
||||||
|
this.ParentStartPosition = parentStartPosition; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
InternalObjectFrame BeginInternalObject(InternalObject internalObject) |
||||||
|
{ |
||||||
|
return BeginInternalObject(internalObject, this.CurrentLocation); |
||||||
|
} |
||||||
|
|
||||||
|
InternalObjectFrame BeginInternalObject(InternalObject internalObject, int beginLocation) |
||||||
|
{ |
||||||
|
internalObject.StartRelativeToParent = beginLocation - internalObjectStartPosition; |
||||||
|
|
||||||
|
var frame = new InternalObjectFrame(internalObject, internalObjectStartPosition); |
||||||
|
|
||||||
|
internalObjectStartPosition = CurrentLocation; |
||||||
|
return frame; |
||||||
|
} |
||||||
|
|
||||||
|
void EndInternalObject(InternalObjectFrame frame, bool storeNewObject = true) |
||||||
|
{ |
||||||
|
frame.InternalObject.Length = this.CurrentRelativeLocation; |
||||||
|
frame.InternalObject.SyntaxErrors = GetSyntaxErrors(); |
||||||
|
if (storeNewObject) |
||||||
|
objects.Add(frame.InternalObject); |
||||||
|
internalObjectStartPosition = frame.ParentStartPosition; |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Read Tag
|
||||||
|
/// <summary>
|
||||||
|
/// Context: "<"
|
||||||
|
/// </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); |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Read DTD
|
||||||
|
void ReadContentOfDTD() |
||||||
|
{ |
||||||
|
int start = this.CurrentLocation; |
||||||
|
while (HasMoreData()) { |
||||||
|
TryMoveToNonWhiteSpace(); // Skip whitespace
|
||||||
|
if (TryRead('\'')) TryMoveTo('\''); // Skip single quoted string TODO: Bug
|
||||||
|
if (TryRead('\"')) TryMoveTo('\"'); // Skip single quoted string
|
||||||
|
if (TryRead('[')) { // Start of nested infoset
|
||||||
|
// Reading infoset
|
||||||
|
while (HasMoreData()) { |
||||||
|
TryMoveToAnyOf('<', ']'); |
||||||
|
if (TryPeek('<')) { |
||||||
|
if (start != this.CurrentLocation) { // Two following tags
|
||||||
|
MakeText(start, this.CurrentLocation); |
||||||
|
} |
||||||
|
ReadTag(); |
||||||
|
start = this.CurrentLocation; |
||||||
|
} |
||||||
|
if (TryPeek(']')) break; |
||||||
|
} |
||||||
|
} |
||||||
|
TryRead(']'); // End of nested infoset
|
||||||
|
if (TryPeek('>')) break; // Proper closing
|
||||||
|
if (TryPeek('<')) break; // Malformed XML
|
||||||
|
TryMoveNext(); // Skip anything else
|
||||||
|
} |
||||||
|
if (start != this.CurrentLocation) { |
||||||
|
MakeText(start, this.CurrentLocation); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void MakeText(int start, int end) |
||||||
|
{ |
||||||
|
Log.DebugAssert(end > start, "Empty text"); |
||||||
|
Log.DebugAssert(end == this.CurrentLocation, "end == current location"); |
||||||
|
|
||||||
|
InternalText text = new InternalText(); |
||||||
|
var frame = BeginInternalObject(text, start); |
||||||
|
text.Type = TextType.Other; |
||||||
|
text.Value = GetText(start, end); |
||||||
|
EndInternalObject(frame); |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Read Brackets
|
||||||
|
/// <summary>
|
||||||
|
/// Reads any of the know opening brackets. (only full bracket)
|
||||||
|
/// Context: "<"
|
||||||
|
/// </summary>
|
||||||
|
string ReadOpeningBracket() |
||||||
|
{ |
||||||
|
// We are using a lot of string literals so that the memory instances are shared
|
||||||
|
//int start = this.CurrentLocation;
|
||||||
|
if (TryRead('<')) { |
||||||
|
if (TryRead('/')) { |
||||||
|
return "</"; |
||||||
|
} else if (TryRead('?')) { |
||||||
|
return "<?"; |
||||||
|
} else if (TryRead('!')) { |
||||||
|
if (TryRead("--")) { |
||||||
|
return "<!--"; |
||||||
|
} else if (TryRead("[CDATA[")) { |
||||||
|
return "<![CDATA["; |
||||||
|
} else { |
||||||
|
foreach (string dtdName in AXmlTag.DtdNames) { |
||||||
|
// the dtdName includes "<!"
|
||||||
|
if (TryRead(dtdName.Remove(0, 2))) return dtdName; |
||||||
|
} |
||||||
|
return "<!"; |
||||||
|
} |
||||||
|
} else { |
||||||
|
return "<"; |
||||||
|
} |
||||||
|
} else { |
||||||
|
throw new InternalException("'<' expected"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads any of the know closing brackets. (only full bracket)
|
||||||
|
/// Context: any
|
||||||
|
/// </summary>
|
||||||
|
bool TryReadClosingBracket(out string bracket) |
||||||
|
{ |
||||||
|
// We are using a lot of string literals so that the memory instances are shared
|
||||||
|
if (TryRead('>')) { |
||||||
|
bracket = ">"; |
||||||
|
} else if (TryRead("/>")) { |
||||||
|
bracket = "/>"; |
||||||
|
} else if (TryRead("?>")) { |
||||||
|
bracket = "?>"; |
||||||
|
} else if (TryRead("-->")) { |
||||||
|
bracket = "-->"; |
||||||
|
} else if (TryRead("]]>")) { |
||||||
|
bracket = "]]>"; |
||||||
|
} else { |
||||||
|
bracket = string.Empty; |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Attributes
|
||||||
|
/// <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>
|
||||||
|
/// Read everything up to quote (excluding), opening/closing tag or attribute signature
|
||||||
|
/// </summary>
|
||||||
|
void ReadAttributeValue(char? quote) |
||||||
|
{ |
||||||
|
while (HasMoreData()) { |
||||||
|
// What is next?
|
||||||
|
int start = this.CurrentLocation; |
||||||
|
TryMoveToNonWhiteSpace(); // Read white space (if any)
|
||||||
|
if (quote.HasValue) { |
||||||
|
if (TryPeek(quote.Value)) return; |
||||||
|
} else { |
||||||
|
if (TryPeek('"') || TryPeek('\'')) return; |
||||||
|
} |
||||||
|
// Opening/closing tag
|
||||||
|
string endBr; |
||||||
|
if (TryPeek('<') || TryReadClosingBracket(out endBr)) { |
||||||
|
GoBack(start); |
||||||
|
return; |
||||||
|
} |
||||||
|
// Try reading attribute signature
|
||||||
|
if (TryReadName()) { |
||||||
|
int nameEnd = this.CurrentLocation; |
||||||
|
if (TryMoveToNonWhiteSpace() && TryRead("=") && |
||||||
|
TryMoveToNonWhiteSpace() && TryPeekAnyOf('"', '\'')) |
||||||
|
{ |
||||||
|
// Start of attribute. Great
|
||||||
|
GoBack(start); |
||||||
|
return; // Done
|
||||||
|
} else { |
||||||
|
// Just some gargabe - make it part of the value
|
||||||
|
GoBack(nameEnd); |
||||||
|
continue; // Read more
|
||||||
|
} |
||||||
|
} |
||||||
|
TryMoveNext(); // Accept everyting else
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> Remove quoting from the given string </summary>
|
||||||
|
static string Unquote(string quoted) |
||||||
|
{ |
||||||
|
if (string.IsNullOrEmpty(quoted)) return string.Empty; |
||||||
|
char first = quoted[0]; |
||||||
|
if (quoted.Length == 1) return (first == '"' || first == '\'') ? string.Empty : quoted; |
||||||
|
char last = quoted[quoted.Length - 1]; |
||||||
|
if (first == '"' || first == '\'') { |
||||||
|
if (first == last) { |
||||||
|
// Remove both quotes
|
||||||
|
return quoted.Substring(1, quoted.Length - 2); |
||||||
|
} else { |
||||||
|
// Remove first quote
|
||||||
|
return quoted.Remove(0, 1); |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (last == '"' || last == '\'') { |
||||||
|
// Remove last quote
|
||||||
|
return quoted.Substring(0, quoted.Length - 1); |
||||||
|
} else { |
||||||
|
// Keep whole string
|
||||||
|
return quoted; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Text
|
||||||
|
/// <summary>
|
||||||
|
/// Reads text and optionaly separates it into fragments.
|
||||||
|
/// It can also return empty set for no appropriate text input.
|
||||||
|
/// Make sure you enumerate it only once
|
||||||
|
/// </summary>
|
||||||
|
void ReadText(TextType type) |
||||||
|
{ |
||||||
|
const int maxTextFragmentSize = 128; |
||||||
|
bool finished; |
||||||
|
do { |
||||||
|
var text = new InternalText(); |
||||||
|
var frame = BeginInternalObject(text); |
||||||
|
text.Type = type; |
||||||
|
|
||||||
|
// Limit the reading to just a few characters
|
||||||
|
// (the first character not to be read)
|
||||||
|
int fragmentEnd = Math.Min(this.CurrentLocation + maxTextFragmentSize, this.InputLength); |
||||||
|
|
||||||
|
int start = this.CurrentLocation; |
||||||
|
|
||||||
|
// Whitespace would be skipped anyway by any operation
|
||||||
|
TryMoveToNonWhiteSpace(fragmentEnd); |
||||||
|
int wsEnd = this.CurrentLocation; |
||||||
|
|
||||||
|
// Try move to the terminator given by the context
|
||||||
|
if (type == TextType.WhiteSpace) { |
||||||
|
TryMoveToNonWhiteSpace(fragmentEnd); |
||||||
|
} else if (type == TextType.CharacterData) { |
||||||
|
while(true) { |
||||||
|
if (!TryMoveToAnyOf(new char[] {'<', ']'}, fragmentEnd)) break; // End of fragment
|
||||||
|
if (TryPeek('<')) break; |
||||||
|
if (TryPeek(']')) { |
||||||
|
if (TryPeek("]]>")) { |
||||||
|
OnSyntaxError(this.CurrentLocation, this.CurrentLocation + 3, "']]>' is not allowed in text"); |
||||||
|
} |
||||||
|
TryMoveNext(); |
||||||
|
continue; |
||||||
|
} |
||||||
|
throw new InternalException("Infinite loop"); |
||||||
|
} |
||||||
|
} else if (type == TextType.Comment) { |
||||||
|
// Do not report too many errors
|
||||||
|
bool errorReported = false; |
||||||
|
while(true) { |
||||||
|
if (!TryMoveTo('-', fragmentEnd)) break; // End of fragment
|
||||||
|
if (TryPeek("-->")) break; |
||||||
|
if (TryPeek("--") && !errorReported) { |
||||||
|
OnSyntaxError(this.CurrentLocation, this.CurrentLocation + 2, "'--' is not allowed in comment"); |
||||||
|
errorReported = true; |
||||||
|
} |
||||||
|
TryMoveNext(); |
||||||
|
} |
||||||
|
} else if (type == TextType.CData) { |
||||||
|
while(true) { |
||||||
|
// We can not use use TryMoveTo("]]>", fragmentEnd) because it may incorectly accept "]" at the end of fragment
|
||||||
|
if (!TryMoveTo(']', fragmentEnd)) break; // End of fragment
|
||||||
|
if (TryPeek("]]>")) break; |
||||||
|
TryMoveNext(); |
||||||
|
} |
||||||
|
} else if (type == TextType.ProcessingInstruction) { |
||||||
|
while(true) { |
||||||
|
if (!TryMoveTo('?', fragmentEnd)) break; // End of fragment
|
||||||
|
if (TryPeek("?>")) break; |
||||||
|
TryMoveNext(); |
||||||
|
} |
||||||
|
} else if (type == TextType.UnknownBang) { |
||||||
|
TryMoveToAnyOf(new char[] {'<', '>'}, fragmentEnd); |
||||||
|
} else { |
||||||
|
throw new InternalException("Uknown type " + type); |
||||||
|
} |
||||||
|
|
||||||
|
text.ContainsOnlyWhitespace = (wsEnd == this.CurrentLocation); |
||||||
|
|
||||||
|
// Terminal found or real end was reached;
|
||||||
|
finished = this.CurrentLocation < fragmentEnd || IsEndOfFile(); |
||||||
|
|
||||||
|
if (!finished) { |
||||||
|
// We have to continue reading more text fragments
|
||||||
|
|
||||||
|
// If there is entity reference, make sure the next segment starts with it to prevent framentation
|
||||||
|
int entitySearchStart = Math.Max(start + 1 /* data for us */, this.CurrentLocation - maxEntityLength); |
||||||
|
int entitySearchLength = this.CurrentLocation - entitySearchStart; |
||||||
|
if (entitySearchLength > 0) { |
||||||
|
// Note that LastIndexOf works backward
|
||||||
|
int entityIndex = input.LastIndexOf('&', this.CurrentLocation - entitySearchLength, entitySearchLength); |
||||||
|
if (entityIndex != -1) { |
||||||
|
GoBack(entityIndex); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
string escapedValue = GetText(start, this.CurrentLocation); |
||||||
|
if (type == TextType.CharacterData) { |
||||||
|
// Normalize end of line first
|
||||||
|
text.Value = Dereference(NormalizeEndOfLine(escapedValue), start); |
||||||
|
} else { |
||||||
|
text.Value = escapedValue; |
||||||
|
} |
||||||
|
text.Value = GetCachedString(text.Value); |
||||||
|
|
||||||
|
EndInternalObject(frame, storeNewObject: this.CurrentLocation > start); |
||||||
|
|
||||||
|
} while (!finished); |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Dereference
|
||||||
|
const int maxEntityLength = 16; // The longest built-in one is 10 ("")
|
||||||
|
|
||||||
|
string Dereference(string text, int textLocation) |
||||||
|
{ |
||||||
|
StringBuilder sb = null; // The dereferenced text so far (all up to 'curr')
|
||||||
|
int curr = 0; |
||||||
|
while(true) { |
||||||
|
// Reached end of input
|
||||||
|
if (curr == text.Length) { |
||||||
|
if (sb != null) { |
||||||
|
return sb.ToString(); |
||||||
|
} else { |
||||||
|
return text; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Try to find reference
|
||||||
|
int start = text.IndexOf('&', curr); |
||||||
|
|
||||||
|
// No more references found
|
||||||
|
if (start == -1) { |
||||||
|
if (sb != null) { |
||||||
|
sb.Append(text, curr, text.Length - curr); // Add rest
|
||||||
|
return sb.ToString(); |
||||||
|
} else { |
||||||
|
return text; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Append text before the enitiy reference
|
||||||
|
if (sb == null) sb = new StringBuilder(text.Length); |
||||||
|
sb.Append(text, curr, start - curr); |
||||||
|
curr = start; |
||||||
|
|
||||||
|
// Process the entity
|
||||||
|
int errorLoc = textLocation + sb.Length; |
||||||
|
|
||||||
|
// Find entity name
|
||||||
|
int end = text.IndexOfAny(new char[] {'&', ';'}, start + 1, Math.Min(maxEntityLength, text.Length - (start + 1))); |
||||||
|
if (end == -1 || text[end] == '&') { |
||||||
|
// Not found
|
||||||
|
OnSyntaxError(errorLoc, errorLoc + 1, "Entity reference must be terminated with ';'"); |
||||||
|
// Keep '&'
|
||||||
|
sb.Append('&'); |
||||||
|
curr++; |
||||||
|
continue; // Restart and next character location
|
||||||
|
} |
||||||
|
string name = text.Substring(start + 1, end - (start + 1)); |
||||||
|
|
||||||
|
// Resolve the name
|
||||||
|
string replacement; |
||||||
|
if (name.Length == 0) { |
||||||
|
replacement = null; |
||||||
|
OnSyntaxError(errorLoc + 1, errorLoc + 1, "Entity name expected"); |
||||||
|
} else if (name == "amp") { |
||||||
|
replacement = "&"; |
||||||
|
} else if (name == "lt") { |
||||||
|
replacement = "<"; |
||||||
|
} else if (name == "gt") { |
||||||
|
replacement = ">"; |
||||||
|
} else if (name == "apos") { |
||||||
|
replacement = "'"; |
||||||
|
} else if (name == "quot") { |
||||||
|
replacement = "\""; |
||||||
|
} else if (name.Length > 0 && name[0] == '#') { |
||||||
|
int num; |
||||||
|
if (name.Length > 1 && name[1] == 'x') { |
||||||
|
if (!int.TryParse(name.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture.NumberFormat, out num)) { |
||||||
|
num = -1; |
||||||
|
OnSyntaxError(errorLoc + 3, errorLoc + 1 + name.Length, "Hexadecimal code of unicode character expected"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (!int.TryParse(name.Substring(1), NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out num)) { |
||||||
|
num = -1; |
||||||
|
OnSyntaxError(errorLoc + 2, errorLoc + 1 + name.Length, "Numeric code of unicode character expected"); |
||||||
|
} |
||||||
|
} |
||||||
|
if (num != -1) { |
||||||
|
try { |
||||||
|
replacement = char.ConvertFromUtf32(num); |
||||||
|
} catch (ArgumentOutOfRangeException) { |
||||||
|
replacement = null; |
||||||
|
OnSyntaxError(errorLoc + 2, errorLoc + 1 + name.Length, "Invalid unicode character U+{0:X} ({0})", num); |
||||||
|
} |
||||||
|
} else { |
||||||
|
replacement = null; |
||||||
|
} |
||||||
|
} else if (!IsValidName(name)) { |
||||||
|
replacement = null; |
||||||
|
OnSyntaxError(errorLoc + 1, errorLoc + 1, "Invalid entity name"); |
||||||
|
} else { |
||||||
|
replacement = null; |
||||||
|
if (tagSoupParser.UnknownEntityReferenceIsError) { |
||||||
|
OnSyntaxError(errorLoc, errorLoc + 1 + name.Length + 1, "Unknown entity reference '{0}'", name); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Append the replacement to output
|
||||||
|
if (replacement != null) { |
||||||
|
sb.Append(replacement); |
||||||
|
} else { |
||||||
|
sb.Append('&'); |
||||||
|
sb.Append(name); |
||||||
|
sb.Append(';'); |
||||||
|
} |
||||||
|
curr = end + 1; |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Syntax Errors
|
||||||
|
List<InternalSyntaxError> syntaxErrors = new List<InternalSyntaxError>(); |
||||||
|
|
||||||
|
InternalSyntaxError[] GetSyntaxErrors() |
||||||
|
{ |
||||||
|
if (syntaxErrors.Count > 0) { |
||||||
|
var arr = syntaxErrors.ToArray(); |
||||||
|
syntaxErrors.Clear(); |
||||||
|
return arr; |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void OnSyntaxError(string message, params object[] args) |
||||||
|
{ |
||||||
|
OnSyntaxError(this.CurrentLocation, this.CurrentLocation + 1, message, args); |
||||||
|
} |
||||||
|
|
||||||
|
void OnSyntaxError(int start, int end, string message, params object[] args) |
||||||
|
{ |
||||||
|
if (end <= start) end = start + 1; |
||||||
|
string formattedMessage = string.Format(CultureInfo.InvariantCulture, message, args); |
||||||
|
Log.WriteLine("Syntax error ({0}-{1}): {2}", start, end, formattedMessage); |
||||||
|
syntaxErrors.Add(new InternalSyntaxError(start - internalObjectStartPosition, end - internalObjectStartPosition, formattedMessage)); |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Helper functions
|
||||||
|
static bool IsValidName(string name) |
||||||
|
{ |
||||||
|
try { |
||||||
|
System.Xml.XmlConvert.VerifyName(name); |
||||||
|
return true; |
||||||
|
} catch (System.Xml.XmlException) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static string NormalizeEndOfLine(string text) |
||||||
|
{ |
||||||
|
return text.Replace("\r\n", "\n").Replace('\r', '\n'); |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,70 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// XML tag soup parser that .
|
||||||
|
/// </summary>
|
||||||
|
public class TagSoupParser |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Generate syntax error when seeing entity reference other then the built-in ones
|
||||||
|
/// </summary>
|
||||||
|
public bool UnknownEntityReferenceIsError { get; set; } |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses a document.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Parsed tag soup.</returns>
|
||||||
|
public IList<AXmlObject> Parse(ITextSource textSource) |
||||||
|
{ |
||||||
|
if (textSource == null) |
||||||
|
throw new ArgumentNullException("textSource"); |
||||||
|
var reader = new TagReader(this, textSource); |
||||||
|
var internalObjects = reader.ReadAllObjects(); |
||||||
|
var publicObjects = new AXmlObject[internalObjects.Length]; |
||||||
|
int pos = 0; |
||||||
|
for (int i = 0; i < internalObjects.Length; i++) { |
||||||
|
publicObjects[i] = internalObjects[i].CreatePublicObject(null, pos); |
||||||
|
pos += internalObjects[i].Length; |
||||||
|
} |
||||||
|
return Array.AsReadOnly(publicObjects); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses a document incrementally.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="oldParserState">The parser state from a previous call to ParseIncremental(). Use null for the first call.</param>
|
||||||
|
/// <param name="newTextSource">The text source for the new document version.</param>
|
||||||
|
/// <param name="newParserState">Out: the new parser state, pass this to the next ParseIncremental() call.</param>
|
||||||
|
/// <returns>Parsed tag soup.</returns>
|
||||||
|
public IList<AXmlObject> ParseIncremental(IncrementalParserState oldParserState, ITextSource newTextSource, out IncrementalParserState newParserState) |
||||||
|
{ |
||||||
|
if (newTextSource == null) |
||||||
|
throw new ArgumentNullException("newTextSource"); |
||||||
|
// TODO: incremental parser
|
||||||
|
newParserState = null; |
||||||
|
return Parse(newTextSource); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,47 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
/// <summary> Identifies the context in which the text occured </summary>
|
||||||
|
enum TextType |
||||||
|
{ |
||||||
|
/// <summary> Ends with non-whitespace </summary>
|
||||||
|
WhiteSpace, |
||||||
|
|
||||||
|
/// <summary> Ends with "<"; "]]>" is error </summary>
|
||||||
|
CharacterData, |
||||||
|
|
||||||
|
/// <summary> Ends with "-->"; "--" is error </summary>
|
||||||
|
Comment, |
||||||
|
|
||||||
|
/// <summary> Ends with "]]>" </summary>
|
||||||
|
CData, |
||||||
|
|
||||||
|
/// <summary> Ends with "?>" </summary>
|
||||||
|
ProcessingInstruction, |
||||||
|
|
||||||
|
/// <summary> Ends with "<" or ">" </summary>
|
||||||
|
UnknownBang, |
||||||
|
|
||||||
|
/// <summary> Unknown </summary>
|
||||||
|
Other |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,348 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Diagnostics; |
||||||
|
using System.Linq; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
class TokenReader |
||||||
|
{ |
||||||
|
protected readonly ITextSource input; |
||||||
|
protected readonly int inputLength; |
||||||
|
int currentLocation; |
||||||
|
|
||||||
|
// CurrentLocation is assumed to be touched and the fact does not
|
||||||
|
// have to be recorded in this variable.
|
||||||
|
// This stores any value bigger than that if applicable.
|
||||||
|
// Actual value is max(currentLocation, maxTouchedLocation).
|
||||||
|
int maxTouchedLocation; |
||||||
|
|
||||||
|
public int InputLength { |
||||||
|
get { return inputLength; } |
||||||
|
} |
||||||
|
|
||||||
|
public int CurrentLocation { |
||||||
|
get { return currentLocation; } |
||||||
|
} |
||||||
|
|
||||||
|
public int MaxTouchedLocation { |
||||||
|
get { return Math.Max(currentLocation, maxTouchedLocation); } |
||||||
|
} |
||||||
|
|
||||||
|
public TokenReader(ITextSource input) |
||||||
|
{ |
||||||
|
this.input = input; |
||||||
|
this.inputLength = input.TextLength; |
||||||
|
} |
||||||
|
|
||||||
|
protected bool IsEndOfFile() |
||||||
|
{ |
||||||
|
return currentLocation == inputLength; |
||||||
|
} |
||||||
|
|
||||||
|
protected bool HasMoreData() |
||||||
|
{ |
||||||
|
return currentLocation < inputLength; |
||||||
|
} |
||||||
|
|
||||||
|
protected void AssertHasMoreData() |
||||||
|
{ |
||||||
|
Log.Assert(HasMoreData(), "Unexpected end of file"); |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryMoveNext() |
||||||
|
{ |
||||||
|
if (currentLocation == inputLength) return false; |
||||||
|
|
||||||
|
currentLocation++; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
protected void Skip(int count) |
||||||
|
{ |
||||||
|
Log.Assert(currentLocation + count <= inputLength, "Skipping after the end of file"); |
||||||
|
currentLocation += count; |
||||||
|
} |
||||||
|
|
||||||
|
protected void GoBack(int oldLocation) |
||||||
|
{ |
||||||
|
Log.Assert(oldLocation <= currentLocation, "Trying to move forward"); |
||||||
|
maxTouchedLocation = Math.Max(maxTouchedLocation, currentLocation); |
||||||
|
currentLocation = oldLocation; |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryRead(char c) |
||||||
|
{ |
||||||
|
if (currentLocation == inputLength) return false; |
||||||
|
|
||||||
|
if (input.GetCharAt(currentLocation) == c) { |
||||||
|
currentLocation++; |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryReadAnyOf(params char[] c) |
||||||
|
{ |
||||||
|
if (currentLocation == inputLength) return false; |
||||||
|
|
||||||
|
if (c.Contains(input.GetCharAt(currentLocation))) { |
||||||
|
currentLocation++; |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryRead(string text) |
||||||
|
{ |
||||||
|
if (TryPeek(text)) { |
||||||
|
currentLocation += text.Length; |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryPeekPrevious(char c, int back) |
||||||
|
{ |
||||||
|
if (currentLocation - back == inputLength) return false; |
||||||
|
if (currentLocation - back < 0 ) return false; |
||||||
|
|
||||||
|
return input.GetCharAt(currentLocation - back) == c; |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryPeek(char c) |
||||||
|
{ |
||||||
|
if (currentLocation == inputLength) return false; |
||||||
|
|
||||||
|
return input.GetCharAt(currentLocation) == c; |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryPeekAnyOf(params char[] chars) |
||||||
|
{ |
||||||
|
if (currentLocation == inputLength) return false; |
||||||
|
|
||||||
|
return chars.Contains(input.GetCharAt(currentLocation)); |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryPeek(string text) |
||||||
|
{ |
||||||
|
if (!TryPeek(text[0])) return false; // Early exit
|
||||||
|
|
||||||
|
maxTouchedLocation = Math.Max(maxTouchedLocation, currentLocation + (text.Length - 1)); |
||||||
|
// The following comparison 'touches' the end of file - it does depend on the end being there
|
||||||
|
if (currentLocation + text.Length > inputLength) return false; |
||||||
|
|
||||||
|
return input.GetText(currentLocation, text.Length) == text; |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryPeekWhiteSpace() |
||||||
|
{ |
||||||
|
if (currentLocation == inputLength) return false; |
||||||
|
|
||||||
|
char c = input.GetCharAt(currentLocation); |
||||||
|
return ((int)c <= 0x20) && (c == ' ' || c == '\t' || c == '\n' || c == '\r'); |
||||||
|
} |
||||||
|
|
||||||
|
// The move functions do not have to move if already at target
|
||||||
|
// The move functions allow 'overriding' of the document length
|
||||||
|
|
||||||
|
protected bool TryMoveTo(char c) |
||||||
|
{ |
||||||
|
return TryMoveTo(c, inputLength); |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryMoveTo(char c, int inputLength) |
||||||
|
{ |
||||||
|
if (currentLocation == inputLength) return false; |
||||||
|
int index = input.IndexOf(c, currentLocation, inputLength - currentLocation); |
||||||
|
if (index != -1) { |
||||||
|
currentLocation = index; |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
currentLocation = inputLength; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryMoveToAnyOf(params char[] c) |
||||||
|
{ |
||||||
|
return TryMoveToAnyOf(c, inputLength); |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryMoveToAnyOf(char[] c, int inputLength) |
||||||
|
{ |
||||||
|
if (currentLocation == inputLength) return false; |
||||||
|
int index = input.IndexOfAny(c, currentLocation, inputLength - currentLocation); |
||||||
|
if (index != -1) { |
||||||
|
currentLocation = index; |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
currentLocation = inputLength; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryMoveTo(string text) |
||||||
|
{ |
||||||
|
return TryMoveTo(text, inputLength); |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryMoveTo(string text, int inputLength) |
||||||
|
{ |
||||||
|
if (currentLocation == inputLength) return false; |
||||||
|
int index = input.IndexOf(text, currentLocation, inputLength - currentLocation, StringComparison.Ordinal); |
||||||
|
if (index != -1) { |
||||||
|
maxTouchedLocation = index + text.Length - 1; |
||||||
|
currentLocation = index; |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
currentLocation = inputLength; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryMoveToNonWhiteSpace() |
||||||
|
{ |
||||||
|
return TryMoveToNonWhiteSpace(inputLength); |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryMoveToNonWhiteSpace(int inputLength) |
||||||
|
{ |
||||||
|
while(true) { |
||||||
|
if (currentLocation == inputLength) return false; // Reject end of file
|
||||||
|
char c = input.GetCharAt(currentLocation); |
||||||
|
if (((int)c <= 0x20) && (c == ' ' || c == '\t' || c == '\n' || c == '\r')) { |
||||||
|
currentLocation++; // Accept white-space
|
||||||
|
continue; |
||||||
|
} else { |
||||||
|
return true; // Found non-white-space
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a name token.
|
||||||
|
/// The following characters are not allowed:
|
||||||
|
/// "" End of file
|
||||||
|
/// " \n\r\t" Whitesapce
|
||||||
|
/// "=\'\"" Attribute value
|
||||||
|
/// "<>/?" Tags
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Returns the length of the name</returns>
|
||||||
|
protected bool TryReadName() |
||||||
|
{ |
||||||
|
int start = currentLocation; |
||||||
|
// Keep reading up to invalid character
|
||||||
|
while (HasMoreData()) { |
||||||
|
char c = input.GetCharAt(currentLocation); |
||||||
|
if (0x41 <= (int)c) { // Accpet from 'A' onwards
|
||||||
|
currentLocation++; |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (c == ' ' || c == '\n' || c == '\r' || c == '\t' || // Reject whitesapce
|
||||||
|
c == '=' || c == '\'' || c == '"' || // Reject attributes
|
||||||
|
c == '<' || c == '>' || c == '/' || c == '?') { // Reject tags
|
||||||
|
break; |
||||||
|
} else { |
||||||
|
currentLocation++; |
||||||
|
continue; // Accept other character
|
||||||
|
} |
||||||
|
} |
||||||
|
return currentLocation > start; |
||||||
|
} |
||||||
|
|
||||||
|
protected bool TryReadName(out string name) |
||||||
|
{ |
||||||
|
int start = currentLocation; |
||||||
|
if (TryReadName()) { |
||||||
|
name = GetCachedString(GetText(start, currentLocation)); |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
name = string.Empty; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected string GetText(int start, int end) |
||||||
|
{ |
||||||
|
Log.Assert(end <= currentLocation, "Reading ahead of current location"); |
||||||
|
return input.GetText(start, end - start); |
||||||
|
} |
||||||
|
|
||||||
|
Dictionary<string, string> stringCache = new Dictionary<string, string>(); |
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
int stringCacheRequestedCount; |
||||||
|
int stringCacheRequestedSize; |
||||||
|
int stringCacheStoredCount; |
||||||
|
int stringCacheStoredSize; |
||||||
|
#endif
|
||||||
|
|
||||||
|
internal void PrintStringCacheStats() |
||||||
|
{ |
||||||
|
#if DEBUG
|
||||||
|
Log.WriteLine("String cache: Requested {0} ({1} bytes); Actaully stored {2} ({3} bytes); {4}% stored", stringCacheRequestedCount, stringCacheRequestedSize, stringCacheStoredCount, stringCacheStoredSize, stringCacheRequestedSize == 0 ? 0 : stringCacheStoredSize * 100 / stringCacheRequestedSize); |
||||||
|
#endif
|
||||||
|
} |
||||||
|
|
||||||
|
[Conditional("DEBUG")] |
||||||
|
void AddToRequestedSize(string text) |
||||||
|
{ |
||||||
|
#if DEBUG
|
||||||
|
stringCacheRequestedCount += 1; |
||||||
|
stringCacheRequestedSize += 8 + 2 * text.Length; |
||||||
|
#endif
|
||||||
|
} |
||||||
|
|
||||||
|
[Conditional("DEBUG")] |
||||||
|
void AddToStoredSize(string text) |
||||||
|
{ |
||||||
|
#if DEBUG
|
||||||
|
stringCacheStoredCount += 1; |
||||||
|
stringCacheStoredSize += 8 + 2 * text.Length; |
||||||
|
#endif
|
||||||
|
} |
||||||
|
|
||||||
|
protected string GetCachedString(string cached) |
||||||
|
{ |
||||||
|
AddToRequestedSize(cached); |
||||||
|
// Do not bother with long strings
|
||||||
|
if (cached.Length > 32) { |
||||||
|
AddToStoredSize(cached); |
||||||
|
return cached; |
||||||
|
} |
||||||
|
string result; |
||||||
|
if (stringCache.TryGetValue(cached, out result)) { |
||||||
|
// Get the instance from the cache instead
|
||||||
|
return result; |
||||||
|
} else { |
||||||
|
// Add to cache
|
||||||
|
AddToStoredSize(cached); |
||||||
|
stringCache.Add(cached, cached); |
||||||
|
return cached; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,48 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.Xml |
||||||
|
{ |
||||||
|
sealed class XmlSegment : ISegment |
||||||
|
{ |
||||||
|
readonly int startOffset, endOffset; |
||||||
|
|
||||||
|
public XmlSegment(int startOffset, int endOffset) |
||||||
|
{ |
||||||
|
if (endOffset < startOffset) |
||||||
|
throw new ArgumentOutOfRangeException(); |
||||||
|
this.startOffset = startOffset; |
||||||
|
this.endOffset = endOffset; |
||||||
|
} |
||||||
|
|
||||||
|
int ISegment.Offset { |
||||||
|
get { return startOffset; } |
||||||
|
} |
||||||
|
|
||||||
|
int ISegment.Length { |
||||||
|
get { return endOffset - startOffset; } |
||||||
|
} |
||||||
|
|
||||||
|
int ISegment.EndOffset { |
||||||
|
get { return endOffset; } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
#region Using directives
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Reflection; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyCompany("ICSharpCode")] |
||||||
|
[assembly: AssemblyProduct("SharpDevelop/MonoDevelop")] |
||||||
|
[assembly: AssemblyCopyright("Copyright 2010-2012 AlphaSierraPapa")] |
||||||
|
|
||||||
|
// This sets the default COM visibility of types in the assembly to invisible.
|
||||||
|
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
|
||||||
|
[assembly: ComVisible(false)] |
||||||
|
|
||||||
|
// The assembly version has following format :
|
||||||
|
//
|
||||||
|
// Major.Minor.Build.Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can use the default the Revision and
|
||||||
|
// Build Numbers by using the '*' as shown below:
|
||||||
|
[assembly: AssemblyVersion("5.0.0.4")] |
||||||
Loading…
Reference in new issue