7
// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
9
// Permission is hereby granted, free of charge, to any person obtaining
10
// a copy of this software and associated documentation files (the
11
// "Software"), to deal in the Software without restriction, including
12
// without limitation the rights to use, copy, modify, merge, publish,
13
// distribute, sublicense, and/or sell copies of the Software, and to
14
// permit persons to whom the Software is furnished to do so, subject to
15
// the following conditions:
17
// The above copyright notice and this permission notice shall be
18
// included in all copies or substantial portions of the Software.
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
using System.Collections;
31
using System.Collections.Generic;
34
using System.Xml.Serialization;
35
using System.Collections.Specialized;
36
using Mono.Addins.Serialization;
37
using Mono.Addins.Database;
40
namespace Mono.Addins.Description
43
/// An add-in description
46
/// This class represent an add-in manifest. It has properties for getting
47
/// all information, and methods for loading and saving files.
49
public class AddinDescription: IBinaryXmlElement
51
XmlDocument configDoc;
53
AddinDatabase ownerDatabase;
66
string sourceAddinFile;
70
bool defaultEnabled = true;
71
AddinFlags flags = AddinFlags.None;
74
ModuleDescription mainModule;
75
ModuleCollection optionalModules;
76
ExtensionNodeSetCollection nodeSets;
77
ConditionTypeDescriptionCollection conditionTypes;
78
ExtensionPointCollection extensionPoints;
79
ExtensionNodeDescription localizer;
82
AddinPropertyCollectionImpl properties;
83
Dictionary<string,string> variables;
85
internal static BinaryXmlTypeMap typeMap;
87
static AddinDescription ()
89
typeMap = new BinaryXmlTypeMap ();
90
typeMap.RegisterType (typeof(AddinDescription), "AddinDescription");
91
typeMap.RegisterType (typeof(Extension), "Extension");
92
typeMap.RegisterType (typeof(ExtensionNodeDescription), "Node");
93
typeMap.RegisterType (typeof(ExtensionNodeSet), "NodeSet");
94
typeMap.RegisterType (typeof(ExtensionNodeType), "NodeType");
95
typeMap.RegisterType (typeof(ExtensionPoint), "ExtensionPoint");
96
typeMap.RegisterType (typeof(ModuleDescription), "ModuleDescription");
97
typeMap.RegisterType (typeof(ConditionTypeDescription), "ConditionType");
98
typeMap.RegisterType (typeof(Condition), "Condition");
99
typeMap.RegisterType (typeof(AddinDependency), "AddinDependency");
100
typeMap.RegisterType (typeof(AssemblyDependency), "AssemblyDependency");
101
typeMap.RegisterType (typeof(NodeTypeAttribute), "NodeTypeAttribute");
102
typeMap.RegisterType (typeof(AddinFileInfo), "FileInfo");
103
typeMap.RegisterType (typeof(AddinProperty), "Property");
106
internal AddinDatabase OwnerDatabase {
107
get { return ownerDatabase; }
108
set { ownerDatabase = value; }
112
/// Gets or sets the path to the main addin file.
118
/// The add-in file can be either the main assembly of an add-in or an xml manifest.
120
public string AddinFile {
121
get { return sourceAddinFile; }
122
set { sourceAddinFile = value; }
126
/// Gets the addin identifier.
129
/// The addin identifier.
131
public string AddinId {
132
get { return Addin.GetFullId (Namespace, LocalId, Version); }
136
/// Gets or sets the local identifier.
139
/// The local identifier.
141
public string LocalId {
142
get { return id != null ? ParseString (id) : string.Empty; }
143
set { id = value; hasUserId = true; }
147
/// Gets or sets the namespace.
152
public string Namespace {
153
get { return ns != null ? ParseString (ns) : string.Empty; }
158
/// Gets or sets the display name of the add-in.
165
string val = Properties.GetPropertyValue ("Name");
168
if (name != null && name.Length > 0)
169
return ParseString (name);
172
else if (sourceAddinFile != null)
173
return Path.GetFileNameWithoutExtension (sourceAddinFile);
177
set { name = value; }
181
/// Gets or sets the version.
186
public string Version {
187
get { return version != null ? ParseString (version) : string.Empty; }
188
set { version = value; }
192
/// Gets or sets the version of the add-in with which this add-in is backwards compatible.
195
/// The compat version.
197
public string CompatVersion {
198
get { return compatVersion != null ? ParseString (compatVersion) : string.Empty; }
199
set { compatVersion = value; }
203
/// Gets or sets the author.
208
public string Author {
210
string val = Properties.GetPropertyValue ("Author");
213
return ParseString (author) ?? string.Empty;
215
set { author = value; }
219
/// Gets or sets the Url where more information about the add-in can be found.
226
string val = Properties.GetPropertyValue ("Url");
229
return ParseString (url) ?? string.Empty;
235
/// Gets or sets the copyright.
240
public string Copyright {
242
string val = Properties.GetPropertyValue ("Copyright");
245
return ParseString (copyright) ?? string.Empty;
247
set { copyright = value; }
251
/// Gets or sets the description of the add-in.
256
public string Description {
258
string val = Properties.GetPropertyValue ("Description");
261
return ParseString (description) ?? string.Empty;
263
set { description = value; }
267
/// Gets or sets the category of the add-in.
272
public string Category {
274
string val = Properties.GetPropertyValue ("Category");
277
return ParseString (category) ?? string.Empty;
279
set { category = value; }
283
/// Gets the base path for locating external files relative to the add-in.
288
public string BasePath {
289
get { return basePath != null ? basePath : string.Empty; }
292
internal void SetBasePath (string path)
298
/// Gets or sets a value indicating whether this instance is an add-in root.
301
/// <c>true</c> if this instance is an add-in root; otherwise, <c>false</c>.
304
get { return isroot; }
305
set { isroot = value; }
309
/// Gets or sets a value indicating whether this add-in is enabled by default.
312
/// <c>true</c> if enabled by default; otherwise, <c>false</c>.
314
public bool EnabledByDefault {
315
get { return defaultEnabled; }
316
set { defaultEnabled = value; }
320
/// Gets or sets the add-in flags.
325
public AddinFlags Flags {
326
get { return flags; }
327
set { flags = value; }
330
internal bool HasUserId {
331
get { return hasUserId; }
332
set { hasUserId = value; }
336
/// Gets a value indicating whether this add-in can be disabled.
339
/// <c>true</c> if this add-in can be disabled; otherwise, <c>false</c>.
341
public bool CanDisable {
342
get { return (flags & AddinFlags.CantDisable) == 0 && !IsHidden; }
346
/// Gets a value indicating whether this add-in can be uninstalled.
349
/// <c>true</c> if this instance can be uninstalled; otherwise, <c>false</c>.
351
public bool CanUninstall {
352
get { return (flags & AddinFlags.CantUninstall) == 0 && !IsHidden; }
356
/// Gets a value indicating whether this add-in is hidden.
359
/// <c>true</c> if this add-in is hidden; otherwise, <c>false</c>.
361
public bool IsHidden {
362
get { return (flags & AddinFlags.Hidden) != 0; }
365
internal bool SupportsVersion (string ver)
367
return Addin.CompareVersions (ver, Version) >= 0 &&
368
(CompatVersion.Length == 0 || Addin.CompareVersions (ver, CompatVersion) <= 0);
372
/// Gets all external files
378
/// External files are data files and assemblies explicitly referenced in the Runtime section of the add-in manifest.
380
public StringCollection AllFiles {
382
StringCollection col = new StringCollection ();
383
foreach (string s in MainModule.AllFiles)
386
foreach (ModuleDescription mod in OptionalModules) {
387
foreach (string s in mod.AllFiles)
395
/// Gets all paths to be ignored by the add-in scanner.
398
/// All paths to be ignored.
400
public StringCollection AllIgnorePaths {
402
StringCollection col = new StringCollection ();
403
foreach (string s in MainModule.IgnorePaths)
406
foreach (ModuleDescription mod in OptionalModules) {
407
foreach (string s in mod.IgnorePaths)
415
/// Gets the main module.
420
public ModuleDescription MainModule {
422
if (mainModule == null) {
423
if (RootElement == null)
424
mainModule = new ModuleDescription ();
426
mainModule = new ModuleDescription (RootElement);
427
mainModule.SetParent (this);
434
/// Gets the optional modules.
437
/// The optional modules.
440
/// Optional modules can be used to declare extensions which will be registered only if some specified
441
/// add-in dependencies can be satisfied. Dependencies specified in optional modules are 'soft dependencies',
442
/// which means that they don't need to be satisfied in order to load the add-in.
444
public ModuleCollection OptionalModules {
446
if (optionalModules == null) {
447
optionalModules = new ModuleCollection (this);
448
if (RootElement != null) {
449
foreach (XmlElement mod in RootElement.SelectNodes ("Module"))
450
optionalModules.Add (new ModuleDescription (mod));
453
return optionalModules;
458
/// Gets all modules (including the main module and all optional modules)
463
public ModuleCollection AllModules {
465
ModuleCollection col = new ModuleCollection (this);
466
col.Add (MainModule);
467
foreach (ModuleDescription mod in OptionalModules)
474
/// Gets the extension node sets.
477
/// The extension node sets.
479
public ExtensionNodeSetCollection ExtensionNodeSets {
481
if (nodeSets == null) {
482
nodeSets = new ExtensionNodeSetCollection (this);
483
if (RootElement != null) {
484
foreach (XmlElement elem in RootElement.SelectNodes ("ExtensionNodeSet"))
485
nodeSets.Add (new ExtensionNodeSet (elem));
493
/// Gets the extension points.
496
/// The extension points.
498
public ExtensionPointCollection ExtensionPoints {
500
if (extensionPoints == null) {
501
extensionPoints = new ExtensionPointCollection (this);
502
if (RootElement != null) {
503
foreach (XmlElement elem in RootElement.SelectNodes ("ExtensionPoint"))
504
extensionPoints.Add (new ExtensionPoint (elem));
507
return extensionPoints;
512
/// Gets the condition types.
515
/// The condition types.
517
public ConditionTypeDescriptionCollection ConditionTypes {
519
if (conditionTypes == null) {
520
conditionTypes = new ConditionTypeDescriptionCollection (this);
521
if (RootElement != null) {
522
foreach (XmlElement elem in RootElement.SelectNodes ("ConditionType"))
523
conditionTypes.Add (new ConditionTypeDescription (elem));
526
return conditionTypes;
531
/// Gets or sets the add-in localizer.
534
/// The description of the add-in localizer for this add-in.
536
public ExtensionNodeDescription Localizer {
537
get { return localizer; }
538
set { localizer = value; }
542
/// Custom properties specified in the add-in header
544
public AddinPropertyCollection Properties {
546
if (properties == null)
547
properties = new AddinPropertyCollectionImpl (this);
553
/// Adds an extension point.
556
/// The extension point.
558
/// <param name='path'>
559
/// Path that identifies the new extension point.
561
public ExtensionPoint AddExtensionPoint (string path)
563
ExtensionPoint ep = new ExtensionPoint ();
565
ExtensionPoints.Add (ep);
569
internal ExtensionNodeDescription FindExtensionNode (string path, bool lookInDeps)
571
// Look in the extensions of this add-in
573
foreach (Extension ext in MainModule.Extensions) {
574
if (path.StartsWith (ext.Path + "/")) {
575
string subp = path.Substring (ext.Path.Length).Trim ('/');
576
ExtensionNodeDescriptionCollection nodes = ext.ExtensionNodes;
577
ExtensionNodeDescription node = null;
578
foreach (string p in subp.Split ('/')) {
579
if (p.Length == 0) continue;
583
nodes = node.ChildNodes;
590
if (!lookInDeps || OwnerDatabase == null)
593
// Look in dependencies
595
foreach (Dependency dep in MainModule.Dependencies) {
596
AddinDependency adep = dep as AddinDependency;
597
if (adep == null) continue;
598
Addin ad = OwnerDatabase.GetInstalledAddin (Domain, adep.FullAddinId);
599
if (ad != null && ad.Description != null) {
600
ExtensionNodeDescription node = ad.Description.FindExtensionNode (path, false);
608
XmlElement RootElement {
610
if (configDoc != null)
611
return configDoc.DocumentElement;
618
/// Gets or sets file where this description is stored
623
public string FileName {
624
get { return configFile; }
625
set { configFile = value; }
628
internal string Domain {
629
get { return domain; }
630
set { domain = value; }
633
internal void StoreFileInfo ()
635
ArrayList list = new ArrayList ();
636
foreach (string f in AllFiles) {
637
string file = Path.Combine (this.BasePath, f);
638
AddinFileInfo fi = new AddinFileInfo ();
640
fi.Timestamp = File.GetLastWriteTime (file);
643
fileInfo = list.ToArray ();
646
internal bool FilesChanged ()
648
// Checks if the files of the add-in have changed.
649
if (fileInfo == null)
652
foreach (AddinFileInfo f in fileInfo) {
653
string file = Path.Combine (this.BasePath, f.FileName);
654
if (!File.Exists (file))
656
if (f.Timestamp != File.GetLastWriteTime (file))
663
void TransferCoreProperties (bool removeProperties)
665
if (properties == null)
668
string val = properties.ExtractCoreProperty ("Id", removeProperties);
672
val = properties.ExtractCoreProperty ("Namespace", removeProperties);
676
val = properties.ExtractCoreProperty ("Version", removeProperties);
680
val = properties.ExtractCoreProperty ("CompatVersion", removeProperties);
684
val = properties.ExtractCoreProperty ("DefaultEnabled", removeProperties);
686
defaultEnabled = GetBool (val, true);
688
val = properties.ExtractCoreProperty ("IsRoot", removeProperties);
690
isroot = GetBool (val, true);
692
val = properties.ExtractCoreProperty ("Flags", removeProperties);
694
flags = (AddinFlags) Enum.Parse (typeof(AddinFlags), val);
698
/// Saves the add-in description.
700
/// <param name='fileName'>
701
/// File name where to save this instance
704
/// Saves the add-in description to the specified file and sets the FileName property.
706
public void Save (string fileName)
708
configFile = fileName;
713
/// Saves the add-in description.
715
/// <exception cref='InvalidOperationException'>
716
/// It is thrown if FileName is not set
719
/// The description is saved to the file specified in the FileName property.
723
if (configFile == null)
724
throw new InvalidOperationException ("File name not specified.");
728
using (StreamWriter sw = new StreamWriter (configFile)) {
729
XmlTextWriter tw = new XmlTextWriter (sw);
730
tw.Formatting = Formatting.Indented;
736
/// Generates an XML representation of the add-in description
741
public XmlDocument SaveToXml ()
750
throw new InvalidOperationException ("Can't write incomplete description.");
754
if (configDoc == null) {
755
configDoc = new XmlDocument ();
756
configDoc.AppendChild (configDoc.CreateElement ("Addin"));
759
elem = configDoc.DocumentElement;
761
SaveCoreProperty (elem, HasUserId ? id : null, "id", "Id");
762
SaveCoreProperty (elem, version, "version", "Version");
763
SaveCoreProperty (elem, ns, "namespace", "Namespace");
764
SaveCoreProperty (elem, isroot ? "true" : null, "isroot", "IsRoot");
766
// Name will return the file name when HasUserId=false
767
if (!string.IsNullOrEmpty (name))
768
elem.SetAttribute ("name", name);
770
elem.RemoveAttribute ("name");
772
SaveCoreProperty (elem, compatVersion, "compatVersion", "CompatVersion");
773
SaveCoreProperty (elem, defaultEnabled ? null : "false", "defaultEnabled", "DefaultEnabled");
774
SaveCoreProperty (elem, flags != AddinFlags.None ? flags.ToString () : null, "flags", "Flags");
776
if (author != null && author.Length > 0)
777
elem.SetAttribute ("author", author);
779
elem.RemoveAttribute ("author");
781
if (url != null && url.Length > 0)
782
elem.SetAttribute ("url", url);
784
elem.RemoveAttribute ("url");
786
if (copyright != null && copyright.Length > 0)
787
elem.SetAttribute ("copyright", copyright);
789
elem.RemoveAttribute ("copyright");
791
if (description != null && description.Length > 0)
792
elem.SetAttribute ("description", description);
794
elem.RemoveAttribute ("description");
796
if (category != null && category.Length > 0)
797
elem.SetAttribute ("category", category);
799
elem.RemoveAttribute ("category");
801
if (localizer == null || localizer.Element == null) {
802
// Remove old element if it exists
803
XmlElement oldLoc = (XmlElement) elem.SelectSingleNode ("Localizer");
805
elem.RemoveChild (oldLoc);
807
if (localizer != null)
808
localizer.SaveXml (elem);
810
if (mainModule != null) {
811
mainModule.Element = elem;
812
mainModule.SaveXml (elem);
815
if (optionalModules != null)
816
optionalModules.SaveXml (elem);
818
if (nodeSets != null)
819
nodeSets.SaveXml (elem);
821
if (extensionPoints != null)
822
extensionPoints.SaveXml (elem);
824
XmlElement oldHeader = (XmlElement) elem.SelectSingleNode ("Header");
825
if (properties == null || properties.Count == 0) {
826
if (oldHeader != null)
827
elem.RemoveChild (oldHeader);
829
if (oldHeader == null) {
830
oldHeader = elem.OwnerDocument.CreateElement ("Header");
831
if (elem.FirstChild != null)
832
elem.InsertBefore (oldHeader, elem.FirstChild);
834
elem.AppendChild (oldHeader);
837
oldHeader.RemoveAll ();
838
foreach (var prop in properties) {
839
XmlElement propElem = elem.OwnerDocument.CreateElement (prop.Name);
840
if (!string.IsNullOrEmpty (prop.Locale))
841
propElem.SetAttribute ("locale", prop.Locale);
842
propElem.InnerText = prop.Value ?? string.Empty;
843
oldHeader.AppendChild (propElem);
847
XmlElement oldVars = (XmlElement) elem.SelectSingleNode ("Variables");
848
if (variables == null || variables.Count == 0) {
850
elem.RemoveChild (oldVars);
852
if (oldVars == null) {
853
oldVars = elem.OwnerDocument.CreateElement ("Variables");
854
if (elem.FirstChild != null)
855
elem.InsertBefore (oldVars, elem.FirstChild);
857
elem.AppendChild (oldVars);
860
oldVars.RemoveAll ();
861
foreach (var prop in variables) {
862
XmlElement propElem = elem.OwnerDocument.CreateElement (prop.Key);
863
propElem.InnerText = prop.Value ?? string.Empty;
864
oldVars.AppendChild (propElem);
869
void SaveCoreProperty (XmlElement elem, string val, string attr, string prop)
871
if (properties != null && properties.HasProperty (prop)) {
872
elem.RemoveAttribute (attr);
873
if (!string.IsNullOrEmpty (val))
874
properties.SetPropertyValue (prop, val);
876
properties.RemoveProperty (prop);
878
else if (string.IsNullOrEmpty (val))
879
elem.RemoveAttribute (attr);
881
elem.SetAttribute (attr, val);
886
/// Load an add-in description from a file
888
/// <param name='configFile'>
891
public static AddinDescription Read (string configFile)
893
AddinDescription config;
894
using (Stream s = File.OpenRead (configFile)) {
895
config = Read (s, Path.GetDirectoryName (configFile));
897
config.configFile = configFile;
902
/// Load an add-in description from a stream
904
/// <param name='stream'>
907
/// <param name='basePath'>
908
/// The path to be used to resolve relative file paths.
910
public static AddinDescription Read (Stream stream, string basePath)
912
return Read (new StreamReader (stream), basePath);
916
/// Load an add-in description from a text reader
918
/// <param name='reader'>
921
/// <param name='basePath'>
922
/// The path to be used to resolve relative file paths.
924
public static AddinDescription Read (TextReader reader, string basePath)
926
AddinDescription config = new AddinDescription ();
929
config.configDoc = new XmlDocument ();
930
config.configDoc.Load (reader);
931
} catch (Exception ex) {
932
throw new InvalidOperationException ("The add-in configuration file is invalid: " + ex.Message, ex);
935
XmlElement elem = config.configDoc.DocumentElement;
936
if (elem.LocalName == "ExtensionModel")
939
XmlElement varsElem = (XmlElement) elem.SelectSingleNode ("Variables");
940
if (varsElem != null) {
941
foreach (XmlNode node in varsElem.ChildNodes) {
942
XmlElement prop = node as XmlElement;
945
if (config.variables == null)
946
config.variables = new Dictionary<string, string> ();
947
config.variables [prop.LocalName] = prop.InnerText;
951
config.id = elem.GetAttribute ("id");
952
config.ns = elem.GetAttribute ("namespace");
953
config.name = elem.GetAttribute ("name");
954
config.version = elem.GetAttribute ("version");
955
config.compatVersion = elem.GetAttribute ("compatVersion");
956
config.author = elem.GetAttribute ("author");
957
config.url = elem.GetAttribute ("url");
958
config.copyright = elem.GetAttribute ("copyright");
959
config.description = elem.GetAttribute ("description");
960
config.category = elem.GetAttribute ("category");
961
config.basePath = elem.GetAttribute ("basePath");
962
config.domain = "global";
964
string s = elem.GetAttribute ("isRoot");
965
if (s.Length == 0) s = elem.GetAttribute ("isroot");
966
config.isroot = GetBool (s, false);
968
config.defaultEnabled = GetBool (elem.GetAttribute ("defaultEnabled"), true);
970
string prot = elem.GetAttribute ("flags");
971
if (prot.Length == 0)
972
config.flags = AddinFlags.None;
974
config.flags = (AddinFlags) Enum.Parse (typeof(AddinFlags), prot);
976
XmlElement localizerElem = (XmlElement) elem.SelectSingleNode ("Localizer");
977
if (localizerElem != null)
978
config.localizer = new ExtensionNodeDescription (localizerElem);
980
XmlElement headerElem = (XmlElement) elem.SelectSingleNode ("Header");
981
if (headerElem != null) {
982
foreach (XmlNode node in headerElem.ChildNodes) {
983
XmlElement prop = node as XmlElement;
986
config.Properties.SetPropertyValue (prop.LocalName, prop.InnerText, prop.GetAttribute ("locale"));
990
config.TransferCoreProperties (false);
992
if (config.id.Length > 0)
993
config.hasUserId = true;
998
internal string ParseString (string input)
1000
if (input == null || input.Length < 4 || variables == null || variables.Count == 0)
1003
int i = input.IndexOf ("$(");
1007
StringBuilder result = new StringBuilder (input.Substring (0, i), input.Length);
1009
while (i < input.Length) {
1010
if (input [i] == '$') {
1013
if (i >= input.Length || input[i] != '(') {
1014
result.Append ('$');
1020
while (i < input.Length && input [i] != ')')
1023
string tag = input.Substring (start, i - start);
1026
if (variables.TryGetValue (tag, out tagValue))
1027
result.Append (tagValue);
1029
result.Append ('$');
1033
result.Append (input [i]);
1037
return result.ToString ();
1040
static bool GetBool (string s, bool defval)
1045
return s == "true" || s == "yes";
1048
internal static AddinDescription ReadBinary (FileDatabase fdb, string configFile)
1050
AddinDescription description = (AddinDescription) fdb.ReadSharedObject (configFile, typeMap);
1051
if (description != null) {
1052
description.FileName = configFile;
1053
description.canWrite = !fdb.IgnoreDescriptionData;
1058
internal void SaveBinary (FileDatabase fdb, string file)
1064
internal void SaveBinary (FileDatabase fdb)
1067
throw new InvalidOperationException ("Can't write incomplete description.");
1068
fdb.WriteSharedObject (AddinFile, FileName, typeMap, this);
1069
// BinaryXmlReader.DumpFile (configFile);
1073
/// Verify this instance.
1076
/// This method checks all the definitions in the description and returns a list of errors.
1077
/// If the returned list is empty, it means that the description is valid.
1079
public StringCollection Verify ()
1081
return Verify (new AddinFileSystemExtension ());
1084
internal StringCollection Verify (AddinFileSystemExtension fs)
1086
StringCollection errors = new StringCollection ();
1089
if (OptionalModules.Count > 0)
1090
errors.Add ("Root add-in hosts can't have optional modules.");
1093
if (AddinId.Length == 0 || Version.Length == 0) {
1094
if (ExtensionPoints.Count > 0)
1095
errors.Add ("Add-ins which define new extension points must have an Id and Version.");
1098
MainModule.Verify ("", errors);
1099
OptionalModules.Verify ("", errors);
1100
ExtensionNodeSets.Verify ("", errors);
1101
ExtensionPoints.Verify ("", errors);
1102
ConditionTypes.Verify ("", errors);
1104
foreach (ExtensionNodeSet nset in ExtensionNodeSets) {
1105
if (nset.Id.Length == 0)
1106
errors.Add ("Attribute 'id' can't be empty for global node sets.");
1110
if (BasePath.Length > 0)
1112
else if (sourceAddinFile != null && sourceAddinFile.Length > 0)
1113
bp = Path.GetDirectoryName (AddinFile);
1114
else if (configFile != null && configFile.Length > 0)
1115
bp = Path.GetDirectoryName (configFile);
1118
foreach (string file in AllFiles) {
1119
string asmFile = Path.Combine (bp, file);
1120
if (!fs.FileExists (asmFile))
1121
errors.Add ("The file '" + asmFile + "' referenced in the manifest could not be found.");
1125
if (localizer != null && localizer.GetAttribute ("type").Length == 0) {
1126
errors.Add ("The attribute 'type' in the Location element is required.");
1129
// Ensure that there are no duplicated properties
1131
if (properties != null) {
1132
HashSet<string> props = new HashSet<string> ();
1133
foreach (var prop in properties) {
1134
if (!props.Add (prop.Name + " " + prop.Locale))
1135
errors.Add (string.Format ("Property {0} specified more than once", prop.Name + (prop.Locale != null ? " (" + prop.Locale + ")" : "")));
1142
internal void SetExtensionsAddinId (string addinId)
1144
foreach (ExtensionPoint ep in ExtensionPoints)
1145
ep.SetExtensionsAddinId (addinId);
1147
foreach (ExtensionNodeSet ns in ExtensionNodeSets)
1148
ns.SetExtensionsAddinId (addinId);
1151
internal void UnmergeExternalData (Hashtable addins)
1153
// Removes extension types and extension sets coming from other add-ins.
1154
foreach (ExtensionPoint ep in ExtensionPoints)
1155
ep.UnmergeExternalData (AddinId, addins);
1157
foreach (ExtensionNodeSet ns in ExtensionNodeSets)
1158
ns.UnmergeExternalData (AddinId, addins);
1161
internal void MergeExternalData (AddinDescription other)
1163
// Removes extension types and extension sets coming from other add-ins.
1164
foreach (ExtensionPoint ep in other.ExtensionPoints) {
1165
ExtensionPoint tep = ExtensionPoints [ep.Path];
1167
tep.MergeWith (AddinId, ep);
1170
foreach (ExtensionNodeSet ns in other.ExtensionNodeSets) {
1171
ExtensionNodeSet tns = ExtensionNodeSets [ns.Id];
1173
tns.MergeWith (AddinId, ns);
1177
internal bool IsExtensionModel {
1178
get { return RootElement.LocalName == "ExtensionModel"; }
1181
internal static AddinDescription Merge (AddinDescription desc1, AddinDescription desc2)
1183
if (!desc2.IsExtensionModel) {
1184
AddinDescription tmp = desc1;
1185
desc1 = desc2; desc2 = tmp;
1187
((AddinPropertyCollectionImpl)desc1.Properties).AddRange (desc2.Properties);
1188
desc1.ExtensionPoints.AddRange (desc2.ExtensionPoints);
1189
desc1.ExtensionNodeSets.AddRange (desc2.ExtensionNodeSets);
1190
desc1.ConditionTypes.AddRange (desc2.ConditionTypes);
1191
desc1.OptionalModules.AddRange (desc2.OptionalModules);
1192
foreach (string s in desc2.MainModule.Assemblies)
1193
desc1.MainModule.Assemblies.Add (s);
1194
foreach (string s in desc2.MainModule.DataFiles)
1195
desc1.MainModule.DataFiles.Add (s);
1196
desc1.MainModule.Dependencies.AddRange (desc2.MainModule.Dependencies);
1197
desc1.MainModule.Extensions.AddRange (desc2.MainModule.Extensions);
1201
void IBinaryXmlElement.Write (BinaryXmlWriter writer)
1203
TransferCoreProperties (true);
1204
writer.WriteValue ("id", ParseString (id));
1205
writer.WriteValue ("ns", ParseString (ns));
1206
writer.WriteValue ("isroot", isroot);
1207
writer.WriteValue ("name", ParseString (name));
1208
writer.WriteValue ("version", ParseString (version));
1209
writer.WriteValue ("compatVersion", ParseString (compatVersion));
1210
writer.WriteValue ("hasUserId", hasUserId);
1211
writer.WriteValue ("author", ParseString (author));
1212
writer.WriteValue ("url", ParseString (url));
1213
writer.WriteValue ("copyright", ParseString (copyright));
1214
writer.WriteValue ("description", ParseString (description));
1215
writer.WriteValue ("category", ParseString (category));
1216
writer.WriteValue ("basePath", basePath);
1217
writer.WriteValue ("sourceAddinFile", sourceAddinFile);
1218
writer.WriteValue ("defaultEnabled", defaultEnabled);
1219
writer.WriteValue ("domain", domain);
1220
writer.WriteValue ("MainModule", MainModule);
1221
writer.WriteValue ("OptionalModules", OptionalModules);
1222
writer.WriteValue ("NodeSets", ExtensionNodeSets);
1223
writer.WriteValue ("ExtensionPoints", ExtensionPoints);
1224
writer.WriteValue ("ConditionTypes", ConditionTypes);
1225
writer.WriteValue ("FilesInfo", fileInfo);
1226
writer.WriteValue ("Localizer", localizer);
1227
writer.WriteValue ("flags", (int)flags);
1228
writer.WriteValue ("Properties", properties);
1231
void IBinaryXmlElement.Read (BinaryXmlReader reader)
1233
id = reader.ReadStringValue ("id");
1234
ns = reader.ReadStringValue ("ns");
1235
isroot = reader.ReadBooleanValue ("isroot");
1236
name = reader.ReadStringValue ("name");
1237
version = reader.ReadStringValue ("version");
1238
compatVersion = reader.ReadStringValue ("compatVersion");
1239
hasUserId = reader.ReadBooleanValue ("hasUserId");
1240
author = reader.ReadStringValue ("author");
1241
url = reader.ReadStringValue ("url");
1242
copyright = reader.ReadStringValue ("copyright");
1243
description = reader.ReadStringValue ("description");
1244
category = reader.ReadStringValue ("category");
1245
basePath = reader.ReadStringValue ("basePath");
1246
sourceAddinFile = reader.ReadStringValue ("sourceAddinFile");
1247
defaultEnabled = reader.ReadBooleanValue ("defaultEnabled");
1248
domain = reader.ReadStringValue ("domain");
1249
mainModule = (ModuleDescription) reader.ReadValue ("MainModule");
1250
optionalModules = (ModuleCollection) reader.ReadValue ("OptionalModules", new ModuleCollection (this));
1251
nodeSets = (ExtensionNodeSetCollection) reader.ReadValue ("NodeSets", new ExtensionNodeSetCollection (this));
1252
extensionPoints = (ExtensionPointCollection) reader.ReadValue ("ExtensionPoints", new ExtensionPointCollection (this));
1253
conditionTypes = (ConditionTypeDescriptionCollection) reader.ReadValue ("ConditionTypes", new ConditionTypeDescriptionCollection (this));
1254
fileInfo = (object[]) reader.ReadValue ("FilesInfo", null);
1255
localizer = (ExtensionNodeDescription) reader.ReadValue ("Localizer");
1256
flags = (AddinFlags) reader.ReadInt32Value ("flags");
1257
properties = (AddinPropertyCollectionImpl) reader.ReadValue ("Properties", new AddinPropertyCollectionImpl (this));
1259
if (mainModule != null)
1260
mainModule.SetParent (this);
1264
class AddinFileInfo: IBinaryXmlElement
1269
public string FileName {
1278
public System.DateTime Timestamp {
1287
public void Read (BinaryXmlReader reader)
1289
fileName = reader.ReadStringValue ("fileName");
1290
timestamp = reader.ReadDateTimeValue ("timestamp");
1293
public void Write (BinaryXmlWriter writer)
1295
writer.WriteValue ("fileName", fileName);
1296
writer.WriteValue ("timestamp", timestamp);