25 changed files with 2143 additions and 21 deletions
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
|
||||
bin/ |
||||
obj/ |
@ -0,0 +1,61 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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