[mapguide-commits] r7062 - in branches/maestro-4.0.x: . MaestroAPITests OSGeo.MapGuide.MaestroAPI

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Mon Oct 1 10:58:36 PDT 2012


Author: jng
Date: 2012-10-01 10:58:35 -0700 (Mon, 01 Oct 2012)
New Revision: 7062

Modified:
   branches/maestro-4.0.x/
   branches/maestro-4.0.x/MaestroAPITests/SchemaTests.cs
   branches/maestro-4.0.x/OSGeo.MapGuide.MaestroAPI/Utility.cs
Log:
#2136: Fix FDO class name encoding/decoding. The previously introduced failing test case now passes. For 4.0.x, we had to do some changes as XmlConvert.IsNCNameChar() is a .net 4.0 only API. We should probably port this back into trunk as Mono would probably still be playing catch up on APIs like this.


Property changes on: branches/maestro-4.0.x
___________________________________________________________________
Modified: svn:mergeinfo
   - /trunk/Tools/Maestro:6490-6494,6923-6924,6926,6928,7026,7034,7056,7058
   + /trunk/Tools/Maestro:6490-6494,6923-6924,6926,6928,7026,7034,7056,7058,7060-7061

Modified: branches/maestro-4.0.x/MaestroAPITests/SchemaTests.cs
===================================================================
--- branches/maestro-4.0.x/MaestroAPITests/SchemaTests.cs	2012-10-01 17:24:15 UTC (rev 7061)
+++ branches/maestro-4.0.x/MaestroAPITests/SchemaTests.cs	2012-10-01 17:58:35 UTC (rev 7062)
@@ -24,6 +24,7 @@
 using OSGeo.MapGuide.MaestroAPI.Schema;
 using System.IO;
 using System.Xml;
+using OSGeo.MapGuide.MaestroAPI;
 
 namespace MaestroAPITests
 {
@@ -31,7 +32,7 @@
     public class SchemaTests
     {
         //These tests are to verify that we can read FDO XML configuration and schema documents without problems
-
+        
         [Test]
         public void TestMySqlSchema()
         {
@@ -307,5 +308,114 @@
             schema.AddClass(cls);
             Assert.AreEqual(schema, cls.Parent);
         }
+        
+        [Test]
+        public void TestClassNameEncoding()
+        {
+            // Round-trip various invalid XML names. Copied from FDO test suite
+            string name1 = "Abc def";
+            string name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "Abc-x20-def" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+ 
+            name1 = " Abc#defg$$";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x20-Abc-x23-defg-x24--x24-" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+        
+            name1 = " Abc#defg hij";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x20-Abc-x23-defg-x20-hij" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+        
+            name1 = "--abc-def---ghi--";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x2d--abc-def---ghi--" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+        
+            name1 = "--abc-x20-def-x23--x24-ghi--";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x2d--abc-x2d-x20-def-x2d-x23--x2d-x24-ghi--" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "-xab";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x2d-xab" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "&Entity";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x26-Entity" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "11ab";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x31-1ab" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "2_Class";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x32-_Class" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "2%Class";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x32--x25-Class" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "2-Class";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x32--Class" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "2-x2f-Class";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x32--x2d-x2f-Class" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "_x2d-";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x00-_x2d-" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "-x3d-";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x2d-x3d-" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "_x2d-_x3f-";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x00-_x2d-_x3f-" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "__x2d-_x3f-";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "__x2d-_x3f-" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "_Class";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_Class" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "_5Class";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_5Class" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "_-5Class";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_-5Class" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            name1 = "-_x2f-Class";
+            name2 = Utility.EncodeFDOName(name1);
+            Assert.AreEqual( name2, "_x2d-_x2f-Class" );
+            Assert.AreEqual( name1, Utility.DecodeFDOName(name2) );
+
+            // Backward compatibility check. Make sure old-style 1st character encodings get decoded.
+            name2 = "-x40-A";
+            Assert.AreEqual( Utility.DecodeFDOName(name2), "@A" );
+        }
     }
 }

