diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f7ce4a..f8985ea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,6 +38,9 @@ jobs: fileName: 'Portable.Xaml.snk' encodedString: ${{ secrets.STRONG_NAME_KEY }} + - name: Clear package cache as a temporary workaround for actions/setup-dotnet#155 + run: dotnet clean && dotnet nuget locals all --clear + - name: Build run: dotnet build ${{ env.PlatformBuildParameters }} ${{ env.BuildParameters }} "/p:AssemblyOriginatorKeyFile=${{ steps.get_strong_name.outputs.filePath }}" diff --git a/src/Portable.Xaml/Portable.Xaml/PrefixLookup.cs b/src/Portable.Xaml/Portable.Xaml/PrefixLookup.cs index 6558504..74e6054 100644 --- a/src/Portable.Xaml/Portable.Xaml/PrefixLookup.cs +++ b/src/Portable.Xaml/Portable.Xaml/PrefixLookup.cs @@ -70,16 +70,45 @@ public string AddNamespace (string ns) string prefix, s; if (ns == XamlLanguage.Xaml2006Namespace) prefix = "x"; - else if (!l.Any (i => i.Prefix == String.Empty)) + else if (!AnyHavePrefix(l, String.Empty)) prefix = String.Empty; - else if ((s = GetAcronym (ns)) != null && !l.Any (i => i.Prefix == s)) - prefix = s; else - prefix = sctx.GetPreferredPrefix (ns); + { + s = GetAcronym(ns) ?? sctx.GetPreferredPrefix(ns); + prefix = AnyHavePrefix(l, s) ? MakePrefixAddNumber(l, s) : s; + } l.Add (new NamespaceDeclaration (ns, prefix)); return prefix; } + string MakePrefixAddNumber(List namespaces, string prefix) + { + var prefixLen = prefix.Length; + + int max = 0; + for (int i = 0; i < namespaces.Count; i++) + { + var p = namespaces[i].Prefix; + var suffix = !p.StartsWith(prefix) ? 0 : + int.TryParse(p.Substring(prefixLen), out var idx) ? idx : + 0; + max = Math.Max(suffix, max); + } + + return prefix + (max+1); + } + + bool AnyHavePrefix(List namespaces, string prefix) + { + for (int i = 0; i < namespaces.Count; i++) + { + var ns = namespaces[i]; + if (ns.Prefix == prefix) + return true; + } + return false; + } + const string pre = "clr-namespace:"; string GetAcronym (string ns) diff --git a/src/Test/System.Xaml/XamlXmlWriterTest.cs b/src/Test/System.Xaml/XamlXmlWriterTest.cs index 99f6918..6b5a4c6 100644 --- a/src/Test/System.Xaml/XamlXmlWriterTest.cs +++ b/src/Test/System.Xaml/XamlXmlWriterTest.cs @@ -1,4 +1,4 @@ -// +// // Copyright (C) 2010 Novell Inc. http://novell.com // // Permission is hereby granted, free of charge, to any person obtaining @@ -27,6 +27,7 @@ using System.Linq; using System.Reflection; using NUnit.Framework; +using System.Xml.Serialization; #if PORTABLE_XAML using Portable.Xaml.Markup; using Portable.Xaml.ComponentModel; @@ -1281,10 +1282,114 @@ public void Write_ShoudSerializeObjectInCollection() Assert.AreEqual(xaml, actual); } } + + [Test] + public void Write_AcronymNonDuplicate() + { + var list = new List(); + list.Add(new Acronym.Class1()); + + var genXaml = XamlServices.Save(list); + + // match object + var readList = XamlServices.Parse(genXaml); + Assert.AreEqual(list, readList, "#1"); + + // check namespace + Assert.IsTrue(genXaml.Contains(@"xmlns:tpxa=""clr-namespace:Tests.Portable.Xaml.Acronym;assembly="), "#2"); + } + [Test] + public void Write_AcronymDuplicate1() + { + var list = new List(); + list.Add(new Acronym.Class1()); + list.Add(new Acronym1.Class1()); + + var genXaml = XamlServices.Save(list); + + // match object + var readList = XamlServices.Parse(genXaml); + Assert.AreEqual(list, readList, "#1"); + + // check namespace + Assert.IsTrue(genXaml.Contains(@"xmlns:tpxa=""clr-namespace:Tests.Portable.Xaml.Acronym;assembly="), "#2"); + Assert.IsTrue(genXaml.Contains(@"xmlns:tpxa1=""clr-namespace:Tests.Portable.Xaml.Acronym1;assembly="), "#3"); + } + + [Test] + public void Write_AcronymDuplicate2() + { + var list = new List(); + list.Add(new Acronym.Class1()); + list.Add(new Acronym1.Class1()); + list.Add(new Acronym2.Class1()); + + var genXaml = XamlServices.Save(list); + + // match object + var readList = XamlServices.Parse(genXaml); + Assert.AreEqual(list, readList, "#1"); + + // check namespace + Assert.IsTrue(genXaml.Contains(@"xmlns:tpxa=""clr-namespace:Tests.Portable.Xaml.Acronym;assembly="), "#2"); + Assert.IsTrue(genXaml.Contains(@"xmlns:tpxa1=""clr-namespace:Tests.Portable.Xaml.Acronym1;assembly="), "#3"); + Assert.IsTrue(genXaml.Contains(@"xmlns:tpxa2=""clr-namespace:Tests.Portable.Xaml.Acronym2;assembly="), "#4"); + } + + [Test] + public void Write_AcronymDuplicate3() + { + var list = new List(); + list.Add(new Acronym.Class1()); + list.Add(new Acronym1.Class1()); + list.Add(new Acronym2.Class1()); + list.Add(new Acronym3.Class1()); + + var genXaml = XamlServices.Save(list); + + // match object + var readList = XamlServices.Parse(genXaml); + Assert.AreEqual(list, readList, "#1"); + + // check namespace + Assert.IsTrue(genXaml.Contains(@"xmlns:tpxa=""clr-namespace:Tests.Portable.Xaml.Acronym;assembly="), "#2"); + Assert.IsTrue(genXaml.Contains(@"xmlns:tpxa1=""clr-namespace:Tests.Portable.Xaml.Acronym1;assembly="), "#3"); + Assert.IsTrue(genXaml.Contains(@"xmlns:tpxa2=""clr-namespace:Tests.Portable.Xaml.Acronym2;assembly="), "#4"); + Assert.IsTrue(genXaml.Contains(@"xmlns:tpxa3=""clr-namespace:Tests.Portable.Xaml.Acronym3;assembly="), "#5"); + } } public class TestXmlWriterClass1 { public int Foo { get; set; } } + + namespace Acronym + { + public class Class1 { + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + + return GetType() == obj.GetType(); + } + + public override int GetHashCode() =>this.GetType().GetHashCode(); + } + } + namespace Acronym1 + { + public class Class1: Acronym.Class1 { } + } + namespace Acronym2 + { + public class Class1: Acronym.Class1 { } + } + namespace Acronym3 + { + public class Class1: Acronym.Class1 { } + } }