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;
33
using System.Xml.Serialization;
34
using System.Collections.Specialized;
35
using Mono.Addins.Serialization;
36
using Mono.Addins.Database;
38
namespace Mono.Addins.Description
40
// This class represent an add-in configuration file. It has properties for getting
41
// all information, and methods for loading and saving files.
42
public class AddinDescription: IBinaryXmlElement
44
XmlDocument configDoc;
47
AddinDatabase ownerDatabase;
60
string sourceAddinFile;
64
bool defaultEnabled = true;
66
ModuleDescription mainModule;
67
ModuleCollection optionalModules;
68
ExtensionNodeSetCollection nodeSets;
69
ConditionTypeDescriptionCollection conditionTypes;
70
ExtensionPointCollection extensionPoints;
72
internal static BinaryXmlTypeMap typeMap;
74
static AddinDescription ()
76
typeMap = new BinaryXmlTypeMap ();
77
typeMap.RegisterType (typeof(AddinDescription), "AddinDescription");
78
typeMap.RegisterType (typeof(Extension), "Extension");
79
typeMap.RegisterType (typeof(ExtensionNodeDescription), "Node");
80
typeMap.RegisterType (typeof(ExtensionNodeSet), "NodeSet");
81
typeMap.RegisterType (typeof(ExtensionNodeType), "NodeType");
82
typeMap.RegisterType (typeof(ExtensionPoint), "ExtensionPoint");
83
typeMap.RegisterType (typeof(ModuleDescription), "ModuleDescription");
84
typeMap.RegisterType (typeof(ConditionTypeDescription), "ConditionType");
85
typeMap.RegisterType (typeof(Condition), "Condition");
86
typeMap.RegisterType (typeof(AddinDependency), "AddinDependency");
87
typeMap.RegisterType (typeof(AssemblyDependency), "AssemblyDependency");
88
typeMap.RegisterType (typeof(NodeTypeAttribute), "NodeTypeAttribute");
91
internal AddinDatabase OwnerDatabase {
92
get { return ownerDatabase; }
93
set { ownerDatabase = value; }
96
public string AddinFile {
97
get { return sourceAddinFile; }
98
set { sourceAddinFile = value; }
101
public string AddinId {
102
get { return Addin.GetFullId (Namespace, LocalId, Version); }
105
public string LocalId {
106
get { return id != null ? id : string.Empty; }
107
set { id = value; hasUserId = true; }
110
public string Namespace {
111
get { return ns != null ? ns : string.Empty; }
117
if (name != null && name.Length > 0)
121
else if (sourceAddinFile != null)
122
return Path.GetFileNameWithoutExtension (sourceAddinFile);
126
set { name = value; }
129
public string Version {
130
get { return version != null ? version : string.Empty; }
131
set { version = value; }
134
public string CompatVersion {
135
get { return compatVersion != null ? compatVersion : string.Empty; }
136
set { compatVersion = value; }
139
public string Author {
140
get { return author != null ? author : string.Empty; }
141
set { author = value; }
145
get { return url != null ? url : string.Empty; }
149
public string Copyright {
150
get { return copyright != null ? copyright : string.Empty; }
151
set { copyright = value; }
154
public string Description {
155
get { return description != null ? description : string.Empty; }
156
set { description = value; }
159
public string Category {
160
get { return category != null ? category : string.Empty; }
161
set { category = value; }
164
internal string BasePath {
165
get { return basePath != null ? basePath : string.Empty; }
166
set { basePath = value; }
170
get { return isroot; }
171
set { isroot = value; }
174
public bool EnabledByDefault {
175
get { return defaultEnabled; }
176
set { defaultEnabled = value; }
179
internal bool HasUserId {
180
get { return hasUserId; }
181
set { hasUserId = value; }
184
public StringCollection AllFiles {
186
StringCollection col = new StringCollection ();
187
foreach (string s in MainModule.AllFiles)
190
foreach (ModuleDescription mod in OptionalModules) {
191
foreach (string s in mod.AllFiles)
198
public ModuleDescription MainModule {
200
if (mainModule == null) {
201
if (RootElement == null)
202
mainModule = new ModuleDescription ();
204
mainModule = new ModuleDescription (RootElement);
205
mainModule.SetParent (this);
211
public ModuleCollection OptionalModules {
213
if (optionalModules == null) {
214
optionalModules = new ModuleCollection (this);
215
if (RootElement != null) {
216
foreach (XmlElement mod in RootElement.SelectNodes ("Module"))
217
optionalModules.Add (new ModuleDescription (mod));
220
return optionalModules;
224
public ModuleCollection AllModules {
226
ModuleCollection col = new ModuleCollection (this);
227
col.Add (MainModule);
228
foreach (ModuleDescription mod in OptionalModules)
234
public ExtensionNodeSetCollection ExtensionNodeSets {
236
if (nodeSets == null) {
237
nodeSets = new ExtensionNodeSetCollection (this);
238
if (RootElement != null) {
239
foreach (XmlElement elem in RootElement.SelectNodes ("ExtensionNodeSet"))
240
nodeSets.Add (new ExtensionNodeSet (elem));
247
public ExtensionPointCollection ExtensionPoints {
249
if (extensionPoints == null) {
250
extensionPoints = new ExtensionPointCollection (this);
251
if (RootElement != null) {
252
foreach (XmlElement elem in RootElement.SelectNodes ("ExtensionPoint"))
253
extensionPoints.Add (new ExtensionPoint (elem));
256
return extensionPoints;
260
public ConditionTypeDescriptionCollection ConditionTypes {
262
if (conditionTypes == null) {
263
conditionTypes = new ConditionTypeDescriptionCollection (this);
264
if (RootElement != null) {
265
foreach (XmlElement elem in RootElement.SelectNodes ("ConditionType"))
266
conditionTypes.Add (new ConditionTypeDescription (elem));
269
return conditionTypes;
273
public ExtensionPoint AddExtensionPoint (string path)
275
ExtensionPoint ep = new ExtensionPoint ();
277
ExtensionPoints.Add (ep);
281
internal ExtensionNodeDescription FindExtensionNode (string path, bool lookInDeps)
283
// Look in the extensions of this add-in
285
foreach (Extension ext in MainModule.Extensions) {
286
if (path.StartsWith (ext.Path + "/")) {
287
string subp = path.Substring (ext.Path.Length).Trim ('/');
288
ExtensionNodeDescriptionCollection nodes = ext.ExtensionNodes;
289
ExtensionNodeDescription node = null;
290
foreach (string p in subp.Split ('/')) {
291
if (p.Length == 0) continue;
295
nodes = node.ChildNodes;
302
if (!lookInDeps || OwnerDatabase == null)
305
// Look in dependencies
307
foreach (Dependency dep in MainModule.Dependencies) {
308
AddinDependency adep = dep as AddinDependency;
309
if (adep == null) continue;
310
Addin ad = OwnerDatabase.GetInstalledAddin (adep.FullAddinId);
311
if (ad != null && ad.Description != null) {
312
ExtensionNodeDescription node = ad.Description.FindExtensionNode (path, false);
320
XmlElement RootElement {
322
if (configDoc != null)
323
return configDoc.DocumentElement;
329
public string FileName {
330
get { return configFile; }
331
set { configFile = value; }
334
public void Save (string fileName)
336
configFile = fileName;
342
if (configFile == null)
343
throw new InvalidOperationException ("File name not specified.");
347
using (StreamWriter sw = new StreamWriter (configFile)) {
348
XmlTextWriter tw = new XmlTextWriter (sw);
349
tw.Formatting = Formatting.Indented;
354
public XmlDocument SaveToXml ()
363
throw new InvalidOperationException ("Can't write incomplete description.");
367
if (configDoc == null) {
368
configDoc = new XmlDocument ();
369
configDoc.AppendChild (configDoc.CreateElement ("Addin"));
372
elem = configDoc.DocumentElement;
375
elem.SetAttribute ("id", id);
377
elem.RemoveAttribute ("id");
379
elem.SetAttribute ("version", version);
380
elem.SetAttribute ("namespace", ns);
383
elem.SetAttribute ("isroot", "true");
385
elem.RemoveAttribute ("isroot");
387
// Name will return the file name when HasUserId=false
389
elem.SetAttribute ("name", Name);
391
elem.RemoveAttribute ("name");
393
if (compatVersion != null && compatVersion.Length > 0)
394
elem.SetAttribute ("compatVersion", compatVersion);
396
elem.RemoveAttribute ("compatVersion");
399
elem.RemoveAttribute ("defaultEnabled");
401
elem.SetAttribute ("defaultEnabled", "false");
403
if (author != null && author.Length > 0)
404
elem.SetAttribute ("author", author);
406
elem.RemoveAttribute ("author");
408
if (url != null && url.Length > 0)
409
elem.SetAttribute ("url", url);
411
elem.RemoveAttribute ("url");
413
if (copyright != null && copyright.Length > 0)
414
elem.SetAttribute ("copyright", copyright);
416
elem.RemoveAttribute ("copyright");
418
if (description != null && description.Length > 0)
419
elem.SetAttribute ("description", description);
421
elem.RemoveAttribute ("description");
423
if (category != null && category.Length > 0)
424
elem.SetAttribute ("category", category);
426
elem.RemoveAttribute ("category");
428
if (mainModule != null) {
429
mainModule.Element = elem;
430
mainModule.SaveXml (elem);
433
if (optionalModules != null)
434
optionalModules.SaveXml (elem);
436
if (nodeSets != null)
437
nodeSets.SaveXml (elem);
439
if (extensionPoints != null)
440
extensionPoints.SaveXml (elem);
444
public static AddinDescription Read (string configFile)
446
AddinDescription config;
447
using (Stream s = File.OpenRead (configFile)) {
448
config = Read (s, Path.GetDirectoryName (configFile));
450
config.configFile = configFile;
454
public static AddinDescription Read (Stream stream, string basePath)
456
AddinDescription config = new AddinDescription ();
459
config.configDoc = new XmlDocument ();
460
config.configDoc.Load (stream);
461
} catch (Exception ex) {
462
throw new InvalidOperationException ("The add-in configuration file is invalid.", ex);
465
XmlElement elem = config.configDoc.DocumentElement;
466
config.id = elem.GetAttribute ("id");
467
config.ns = elem.GetAttribute ("namespace");
468
config.name = elem.GetAttribute ("name");
469
config.version = elem.GetAttribute ("version");
470
config.compatVersion = elem.GetAttribute ("compatVersion");
471
config.author = elem.GetAttribute ("author");
472
config.url = elem.GetAttribute ("url");
473
config.copyright = elem.GetAttribute ("copyright");
474
config.description = elem.GetAttribute ("description");
475
config.category = elem.GetAttribute ("category");
476
config.basePath = elem.GetAttribute ("basePath");
478
string s = elem.GetAttribute ("isRoot");
479
if (s.Length == 0) s = elem.GetAttribute ("isroot");
480
config.isroot = s == "true" || s == "yes";
482
s = elem.GetAttribute ("defaultEnabled");
483
config.defaultEnabled = s.Length == 0 || s == "true" || s == "yes";
485
if (config.id.Length > 0)
486
config.hasUserId = true;
491
internal static AddinDescription ReadBinary (FileDatabase fdb, string configFile)
493
AddinDescription description = (AddinDescription) fdb.ReadSharedObject (configFile, typeMap);
494
if (description != null) {
495
description.FileName = configFile;
496
description.fromBinaryFile = true;
497
description.canWrite = !fdb.IgnoreDescriptionData;
502
internal static AddinDescription ReadHostBinary (FileDatabase fdb, string basePath, string addinId, string addinFile)
505
AddinDescription description = (AddinDescription) fdb.ReadSharedObject (basePath, addinId, ".mroot", Util.GetFullPath (addinFile), typeMap, out fileName);
506
if (description != null) {
507
description.FileName = fileName;
508
description.fromBinaryFile = true;
509
description.canWrite = !fdb.IgnoreDescriptionData;
514
internal void SaveBinary (FileDatabase fdb, string file)
520
internal void SaveBinary (FileDatabase fdb)
523
throw new InvalidOperationException ("Can't write incomplete description.");
524
fdb.WriteSharedObject (AddinFile, FileName, typeMap, this);
525
// BinaryXmlReader.DumpFile (configFile);
528
internal void SaveHostBinary (FileDatabase fdb, string basePath)
531
throw new InvalidOperationException ("Can't write incomplete description.");
534
FileName = fdb.WriteSharedObject (basePath, AddinId, ".mroot", AddinFile, FileName, typeMap, this);
537
public StringCollection Verify ()
539
StringCollection errors = new StringCollection ();
542
if (OptionalModules.Count > 0)
543
errors.Add ("Root add-in hosts can't have optional modules.");
544
if (MainModule.Dependencies.Count > 0)
545
errors.Add ("Root add-in hosts can't have dependencies.");
548
if (AddinId.Length == 0 || Version.Length == 0) {
549
if (ExtensionPoints.Count > 0)
550
errors.Add ("Add-ins which define new extension points must have an Id and Version.");
553
MainModule.Verify ("", errors);
554
OptionalModules.Verify ("", errors);
555
ExtensionNodeSets.Verify ("", errors);
556
ExtensionPoints.Verify ("", errors);
557
ConditionTypes.Verify ("", errors);
559
foreach (ExtensionNodeSet nset in ExtensionNodeSets) {
560
if (nset.Id.Length == 0)
561
errors.Add ("Attribute 'id' can't be empty for global node sets.");
565
if (BasePath.Length > 0)
567
else if (sourceAddinFile != null && sourceAddinFile.Length > 0)
568
bp = Path.GetDirectoryName (AddinFile);
569
else if (configFile != null && configFile.Length > 0)
570
bp = Path.GetDirectoryName (configFile);
573
foreach (string file in AllFiles) {
574
string asmFile = Path.Combine (BasePath, file);
575
if (!File.Exists (asmFile))
576
errors.Add ("The file '" + file + "' referenced in the manifest could not be found.");
583
internal void SetExtensionsAddinId (string addinId)
585
foreach (ExtensionPoint ep in ExtensionPoints)
586
ep.SetExtensionsAddinId (addinId);
588
foreach (ExtensionNodeSet ns in ExtensionNodeSets)
589
ns.SetExtensionsAddinId (addinId);
592
internal void UnmergeExternalData (Hashtable addins)
594
// Removes extension types and extension sets coming from other add-ins.
595
foreach (ExtensionPoint ep in ExtensionPoints)
596
ep.UnmergeExternalData (AddinId, addins);
598
foreach (ExtensionNodeSet ns in ExtensionNodeSets)
599
ns.UnmergeExternalData (AddinId, addins);
602
internal void MergeExternalData (AddinDescription other)
604
// Removes extension types and extension sets coming from other add-ins.
605
foreach (ExtensionPoint ep in other.ExtensionPoints) {
606
ExtensionPoint tep = ExtensionPoints [ep.Path];
608
tep.MergeWith (AddinId, ep);
611
foreach (ExtensionNodeSet ns in other.ExtensionNodeSets) {
612
ExtensionNodeSet tns = ExtensionNodeSets [ns.Id];
614
tns.MergeWith (AddinId, ns);
618
void IBinaryXmlElement.Write (BinaryXmlWriter writer)
620
writer.WriteValue ("id", id);
621
writer.WriteValue ("ns", ns);
622
writer.WriteValue ("isroot", isroot);
623
writer.WriteValue ("name", name);
624
writer.WriteValue ("version", version);
625
writer.WriteValue ("compatVersion", compatVersion);
626
writer.WriteValue ("hasUserId", hasUserId);
627
writer.WriteValue ("author", author);
628
writer.WriteValue ("url", url);
629
writer.WriteValue ("copyright", copyright);
630
writer.WriteValue ("description", description);
631
writer.WriteValue ("category", category);
632
writer.WriteValue ("basePath", basePath);
633
writer.WriteValue ("sourceAddinFile", sourceAddinFile);
634
writer.WriteValue ("defaultEnabled", defaultEnabled);
635
writer.WriteValue ("MainModule", MainModule);
636
writer.WriteValue ("OptionalModules", OptionalModules);
637
writer.WriteValue ("NodeSets", ExtensionNodeSets);
638
writer.WriteValue ("ExtensionPoints", ExtensionPoints);
639
writer.WriteValue ("ConditionTypes", ConditionTypes);
642
void IBinaryXmlElement.Read (BinaryXmlReader reader)
644
id = reader.ReadStringValue ("id");
645
ns = reader.ReadStringValue ("ns");
646
isroot = reader.ReadBooleanValue ("isroot");
647
name = reader.ReadStringValue ("name");
648
version = reader.ReadStringValue ("version");
649
compatVersion = reader.ReadStringValue ("compatVersion");
650
hasUserId = reader.ReadBooleanValue ("hasUserId");
651
author = reader.ReadStringValue ("author");
652
url = reader.ReadStringValue ("url");
653
copyright = reader.ReadStringValue ("copyright");
654
description = reader.ReadStringValue ("description");
655
category = reader.ReadStringValue ("category");
656
basePath = reader.ReadStringValue ("basePath");
657
sourceAddinFile = reader.ReadStringValue ("sourceAddinFile");
658
defaultEnabled = reader.ReadBooleanValue ("defaultEnabled");
659
mainModule = (ModuleDescription) reader.ReadValue ("MainModule");
660
optionalModules = (ModuleCollection) reader.ReadValue ("OptionalModules", new ModuleCollection (this));
661
nodeSets = (ExtensionNodeSetCollection) reader.ReadValue ("NodeSets", new ExtensionNodeSetCollection (this));
662
extensionPoints = (ExtensionPointCollection) reader.ReadValue ("ExtensionPoints", new ExtensionPointCollection (this));
663
conditionTypes = (ConditionTypeDescriptionCollection) reader.ReadValue ("ConditionTypes", new ConditionTypeDescriptionCollection (this));
665
if (mainModule != null)
666
mainModule.SetParent (this);