Modified: branches/maestro-4.0.x/OSGeo.MapGuide.MaestroAPI/Utility.cs
===================================================================
--- branches/maestro-4.0.x/OSGeo.MapGuide.MaestroAPI/Utility.cs	2012-10-01 17:24:15 UTC (rev 7061)
+++ branches/maestro-4.0.x/OSGeo.MapGuide.MaestroAPI/Utility.cs	2012-10-01 17:58:35 UTC (rev 7062)
@@ -27,6 +27,8 @@
 using OSGeo.MapGuide.ObjectModels;
 using System.Xml;
 using GeoAPI.Geometries;
+using System.Text.RegularExpressions;
+using System.Text;
 
 namespace OSGeo.MapGuide.MaestroAPI
 {
@@ -600,8 +602,12 @@
                 return string.Format("{0} bytes", size);
         }
 
-        private static System.Text.RegularExpressions.Regex EncRegExp = new System.Text.RegularExpressions.Regex(@"(\-x([0-9]|[a-e]|[A-E])([0-9]|[a-e]|[A-E])\-)|(\-dot\-)|(\-colon\-)", System.Text.RegularExpressions.RegexOptions.Compiled);
+        private static Regex EncRegExp = new System.Text.RegularExpressions.Regex(@"(\-x[0-2a-fA-F][0-9a-fA-F]\-)|(\-dot\-)|(\-colon\-)", System.Text.RegularExpressions.RegexOptions.Compiled);
 
+        private static Regex TokenRegex = new Regex("^x[0-9a-fA-F][0-9a-fA-F]", RegexOptions.Compiled);
+
+        private static Regex TokenRegex2 = new Regex("^_x[0-9a-fA-F][0-9a-fA-F]", RegexOptions.Compiled);
+
         /// <summary>
         /// FDO encodes a string
         /// </summary>
@@ -609,50 +615,169 @@
         /// <returns></returns>
         public static string EncodeFDOName(string name)
         {
-            return name.Replace("\"", "-x22-")
-                       .Replace("&", "-x26-")
-                       .Replace("'", "-x27-")
-                       .Replace("<", "-x3C-")
-                       .Replace(">", "-x3E-")
-                       .Replace(" ", "-x20-");
+            //Decode characters not allowed by FDO
+            string lName = name.Replace("-dot-", ".")
+                               .Replace("-colon-", ":");
+
+            //Break the name up by '-' delimiters
+            string[] tokens = lName.Split(new char[] { '-' }, StringSplitOptions.None);
+
+            StringBuilder outName = new StringBuilder();
+
+            // Encode any characters that are not allowed in XML names.
+            // The encoding pattern is "-x%x-" where %x is the character value in hexidecimal.
+            // The dash delimeters were an unfortunate choice since dash cannot be the 1st character
+            // in an XML name. When the 1st character needs to be encoded, it is encoded as "_x%x-" to 
+            // resolve this issue.
+            for (int i = 0; i < tokens.Length; i++)
+            {
+                string token = tokens[i];
+                bool bMatchedToken = false;
+                if (TokenRegex.Match(token, 0).Success)
+                {
+                    bMatchedToken = true;
+                    // the token happens to match the encoding pattern. We want to avoid
+                    // decoding this sub-string on decode. This is done by encoding the leading
+                    // dash.
+                    if (outName.Length == 0)
+                        outName.Append(string.Format("_x{0:X}-", Convert.ToInt32('-')).ToLower());
+                    else
+                        outName.Append(string.Format("-x{0:X}-", Convert.ToInt32('-')).ToLower());
+                }
+                else if (TokenRegex2.Match(token, 0).Success && i == 0)
+                {
+                    bMatchedToken = true;
+                    // the token happens to match the encoding pattern for the 1st character. 
+                    // We want to avoid decoding this sub-string on decode. 
+                    // This is done by prepending a dummy encoding for character 0. This character is 
+                    // discarded on decode. 
+                    outName.Append("_x00-");
+                }
+                else
+                {
+                    // The token doesn't match the encoding pattern, just write the dash
+                    // that was discarded by the tokenizer.
+                    if (i > 0)
+                    {
+                        if (outName.Length == 0)
+                            outName.Append("_x2d-"); // 1st character so lead with '_'
+                        else
+                            outName.Append("-");
+                    }
+                }
+                outName.Append(bMatchedToken ? token : ReplaceBadChars(token));
+            }
+
+
+            char c = outName[0];
+
+            //Perform actual substitutions of bad characters
+            outName = outName.Remove(0, 1);
+
+            //Check if the first character requires a meta-escape character replacement
+            string prefix = c + "";
+            switch (c)
+            {
+                case ' ':
+                    prefix = "_x20-";
+                    break;
+                case '-':
+                    prefix = "_x2d-";
+                    break;
+                case '&':
+                    prefix = "_x26-";
+                    break;
+                default:
+                    if (Char.IsDigit(c))
+                    {
+                        prefix = "_x3" + c + "-";
+                    }
+                    break;
+            }
+            string result = prefix + outName.ToString();
+            return result;
         }
 
