[smuxi/experiments/certfp: 20/55] Engine(-Tests): implemented SmartLinks with some basic patterns (closes: #498, #911, #923)



commit 1c0f3e543c812005e7d75a62f3199fc5b551be0e
Author: Oliver Schneider <mail oli-obk de>
Date:   Wed Jul 17 16:34:26 2013 +0200

    Engine(-Tests): implemented SmartLinks with some basic patterns (closes: #498, #911, #923)

 src/Engine-Tests/MessageBuilderTests.cs     |  181 +++++++++++++++++++++++++++
 src/Engine-Tests/MessageModelTests.cs       |    2 -
 src/Engine-Tests/MessageParserTests.cs      |   15 +--
 src/Engine/Config/MessageBuilderSettings.cs |  121 ++++++++++++++++++
 src/Engine/Engine.csproj                    |    2 +-
 src/Engine/Messages/MessageBuilder.cs       |  105 +++++++++++++++-
 src/Engine/Messages/MessageParser.cs        |  161 ------------------------
 src/Engine/Messages/UrlMessagePartModel.cs  |   24 ++++
 8 files changed, 435 insertions(+), 176 deletions(-)
---
diff --git a/src/Engine-Tests/MessageBuilderTests.cs b/src/Engine-Tests/MessageBuilderTests.cs
index 4355270..448d2fd 100644
--- a/src/Engine-Tests/MessageBuilderTests.cs
+++ b/src/Engine-Tests/MessageBuilderTests.cs
@@ -309,5 +309,186 @@ namespace Smuxi.Engine
             var builder = new MessageBuilder();
             builder.AppendFormat("{{{{virtual {0}}}}}", "hugs");
         }
+         
+        void TestMessage(string message, MessageModel expectedMsg)
+        {
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.AppendMessage(message);
+            var actualMsg = builder.ToMessage();
+            Assert.AreEqual(expectedMsg, actualMsg);
+        }
+        
+        [Test]
+        public void AppendTextUrlParsingSanity()
+        {
+            var msg = @"http://ab.cd.ef.de-hlub.gummi.museum/my_script%20windows.php?test=blub&blar=93";;
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.Append(new UrlMessagePartModel(msg));
+            TestMessage(msg, builder.ToMessage());
+            /*
+            var msg = @"<http://smuxi.im/#sometag>";
+            var msg = @"hey look at this: http://test.org, it is really cool";
+            var msg = @"have you recently looked at xkcd.org?";
+            var msg = @"my homepage (http://mine.my) has nothing on it";
+            var msg = @"[smuxi] meebey pushed 2 new commits to stable: 
https://github.com/meebey/smuxi/compare/153153feddd4...ff7d23a7550c";;
+            var msg = @"[372 (Motd)] - FOSSCON [http://www.fosscon.org] and fossevents ";
+            var msg = @"[372 (Motd)] - page (http://freenode.net/policy.shtml). Thank you for usin";
+            var msg = @"look at all those deprecated fields pidgin still sets: <c 
xmlns=""http://jabber.org/protocol/caps""; hash=""sha-1"" node=""http://pidgin.im/""; ext=""voice-v1 camera-v1 
video-v1"" ver=""AcN1/PEN8nq7AHD+9jpxMV4U6YM="" />";
+            var msg = @"16:04:11 <clonkspot> Glückwunsch! (@YouTube http://t.co/IXjWtfGJ5d)";
+            var msg = @"This is a http://sentence.that/ends.with?a. This is another sentence.";
+            */
+        }
+        
+        [Test]
+        public void AppendTextUrlParsingLtGtBrackets()
+        {
+            var msg = @"<http://smuxi.im/#sometag>";
+            var url = @"http://smuxi.im/#sometag";;
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.Append(new TextMessagePartModel("<"));
+            builder.Append(new UrlMessagePartModel(url));
+            builder.Append(new TextMessagePartModel(">"));
+            TestMessage(msg, builder.ToMessage());
+        }
+        [Test]
+        public void AppendTextUrlParsingUrlEndsInComma()
+        {
+            var msg = @"hey look at this: http://test.org, it is really cool";
+            var url = @"http://test.org";;
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.Append(new TextMessagePartModel("hey look at this: "));
+            builder.Append(new UrlMessagePartModel(url));
+            builder.Append(new TextMessagePartModel(", it is really cool"));
+            TestMessage(msg, builder.ToMessage());
+        }
+        
+        [Test]
+        public void AppendTextUrlParsingUrlNoProtocol()
+        {
+            var msg = @"hey look at this: test.org";
+            var url = @"http://test.org";;
+            var urltext = @"test.org";
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.Append(new TextMessagePartModel("hey look at this: "));
+            builder.Append(new UrlMessagePartModel(url, urltext));
+            TestMessage(msg, builder.ToMessage());
+        }
+        
+        [Test]
+        public void AppendTextUrlParsingEndsInQuestionmark()
+        {
+            var msg = @"have you recently looked at xkcd.com?";
+            var url = @"http://xkcd.com";;
+            var urltext = @"xkcd.com";
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.Append(new TextMessagePartModel("have you recently looked at "));
+            builder.Append(new UrlMessagePartModel(url, urltext));
+            builder.Append(new TextMessagePartModel("?"));
+            TestMessage(msg, builder.ToMessage());
+            /*
+            var msg = @"my homepage (http://mine.my) has nothing on it";
+            var msg = @"[smuxi] meebey pushed 2 new commits to stable: 
https://github.com/meebey/smuxi/compare/153153feddd4...ff7d23a7550c";;
+            var msg = @"[372 (Motd)] - FOSSCON [http://www.fosscon.org] and fossevents ";
+            var msg = @"[372 (Motd)] - page (http://freenode.net/policy.shtml). Thank you for usin";
+            var msg = @"look at all those deprecated fields pidgin still sets: <c 
xmlns=""http://jabber.org/protocol/caps""; hash=""sha-1"" node=""http://pidgin.im/""; ext=""voice-v1 camera-v1 
video-v1"" ver=""AcN1/PEN8nq7AHD+9jpxMV4U6YM="" />";
+            var msg = @"16:04:11 <clonkspot> Glückwunsch! (@YouTube http://t.co/IXjWtfGJ5d)";
+            var msg = @"This is a http://sentence.that/ends.with?a. This is another sentence.";
+            */
+        }
+        
+        [Test]
+        public void AppendTextUrlParsingUrlInBrackets()
+        {
+            var msg = @"my homepage (http://mine.my) has nothing on it";
+            var url = @"http://mine.my";;
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.Append(new TextMessagePartModel("my homepage ("));
+            builder.Append(new UrlMessagePartModel(url));
+            builder.Append(new TextMessagePartModel(") has nothing on it"));
+            TestMessage(msg, builder.ToMessage());
+        }
+        
+        [Test]
+        public void AppendTextUrlGithubMessage()
+        {
+            var msg = @"[smuxi] meebey pushed 2 new commits to stable: 
https://github.com/meebey/smuxi/compare/153153feddd4...ff7d23a7550c";;
+            var url = @"https://github.com/meebey/smuxi/compare/153153feddd4...ff7d23a7550c";;
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.Append(new TextMessagePartModel("[smuxi] meebey pushed 2 new commits to stable: "));
+            builder.Append(new UrlMessagePartModel(url));
+            TestMessage(msg, builder.ToMessage());
+        }
+        
+        [Test]
+        public void AppendTextUrlSquareBrackets()
+        {
+            var msg = @"[372 (Motd)] - FOSSCON [http://www.fosscon.org] and fossevents ";
+            var url = @"http://www.fosscon.org";;
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.Append(new TextMessagePartModel("[372 (Motd)] - FOSSCON ["));
+            builder.Append(new UrlMessagePartModel(url));
+            builder.Append(new TextMessagePartModel("] and fossevents "));
+            TestMessage(msg, builder.ToMessage());
+        }
+        
+        [Test]
+        public void AppendTextUrlNormalBrackets()
+        {
+            var msg = @"[372 (Motd)] - page (http://freenode.net/policy.shtml). Thank you for usin";
+            var url = @"http://freenode.net/policy.shtml";;
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.Append(new TextMessagePartModel("[372 (Motd)] - page ("));
+            builder.Append(new UrlMessagePartModel(url));
+            builder.Append(new TextMessagePartModel("). Thank you for usin"));
+            TestMessage(msg, builder.ToMessage());
+        }
+        
+        [Test]
+        public void AppendTextUrlMultipleInQuotes()
+        {
+            var msg = @"look at all those deprecated fields pidgin still sets: <c 
xmlns=""http://jabber.org/protocol/caps""; hash=""sha-1"" node=""http://pidgin.im/""; ext=""voice-v1 camera-v1 
video-v1"" ver=""AcN1/PEN8nq7AHD+9jpxMV4U6YM="" />";
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.Append(new TextMessagePartModel(@"look at all those deprecated fields pidgin still sets: 
<c xmlns="""));
+            builder.Append(new UrlMessagePartModel("http://jabber.org/protocol/caps";));
+            builder.Append(new TextMessagePartModel(@""" hash=""sha-1"" node="""));
+            builder.Append(new UrlMessagePartModel("http://pidgin.im/";));
+            builder.Append(new TextMessagePartModel(@""" ext=""voice-v1 camera-v1 video-v1"" 
ver=""AcN1/PEN8nq7AHD+9jpxMV4U6YM="" />"));
+            TestMessage(msg, builder.ToMessage());
+        }
+        
+        [Test]
+        public void AppendTextUrlEndsInClosedBracket()
+        {
+            var msg = @"16:04:11 <clonkspot> Glückwunsch! (@YouTube http://t.co/IXjWtfGJ5d)";
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.Append(new TextMessagePartModel(@"16:04:11 <clonkspot> Glückwunsch! (@YouTube "));
+            builder.Append(new UrlMessagePartModel("http://t.co/IXjWtfGJ5d";));
+            builder.Append(new TextMessagePartModel(@")"));
+            TestMessage(msg, builder.ToMessage());
+        }
+        
+        [Test]
+        public void AppendTextUrlEndsInDot()
+        {
+            var msg = @"This is a http://sentence.th/at/ends.with?a. This is another sentence.";
+            var builder = new MessageBuilder();
+            builder.TimeStamp = DateTime.MinValue;
+            builder.Append(new TextMessagePartModel(@"This is a "));
+            builder.Append(new UrlMessagePartModel("http://sentence.th/at/ends.with?a";));
+            builder.Append(new TextMessagePartModel(@". This is another sentence."));
+            TestMessage(msg, builder.ToMessage());
+        }
     }
 }
diff --git a/src/Engine-Tests/MessageModelTests.cs b/src/Engine-Tests/MessageModelTests.cs
index ce6c1e7..031879b 100644
--- a/src/Engine-Tests/MessageModelTests.cs
+++ b/src/Engine-Tests/MessageModelTests.cs
@@ -137,7 +137,6 @@ namespace Smuxi.Engine
             var msg = new MessageModel(topic) {
                 IsCompactable = false
             };
-            MessageParser.ParseUrls(msg);
 
             var stream = new MemoryStream(1024);
             formatter.Serialize(stream, msg);
@@ -199,7 +198,6 @@ namespace Smuxi.Engine
                 )
             );
             msg.MessageParts.Add(new TextMessagePartModel(" kannst ja watchen"));
