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.
31
using System.Collections;
34
using System.Reflection;
35
using System.Collections.Specialized;
37
using System.ComponentModel;
39
using Mono.Addins.Description;
41
namespace Mono.Addins.Database
43
class AddinScanner: MarshalByRefObject
45
AddinDatabase database;
47
public AddinScanner (AddinDatabase database)
49
this.database = database;
52
public void ScanFolder (IProgressStatus monitor, string path, AddinScanResult scanResult)
54
path = Util.GetFullPath (path);
56
// Avoid folders including each other
57
if (!scanResult.VisitFolder (path))
60
if (monitor.LogLevel > 1 && !scanResult.LocateAssembliesOnly)
61
monitor.Log ("Checking: " + path);
63
AddinScanFolderInfo folderInfo;
64
if (!database.GetFolderInfoForPath (monitor, path, out folderInfo)) {
65
// folderInfo file was corrupt.
66
// Just in case, we are going to regenerate all relation data.
67
if (!Directory.Exists (path))
68
scanResult.RegenerateRelationData = true;
70
if (folderInfo == null && !Directory.Exists (path))
74
if (folderInfo == null)
75
folderInfo = new AddinScanFolderInfo (path);
77
if (Directory.Exists (path))
79
string[] files = Directory.GetFiles (path);
81
// First of all, look for .addin files. Addin files must be processed before
82
// assemblies, because they may add files to the ignore list (i.e., assemblies
83
// included in .addin files won't be scanned twice).
84
foreach (string file in files) {
85
if (file.EndsWith (".addin.xml") || file.EndsWith (".addin")) {
86
RegisterFileToScan (monitor, file, scanResult, folderInfo);
90
foreach (string file in files) {
91
switch (Path.GetExtension (file)) {
94
RegisterFileToScan (monitor, file, scanResult, folderInfo);
95
scanResult.AddAssemblyLocation (file);
98
ScanAddinsFile (monitor, file, scanResult);
103
else if (!scanResult.LocateAssembliesOnly) {
104
// The folder has been deleted. All add-ins defined in that folder should also be deleted.
105
scanResult.RegenerateRelationData = true;
106
scanResult.ChangesFound = true;
107
if (scanResult.CheckOnly)
109
database.DeleteFolderInfo (monitor, folderInfo);
112
if (scanResult.LocateAssembliesOnly)
115
// Look for deleted add-ins.
117
UpdateDeletedAddins (monitor, folderInfo, scanResult);
120
public void UpdateDeletedAddins (IProgressStatus monitor, AddinScanFolderInfo folderInfo, AddinScanResult scanResult)
122
ArrayList missing = folderInfo.GetMissingAddins ();
123
if (missing.Count > 0) {
124
if (Directory.Exists (folderInfo.Folder))
125
scanResult.ModifiedFolderInfos.Add (folderInfo);
126
scanResult.ChangesFound = true;
127
if (scanResult.CheckOnly)
130
foreach (AddinFileInfo info in missing) {
132
database.UninstallRootAddin (monitor, info.AddinId, info.File, scanResult);
134
database.UninstallAddin (monitor, info.AddinId, scanResult);
139
void RegisterFileToScan (IProgressStatus monitor, string file, AddinScanResult scanResult, AddinScanFolderInfo folderInfo)
141
if (scanResult.LocateAssembliesOnly)
144
AddinFileInfo finfo = folderInfo.GetAddinFileInfo (file);
147
if (finfo != null && File.GetLastWriteTime (file) == finfo.LastScan && !scanResult.RegenerateAllData) {
148
if (finfo.ScanError) {
149
// Always schedule the file for scan if there was an error in a previous scan.
150
// However, don't set ChangesFound=true, in this way if there isn't any other
151
// change in the registry, the file won't be scanned again.
152
scanResult.AddFileToScan (file, folderInfo);
156
if (finfo.AddinId == null || finfo.AddinId.Length == 0)
159
if (database.AddinDescriptionExists (finfo.AddinId))
162
if (database.HostDescriptionExists (finfo.AddinId, file))
167
scanResult.ChangesFound = true;
169
if (!scanResult.CheckOnly && !added)
170
scanResult.AddFileToScan (file, folderInfo);
173
public void ScanFile (IProgressStatus monitor, string file, AddinScanFolderInfo folderInfo, AddinScanResult scanResult)
175
if (scanResult.IgnoreFile (file)) {
176
// The file must be ignored. Maybe it caused a crash in a previous scan, or it
177
// might be included by a .addin file (in which case it will be scanned when processing
179
folderInfo.SetLastScanTime (file, null, false, File.GetLastWriteTime (file), true);
183
if (monitor.LogLevel > 1)
184
monitor.Log ("Scanning file: " + file);
186
// Log the file to be scanned, so in case of a process crash the main process
187
// will know what crashed
188
monitor.Log ("plog:scan:" + file);
190
string scannedAddinId = null;
191
bool scannedIsRoot = false;
192
bool scanSuccessful = false;
195
string ext = Path.GetExtension (file);
196
AddinDescription config = null;
198
if (ext == ".dll" || ext == ".exe")
199
scanSuccessful = ScanAssembly (monitor, file, scanResult, out config);
201
scanSuccessful = ScanConfigAssemblies (monitor, file, scanResult, out config);
203
if (config != null) {
205
AddinFileInfo fi = folderInfo.GetAddinFileInfo (file);
207
// If version is not specified, make up one
208
if (config.Version.Length == 0) {
209
config.Version = "0.0.0.0";
212
if (config.LocalId.Length == 0) {
213
// Generate an internal id for this add-in
214
config.LocalId = database.GetUniqueAddinId (file, (fi != null ? fi.AddinId : null), config.Namespace, config.Version);
215
config.HasUserId = false;
218
// Check errors in the description
219
StringCollection errors = config.Verify ();
221
if (database.IsGlobalRegistry && config.AddinId.IndexOf ('.') == -1) {
222
errors.Add ("Add-ins registered in the global registry must have a namespace.");
225
if (errors.Count > 0) {
226
scanSuccessful = false;
227
monitor.ReportError ("Errors found in add-in '" + file + ":", null);
228
foreach (string err in errors)
229
monitor.ReportError (err, null);
232
// Make sure all extensions sets are initialized with the correct add-in id
234
config.SetExtensionsAddinId (config.AddinId);
236
scanResult.ChangesFound = true;
238
// If the add-in already existed, try to reuse the relation data it had.
239
// Also, the dependencies of the old add-in need to be re-analized
241
AddinDescription existingDescription = null;
245
res = database.GetHostDescription (monitor, config.AddinId, config.AddinFile, out existingDescription);
247
res = database.GetAddinDescription (monitor, config.AddinId, out existingDescription);
249
// If we can't get information about the old assembly, just regenerate all relation data
251
scanResult.RegenerateRelationData = true;
253
string replaceFileName = null;
255
if (existingDescription != null) {
256
// Reuse old relation data
257
config.MergeExternalData (existingDescription);
258
Util.AddDependencies (existingDescription, scanResult);
259
replaceFileName = existingDescription.FileName;
262
// If the scanned file results in an add-in version different from the one obtained from
263
// previous scans, the old add-in needs to be uninstalled.
264
if (fi != null && fi.AddinId != null && fi.AddinId != config.AddinId) {
266
database.UninstallRootAddin (monitor, fi.AddinId, file, scanResult);
268
database.UninstallAddin (monitor, fi.AddinId, scanResult);
270
// If the add-in version has changed, regenerate everything again since old data can't be reused
271
if (Addin.GetIdName (fi.AddinId) == Addin.GetIdName (config.AddinId))
272
scanResult.RegenerateRelationData = true;
275
// If a description could be generated, save it now (if the scan was successful)
276
if (scanSuccessful) {
277
if (database.SaveDescription (monitor, config, replaceFileName)) {
278
// The new dependencies also have to be updated
279
Util.AddDependencies (config, scanResult);
280
scanResult.AddAddinToUpdateRelations (config.AddinId);
281
scannedAddinId = config.AddinId;
282
scannedIsRoot = config.IsRoot;
288
catch (Exception ex) {
289
monitor.ReportError ("Unexpected error while scanning file: " + file, ex);
292
folderInfo.SetLastScanTime (file, scannedAddinId, scannedIsRoot, File.GetLastWriteTime (file), !scanSuccessful);
293
monitor.Log ("plog:endscan");
297
public AddinDescription ScanSingleFile (IProgressStatus monitor, string file, AddinScanResult scanResult)
299
AddinDescription config = null;
301
if (monitor.LogLevel > 1)
302
monitor.Log ("Scanning file: " + file);
304
monitor.Log ("plog:scan:" + file);
307
string ext = Path.GetExtension (file);
310
if (ext == ".dll" || ext == ".exe")
311
scanSuccessful = ScanAssembly (monitor, file, scanResult, out config);
313
scanSuccessful = ScanConfigAssemblies (monitor, file, scanResult, out config);
315
if (scanSuccessful && config != null) {
317
if (config.Version.Length == 0)
318
config.Version = "0.0.0.0";
320
if (config.LocalId.Length == 0) {
321
// Generate an internal id for this add-in
322
config.LocalId = database.GetUniqueAddinId (file, "", config.Namespace, config.Version);
326
catch (Exception ex) {
327
monitor.ReportError ("Unexpected error while scanning file: " + file, ex);
329
monitor.Log ("plog:endscan");
334
public void ScanAddinsFile (IProgressStatus monitor, string file, AddinScanResult scanResult)
336
XmlTextReader r = null;
337
StringCollection directories = new StringCollection ();
338
StringCollection directoriesWithSubdirs = new StringCollection ();
340
r = new XmlTextReader (new StreamReader (file));
342
if (r.IsEmptyElement)
344
r.ReadStartElement ();
346
while (r.NodeType != XmlNodeType.EndElement) {
347
if (r.NodeType == XmlNodeType.Element && r.LocalName == "Directory") {
348
string subs = r.GetAttribute ("include-subdirs");
349
string path = r.ReadElementString ().Trim ();
350
if (path.Length > 0) {
352
directoriesWithSubdirs.Add (path);
354
directories.Add (path);
357
else if (r.NodeType == XmlNodeType.Element && r.LocalName == "GacAssembly") {
358
string aname = r.ReadElementString ().Trim ();
359
if (aname.Length > 0) {
360
aname = Util.GetGacPath (aname);
362
directories.Add (aname);
369
} catch (Exception ex) {
370
monitor.ReportError ("Could not process addins file: " + file, ex);
376
foreach (string d in directories) {
378
if (!Path.IsPathRooted (dir))
379
dir = Path.Combine (Path.GetDirectoryName (file), dir);
380
ScanFolder (monitor, dir, scanResult);
382
foreach (string d in directoriesWithSubdirs) {
384
if (!Path.IsPathRooted (dir))
385
dir = Path.Combine (Path.GetDirectoryName (file), dir);
386
ScanFolderRec (monitor, dir, scanResult);
390
public void ScanFolderRec (IProgressStatus monitor, string dir, AddinScanResult scanResult)
392
ScanFolder (monitor, dir, scanResult);
394
if (!Directory.Exists (dir))
397
foreach (string sd in Directory.GetDirectories (dir))
398
ScanFolderRec (monitor, sd, scanResult);
401
bool ScanConfigAssemblies (IProgressStatus monitor, string filePath, AddinScanResult scanResult, out AddinDescription config)
406
string basePath = Path.GetDirectoryName (filePath);
408
config = AddinDescription.Read (filePath);
409
config.BasePath = basePath;
410
config.AddinFile = filePath;
412
return ScanDescription (monitor, config, null, scanResult);
414
catch (Exception ex) {
415
// Something went wrong while scanning the assembly. We'll ignore it for now.
416
monitor.ReportError ("There was an error while scanning add-in: " + filePath, ex);
421
bool ScanAssembly (IProgressStatus monitor, string filePath, AddinScanResult scanResult, out AddinDescription config)
426
Assembly asm = Util.LoadAssemblyForReflection (filePath);
428
// Get the config file from the resources, if there is one
430
string configFile = null;
431
foreach (string res in asm.GetManifestResourceNames ()) {
432
if (res.EndsWith (".addin") || res.EndsWith (".addin.xml")) {
438
if (configFile != null) {
439
using (Stream s = asm.GetManifestResourceStream (configFile)) {
440
string asmFile = new Uri (asm.CodeBase).LocalPath;
441
config = AddinDescription.Read (s, Path.GetDirectoryName (asmFile));
445
// On this case, only scan the assembly if it has the Addin attribute.
446
AddinAttribute att = (AddinAttribute) Attribute.GetCustomAttribute (asm, typeof(AddinAttribute), false);
451
config = new AddinDescription ();
454
config.BasePath = Path.GetDirectoryName (filePath);
455
config.AddinFile = filePath;
457
string rasmFile = Path.GetFileName (filePath);
458
if (!config.MainModule.Assemblies.Contains (rasmFile))
459
config.MainModule.Assemblies.Add (rasmFile);
461
return ScanDescription (monitor, config, asm, scanResult);
463
catch (Exception ex) {
464
// Something went wrong while scanning the assembly. We'll ignore it for now.
465
monitor.ReportError ("There was an error while scanning assembly: " + filePath, ex);
470
bool ScanDescription (IProgressStatus monitor, AddinDescription config, Assembly rootAssembly, AddinScanResult scanResult)
472
// First of all scan the main module
474
ArrayList assemblies = new ArrayList ();
475
ArrayList asmFiles = new ArrayList ();
476
ArrayList hostExtensionClasses = new ArrayList ();
479
// Add all data files to the ignore file list. It avoids scanning assemblies
480
// which are included as 'data' in an add-in.
481
foreach (string df in config.AllFiles) {
482
string file = Path.Combine (config.BasePath, df);
483
scanResult.AddFileToIgnore (Util.GetFullPath (file));
486
foreach (string s in config.MainModule.Assemblies) {
487
string asmFile = Path.Combine (config.BasePath, s);
488
asmFiles.Add (asmFile);
489
Assembly asm = Util.LoadAssemblyForReflection (asmFile);
490
assemblies.Add (asm);
491
scanResult.AddFileToIgnore (Util.GetFullPath (asmFile));
494
foreach (Assembly asm in assemblies)
495
ScanAssemblyAddinHeaders (config, asm, scanResult);
497
// The add-in id and version must be already assigned at this point
499
// Clean host data from the index. New data will be added.
500
if (scanResult.HostIndex != null)
501
scanResult.HostIndex.RemoveHostData (config.AddinId, config.AddinFile);
503
foreach (Assembly asm in assemblies)
504
ScanAssemblyContents (config, asm, hostExtensionClasses, scanResult);
506
if (config.IsRoot && scanResult.HostIndex != null) {
507
// If the add-in is a root, register its assemblies
508
foreach (string asmFile in asmFiles)
509
scanResult.HostIndex.RegisterAssembly (asmFile, config.AddinId, config.AddinFile);
512
} catch (Exception ex) {
513
if (monitor.LogLevel > 1)
514
monitor.Log ("Could not load some add-in assemblies: " + ex.Message);
515
scanResult.AddFileToWithFailure (config.AddinFile);
519
foreach (Type t in hostExtensionClasses) {
520
RegisterHostTypeNode (config, t, assemblies);
523
// Extension node types may have child nodes declared as attributes. Find them.
525
Hashtable internalNodeSets = new Hashtable ();
527
foreach (ExtensionNodeSet eset in config.ExtensionNodeSets)
528
ScanNodeSet (config, eset, assemblies, internalNodeSets);
530
foreach (ExtensionPoint ep in config.ExtensionPoints) {
531
ScanNodeSet (config, ep.NodeSet, assemblies, internalNodeSets);
534
// Now scan all modules
536
if (!config.IsRoot) {
537
foreach (ModuleDescription mod in config.OptionalModules) {
541
foreach (string s in mod.Assemblies) {
542
string asmFile = Path.Combine (config.BasePath, s);
543
asmFiles.Add (asmFile);
544
Assembly asm = Util.LoadAssemblyForReflection (asmFile);
545
assemblies.Add (asm);
546
scanResult.AddFileToIgnore (Util.GetFullPath (asmFile));
548
foreach (Assembly asm in assemblies)
549
ScanAssemblyContents (config, asm, null, scanResult);
551
if (config.IsRoot && scanResult.HostIndex != null) {
552
// If the add-in is a root, register its assemblies
553
foreach (string asmFile in asmFiles)
554
scanResult.HostIndex.RegisterAssembly (asmFile, config.AddinId, config.AddinFile);
557
} catch (Exception ex) {
558
if (monitor.LogLevel > 1)
559
monitor.Log ("Could not load some add-in assemblies: " + ex.Message);
560
scanResult.AddFileToWithFailure (config.AddinFile);
568
void ScanAssemblyAddinHeaders (AddinDescription config, Assembly asm, AddinScanResult scanResult)
570
// Get basic add-in information
571
AddinAttribute att = (AddinAttribute) Attribute.GetCustomAttribute (asm, typeof(AddinAttribute), false);
573
if (att.Id.Length > 0)
574
config.LocalId = att.Id;
575
if (att.Version.Length > 0)
576
config.Version = att.Version;
577
if (att.Namespace.Length > 0)
578
config.Namespace = att.Namespace;
579
if (att.Category.Length > 0)
580
config.Category = att.Category;
581
config.IsRoot = att is AddinRootAttribute;
585
void ScanAssemblyContents (AddinDescription config, Assembly asm, ArrayList hostExtensionClasses, AddinScanResult scanResult)
589
object[] deps = asm.GetCustomAttributes (typeof(AddinDependencyAttribute), false);
590
foreach (AddinDependencyAttribute dep in deps) {
591
AddinDependency adep = new AddinDependency ();
592
adep.AddinId = dep.Id;
593
adep.Version = dep.Version;
594
config.MainModule.Dependencies.Add (adep);
597
// Get extension points
599
object[] extPoints = asm.GetCustomAttributes (typeof(ExtensionPointAttribute), false);
600
foreach (ExtensionPointAttribute ext in extPoints) {
601
ExtensionPoint ep = config.AddExtensionPoint (ext.Path);
602
ep.Description = ext.Description;
604
ep.AddExtensionNode (ext.NodeName, ext.NodeType.FullName);
607
foreach (Type t in asm.GetTypes ()) {
609
if (Attribute.IsDefined (t, typeof(ExtensionAttribute))) {
610
foreach (ExtensionAttribute eatt in t.GetCustomAttributes (typeof(ExtensionAttribute), false)) {
614
if (eatt.Path.Length == 0) {
616
// The extension point must be one of the defined by the assembly
617
// Look for it later, when the assembly has been fully scanned.
618
hostExtensionClasses.Add (t);
622
path = GetBaseTypeNameList (t);
624
// The type does not implement any interface and has no superclass.
625
// Will be reported later as an error.
626
path = "$" + t.FullName;
632
nodeName = eatt.NodeName;
635
ExtensionNodeDescription elem = config.MainModule.AddExtensionNode (path, nodeName);
636
if (eatt.Id.Length > 0) {
637
elem.SetAttribute ("id", eatt.Id);
638
elem.SetAttribute ("type", t.FullName);
640
elem.SetAttribute ("id", t.FullName);
642
if (eatt.InsertAfter.Length > 0)
643
elem.SetAttribute ("insertafter", eatt.InsertAfter);
644
if (eatt.InsertBefore.Length > 0)
645
elem.SetAttribute ("insertbefore", eatt.InsertAfter);
648
else if (Attribute.IsDefined (t, typeof(TypeExtensionPointAttribute))) {
649
foreach (TypeExtensionPointAttribute epa in t.GetCustomAttributes (typeof(TypeExtensionPointAttribute), false)) {
652
ExtensionNodeType nt = new ExtensionNodeType ();
654
if (epa.Path.Length > 0) {
655
ep = config.AddExtensionPoint (epa.Path);
658
ep = config.AddExtensionPoint (GetDefaultTypeExtensionPath (config, t));
659
nt.ObjectTypeName = t.FullName;
661
nt.Id = epa.NodeName;
662
nt.TypeName = epa.NodeType.FullName;
663
ep.NodeSet.NodeTypes.Add (nt);
664
ep.Description = epa.Description;
666
ep.RootAddin = config.AddinId;
667
ep.SetExtensionsAddinId (config.AddinId);
673
void ScanNodeSet (AddinDescription config, ExtensionNodeSet nset, ArrayList assemblies, Hashtable internalNodeSets)
675
foreach (ExtensionNodeType nt in nset.NodeTypes)
676
ScanNodeType (config, nt, assemblies, internalNodeSets);
679
void ScanNodeType (AddinDescription config, ExtensionNodeType nt, ArrayList assemblies, Hashtable internalNodeSets)
681
if (nt.TypeName.Length == 0)
682
nt.TypeName = "Mono.Addins.TypeExtensionNode";
684
Type ntype = FindAddinType (nt.TypeName, assemblies);
688
// Add type information declared with attributes in the code
689
ExtensionNodeAttribute nodeAtt = (ExtensionNodeAttribute) Attribute.GetCustomAttribute (ntype, typeof(ExtensionNodeAttribute), true);
690
if (nodeAtt != null) {
691
if (nt.Id.Length == 0 && nodeAtt.NodeName.Length > 0)
692
nt.Id = nodeAtt.NodeName;
693
if (nt.Description.Length == 0 && nodeAtt.Description.Length > 0)
694
nt.Description = nodeAtt.Description;
696
// Use the node type name as default name
697
if (nt.Id.Length == 0)
701
// Add information about attributes
702
object[] fieldAtts = ntype.GetCustomAttributes (typeof(NodeAttributeAttribute), true);
703
foreach (NodeAttributeAttribute fatt in fieldAtts) {
704
NodeTypeAttribute natt = new NodeTypeAttribute ();
705
natt.Name = fatt.Name;
706
natt.Required = fatt.Required;
707
if (fatt.Type != null)
708
natt.Type = fatt.Type.FullName;
709
if (fatt.Description.Length > 0)
710
natt.Description = fatt.Description;
711
nt.Attributes.Add (natt);
714
// Check if the type has NodeAttribute attributes applied to fields.
715
foreach (FieldInfo field in ntype.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
716
NodeAttributeAttribute fatt = (NodeAttributeAttribute) Attribute.GetCustomAttribute (field, typeof(NodeAttributeAttribute));
718
NodeTypeAttribute natt = new NodeTypeAttribute ();
719
if (fatt.Name.Length > 0)
720
natt.Name = fatt.Name;
722
natt.Name = field.Name;
723
if (fatt.Description.Length > 0)
724
natt.Description = fatt.Description;
725
natt.Type = field.FieldType.FullName;
726
natt.Required = fatt.Required;
727
nt.Attributes.Add (natt);
731
// Check if the extension type allows children by looking for [ExtensionNodeChild] attributes.
732
// First of all, look in the internalNodeSets hashtable, which is being used as cache
734
string childSet = (string) internalNodeSets [nt.TypeName];
736
if (childSet == null) {
737
object[] ats = ntype.GetCustomAttributes (typeof(ExtensionNodeChildAttribute), true);
738
if (ats.Length > 0) {
739
// Create a new node set for this type. It is necessary to create a new node set
740
// instead of just adding child ExtensionNodeType objects to the this node type
741
// because child types references can be recursive.
742
ExtensionNodeSet internalSet = new ExtensionNodeSet ();
743
internalSet.Id = ntype.Name + "_" + Guid.NewGuid().ToString ();
744
foreach (ExtensionNodeChildAttribute at in ats) {
745
ExtensionNodeType internalType = new ExtensionNodeType ();
746
internalType.Id = at.NodeName;
747
internalType.TypeName = at.ExtensionNodeType.FullName;
748
internalSet.NodeTypes.Add (internalType);
750
config.ExtensionNodeSets.Add (internalSet);
751
nt.NodeSets.Add (internalSet.Id);
753
// Register the new set in a hashtable, to allow recursive references to the
754
// same internal set.
755
internalNodeSets [nt.TypeName] = internalSet.Id;
756
internalNodeSets [ntype.AssemblyQualifiedName] = internalSet.Id;
757
ScanNodeSet (config, internalSet, assemblies, internalNodeSets);
761
if (childSet.Length == 0) {
762
// The extension type does not declare children.
765
// The extension type can have children. The allowed children are
766
// defined in this extension set.
767
nt.NodeSets.Add (childSet);
771
ScanNodeSet (config, nt, assemblies, internalNodeSets);
774
string GetBaseTypeNameList (Type type)
776
StringBuilder sb = new StringBuilder ("$");
777
Type btype = type.BaseType;
778
while (btype != typeof(object)) {
779
sb.Append (btype.FullName).Append (',');
780
btype = btype.BaseType;
782
foreach (Type iterf in type.GetInterfaces ()) {
783
sb.Append (iterf.FullName).Append (',');
786
sb.Remove (sb.Length - 1, 1);
787
return sb.ToString ();
790
void RegisterHostTypeNode (AddinDescription config, Type t, ArrayList assemblies)
792
foreach (ExtensionAttribute eatt in t.GetCustomAttributes (typeof(ExtensionAttribute), false)) {
793
if (eatt.Path.Length > 0)
796
foreach (ExtensionPoint ep in config.ExtensionPoints) {
797
foreach (ExtensionNodeType nt in ep.NodeSet.NodeTypes) {
798
if (nt.ObjectTypeName.Length == 0)
800
Type etype = FindAddinType (nt.ObjectTypeName, assemblies);
801
if (etype != null && etype.IsAssignableFrom (t)) {
802
RegisterTypeNode (config, eatt, ep.Path, nt.Id, t);
810
Type FindAddinType (string typeName, ArrayList assemblies)
812
// Look in the current assembly
813
Type etype = Type.GetType (typeName, false);
817
// Look in referenced assemblies
818
foreach (Assembly asm in assemblies) {
819
etype = asm.GetType (typeName);
824
Hashtable visited = new Hashtable ();
826
// Look in indirectly referenced assemblies
827
foreach (Assembly asm in assemblies) {
828
foreach (AssemblyName aref in asm.GetReferencedAssemblies ()) {
829
if (visited.Contains (aref))
831
visited.Add (aref, aref);
832
Assembly rasm = Assembly.Load (aref);
833
etype = rasm.GetType (typeName);
841
void RegisterTypeNode (AddinDescription config, ExtensionAttribute eatt, string path, string nodeName, Type t)
843
ExtensionNodeDescription elem = config.MainModule.AddExtensionNode (path, nodeName);
844
if (eatt.Id.Length > 0) {
845
elem.SetAttribute ("id", eatt.Id);
846
elem.SetAttribute ("type", t.FullName);
848
elem.SetAttribute ("id", t.FullName);
850
if (eatt.InsertAfter.Length > 0)
851
elem.SetAttribute ("insertafter", eatt.InsertAfter);
852
if (eatt.InsertBefore.Length > 0)
853
elem.SetAttribute ("insertbefore", eatt.InsertAfter);
856
internal string GetDefaultTypeExtensionPath (AddinDescription desc, Type type)
858
return "/" + Addin.GetIdName (desc.AddinId) + "/TypeExtensions/" + type.FullName;