+        private static string ReplaceBadChars(string token)
+        {
+            StringBuilder sb = new StringBuilder();
+            bool bFirstChar = true;
+            foreach (char c in token)
+            {
+                if (Char.IsDigit(c) || IsValidXmlChar(c))
+                    sb.Append(c);
+                else
+                    sb.AppendFormat("{0}x{1:X}-", bFirstChar ? "_" : "-", Convert.ToInt32(c));
+
+                bFirstChar = false;
+            }
+            return sb.ToString();
+        }
+
+        private static bool IsValidXmlChar(char c)
+        {
+            try
+            {
+                XmlConvert.VerifyNCName(c + "");
+                return true;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+
         /// <summary>
         /// Converts FDO encoded characters into their original character.
-        /// Encoded characters have the form -x00-.
         /// </summary>
         /// <param name="name">The FDO encoded string</param>
         /// <returns>The unencoded version of the string</returns>
         public static string DecodeFDOName(string name)
         {
-            System.Text.RegularExpressions.Match m = EncRegExp.Match(name);
-            System.Text.StringBuilder sb = new System.Text.StringBuilder();
-            int previndex = 0;
+            // The encoding pattern is delimited by '-' so break string up by '-'.
+            string[] tokens = name.Split(new char[] { '-' }, StringSplitOptions.None);
+            bool prevDecode = true;
+            StringBuilder decoded = new StringBuilder();
+            for (int i = 0; i < tokens.Length; i++)
+            {
+                string token = tokens[i];
+                //This is a special character inserted during the encoding process.
+                //If we find this at the beginning, discard it
+                if (i == 0 && token == "_x00")
+                    continue;
 
-            while (m != null && m.Success)
-            {
-                string replaceval;
-                if (m.Value == "-dot-")
-                    replaceval = ".";
-                else if (m.Value == "-colon-")
-                    replaceval = ":";
+                var m = TokenRegex.Match(token, 0);
+                var m2 = TokenRegex2.Match(token, 0);
+                if (token.Length == 4 && token.StartsWith("_x3") && Char.IsDigit(token[3]))
+                {
+                    decoded.Append(token[3]);
+                    prevDecode = true;
+                }
                 else
-                    replaceval = ((char)int.Parse(m.Value.Substring(2, 2), System.Globalization.NumberStyles.HexNumber)).ToString();
+                {
+                    if ((!prevDecode) && m.Success)
+                    {
+                        string replace = ((char)int.Parse(m.Value.Substring(1, 2), System.Globalization.NumberStyles.HexNumber)).ToString();
+                        decoded.Append(replace);
+                        prevDecode = true;
+                    }
+                    else if ((i == 0) && m2.Success)
+                    {
+                        string replace = ((char)int.Parse(m2.Value.Substring(2, 2), System.Globalization.NumberStyles.HexNumber)).ToString();
+                        decoded.Append(replace);
+                        prevDecode = true;
+                    }
+                    else
+                    {
+                        if (i > 0 && !prevDecode)
+                            decoded.Append("-");
 
-                sb.Append(name.Substring(previndex, m.Index - previndex));
-                sb.Append(replaceval);
-                previndex = m.Index + m.Value.Length;
-
-                m = m.NextMatch();
+                        decoded.Append(token);
+                        prevDecode = false;
+                    }
+                }
             }
-
-            if (sb.Length == 0)
-                return name;
-            else
-            {
-                sb.Append(name.Substring(previndex));
-                return sb.ToString();
-            }
+            return decoded.ToString();
         }
 
 



More information about the mapguide-commits mailing list