/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// using System; using System.Text.RegularExpressions; namespace JetBrains.Omea.Contacts { public class ContactResolver { private static Regex _multipleBlanksRemover = new Regex( "( +)" ); private static Regex _SrJrSkipper = new Regex( ",? ([JjSs]r|I|II|III)\\.?$" ); private static Regex _prefixCleaner = new Regex( "^([\\\\'\"]+)" ); private static Regex _suffixCleaner = new Regex( "([\\\\'\"]+)$" ); private static Regex TitleSelector = new Regex( "^(Prof|Dr|Mr|Ms|Mrs|Miss)\\.? +", RegexOptions.IgnoreCase ); private static Regex AffixParCleaner = new Regex( " (\\([^()]+\\)) *$", RegexOptions.IgnoreCase ); //--------------------------------------------------------------------- /// /// Method performs a try to extract First name (FN) and Last name (LN) from /// Sender name string. The following templates are supported: /// "FN LN", "LN, FN", "FN LN, Jr", "FN LN, Sr", "FN X. LN", "FN.LN@...", /// "FN LN (string matching the email)", "FN LN (E-mail)", "FN LN (E-mail2)" etc. /// If no template is matched, Sender name is assigned to the First name. /// Resolve fails if sender name is empty (null or length is zero) or /// coinsides with email. /// /// Name of a contact /// Email representing the account /// (out) Resolved Title /// (out) Resolved First name /// (out) Resolved Middle name /// (out) Resolved Last name /// (out) Resolved Suffix (Sr, Jr, I, II, etc) /// (out) Resolved additional specificator (e.g. "(E-mail)") /// public static bool ResolveName( string senderName, string email, out string title, out string firstName, out string middleName, out string lastName, out string suffix, out string addSpec ) { title = firstName = middleName = lastName = suffix = addSpec = string.Empty; // Resolve fails if sender name is null or empty. By contract // this case is allowed. if( senderName == null || senderName.Length == 0 ) return false; string cleanedSenderName, cleanedName; cleanedSenderName = cleanedName = CleanSenderName( senderName ); //----------------------------------------------------------------- // If senderName contains removable garbage, stop processing and // return the same garbage as result. //----------------------------------------------------------------- if( cleanedName.Length == 0 ) { lastName = senderName; return true; } string pureName = ExtractPrefixAndSuffix( ref cleanedName, ref title, ref suffix, ref addSpec ); if( addSpec.Length > 0 ) pureName = CleanSenderName( pureName ); //----------------------------------------------------------------- string[] tokens = pureName.Split( ' ' ); if( tokens.Length == 2 || tokens.Length == 3 ) { bool isRevertedOrder = (tokens[ 0 ][ tokens[ 0 ].Length - 1 ] == ','); firstName = isRevertedOrder ? tokens[ 1 ] : tokens[ 0 ]; lastName = isRevertedOrder ? tokens[ 0 ] : tokens[ tokens.Length - 1 ]; if( tokens.Length == 3 ) middleName = isRevertedOrder ? tokens[ 2 ] : tokens[ 1 ]; if( isRevertedOrder ) lastName = lastName.Substring( 0, lastName.Length - 1 ); if( IsCleanName( firstName ) && IsCleanName( lastName )) return true; } //----------------------------------------------------------------- int atIndex = cleanedSenderName.IndexOf( '@', 0 ); if ( atIndex != -1 ) { string senderNameExtract = cleanedSenderName.Substring( 0, atIndex ); int pointIndex = senderNameExtract.IndexOf( '.', 0 ); // NB: "From: " - eliminate our own processing from the // ContactManager.UnlinkAccountAndContact. if( pointIndex != -1 && SplitByChar( senderNameExtract, '.', ref firstName, ref lastName )) { if( !firstName.StartsWith( "From: " )) return true; } } if( email != null && senderName == email ) return false; // clear if we already polluted these fields title = firstName = middleName = suffix = addSpec = string.Empty; lastName = cleanedSenderName; return true; } private static string CleanSenderName( string senderName ) { string cleanedName = null; if( senderName != null ) { cleanedName = senderName.Trim(); // remove balanced parentheses around the name while( IsParsSurrounded( cleanedName ) ) cleanedName = cleanedName.Substring( 1, cleanedName.Length - 2 ); cleanedName = _multipleBlanksRemover.Replace( cleanedName, " " ); Match match = _prefixCleaner.Match( cleanedName ); if( match.Success ) cleanedName = cleanedName.Substring( match.Value.Length ); match = _suffixCleaner.Match( cleanedName ); if( _suffixCleaner.IsMatch( cleanedName ) ) cleanedName = cleanedName.Substring( 0, cleanedName.Length - match.Value.Length ); cleanedName = cleanedName.Trim(); } return cleanedName; } private static string ExtractPrefixAndSuffix( ref string name, ref string title, ref string suffix, ref string addSpec ) { Match match = AffixParCleaner.Match( name ); if( match.Success ) { addSpec = match.Value.Trim(); name = name.Replace( addSpec, string.Empty ).Trim(); } //----------------------------------------------------------------- string pureName = name; match = _SrJrSkipper.Match( pureName ); if( match.Success ) { suffix = match.Value; pureName = pureName.Replace( suffix, string.Empty ).Trim(); if( suffix[ 0 ] == ',' ) suffix = suffix.Substring( 1 ); suffix = suffix.Trim(); } //----------------------------------------------------------------- match = TitleSelector.Match( pureName ); if( match.Success ) { title = match.Value; pureName = pureName.Substring( title.Length ).Trim(); title = title.Trim(); if( title[ title.Length - 1 ] == ',' ) title = title.Substring( 0, title.Length - 1 ); } return pureName.Trim(); } private static bool SplitByChar( string senderName, char splitter, ref string name1, ref string name2 ) { string[] names = senderName.Split( splitter ); if ( names != null && names.Length == 2 ) { name1 = names[0].Trim(); name2 = names[1].Trim(); return true; } return false; } public static string CompressBlanks( string str ) { if( _multipleBlanksRemover.IsMatch( str ) ) { str = _multipleBlanksRemover.Replace( str, " " ).Trim(); } return str; } private static bool IsCleanName( string str ) { foreach( char ch in str ) { if( !Char.IsLetterOrDigit( ch )) return false; } return true; } private static bool IsParsSurrounded( string str ) { return str.Length > 2 && str[ 0 ] == '(' && str[ str.Length - 1 ] == ')'; } } }