context: 
  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Reflection;
  using System.IO;
  
  using Mono.Unix;
  
  using Tomboy.Sync;
  
  namespace Tomboy
  {
  	public class AddinManager
  	{
  		readonly string tomboy_conf_dir;
  
  		/// <summary>
  		/// Key = TypeExtensionNode.Id
  		/// </summary>
  		Dictionary<string, ApplicationAddin> app_addins;
  
  		/// <summary>
  		/// </summary>
  		Dictionary<Note, List<NoteAddinInfo>> note_addins;
  
  		/// <summary>
  		/// Key = TypeExtensionNode.Id
  		/// </summary>
  		Dictionary<string, List<NoteAddinInfo>> note_addin_infos;
  
  		public event System.EventHandler ApplicationAddinListChanged;
  
  		public AddinManager (string tomboy_conf_dir) : this (tomboy_conf_dir, null)
  		{
  		}
  
  		public AddinManager (string tomboy_conf_dir, string old_tomboy_conf_dir)
  		{
  			this.tomboy_conf_dir = tomboy_conf_dir;
  			app_addins = new Dictionary<string, ApplicationAddin> ();
  			note_addins = new Dictionary<Note, List<NoteAddinInfo>> ();
  			note_addin_infos = new Dictionary<string, List<NoteAddinInfo>> ();
  
  			InitializeMonoAddins (old_tomboy_conf_dir);
  		}
  
  		void InitializeMonoAddins (string old_conf_dir)
  		{
  			Logger.Info ("Initializing Mono.Addins");
  
  			// Perform migration if necessary
  			if (!String.IsNullOrEmpty (old_conf_dir)) {
  				foreach (string dir_path in Directory.GetDirectories (old_conf_dir, "addin*")) {
  					string new_dir_path =
  						Path.Combine (tomboy_conf_dir, Path.GetFileName (dir_path));
  					if (!Directory.Exists (new_dir_path))
  						IOUtils.CopyDirectory (dir_path, new_dir_path);
  				}
  			}
  
  			string addins_dir = Tomboy.Uninstalled ? ".":
  				Path.Combine (tomboy_conf_dir, "addins");
  			if (!Directory.Exists (addins_dir))
  				Directory.CreateDirectory (addins_dir);
  
  			// Make sure a Tomboy.addins file exists
  			string addins_file = Path.Combine (addins_dir, "Tomboy.addins");
  
  			// Always recreate this file.  This means it's
  			// completely hands-off to the user.  This is done to
  			// support upgrades and parallel install scenarios,
  			// ensuring that Tomboy does its best not to load old
  			// versions of addins (which may no longer be compatible
  			// with the new version of Tomboy).
  			// See bug #514931 for details.
  			using (StreamWriter sw = File.CreateText (addins_file)) {
  				string addins_file_contents = String.Format (
  				                                      "<!--\n" +
  				                                      "     This file was automatically generated.  Editing this\n" +
  				                                      "     file by hand is strongly discouraged and such changes\n" +
  			                                              "     may be reverted at any time.\n" +
  				                                      "-->\n\n" +
  				                                      "<Addins>\n" +
  				                                      "\t<Directory>{0}</Directory>\n" +
  				                                      "</Addins>\n",
  				                                      Tomboy.Uninstalled ? "./addins" : Defines.SYS_ADDINS_DIR);
  				sw.Write (addins_file_contents);
  			}
  
  			Mono.Addins.AddinManager.AddinLoaded += OnAddinLoaded;
  			Mono.Addins.AddinManager.AddinUnloaded += OnAddinUnloaded;
  			Mono.Addins.AddinManager.Initialize (Tomboy.Uninstalled ? "." : tomboy_conf_dir);
  			UpgradeOldAddinConfig ();
  			if (Tomboy.Debugging) {
  				Mono.Addins.AddinManager.Registry.Rebuild (null);
  			} else {
  				Mono.Addins.AddinManager.Registry.Update (null);
  			}
  			Mono.Addins.AddinManager.AddExtensionNodeHandler ("/Tomboy/ApplicationAddins", OnApplicationAddinExtensionChanged);
  			// NOTE: A SyncServiceAddin is a specialization of an ApplicationAddin
  			Mono.Addins.AddinManager.AddExtensionNodeHandler ("/Tomboy/SyncServiceAddins", OnApplicationAddinExtensionChanged);
  			Mono.Addins.AddinManager.AddExtensionNodeHandler ("/Tomboy/NoteAddins", OnNoteAddinExtensionChanged);
  		}
  
  		void OnAddinLoaded (object sender, Mono.Addins.AddinEventArgs args)
  		{
  			Logger.Debug ("AddinManager.OnAddinLoaded: {0}", args.AddinId);
  			Mono.Addins.Addin addin = Mono.Addins.AddinManager.Registry.GetAddin (args.AddinId);
  			Logger.Debug ("\t       Name: {0}", addin.Name);
  			Logger.Debug ("\tDescription: {0}", addin.Description.Description);
  			Logger.Debug ("\t  Namespace: {0}", addin.Namespace);
  			Logger.Debug ("\t    Enabled: {0}", addin.Enabled);
  			Logger.Debug ("\t       File: {0}", addin.AddinFile);
  		}
  
  		void OnAddinUnloaded (object sender, Mono.Addins.AddinEventArgs args)
  		{
  			Logger.Debug ("AddinManager.OnAddinUnloaded: {0}", args.AddinId);
  		}
  
  		void OnApplicationAddinExtensionChanged (object sender, Mono.Addins.ExtensionNodeEventArgs args)
  		{
  			Mono.Addins.TypeExtensionNode type_node =
  			        args.ExtensionNode as Mono.Addins.TypeExtensionNode;
  
  			ApplicationAddin addin;
  			if (args.Change == Mono.Addins.ExtensionChange.Add) {
  				// Load NoteAddins
  				if (Tomboy.DefaultNoteManager == null) {
  					return; // too early -- YUCK!  Bad hack
  				}
  
  				addin = type_node.GetInstance (
  				                typeof (ApplicationAddin)) as ApplicationAddin;
  				if (addin != null) {
  					if (addin.Initialized == false) {
  						try {
  							addin.Initialize ();
  							app_addins [type_node.Id] = addin;
  						} catch (Exception e) {
  							Logger.Debug ("Error initializing app addin {0}: {1}\n{2}",
  							              addin.GetType ().ToString (),
  							              e.Message,
  							              e.StackTrace);
  						}
  					}
  				}
  			} else {
  				if (app_addins.ContainsKey (type_node.Id)) {
  					addin = app_addins [type_node.Id];
  					try {
  						addin.Shutdown ();
  					} catch (Exception e1) {
  						Logger.Warn ("Error shutting down app addin {0}: {1}\n{2}",
  						             addin.GetType ().ToString (),
  						             e1.Message,
  						             e1.StackTrace);
  					} finally {
  						app_addins.Remove (type_node.Id);
  					}
  
  					try {
  						addin.Dispose ();
  					} catch (Exception e1) {
  						Logger.Warn ("Error disposing app addin: {0} - {1}",
  						             addin.GetType ().ToString (), e1.Message);
  					}
  				}
  			}
  
  			if (ApplicationAddinListChanged != null)
  				ApplicationAddinListChanged (sender, args);
  		}
  
  		void OnNoteAddinExtensionChanged (object sender, Mono.Addins.ExtensionNodeEventArgs args)
  		{
  			if (args.Change == Mono.Addins.ExtensionChange.Add)
  				OnNoteAddinEnabled (args);
  			else
  				OnNoteAddinDisabled (args);
  		}
  
  		void OnNoteAddinEnabled (Mono.Addins.ExtensionNodeEventArgs args)
  		{
  			// Load NoteAddins
  			if (Tomboy.DefaultNoteManager == null) {
  				return; // too early -- YUCK!  Bad hack
  			}
  
  			foreach (Note note in Tomboy.DefaultNoteManager.Notes) {
  				// Create a new NoteAddin
  				Mono.Addins.TypeExtensionNode type_node =
  				        args.ExtensionNode as Mono.Addins.TypeExtensionNode;
  
  				try {
  					NoteAddin n_addin = type_node.CreateInstance () as NoteAddin;
  
  					// Keep track of the addins added to each note
  					AttachAddin (type_node.Id, note, n_addin);
  				} catch (Exception e) {
  					Logger.Debug ("Couldn't create a NoteAddin instance: {0}", e.Message);
  				}
  			}
  		}
  
  		void OnNoteAddinDisabled (Mono.Addins.ExtensionNodeEventArgs args)
  		{
  			Mono.Addins.TypeExtensionNode type_node =
  			        args.ExtensionNode as Mono.Addins.TypeExtensionNode;
  
  			try {
  				OnDisabledAddin (type_node.Id);
  			} catch (Exception e) {
  				Logger.Debug ("Error unloading add-in: " + e.Message);
  			}
  		}
  
  		public void LoadAddinsForNote (Note note)
  		{
  			Mono.Addins.ExtensionNodeList list = Mono.Addins.AddinManager.GetExtensionNodes ("/Tomboy/NoteAddins");
  			foreach (Mono.Addins.ExtensionNode node in list) {
  				Mono.Addins.TypeExtensionNode type_node =
  				        node as Mono.Addins.TypeExtensionNode;
  
  				try {
  					NoteAddin n_addin = type_node.CreateInstance () as NoteAddin;
  
  					// Keep track of the addins added to each note
  					AttachAddin (type_node.Id, note, n_addin);
  				} catch (Exception e) {
  					Logger.Warn ("Couldn't create a NoteAddin instance: {0}", e.Message);
  				}
  			}
  
  			// Make sure we remove addins when a note is deleted
  			note.Manager.NoteDeleted += OnNoteDeleted;
  		}
  
  		/// <summary>
  		/// Returns an array of ApplicationAddin objects
  		/// </summary>
  		public ApplicationAddin [] GetApplicationAddins ()
  		{
  			ApplicationAddin [] app_addins;
  
  			try {
  				app_addins = (ApplicationAddin [])
  				             Mono.Addins.AddinManager.GetExtensionObjects (
  				                     "/Tomboy/ApplicationAddins",
  				                     typeof (ApplicationAddin),
  				                     true);
  			} catch (Exception e) {
  				Logger.Warn ("No ApplicationAddins found: {0}", e.Message);
  				app_addins = new ApplicationAddin [0];
  			}
  
  			return app_addins;
  		}
  
  		/// <summary>
  		/// Returns an array of NoteAddin objects that tomboy
  		/// currently knows about.
  		/// </summary>
  		public NoteAddin [] GetNoteAddins ()
  		{
  			NoteAddin [] addins;
  
  			try {
  				addins = (NoteAddin [])
  				         Mono.Addins.AddinManager.GetExtensionObjects (
  				                 "/Tomboy/NoteAddins",
  				                 typeof (NoteAddin));
  			} catch (Exception e) {
  				Logger.Warn ("No NoteAddins found: {0}", e.Message);
  				addins = new NoteAddin [0];
  			}
  
  			return addins;
  		}
  		
  		/// <summary>
  		/// Returns an array of PreferenceTabAddin objects.
  		/// </summary>
  		/// <returns>
  		/// A <see cref="PreferenceTabAddin"/>
  		/// </returns>
  		public PreferenceTabAddin [] GetPreferenceTabAddins ()
  		{
  			PreferenceTabAddin [] addins;
  			
  			try {
  				addins = (PreferenceTabAddin [])
  							Mono.Addins.AddinManager.GetExtensionObjects (
  								"/Tomboy/PreferenceTabAddins",
  								typeof (PreferenceTabAddin));
  			} catch (Exception e) {
  				Logger.Warn ("No PreferenceTabAddins found: {0}", e.Message);
  				addins = new PreferenceTabAddin [0];
  			}
  			
  			return addins;
  		}
  
  		/// <summary>
  		/// Returns an array of SyncServiceAddin objects
  		/// </summary>
  		public SyncServiceAddin [] GetSyncServiceAddins ()
  		{
  			SyncServiceAddin [] addins;
  
  			try {
  				addins = (SyncServiceAddin [])
  				         Mono.Addins.AddinManager.GetExtensionObjects (
  				                 "/Tomboy/SyncServiceAddins",
  				                 typeof (SyncServiceAddin));
  			} catch (Exception e) {
  				Logger.Debug ("No SyncServiceAddins found: {0}", e.Message);
  				addins = new SyncServiceAddin [0];
  			}
  
  			return addins;
  		}
  
  		/// <summary>
  		/// Add the addin to the note and save off a reference to the addin that
  		/// will be used when the note is deleted.
  		/// </summary>
  		public List<Mono.Addins.Addin> GetAllAddins ()
  		{
  			List<Mono.Addins.Addin> addins = new List<Mono.Addins.Addin> ();
  
  			Mono.Addins.Addin [] addinsArray =
  			        Mono.Addins.AddinManager.Registry.GetAddins ();
  
  			if (addinsArray != null) {
  				// It just so happens that the NoteAddins that are part of
  				// Tomboy.exe (from Watchers.cs) are not returned by the
  				// above GetAddins () call, so we don't have to do anything
  				// to exclude them here.
  				addins = new List<Mono.Addins.Addin> (addinsArray);
  			}
  
  			return addins;
  		}
  
  		/// <summary>
  		/// Call NoteAddin.Shutdown () and NoteAddin.Dispose () on every
  		/// NoteAddin that's attached to the deleted Note.
  		/// </summary>
  		void OnNoteDeleted (object sender, Note deleted)
  		{
  			if (note_addins.ContainsKey (deleted) == false)
  				return;
  
  			OnDeletedNote (deleted);
  		}
  
  		void AttachAddin (string ext_node_id, Note note, NoteAddin addin)
  		{
  			if (ext_node_id == null || note == null || addin == null)
  				throw new ArgumentNullException ("Cannot pass in a null parameter to AttachAddin");
  
  			// Loading the addin to the note
  			try {
  				addin.Initialize (note);
  			} catch (Exception e) {
  				Logger.Warn ("Error initializing addin: {0}: {1}",
  				             addin.GetType ().ToString (), e.Message);
  				// TODO: Would be nice to figure out how to just disable
  				// the addin altogether if it's failing to initialize so
  				// it doesn't keep causing problems.
  				return;
  			}
  
  			NoteAddinInfo info = new NoteAddinInfo (ext_node_id, note, addin);
  			List<NoteAddinInfo> note_addin_list;
  			List<NoteAddinInfo> ext_node_addin_list;
  
  			if (note_addins.ContainsKey (note))
  				note_addin_list = note_addins [note];
  			else {
  				note_addin_list = new List<NoteAddinInfo> ();
  				note_addins [note] = note_addin_list;
  			}
  
  			if (note_addin_infos.ContainsKey (ext_node_id))
  				ext_node_addin_list = note_addin_infos [ext_node_id];
  			else {
  				ext_node_addin_list = new List<NoteAddinInfo> ();
  				note_addin_infos [ext_node_id] = ext_node_addin_list;
  			}
  
  			note_addin_list.Add (info);
  			ext_node_addin_list.Add (info);
  		}
  
  		void OnDisabledAddin (string ext_node_id)
  		{
  			if (ext_node_id == null)
  				throw new ArgumentNullException (
  				        "Cannot call OnDisabledAddin with null parameters");
  
  			Logger.Debug ("OnDisabledAddin: {0}", ext_node_id);
  
  			// Remove and shut down all the addins
  			if (note_addin_infos.ContainsKey (ext_node_id) == false)
  				throw new ArgumentException (
  				        "Cannot call OnDisabledAddin with an invalid Mono.Addins.ExtensionNode.Id");
  
  			List<NoteAddinInfo> addin_info_list = note_addin_infos [ext_node_id];
  			foreach (NoteAddinInfo info in addin_info_list) {
  				try {
  					info.Addin.Shutdown ();
  				} catch (Exception e) {
  					Logger.Warn ("Error shutting down addin: {0} - {1}",
  					             info.Addin.GetType ().ToString (), e.Message);
  				}
  
  				try {
  					info.Addin.Dispose ();
  				} catch (Exception e1) {
  					Logger.Warn ("Error disposing addin: {0} - {1}",
  					             info.Addin.GetType ().ToString (), e1.Message);
  				}
  
  				// Remove the addin from the Note
  				if (note_addins.ContainsKey (info.Note)) {
  					List<NoteAddinInfo> note_addin_list = note_addins [info.Note];
  					note_addin_list.Remove (info);
  				}
  			}
  			note_addin_infos.Remove (ext_node_id);
  		}
  
  		void OnDeletedNote (Note note)
  		{
  			if (note == null)
  				throw new ArgumentNullException (
  				        "Cannot call OnDeletedNote with null parameters");
  
  			if (note_addins.ContainsKey (note) == false)
  				throw new ArgumentException (
  				        "Cannot call OnDeletedNote with an invalid Note");
  
  			List<NoteAddinInfo> note_addin_list = note_addins [note];
  			foreach (NoteAddinInfo info in note_addin_list) {
  				try {
  					info.Addin.Shutdown ();
  				} catch (Exception e) {
  					Logger.Warn ("Error shutting down addin: {0} - {1}",
  					             info.Addin.GetType ().ToString (), e.Message);
  				}
  
  				try {
  					info.Addin.Dispose ();
  				} catch (Exception e1) {
  					Logger.Warn ("Error disposing addin: {0} - {1}",
  					             info.Addin.GetType ().ToString (), e1.Message);
  				}
  
  				if (note_addin_infos.ContainsKey (info.ExtensionNodeId)) {
  					List<NoteAddinInfo> addin_info_list =
  					        note_addin_infos [info.ExtensionNodeId];
  					addin_info_list.Remove (info);
  				}
  			}
  
  			note_addins.Remove (note);
  		}
  
  		public bool IsAddinConfigurable (Mono.Addins.Addin addin)
  		{
  
gt: 			object o = GetAddinPrefFactory (addin);