-            MessageParser.ParseUrls(msg);
 
             stream = new MemoryStream(1024);
             formatter.Serialize(stream, msg);
diff --git a/src/Engine-Tests/MessageParserTests.cs b/src/Engine-Tests/MessageParserTests.cs
index bf69907..100833e 100644
--- a/src/Engine-Tests/MessageParserTests.cs
+++ b/src/Engine-Tests/MessageParserTests.cs
@@ -36,9 +36,8 @@ namespace Smuxi.Engine
 
             builder = new MessageBuilder();
             builder.TimeStamp = DateTime.MinValue;
-            builder.AppendText("http://example.com";);
+            builder.AppendRichText("http://example.com";);
             var actualMsg = builder.ToMessage();
-            MessageParser.ParseUrls(actualMsg);
 
             Assert.AreEqual(expectedMsg, actualMsg);
         }
@@ -48,16 +47,15 @@ namespace Smuxi.Engine
         {
             var builder = new MessageBuilder();
             builder.TimeStamp = DateTime.MinValue;
-            builder.AppendText("foo ");
-            builder.AppendUrl("http://example.com";, "<http://example.com>");
-            builder.AppendText(" bar");
+            builder.AppendText("foo <");
+            builder.AppendUrl("http://example.com";);
+            builder.AppendText("> bar");
             var expectedMsg = builder.ToMessage();
 
             builder = new MessageBuilder();
             builder.TimeStamp = DateTime.MinValue;
-            builder.AppendText("foo <http://example.com> bar");
+            builder.AppendRichText("foo <http://example.com> bar");
             var actualMsg = builder.ToMessage();
-            MessageParser.ParseUrls(actualMsg);
 
             Assert.AreEqual(expectedMsg, actualMsg);
         }
