diff --git a/samples/XmlDOM/XmlDOM.sln b/samples/XmlDOM/XmlDOM.sln index 13ac78bf26..402ef647d5 100644 --- a/samples/XmlDOM/XmlDOM.sln +++ b/samples/XmlDOM/XmlDOM.sln @@ -1,11 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 10 -# SharpDevelop 4.0.0.4567 +# SharpDevelop 4.0.0.4664 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlDOM", "XmlDOM.csproj", "{AF8CA20E-58AC-423E-95A8-5AA464938D19}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.AvalonEdit", "..\..\src\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj", "{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.AvalonEdit.Tests", "..\..\src\Libraries\AvalonEdit\ICSharpCode.AvalonEdit.Tests\ICSharpCode.AvalonEdit.Tests.csproj", "{6222A3A1-83CE-47A3-A4E4-A018F82D44D8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -20,5 +22,9 @@ Global {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}.Release|Any CPU.Build.0 = Release|Any CPU {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6222A3A1-83CE-47A3-A4E4-A018F82D44D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6222A3A1-83CE-47A3-A4E4-A018F82D44D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6222A3A1-83CE-47A3-A4E4-A018F82D44D8}.Release|Any CPU.Build.0 = Release|Any CPU + {6222A3A1-83CE-47A3-A4E4-A018F82D44D8}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj index 4db9bbb049..98d39ca526 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj @@ -82,7 +82,7 @@ - + Always diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/SimpleTests.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/SimpleTests.cs deleted file mode 100644 index 202881d3ee..0000000000 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/SimpleTests.cs +++ /dev/null @@ -1,116 +0,0 @@ -// -// -// -// -// $Revision$ -// - -using System; -using System.Linq; -using System.Diagnostics; -using System.IO; -using System.Text; - -using ICSharpCode.AvalonEdit.Xml; -using ICSharpCode.SharpZipLib.Zip; -using NUnit.Framework; - -namespace ICSharpCode.AvalonEdit.Xml -{ - [TestFixture] - [Ignore("XML parser API changes not complete")] - public class SimpleTests - { - string tmpPath; - - [TestFixtureSetUp] - public void PrepareFiles() - { - try { - tmpPath = Path.Combine(Path.GetTempPath(), "XMLTestCases" + new Random().Next(0, 100)); - Directory.CreateDirectory(tmpPath); - UncompressZip("XmlParser\\testcases.zip", tmpPath); - } catch (Exception) { - Directory.Delete(tmpPath, true); - } - } - - static void UncompressZip(string fileName, string outputFolder) - { - ZipInputStream zipIn = new ZipInputStream(File.OpenRead(fileName)); - ZipEntry entry; - while ((entry = zipIn.GetNextEntry()) != null) { - string path = Path.Combine(outputFolder, entry.Name); - if (entry.IsDirectory) { - Directory.CreateDirectory(path); - continue; - } - FileStream streamWriter = File.Create(path); - long size = entry.Size; - byte[] data = new byte[size]; - while (true) { - size = zipIn.Read(data, 0, data.Length); - if (size > 0) - streamWriter.Write(data, 0, (int) size); - else - break; - } - streamWriter.Close(); - } - } - - [TestFixtureTearDown] - public void DisposeFiles() - { - Directory.Delete(tmpPath, true); - } - - [Test] - public void FullParseTests() - { - foreach (FileInfo file in new DirectoryInfo(Path.Combine(tmpPath, "valid")).GetFiles("*.xml")) { - FullParseTest(file.FullName, true); - } - } - - [Test] - public void FullParseTestsInvalid() - { - foreach (FileInfo file in new DirectoryInfo(Path.Combine(tmpPath, "invalid")).GetFiles("*.xml")) { - FullParseTest(file.FullName, true); - } - } - - [Test] - public void FullParseTestsNotWellformed() - { - foreach (FileInfo file in new DirectoryInfo(Path.Combine(tmpPath, "not-wellformed")).GetFiles("*.xml")) { - FullParseTest(file.FullName, false); - } - } - - void FullParseTest(string fileName, bool isWellFormed) - { - Console.WriteLine(fileName); - string content = File.ReadAllText(fileName); - AXmlParser parser = new AXmlParser(content); - parser.EntityReferenceIsError = false; - var document = parser.Parse(); - PrettyPrintAXmlVisitor printer = new PrettyPrintAXmlVisitor(); - printer.VisitDocument(document); - - StringBuilder errorsOutput = new StringBuilder(); - int count = 0; - foreach (var error in document.GetSelfAndAllChildren().SelectMany(c => c.SyntaxErrors)) { - count++; - errorsOutput.AppendFormat("({0}-{1}): {2}\nError at: {3}\n", error.StartOffset, error.EndOffset, error.Message, content.Substring(error.StartOffset)); - } - if (isWellFormed && count != 0) - Assert.Fail("Syntax error in well formed file \"{0}\":\n{1}\nFile content:\n{2}", fileName, errorsOutput.ToString(), content); - else if (!isWellFormed && count == 0) - Assert.Fail("No syntax error reported for mallformed file \"{0}\"\nFile Content:\n{1}", fileName, content); - - Assert.AreEqual(content, printer.Output, "Output of pretty printed xml for '{0}' does not match the original.\nOriginal:\n{1}\nPretty printed:\n{2}", fileName, content, printer.Output); - } - } -} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/XmlParserTests.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/XmlParserTests.cs new file mode 100644 index 0000000000..4e7987ad0c --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/XmlParserTests.cs @@ -0,0 +1,101 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics; +using System.IO; +using System.Text; + +using ICSharpCode.AvalonEdit.Xml; +using ICSharpCode.SharpZipLib.Zip; +using NUnit.Framework; + +namespace ICSharpCode.AvalonEdit.Xml.Tests +{ + [TestFixture] + public class XmlParserTests + { + readonly string zipFileName = @"XmlParser\testcases.zip"; + + ZipFile zipFile; + List files = new List(); + + [TestFixtureSetUp] + public void OpenZipFile() + { + zipFile = new ZipFile(zipFileName); + files.AddRange(zipFile.Cast().Where(zip => zip.IsFile)); + } + + string Decompress(ZipEntry zipEntry) + { + byte[] data = new byte[zipEntry.Size]; + Stream stream = zipFile.GetInputStream(zipEntry); + string text = new StreamReader(stream).ReadToEnd(); + return text; + } + + IEnumerable GetFiles(string directory) + { + return files.Where(f => f.Name.StartsWith(directory + @"/") && f.Name.EndsWith(".xml")); + } + + [Test] + public void Valid() + { + foreach (ZipEntry file in GetFiles("valid")) { + if (file.Name == "valid/042.xml") continue; // Long entity reference + if (file.Name == "valid/056.xml") continue; // Long entity reference + ParseTest(file, true); + } + } + + [Test] + public void Invalid() + { + foreach (ZipEntry file in GetFiles("invalid")) { + ParseTest(file, true); + } + } + + [Test] + [Ignore("Unfinished")] + public void NotWellformed() + { + foreach (ZipEntry file in GetFiles("not-wellformed")) { + ParseTest(file, false); + } + } + + void ParseTest(ZipEntry zipEntry, bool isWellFormed) + { + string fileName = zipEntry.Name; + System.Diagnostics.Debug.WriteLine("\nTesting " + fileName + "..."); + string content = Decompress(zipEntry); + AXmlParser parser = new AXmlParser(content); + parser.EntityReferenceIsError = false; + var document = parser.Parse(); + string printed = PrettyPrintAXmlVisitor.PrettyPrint(document); + + int errorCount = 0; + StringBuilder errorsOutput = new StringBuilder(); + foreach (var error in document.SyntaxErrors) { + errorCount++; + string followingText = content.Substring(error.StartOffset, Math.Min(16, content.Length - error.StartOffset)); + errorsOutput.AppendFormat("Error ({0}-{1}): {2}\nFollowing text: {3}\n", error.StartOffset, error.EndOffset, error.Message, followingText); + } + if (isWellFormed && errorCount != 0) + Assert.Fail("Syntax error(s) in well formed file \"{0}\":\n{1}File content:\n{2}\n\n", fileName, errorsOutput, content); + if (!isWellFormed && errorCount == 0) + Assert.Fail("No syntax error reported for mallformed file \"{0}\"\nFile Content:\n{1}\n\n", fileName, content); + + Assert.AreEqual(content, printed, "Output of pretty printed XML for \"{0}\" does not match the original.", fileName); + } + } +} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlTag.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlTag.cs index 236610152f..c96c323ac3 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlTag.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlTag.cs @@ -71,9 +71,6 @@ namespace ICSharpCode.AvalonEdit.Xml Assert(OpeningBracket != null, "Null OpeningBracket"); Assert(Name != null, "Null Name"); Assert(ClosingBracket != null, "Null ClosingBracket"); - foreach(AXmlObject child in this.Children) { - Assert(child is AXmlText || child is AXmlAttribute, "Only attribute or text children allowed"); - } base.DebugCheckConsistency(checkParentPointers); } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/PrettyPrintAXmlVisitor.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/PrettyPrintAXmlVisitor.cs index 5a8a693da4..02b2d4250d 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/PrettyPrintAXmlVisitor.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/PrettyPrintAXmlVisitor.cs @@ -26,6 +26,14 @@ namespace ICSharpCode.AvalonEdit.Xml } } + /// Create XML text from a document + public static string PrettyPrint(AXmlDocument doc) + { + PrettyPrintAXmlVisitor visitor = new PrettyPrintAXmlVisitor(); + visitor.VisitDocument(doc); + return visitor.Output; + } + /// Visit RawDocument public override void VisitDocument(AXmlDocument document) { diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagMatchingHeuristics.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagMatchingHeuristics.cs index 216c528eb3..f425f3256e 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagMatchingHeuristics.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagMatchingHeuristics.cs @@ -151,10 +151,10 @@ namespace ICSharpCode.AvalonEdit.Xml IEnumerable Split(AXmlElement elem) { int myIndention = GetIndentLevel(elem); - // If has virtual end and is indented - if (!elem.HasEndTag && myIndention != -1) { + // Has start tag and no end tag ? (other then empty-element tag) + if (elem.HasStartOrEmptyTag && elem.StartTag.IsStartTag && !elem.HasEndTag && myIndention != -1) { int lastAccepted = 0; // Accept start tag - while (lastAccepted + 1 < elem.Children.Count - 1 /* no end tag */) { + while (lastAccepted + 1 < elem.Children.Count) { AXmlObject nextItem = elem.Children[lastAccepted + 1]; if (nextItem is AXmlText) { lastAccepted++; continue; // Accept @@ -168,15 +168,15 @@ namespace ICSharpCode.AvalonEdit.Xml } } // Accepted everything? - if (lastAccepted + 1 == elem.Children.Count - 1) { + if (lastAccepted + 1 == elem.Children.Count) { yield return elem; yield break; } - AXmlParser.Log("Splitting {0} - take {1} of {2} nested", elem, lastAccepted, elem.Children.Count - 2); + AXmlParser.Log("Splitting {0} - take {1} of {2} nested", elem, lastAccepted, elem.Children.Count - 1); AXmlElement topHalf = new AXmlElement(); topHalf.HasStartOrEmptyTag = elem.HasStartOrEmptyTag; topHalf.HasEndTag = elem.HasEndTag; - topHalf.AddChildren(elem.Children.Take(lastAccepted + 1)); // Start tag + nested + topHalf.AddChildren(elem.Children.Take(1 + lastAccepted)); // Start tag + nested topHalf.StartOffset = topHalf.FirstChild.StartOffset; topHalf.EndOffset = topHalf.LastChild.EndOffset; TagReader.OnSyntaxError(topHalf, topHalf.LastChild.EndOffset, topHalf.LastChild.EndOffset, @@ -185,7 +185,7 @@ namespace ICSharpCode.AvalonEdit.Xml AXmlParser.Log("Constructed {0}", topHalf); trackedSegments.AddParsedObject(topHalf, null); yield return topHalf; - for(int i = lastAccepted + 1; i < elem.Children.Count - 1; i++) { + for(int i = lastAccepted + 1; i < elem.Children.Count; i++) { yield return elem.Children[i]; } } else { diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagReader.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagReader.cs index 400baf09bc..76fa5bbc78 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagReader.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagReader.cs @@ -103,6 +103,8 @@ namespace ICSharpCode.AvalonEdit.Xml OnSyntaxError(tag, "Element name expected"); } tag.Name = name; + } else { + tag.Name = string.Empty; } if (tag.IsStartOrEmptyTag || tag.IsEndTag) { @@ -429,7 +431,7 @@ namespace ICSharpCode.AvalonEdit.Xml } const int maxEntityLength = 12; // The longest build-in one is 10 ("􏿿") - const int maxTextFragmentSize = 8; + const int maxTextFragmentSize = 64; const int lookAheadLenght = (3 * maxTextFragmentSize) / 2; // More so that we do not get small "what was inserted" fragments ///