diff options
author | Lluis Sanchez Gual <lluis@novell.com> | 2011-01-26 19:21:16 +0300 |
---|---|---|
committer | Lluis Sanchez Gual <lluis@novell.com> | 2011-01-26 20:11:53 +0300 |
commit | 315eba6f27e6666bd15c00fb6abd81aa336cffa4 (patch) | |
tree | 320774110b59e0cb75b2cbb6becd68069bc91008 /extras/MonoDevelop.AddinAuthoring | |
parent | bcc3a29aeec24e8d24cc15d8a4797e027c3272f0 (diff) |
Initial implementation of DomAssemblyReflector.
Diffstat (limited to 'extras/MonoDevelop.AddinAuthoring')
3 files changed, 465 insertions, 21 deletions
diff --git a/extras/MonoDevelop.AddinAuthoring/MonoDevelop.AddinAuthoring.csproj b/extras/MonoDevelop.AddinAuthoring/MonoDevelop.AddinAuthoring.csproj index 137e7df7b2..116f9c78ba 100644 --- a/extras/MonoDevelop.AddinAuthoring/MonoDevelop.AddinAuthoring.csproj +++ b/extras/MonoDevelop.AddinAuthoring/MonoDevelop.AddinAuthoring.csproj @@ -93,6 +93,7 @@ <Compile Include="MonoDevelop.AddinAuthoring\ExtensionDomain.cs" /> <Compile Include="MonoDevelop.AddinAuthoring.NodeBuilders\ReferenceNodeBuilder.cs" /> <Compile Include="MonoDevelop.AddinAuthoring\TypeCellEditor.cs" /> + <Compile Include="MonoDevelop.AddinAuthoring\DomAssemblyReflector.cs" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="gtk-gui\gui.stetic"> @@ -132,21 +133,6 @@ <Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" /> <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" /> <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" /> - <Reference Include="Mono.Addins, Version=0.4.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756"> - <SpecificVersion>False</SpecificVersion> - </Reference> - <Reference Include="Mono.Addins.Setup, Version=0.4.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756"> - <SpecificVersion>False</SpecificVersion> - </Reference> - <Reference Include="MonoDevelop.Core.Gui, Version=2.1.0.0, Culture=neutral"> - <Package>monodevelop</Package> - </Reference> - <Reference Include="MonoDevelop.Projects, Version=2.1.0.0, Culture=neutral"> - <Package>monodevelop</Package> - </Reference> - <Reference Include="MonoDevelop.Projects.Gui, Version=2.1.0.0, Culture=neutral"> - <Package>monodevelop</Package> - </Reference> <Reference Include="MonoDevelop.Ide, Version=2.1.0.0, Culture=neutral"> <Package>monodevelop</Package> </Reference> @@ -200,9 +186,6 @@ <Reference Include="MonoDevelop.Core, Version=2.1.0.0, Culture=neutral"> <Package>monodevelop</Package> </Reference> - <Reference Include="MonoDevelop.Components, Version=2.1.0.0, Culture=neutral"> - <Package>monodevelop</Package> - </Reference> <Reference Include="MonoDevelop.AspNet, Version=2.1.0.0, Culture=neutral"> <Package>monodevelop-core-addins</Package> </Reference> @@ -213,7 +196,6 @@ <Package>monodevelop-core-addins</Package> </Reference> <Reference Include="System.Core"> - <RequiredTargetFramework>3.5</RequiredTargetFramework> </Reference> <Reference Include="MonoDevelop.Refactoring, Version=2.1.0.0, Culture=neutral"> <Package>monodevelop-core-addins</Package> @@ -224,10 +206,15 @@ <Reference Include="MonoDevelop.Debugger.Soft, Version=2.2.0.0, Culture=neutral"> <Package>monodevelop-core-addins</Package> </Reference> - <Reference Include="Mono.Debugging, Version=0.0.0.0, Culture=neutral, PublicKeyToken=43ec5927c50c544e"> + <Reference Include="Mono.Addins, Version=0.5.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" /> + <Reference Include="Mono.Addins.Setup, Version=0.5.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" /> + <Reference Include="Mono.Debugging, Version=0.0.0.0, Culture=neutral, PublicKeyToken=5e9ce85b0923c84f"> <Package>monodevelop</Package> </Reference> - <Reference Include="Mono.Debugging.Soft, Version=0.0.0.0, Culture=neutral, PublicKeyToken=1968fe265398bfbb"> + <Reference Include="Mono.Debugging.Soft, Version=0.0.0.0, Culture=neutral, PublicKeyToken=5e9ce85b0923c84f"> + <Package>monodevelop-core-addins</Package> + </Reference> + <Reference Include="Mono.Debugger.Soft, Version=0.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756"> <Package>monodevelop-core-addins</Package> </Reference> </ItemGroup> diff --git a/extras/MonoDevelop.AddinAuthoring/MonoDevelop.AddinAuthoring.make b/extras/MonoDevelop.AddinAuthoring/MonoDevelop.AddinAuthoring.make index 6668d0dbb9..ae61f121a6 100644 --- a/extras/MonoDevelop.AddinAuthoring/MonoDevelop.AddinAuthoring.make +++ b/extras/MonoDevelop.AddinAuthoring/MonoDevelop.AddinAuthoring.make @@ -80,6 +80,7 @@ FILES = \ MonoDevelop.AddinAuthoring/AddinProjectReference.cs \ MonoDevelop.AddinAuthoring/CellRendererExtension.cs \ MonoDevelop.AddinAuthoring/Commands.cs \ + MonoDevelop.AddinAuthoring/DomAssemblyReflector.cs \ MonoDevelop.AddinAuthoring/ExtensionDomain.cs \ MonoDevelop.AddinAuthoring/ExtensionEditorWidget.cs \ MonoDevelop.AddinAuthoring/ExtensionPointsEditorWidget.cs \ diff --git a/extras/MonoDevelop.AddinAuthoring/MonoDevelop.AddinAuthoring/DomAssemblyReflector.cs b/extras/MonoDevelop.AddinAuthoring/MonoDevelop.AddinAuthoring/DomAssemblyReflector.cs new file mode 100644 index 0000000000..7385fbc588 --- /dev/null +++ b/extras/MonoDevelop.AddinAuthoring/MonoDevelop.AddinAuthoring/DomAssemblyReflector.cs @@ -0,0 +1,456 @@ +// +// DomAssemblyReflector.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Linq; +using Mono.Addins.Database; +using System.Collections; +using System.Collections.Generic; +using MonoDevelop.Projects; +using MonoDevelop.Ide; +using MonoDevelop.Projects.Dom.Parser; +using System.IO; +using MonoDevelop.Projects.Dom; +using System.CodeDom; +using System.Reflection; +using MA = Mono.Addins.Database; +using Mono.Addins; + +namespace MonoDevelop.AddinAuthoring +{ + public class DomAssemblyReflector: IAssemblyReflector + { + Solution solution; + + public DomAssemblyReflector (Solution solution) + { + this.solution = solution; + } + + #region IAssemblyReflector implementation + public void Initialize (IAssemblyLocator locator) + { + } + + public object GetCustomAttribute (object obj, Type type, bool inherit) + { + foreach (object att in GetCustomAttributes (obj, type, inherit)) + if (type.IsInstanceOfType (att)) + return att; + return null; + } + + IEnumerable<IAttribute> GetAttributes (object ob) + { + if (ob is IMember) + return ((IMember)ob).Attributes; + else if (ob is IParameter) + return ((IParameter)ob).Attributes; + else if (ob is ProjectDom) + return new IAttribute [0]; + else + throw new NotSupportedException (); + } + + public object[] GetCustomAttributes (object obj, Type type, bool inherit) + { + ArrayList atts = new ArrayList (); + foreach (IAttribute att in GetAttributes (obj)) { + object catt = ConvertAttribute (att, type); + if (catt != null) + atts.Add (catt); + } + if (inherit && (obj is IType)) { + IType td = (IType) obj; + if (td.BaseType != null && td.BaseType.FullName != "System.Object") { + IType bt = td.SourceProjectDom.GetType (td.BaseType); + if (bt != null) + atts.AddRange (GetCustomAttributes (bt, type, true)); + } + } + return atts.ToArray (); + } + + + object ConvertAttribute (IAttribute att, Type expectedType) + { + Type attype = typeof(IAssemblyReflector).Assembly.GetType (att.AttributeType.FullName); + + if (attype == null || !expectedType.IsAssignableFrom (attype)) + return null; + + object ob; + + var args = att.PositionalArguments; + if (args.Count > 0) { + object[] cargs = new object [args.Count]; + ArrayList typeParameters = null; + + // Constructor parameters of type System.Type can't be set because types from the assembly + // can't be loaded. The parameter value will be set later using a type name property. + for (int n=0; n<cargs.Length; n++) { + var res = Evaluate (args [n]); + cargs [n] = res.Value; + if (res.Type == "System.Type") { + if (typeParameters == null) + typeParameters = new ArrayList (); + cargs [n] = typeof(object); + typeParameters.Add (n); + } + } + ob = Activator.CreateInstance (attype, cargs); + + // If there are arguments of type System.Type, set them using the property + if (typeParameters != null) { + Type[] ptypes = new Type [cargs.Length]; + for (int n=0; n<cargs.Length; n++) { + ptypes [n] = cargs [n].GetType (); + } + ConstructorInfo ci = attype.GetConstructor (ptypes); + ParameterInfo[] ciParams = ci.GetParameters (); + + for (int n=0; n<typeParameters.Count; n++) { + int ip = (int) typeParameters [n]; + string propName = ciParams[ip].Name; + propName = char.ToUpper (propName [0]) + propName.Substring (1) + "Name"; + PropertyInfo pi = attype.GetProperty (propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + if (pi == null) + throw new InvalidOperationException ("Property '" + propName + "' not found in type '" + attype + "'."); + + pi.SetValue (ob, Evaluate (args [ip]).Value, null); + } + } + } else { + ob = Activator.CreateInstance (attype); + } + + foreach (var namedArgument in att.NamedArguments) { + string pname = namedArgument.Key; + PropertyInfo prop = attype.GetProperty (pname); + var res = Evaluate (namedArgument.Value); + if (prop != null) { + if (prop.PropertyType == typeof(System.Type)) { + // We can't load the type. We have to use the typeName property instead. + pname += "Name"; + prop = attype.GetProperty (pname, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + if (prop == null) + throw new InvalidOperationException ("Property '" + pname + "' not found in type '" + attype + "'."); + } + prop.SetValue (ob, res.Value, null); + } + } + return ob; + } + + public List<MA.CustomAttribute> GetRawCustomAttributes (object obj, Type type, bool inherit) + { + ProjectDom dom; + if (obj is ProjectDom) + dom = (ProjectDom)obj; + else if (obj is IType) + dom = ((IType)obj).SourceProjectDom; + else if (obj is IMember) + dom = ((IMember)obj).DeclaringType.SourceProjectDom; + else if (obj is IParameter) + dom = ((IParameter)obj).DeclaringMember.DeclaringType.SourceProjectDom; + else + throw new NotSupportedException (); + + List<MA.CustomAttribute> atts = new List<MA.CustomAttribute> (); + + foreach (IAttribute att in GetAttributes (obj)) { + MA.CustomAttribute catt = ConvertToRawAttribute (dom, att, type.FullName); + if (catt != null) + atts.Add (catt); + } + if (inherit && (obj is IType)) { + IType td = (IType) obj; + if (td.BaseType != null && td.BaseType.FullName != "System.Object") { + IType bt = td.SourceProjectDom.GetType (td.BaseType); + if (bt != null) + atts.AddRange (GetRawCustomAttributes (bt, type, true)); + } + } + return atts; + } + + + MA.CustomAttribute ConvertToRawAttribute (ProjectDom dom, IAttribute att, string expectedType) + { + IType attType = dom.GetType (att.AttributeType); + + if (attType == null || !TypeIsAssignableFrom (expectedType, attType)) + return null; + + MA.CustomAttribute mat = new MA.CustomAttribute (); + mat.TypeName = att.AttributeType.FullName; + + var arguments = att.PositionalArguments; + if (arguments.Count > 0) { + + IMethod constructor = FindConstructor (dom, att); + if (constructor == null) + throw new InvalidOperationException ("Custom attribute constructor not found"); + + for (int n=0; n<arguments.Count; n++) { + IParameter par = constructor.Parameters[n]; + object val = Evaluate (arguments [n]).Value; + if (val != null) { + string name = par.Name; + NodeAttributeAttribute bat = (NodeAttributeAttribute) GetCustomAttribute (par, typeof(NodeAttributeAttribute), false); + if (bat != null) + name = bat.Name; + mat.Add (name, Convert.ToString (val, System.Globalization.CultureInfo.InvariantCulture)); + } + } + } + + foreach (var namedArgument in att.NamedArguments) { + string pname = namedArgument.Key; + object val = Evaluate (namedArgument.Value).Value; + if (val == null) + continue; + + foreach (IType td in GetInheritanceChain (attType)) { + IMember prop = GetMember (td.Members, pname); + if (prop == null) + continue; + + NodeAttributeAttribute bat = (NodeAttributeAttribute) GetCustomAttribute (prop, typeof(NodeAttributeAttribute), false); + if (bat != null) { + string name = string.IsNullOrEmpty (bat.Name) ? prop.Name : bat.Name; + mat.Add (name, Convert.ToString (val, System.Globalization.CultureInfo.InvariantCulture)); + } + } + } + + return mat; + } + + static TMember GetMember<TMember> (IEnumerable<TMember> members, string name) where TMember : class, IMember + { + foreach (var member in members) + if (member.Name == name) + return member; + + return null; + } + + IEnumerable<IType> GetInheritanceChain (IType td) + { + yield return td; + while (td != null && td.BaseType != null && td.BaseType.FullName != "System.Object") { + td = td.SourceProjectDom.GetType (td.BaseType); + if (td != null) + yield return td; + } + } + + IMethod FindConstructor (ProjectDom dom, IAttribute att) + { + // The constructor provided by CustomAttribute.Constructor is lacking some information, such as the parameter + // name and custom attributes. Since we need the full info, we have to look it up in the declaring type. + + IType atd = dom.GetType (att.AttributeType); + foreach (IMethod met in atd.Methods) { + if (met.IsConstructor) + continue; + + if (met.Parameters.Count == att.PositionalArguments.Count) { + for (int n = met.Parameters.Count - 1; n >= 0; n--) { + var res = Evaluate (att.PositionalArguments [n]); + if (met.Parameters[n].ReturnType.FullName != res.Type) + break; + if (n == 0) + return met; + } + } + } + return null; + } + + EvalResult Evaluate (CodeExpression exp) + { + if (exp is CodePrimitiveExpression) { + CodePrimitiveExpression pe = (CodePrimitiveExpression) exp; + return new EvalResult () { Type = pe.Value.GetType ().FullName, Value = pe.Value }; + } + else if (exp is CodeTypeOfExpression) { + CodeTypeOfExpression ce = (CodeTypeOfExpression) exp; + return new EvalResult () { Type = "String.Type", Value = ce.Type.BaseType }; + } + else + throw new NotSupportedException (); + } + + class EvalResult + { + public string Type; + public object Value; + } + + public object LoadAssembly (string file) + { + DotNetProject p = solution.FindSolutionItem (file) as DotNetProject; + if (p != null) + return ProjectDomService.GetProjectDom (p); + else + return null; + } + + public object LoadAssemblyFromReference (object asmReference) + { + return asmReference; + } + + public string[] GetResourceNames (object asm) + { + DotNetProject p = (DotNetProject) ((ProjectDom) asm).Project; + List<string> res = new List<string> (); + foreach (ProjectFile f in p.Files) { + if (f.BuildAction == BuildAction.EmbeddedResource) + res.Add (f.ResourceId); + } + return res.ToArray (); + } + + public System.IO.Stream GetResourceStream (object asm, string resourceName) + { + DotNetProject p = (DotNetProject) ((ProjectDom) asm).Project; + foreach (ProjectFile f in p.Files) { + if (f.BuildAction == BuildAction.EmbeddedResource && f.ResourceId == resourceName) + return File.OpenRead (f.FilePath); + } + throw new Exception ("Resource '" + resourceName + "' not found"); + } + + public IEnumerable GetAssemblyTypes (object asm) + { + ProjectDom dom = (ProjectDom) asm; + return dom.Types; + } + + public IEnumerable GetAssemblyReferences (object asm) + { + ProjectDom dom = (ProjectDom) asm; + return dom.References; + } + + public object GetType (object asm, string typeName) + { + ProjectDom dom = (ProjectDom) asm; + return dom.GetType (typeName); + } + + public string GetTypeName (object type) + { + IType t = (IType) type; + return t.Name; + } + + public string GetTypeFullName (object type) + { + IType t = (IType) type; + return t.FullName; + } + + public string GetTypeAssemblyQualifiedName (object type) + { + IType t = (IType) type; + DotNetProject p = (DotNetProject) t.SourceProjectDom.Project; + DotNetProjectConfiguration conf = (DotNetProjectConfiguration) p.GetConfiguration (ConfigurationSelector.Default); + return t.FullName + ", " + conf.CompiledOutputName.FileNameWithoutExtension; + } + + public IEnumerable GetBaseTypeFullNameList (object type) + { + IType t = (IType) type; + + ArrayList list = new ArrayList (); + Hashtable visited = new Hashtable (); + GetBaseTypeFullNameList (visited, list, t); + list.Remove (t.FullName); + return list; + } + + void GetBaseTypeFullNameList (Hashtable visited, ArrayList list, IType tr) + { + if (tr.FullName == "System.Object" || visited.Contains (tr.FullName)) + return; + + visited [tr.FullName] = tr; + list.Add (tr.FullName); + + if (tr.BaseType != null) { + IType bt = tr.SourceProjectDom.GetType (tr.BaseType); + if (bt != null) + GetBaseTypeFullNameList (visited, list, bt); + } + + foreach (IReturnType interf in tr.ImplementedInterfaces) { + IType bt = tr.SourceProjectDom.GetType (interf); + if (bt != null) + GetBaseTypeFullNameList (visited, list, bt); + } + } + + public bool TypeIsAssignableFrom (object baseType, object type) + { + IType tbase = (IType) baseType; + IType ttype = (IType) type; + return ttype.IsBaseType (tbase.ReturnType); + } + + public bool TypeIsAssignableFrom (string baseTypeName, object type) + { + foreach (string bt in GetBaseTypeFullNameList (type)) + if (bt == baseTypeName) + return true; + return false; + } + + public IEnumerable GetFields (object type) + { + IType t = (IType) type; + return t.Fields; + } + + public string GetFieldName (object field) + { + IField f = (IField) field; + return f.Name; + } + + public string GetFieldTypeFullName (object field) + { + IField f = (IField) field; + return f.ReturnType.FullName; + } + #endregion + } +} + |