// // // // // $Revision: 4498 $ // using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Resources; namespace ICSharpCode.Core { /// /// Static class containing the AddInTree. Contains methods for accessing tree nodes and building items. /// public static class AddInTree { static List addIns = new List(); static AddInTreeNode rootNode = new AddInTreeNode(); static Dictionary doozers = new Dictionary(); static Dictionary conditionEvaluators = new Dictionary(); static AddInTree() { doozers.Add("Class", new ClassDoozer()); doozers.Add("FileFilter", new FileFilterDoozer()); doozers.Add("String", new StringDoozer()); doozers.Add("Icon", new IconDoozer()); doozers.Add("MenuItem", new MenuItemDoozer()); doozers.Add("ToolbarItem", new ToolbarItemDoozer()); doozers.Add("Include", new IncludeDoozer()); conditionEvaluators.Add("Compare", new CompareConditionEvaluator()); conditionEvaluators.Add("Ownerstate", new OwnerStateConditionEvaluator()); ApplicationStateInfoService.RegisterStateGetter("Installed 3rd party AddIns", GetInstalledThirdPartyAddInsListAsString); } static object GetInstalledThirdPartyAddInsListAsString() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (AddIn addIn in AddIns) { // Skip preinstalled AddIns (show only third party AddIns) if (FileUtility.IsBaseDirectory(FileUtility.ApplicationRootPath, addIn.FileName)) { string hidden = addIn.Properties["addInManagerHidden"]; if (string.Equals(hidden, "true", StringComparison.OrdinalIgnoreCase) || string.Equals(hidden, "preinstalled", StringComparison.OrdinalIgnoreCase)) continue; } if (sb.Length > 0) sb.Append(", "); sb.Append("["); sb.Append(addIn.Name); if (addIn.Version != null) { sb.Append(' '); sb.Append(addIn.Version.ToString()); } if (!addIn.Enabled) { sb.Append(", Enabled="); sb.Append(addIn.Enabled); } if (addIn.Action != AddInAction.Enable) { sb.Append(", Action="); sb.Append(addIn.Action.ToString()); } sb.Append("]"); } return sb.ToString(); } /// /// Gets the list of loaded AddIns. /// public static IList AddIns { get { return addIns.AsReadOnly(); } } /// /// Gets a dictionary of registered doozers. /// public static Dictionary Doozers { get { return doozers; } } /// /// Gets a dictionary of registered condition evaluators. /// public static Dictionary ConditionEvaluators { get { return conditionEvaluators; } } /// /// Checks whether the specified path exists in the AddIn tree. /// public static bool ExistsTreeNode(string path) { if (path == null || path.Length == 0) { return true; } string[] splittedPath = path.Split('/'); AddInTreeNode curPath = rootNode; int i = 0; while (i < splittedPath.Length) { // curPath = curPath.ChildNodes[splittedPath[i]] - check if child path exists if (!curPath.ChildNodes.TryGetValue(splittedPath[i], out curPath)) { return false; } ++i; } return true; } /// /// Gets the representing the specified path. /// This method throws a when the /// path does not exist. /// public static AddInTreeNode GetTreeNode(string path) { return GetTreeNode(path, true); } /// /// Gets the representing the specified path. /// /// The path of the AddIn tree node /// /// If set to true, this method throws a /// when the path does not exist. /// If set to false, null is returned for non-existing paths. /// public static AddInTreeNode GetTreeNode(string path, bool throwOnNotFound) { if (path == null || path.Length == 0) { return rootNode; } string[] splittedPath = path.Split('/'); AddInTreeNode curPath = rootNode; int i = 0; while (i < splittedPath.Length) { if (!curPath.ChildNodes.TryGetValue(splittedPath[i], out curPath)) { if (throwOnNotFound) throw new TreePathNotFoundException(path); else return null; } // curPath = curPath.ChildNodes[splittedPath[i]]; already done by TryGetValue ++i; } return curPath; } /// /// Builds a single item in the addin tree. /// /// A path to the item in the addin tree. /// The owner used to create the objects. /// The path does not /// exist or does not point to an item. public static object BuildItem(string path, object caller) { int pos = path.LastIndexOf('/'); string parent = path.Substring(0, pos); string child = path.Substring(pos + 1); AddInTreeNode node = GetTreeNode(parent); return node.BuildChildItem(child, caller, new ArrayList(BuildItems(path, caller, false))); } /// /// Builds the items in the path. Ensures that all items have the type T. /// Throws a if the path is not found. /// /// A path in the addin tree. /// The owner used to create the objects. public static List BuildItems(string path, object caller) { return BuildItems(path, caller, true); } /// /// Builds the items in the path. Ensures that all items have the type T. /// /// A path in the addin tree. /// The owner used to create the objects. /// If true, throws a /// if the path is not found. If false, an empty ArrayList is returned when the /// path is not found. public static List BuildItems(string path, object caller, bool throwOnNotFound) { AddInTreeNode node = GetTreeNode(path, throwOnNotFound); if (node == null) return new List(); else return node.BuildChildItems(caller); } static AddInTreeNode CreatePath(AddInTreeNode localRoot, string path) { if (path == null || path.Length == 0) { return localRoot; } string[] splittedPath = path.Split('/'); AddInTreeNode curPath = localRoot; int i = 0; while (i < splittedPath.Length) { if (!curPath.ChildNodes.ContainsKey(splittedPath[i])) { curPath.ChildNodes[splittedPath[i]] = new AddInTreeNode(); } curPath = curPath.ChildNodes[splittedPath[i]]; ++i; } return curPath; } static void AddExtensionPath(ExtensionPath path) { AddInTreeNode treePath = CreatePath(rootNode, path.Name); foreach (Codon codon in path.Codons) { treePath.Codons.Add(codon); } } /// /// The specified AddIn is added to the collection. /// If the AddIn is enabled, its doozers, condition evaluators and extension /// paths are added to the AddInTree and its resources are added to the /// . /// public static void InsertAddIn(AddIn addIn) { if (addIn.Enabled) { foreach (ExtensionPath path in addIn.Paths.Values) { AddExtensionPath(path); } foreach (Runtime runtime in addIn.Runtimes) { if (runtime.IsActive) { foreach (LazyLoadDoozer doozer in runtime.DefinedDoozers) { if (AddInTree.Doozers.ContainsKey(doozer.Name)) { throw new AddInLoadException("Duplicate doozer: " + doozer.Name); } AddInTree.Doozers.Add(doozer.Name, doozer); } foreach (LazyConditionEvaluator condition in runtime.DefinedConditionEvaluators) { if (AddInTree.ConditionEvaluators.ContainsKey(condition.Name)) { throw new AddInLoadException("Duplicate condition evaluator: " + condition.Name); } AddInTree.ConditionEvaluators.Add(condition.Name, condition); } } } string addInRoot = Path.GetDirectoryName(addIn.FileName); foreach(string bitmapResource in addIn.BitmapResources) { string path = Path.Combine(addInRoot, bitmapResource); ResourceManager resourceManager = ResourceManager.CreateFileBasedResourceManager(Path.GetFileNameWithoutExtension(path), Path.GetDirectoryName(path), null); ResourceService.RegisterNeutralImages(resourceManager); } foreach(string stringResource in addIn.StringResources) { string path = Path.Combine(addInRoot, stringResource); ResourceManager resourceManager = ResourceManager.CreateFileBasedResourceManager(Path.GetFileNameWithoutExtension(path), Path.GetDirectoryName(path), null); ResourceService.RegisterNeutralStrings(resourceManager); } } addIns.Add(addIn); } /// /// The specified AddIn is removed to the collection. /// This is only possible for disabled AddIns, enabled AddIns require /// a restart of the application to be removed. /// /// Occurs when trying to remove an enabled AddIn. public static void RemoveAddIn(AddIn addIn) { if (addIn.Enabled) { throw new ArgumentException("Cannot remove enabled AddIns at runtime."); } addIns.Remove(addIn); } // As long as the show form takes 10 times of loading the xml representation I'm not implementing // binary serialization. // static Dictionary nameLookupTable = new Dictionary(); // static Dictionary addInLookupTable = new Dictionary(); // // public static ushort GetAddInOffset(AddIn addIn) // { // return addInLookupTable[addIn]; // } // // public static ushort GetNameOffset(string name) // { // if (!nameLookupTable.ContainsKey(name)) { // nameLookupTable[name] = (ushort)nameLookupTable.Count; // } // return nameLookupTable[name]; // } // // public static void BinarySerialize(string fileName) // { // for (int i = 0; i < addIns.Count; ++i) { // addInLookupTable[addIns] = (ushort)i; // } // using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(fileName))) { // rootNode.BinarySerialize(writer); // writer.Write((ushort)addIns.Count); // for (int i = 0; i < addIns.Count; ++i) { // addIns[i].BinarySerialize(writer); // } // writer.Write((ushort)nameLookupTable.Count); // foreach (string name in nameLookupTable.Keys) { // writer.Write(name); // } // } // } // used by Load(): disables an addin and removes it from the dictionaries. static void DisableAddin(AddIn addIn, Dictionary dict, Dictionary addInDict) { addIn.Enabled = false; addIn.Action = AddInAction.DependencyError; foreach (string name in addIn.Manifest.Identities.Keys) { dict.Remove(name); addInDict.Remove(name); } } /// /// Loads a list of .addin files, ensuring that dependencies are satisfied. /// This method is normally called by . /// /// /// The list of .addin file names to load. /// /// /// The list of disabled AddIn identity names. /// public static void Load(List addInFiles, List disabledAddIns) { List list = new List(); Dictionary dict = new Dictionary(); Dictionary addInDict = new Dictionary(); foreach (string fileName in addInFiles) { AddIn addIn; try { addIn = AddIn.Load(fileName); } catch (AddInLoadException ex) { LoggingService.Error(ex); if (ex.InnerException != null) { MessageService.ShowError("Error loading AddIn " + fileName + ":\n" + ex.InnerException.Message); } else { MessageService.ShowError("Error loading AddIn " + fileName + ":\n" + ex.Message); } addIn = new AddIn(); addIn.addInFileName = fileName; addIn.CustomErrorMessage = ex.Message; } if (addIn.Action == AddInAction.CustomError) { list.Add(addIn); continue; } addIn.Enabled = true; if (disabledAddIns != null && disabledAddIns.Count > 0) { foreach (string name in addIn.Manifest.Identities.Keys) { if (disabledAddIns.Contains(name)) { addIn.Enabled = false; break; } } } if (addIn.Enabled) { foreach (KeyValuePair pair in addIn.Manifest.Identities) { if (dict.ContainsKey(pair.Key)) { MessageService.ShowError("Name '" + pair.Key + "' is used by " + "'" + addInDict[pair.Key].FileName + "' and '" + fileName + "'"); addIn.Enabled = false; addIn.Action = AddInAction.InstalledTwice; break; } else { dict.Add(pair.Key, pair.Value); addInDict.Add(pair.Key, addIn); } } } list.Add(addIn); } checkDependencies: for (int i = 0; i < list.Count; i++) { AddIn addIn = list[i]; if (!addIn.Enabled) continue; Version versionFound; foreach (AddInReference reference in addIn.Manifest.Conflicts) { if (reference.Check(dict, out versionFound)) { MessageService.ShowError(addIn.Name + " conflicts with " + reference.ToString() + " and has been disabled."); DisableAddin(addIn, dict, addInDict); goto checkDependencies; // after removing one addin, others could break } } foreach (AddInReference reference in addIn.Manifest.Dependencies) { if (!reference.Check(dict, out versionFound)) { if (versionFound != null) { MessageService.ShowError(addIn.Name + " has not been loaded because it requires " + reference.ToString() + ", but version " + versionFound.ToString() + " is installed."); } else { MessageService.ShowError(addIn.Name + " has not been loaded because it requires " + reference.ToString() + "."); } DisableAddin(addIn, dict, addInDict); goto checkDependencies; // after removing one addin, others could break } } } foreach (AddIn addIn in list) { try { InsertAddIn(addIn); } catch (AddInLoadException ex) { LoggingService.Error(ex); MessageService.ShowError("Error loading AddIn " + addIn.FileName + ":\n" + ex.Message); } } } } }