User:VasilievVV/LinkyParser.php

From Meta, a Wikimedia project coordination wiki

Initially wasn't supposed to be published. But HardDisk asked it :). Distributed under GPL 2 terms.

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

using WikiTools.Access;

namespace LinkyParser
{
	/// <summary>
	/// Class to parse links in IRC messages
	/// </summary>
	public class LinkyParser
	{
		Wiki w;
		public string Channel;
		
		public LinkyParser(Wiki w)
		{
			this.w = w;
			w.LoadInterwikis();
		}
		
		int curpos = 0;
		string curstr = "";
		char cc = '\0';	//Current symbol
		
		public static bool IsValidTitleChar(char c)
		{
			//From DefaultSettings.php
			return Regex.IsMatch( new string(c, 1), "[ %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\u0080-\\uFFFF+]", RegexOptions.IgnoreCase );
		}
		
		public static string NormalizeTitle(string s)
		{
			if( s == "")
				return s;
			s = s.Replace('_', ' ');
			if( Char.IsLetter( s[0] ) )
				s = Char.ToUpper( s[0] ) + s.Substring(1);
			return s;
		}
		
		public void Parse(string str)
		{
			int linkcount = 0;
			curpos = 0;
			curstr = str;
			if( str.Length == 0 )
				return;
			cc = str[0];
			StringBuilder ctitlestr = null, cfragmentstr = null;
			
			if(str.Contains("@nl@") )
				return;
			
			do
			{
				if( cc == '[' && NextChar() && cc == '[' && NextChar() )
				{
					ctitlestr = new StringBuilder();
					cfragmentstr = new StringBuilder();
					bool isFragment = false;
					bool skipchars = false;
					
					do
					{
						if( cc == ']' && NextChar() && cc == ']')
						{
							linkcount++;
							if( linkcount > 3 )
								break;
							if( Callback != null )
								Callback( this as object, NormalizeTitle( ctitlestr.ToString().Trim() ) );
							if( UriCallback != null )
								CallUriCallback( ctitlestr.ToString().Trim(),
									cfragmentstr.ToString().Trim() );
							break;
						}
						if( cc == '|' ) skipchars = true;
						if( cc == '#' ) {
							isFragment = true;
						}
						if( IsValidTitleChar( cc ) ) {
							if( !skipchars ) {
								if( isFragment ) {
									cfragmentstr.Append(cc);
								} else {
									ctitlestr.Append(cc);
								}
							}
						}
					}
					while( NextChar() );
					//Ok, we're out of string. Perhaps it's something like "[[Test". Just ignore it
				}
				
				if( cc == '{' && NextChar() && cc == '{' && NextChar() && 
				   cc == 't' && NextChar() && cc == 'l' && NextChar() &&
				   cc == '|' && NextChar() )
				{
					ctitlestr = new StringBuilder();
					
					do
					{
						if( cc == '}' && NextChar() && cc == '}')
						{
							linkcount++;
							if( linkcount > 3 )
								break;
							if( Callback != null )
								Callback( this as object, w.NamespacesUtils.GetNamespaceByID(Namespaces.Template)
									+ ":" + NormalizeTitle( ctitlestr.ToString().Trim() ) );
							if( UriCallback != null )
								CallUriCallback( w.NamespacesUtils.GetNamespaceByID(Namespaces.Template)
									+ ":" + ctitlestr.ToString().Trim(), null );
							break;
						}
						if( IsValidTitleChar( cc ) )
							ctitlestr.Append(cc);
					}
					while( NextChar() );
					//Ok, we're out of string. Perhaps it's something like "[[Test"
				}
			}
			while( NextChar() );
		}
		
		public bool NextChar()
		{
			curpos++;
			if (curpos >= curstr.Length)
				return false;
			cc = curstr[curpos];
			return true;
		}
		
		public void CallUriCallback(string title, string fragment) {
			UriCallback( this as object, FormatUri( title, fragment ) );
		}
		
		public string FormatUri(string title, string fragment) {
			foreach( InterwikiMapEntry entry in w.Interwikis ) {
				if( title.StartsWith( entry.Prefix + ":" ) ) {
					return entry.Uri.Replace( "$1",
						EscapeTitleForUri( title.Substring(entry.Prefix.Length + 1) ) );
				}
			}
			string frag = "";
			if( !String.IsNullOrEmpty( fragment ) ) {
				frag = "#" + EscpaeFragment( fragment );
			}
			return w.WikiURI.Replace("/w", "/wiki/") +  EscapeTitleForUri(title) + frag;
		}
		
		public static string EscapeTitleForUri(string title) {
			//title = title.Replace("%", Uri.EscapeDataString("%"));
			title = title.Replace(" ", "_");
			title = title.Replace("?", Uri.EscapeDataString("?"));
			return title;
		}
		
		public static string EscpaeFragment(string f) {
			f = Uri.EscapeDataString( f );
			f = f.Replace( "%", "." );
			f = f.Replace( ".20", "_" );
			f = f.Replace( ".3A", ":" );
			return f;
		}
		
		public event LinkyCallback Callback;
		public event LinkyCallback UriCallback;
	}
	
	public delegate void LinkyCallback(object sender, string name);
}