@@ -74,9 +72,8 @@ namespace Smuxi.Engine
 
             builder = new MessageBuilder();
             builder.TimeStamp = DateTime.MinValue;
-            builder.AppendText("foo (http://example.com) bar");
+            builder.AppendRichText("foo (http://example.com) bar");
             var actualMsg = builder.ToMessage();
-            MessageParser.ParseUrls(actualMsg);
 
             Assert.AreEqual(expectedMsg, actualMsg);
         }
diff --git a/src/Engine/Config/MessageBuilderSettings.cs b/src/Engine/Config/MessageBuilderSettings.cs
new file mode 100644
index 0000000..12d5495
--- /dev/null
+++ b/src/Engine/Config/MessageBuilderSettings.cs
@@ -0,0 +1,121 @@
+// Smuxi - Smart MUltipleXed Irc
+//
+// Copyright (c) 2011 Mirco Bauer <meebey meebey net>
+//
+// Full GPL License: <http://www.gnu.org/licenses/gpl.txt>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+using System;
+using System.Text.RegularExpressions;
+using System.Collections.Generic;
+
+namespace Smuxi.Engine
+{
+    public class MessageBuilderSettings
+    {
+
+        public class SmartLink
+        {
+            public enum ETargetType
+            {
+                Text, Url, Image
+            }
+
+            public Regex MessagePartPattern { get; set; }
+            // what is linked to
+            public string LinkFormat { get; set; }
+            // what is displayed
+            public string TextFormat { get; set; }
+            public ETargetType Type { get; set; }
+        };
+
+        public List<SmartLink> SmartLinks { get; private set; }
+
+        void CreateSmartLink(Regex regex)
+        {
+            CreateSmartLink(regex, null, null);
+        }
+
+        void CreateSmartLink(Regex regex, string linkPattern)
+        {
+            CreateSmartLink(regex, linkPattern, null);
+        }
+
+        void CreateSmartLink(Regex regex, string linkPattern, string textPattern)
+        {
+            var link = new SmartLink();
+            link.MessagePartPattern = regex;
+            link.LinkFormat = linkPattern;
+            link.TextFormat = textPattern;
+            link.Type = SmartLink.ETargetType.Url;
+            SmartLinks.Add(link);
+        }
+
+        void CreateSmartText(Regex regex, string textPattern)
+        {
+            var link = new SmartLink();
+            link.MessagePartPattern = regex;
+            link.TextFormat = textPattern;
+            link.Type = SmartLink.ETargetType.Text;
+            SmartLinks.Add(link);
+        }
+
+        public MessageBuilderSettings()
+        {
+            SmartLinks = new List<SmartLink>();
+            string path_last_chars = @"a-z0-9#/%&=\-_+";
+            string path_chars = path_last_chars + @")(?.,";
+            string domainchars = @"[a-z0-9\-]+";
+            string subdomain = domainchars + @"\.";
+            string tld = @"com|net|org|info|biz|gov|name|edu|museum|[a-z][a-z]";
+            string domain = @"(?:(?:" + subdomain + ")+(?:" + tld + ")|localhost)";
+            string port = ":[1-9][0-9]{1,4}";
+            string domain_port = domain + "(?:" + port + ")?";
+            string path = @"/(?:["+ path_chars +"]*["+ path_last_chars +"]+)?";
+            string address = domain_port + "(?:" + path + ")?";
+
+            // facebook attachment
+            CreateSmartLink(new Regex(@"(<[1-9][0-9]* attachments?>) 
(http://www\.facebook\.com/messages/\?action=read&tid=[0-9a-f]+)"), "{2}", "{1}");
+
+            // protocol://domain
+            CreateSmartLink(new Regex(@"[a-z][a-z0-9\-]*://" + address, RegexOptions.IgnoreCase));
+
+            // E-Mail
+            CreateSmartLink(new Regex(@"([a-z0-9._%+-]+@(?:[a-z0-9-]+\.)+[a-z]{2,})", 
RegexOptions.IgnoreCase), "mailto:{0}";);
+            // addresses without protocol
+            CreateSmartLink(new Regex(address, RegexOptions.IgnoreCase), "http://{0}";);
+            // smuxi bugtracker
+            CreateSmartLink(new Regex(@"#([0-9]+)"), "http://www.smuxi.org/issues/{0}";);
+
+            // TODO: msgid -> http://mid.gmane.org/{1}
+            // TODO: RFC -> http://www.ietf.org/rfc/rfc{1}.txt
+            // TODO: CVE-YYYY-XXXX -> http://cve.mitre.org/cgi-bin/cvename.cgi?name={1}
+            // TODO: ISSN/ISBN
+            // TODO: Path: / or X:\
+            // TODO: GPS -> Google Maps
+            // TODO: IP -> Browser / Whois
+            // TODO: Domain -> Browser / Whois
+            // TODO: ISO -> http://www.iso.org/iso/search.htm?qt={1}&published=on
+            // TODO: ANSI
+            // TODO: ECMA
+            // TODO: maybe more on http://ikiwiki.info/shortcuts/
+            // TODO: JID
+        }
+
+        public void ApplyConfig(UserConfig userConfig)
+        {
+        }
+    }
+}
diff --git a/src/Engine/Engine.csproj b/src/Engine/Engine.csproj
index b593a18..b2a4a07 100644
--- a/src/Engine/Engine.csproj
+++ b/src/Engine/Engine.csproj
@@ -80,7 +80,6 @@
     <Compile Include="Config\UserListController.cs" />
     <Compile Include="Config\FilterListController.cs" />
     <Compile Include="Config\FilterModel.cs" />
-    <Compile Include="Messages\MessageParser.cs" />
     <Compile Include="Messages\MessageBuilder.cs" />
     <Compile Include="PresenceStatus.cs" />
     <Compile Include="Config\ProxyType.cs" />
@@ -106,6 +105,7 @@
     <Compile Include="Hooks\Commands\ProtocolManagerHookCommand.cs" />
     <Compile Include="Hooks\Commands\SessionHookCommand.cs" />
     <Compile Include="Hooks\Environments\CommandHookEnvironment.cs" />
+    <Compile Include="Config\MessageBuilderSettings.cs" />
   </ItemGroup>
   <ItemGroup>
     <Folder Include="Protocols\" />
diff --git a/src/Engine/Messages/MessageBuilder.cs b/src/Engine/Messages/MessageBuilder.cs
index cc15d60..87041e6 100644
--- a/src/Engine/Messages/MessageBuilder.cs
+++ b/src/Engine/Messages/MessageBuilder.cs
@@ -20,6 +20,7 @@
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
 using System;
+using System.Linq;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Collections.Generic;
@@ -43,6 +44,7 @@ namespace Smuxi.Engine
         public TextColor HighlightColor { get; set; }
         public List<string> HighlightWords { get; set; }
         public PersonModel Me { get; set; }
+        public MessageBuilderSettings Settings { get; private set; }
 
         public MessageType MessageType {
             get {
@@ -71,13 +73,12 @@ namespace Smuxi.Engine
         public MessageBuilder()
         {
             Message = new MessageModel();
+            Settings = new MessageBuilderSettings();
             NickColors = true;
         }
 
         public MessageModel ToMessage()
         {
-            //MessageParser.ParseSmileys
-            MessageParser.ParseUrls(Message);
             Message.Compact();
             return Message;
         }
@@ -272,9 +273,15 @@ namespace Smuxi.Engine
             return AppendText(CreateHeader(text, args));
         }
 
+        [Obsolete("AppendMessage() is deprecated, use AppendRichText() instead")]
         public virtual MessageBuilder AppendMessage(string msg)
         {
-            return AppendText(msg);
+            return AppendRichText(msg);
+        }
+
+        public virtual MessageBuilder AppendRichText(string msg)
+        {
+            return Append(ParseSmartLinks(CreateText(msg)));
         }
 
         public  MessageBuilder AppendMessage(ContactModel sender, string msg)
@@ -854,5 +861,97 @@ namespace Smuxi.Engine
         {
             return LibraryCatalog.GetString(msg, LibraryTextDomain);
         }
+
+        public static IList<MessagePartModel> ParseSmartLinks(TextMessagePartModel textPart,
+                                                              List<MessageBuilderSettings.SmartLink> links)
+        {
+            IList<MessagePartModel> msg = new List<MessagePartModel>();
+            if (links.Count == 0) {
+                // all smartlinks have been tried -> this text is PURE text
+                msg.Add(textPart);
+                return msg;
+            }
+            var subLinks = new List<MessageBuilderSettings.SmartLink>(links);
+            MessageBuilderSettings.SmartLink link = subLinks.First();
+            subLinks.Remove(link);
+            
+            Match linkMatch = link.MessagePartPattern.Match(textPart.Text);
+            if (!linkMatch.Success) {
+                // no smartlinks in this MessagePart, try other smartlinks
+                return ParseSmartLinks(textPart, subLinks);
+            }
+            
+            int lastindex = 0;
+            do {
+                var groupValues = new string[linkMatch.Groups.Count];
+                int i = 0;
+                foreach (Group @group in linkMatch.Groups) {
+                    groupValues[i++] = @group.Value;
+                }
+                
+                string url;
+                if (link.LinkFormat != null) {
+                    url = String.Format(link.LinkFormat, groupValues);
+                } else {
+                    url = linkMatch.Value;
+                }
+                string text;
+                if (link.TextFormat != null) {
+                    text = String.Format(link.TextFormat, groupValues);
+                } else {
+                    text = (linkMatch.Value == url)?null:linkMatch.Value;
+                }
+                
+                
+                if (lastindex != linkMatch.Index) {
+                    // there were some non-url-chars before this url
+                    // copy TextMessagePartModel
+                    TextMessagePartModel notLinkPart = new TextMessagePartModel(textPart);
+                    // only take the proper chunk of text
+                    notLinkPart.Text = textPart.Text.Substring(lastindex, linkMatch.Index-lastindex);
+                    // and try other smartlinks on this part
+                    var parts = ParseSmartLinks(notLinkPart, subLinks);
+                    foreach (var part in parts) {
+                        msg.Add(part);
+                    }
+                }
+                
+                MessagePartModel model;
+                switch (link.Type) {
+                    case MessageBuilderSettings.SmartLink.ETargetType.Url:
+                        model = new UrlMessagePartModel(url, text);
+                        break;
+                    case MessageBuilderSettings.SmartLink.ETargetType.Image:
+                        model = new ImageMessagePartModel(url, text);
+                        break;
+                    case MessageBuilderSettings.SmartLink.ETargetType.Text:
+                    default:
+                        model = new TextMessagePartModel(text);
+                        break;
+                }
+                msg.Add(model);
+                lastindex = linkMatch.Index + linkMatch.Length;
+                linkMatch = linkMatch.NextMatch();
+            } while (linkMatch.Success);
+            
+            if (lastindex != textPart.Text.Length) {
+                // there were some non-url-chars before this url
+                // copy TextMessagePartModel
+                TextMessagePartModel notLinkPart = new TextMessagePartModel(textPart);
+                // only take the proper chunk of text
+                notLinkPart.Text = textPart.Text.Substring(lastindex);
+                // and try other smartlinks on this part
+                var parts = ParseSmartLinks(notLinkPart, subLinks);
+                foreach (var part in parts) {
+                    msg.Add(part);
+                }
+            }
+            return msg;
+        }
+        
+        public IEnumerable<MessagePartModel> ParseSmartLinks(TextMessagePartModel part)
+        {
+            return ParseSmartLinks(part, Settings.SmartLinks);
+        }
     }
 }
diff --git a/src/Engine/Messages/UrlMessagePartModel.cs b/src/Engine/Messages/UrlMessagePartModel.cs
index 8c4c83b..e738e2d 100644
--- a/src/Engine/Messages/UrlMessagePartModel.cs
+++ b/src/Engine/Messages/UrlMessagePartModel.cs
@@ -120,5 +120,29 @@ namespace Smuxi.Engine
                 return "[" + _Url + " " + Text + "]";
             }
         }
+        
+        public override bool Equals(object obj)
+        {
+            if (!(obj is UrlMessagePartModel)) {
+                return false;
+            }
+
+            var urlPart = (UrlMessagePartModel) obj;
+            return Equals(urlPart);
+        }
+        
+        public override bool Equals(MessagePartModel part)
+        {
+            var urlPart = part as UrlMessagePartModel;
+            if ((object) urlPart == null) {
+                return false;
+            }
+            
+            if (_Url != urlPart._Url) {
+                return false;
+            }
+                
+            return base.Equals(urlPart);
+        }
     }
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]