2 // MoonlightA11yDescriptorGenerator.cs
5 // Andrés G. Aragoneses (aaragoneses@novell.com)
7 // (C) 2009 Novell, Inc.
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
.Text
.RegularExpressions
;
37 using System
.Xml
.XPath
;
40 using Mono
.Linker
.Steps
;
44 namespace Mono
.Tuner
{
46 public class MoonlightA11yDescriptorGenerator
: BaseStep
{
48 XmlTextWriter writer
= null;
49 protected override void ProcessAssembly (AssemblyDefinition assembly
)
51 if (assembly
.Name
.Name
== "MoonAtkBridge" || assembly
.Name
.Name
== "System.Windows" ||
52 assembly
.Name
.Name
.Contains ("Dummy"))
56 writer
= new XmlTextWriter (System
.Console
.Out
);
57 writer
.Formatting
= Formatting
.Indented
;
58 writer
.WriteStartElement("linker");
61 IDictionary types
= ScanAssembly (assembly
);
62 if (types
!= null && types
.Count
> 0) {
63 writer
.WriteStartElement("assembly");
64 writer
.WriteAttributeString ("fullname", assembly
.Name
.Name
);
66 foreach (TypeDefinition type
in types
.Keys
) {
67 IList members
= (IList
)types
[type
];
68 if (members
!= null && members
.Count
> 0) {
69 writer
.WriteStartElement("type");
70 writer
.WriteAttributeString ("fullname", type
.FullName
);
72 foreach (IAnnotationProvider member
in members
) {
73 MethodDefinition method
= member
as MethodDefinition
;
75 writer
.WriteStartElement("method");
76 writer
.WriteAttributeString ("signature",
77 method
.ReturnType
.ReturnType
.FullName
+ " " +
78 method
.Name
+ GetMethodParams (method
));
79 writer
.WriteEndElement ();
83 FieldDefinition field
= member
as FieldDefinition
;
85 writer
.WriteStartElement("field");
86 writer
.WriteAttributeString ("signature", field
.DeclaringType
.FullName
+ " " + field
.Name
);
87 writer
.WriteEndElement ();
90 writer
.WriteEndElement ();
94 writer
.WriteEndElement ();
99 protected override void EndProcess ()
101 if (writer
!= null) {
102 writer
.WriteEndElement ();
108 //this is almost the ToString method of MethodDefinition...
109 private string GetMethodParams (MethodDefinition method
)
111 string @params = "(";
112 if (method
.HasParameters
) {
113 for (int i
= 0; i
< method
.Parameters
.Count
; i
++) {
117 @params += method
.Parameters
[i
].ParameterType
.FullName
;
124 Hashtable
/*Dictionary<TypeDefinition,List<IAnnotationProvider>*/ ScanAssembly (AssemblyDefinition assembly
)
126 if (Annotations
.GetAction (assembly
) != AssemblyAction
.Link
)
129 Hashtable members_used
= new Hashtable ();
130 foreach (TypeDefinition type
in assembly
.MainModule
.Types
) {
131 IList used_providers
= FilterPublicMembers (ScanType (type
));
132 if (used_providers
.Count
> 0)
133 members_used
[type
] = used_providers
;
134 else if (IsInternal (type
, true) &&
135 Annotations
.IsMarked (type
))
136 throw new NotSupportedException (String
.Format ("The type {0} is used while its API is not", type
.ToString ()));
141 static IList
ScanType (TypeDefinition type
)
143 return ExtractUsedProviders (type
.Methods
, type
.Constructors
, type
.Fields
);
146 static IList
FilterPublicMembers (IList members
)
148 IList new_list
= new ArrayList ();
149 foreach (MemberReference item
in members
)
150 if (IsInternal (item
, true))
156 static string FindMasterInfoFile (string name
)
158 string [] master_infos
= Directory
.GetFiles (Environment
.CurrentDirectory
, "*.info");
160 if (master_infos
.Length
== 0)
161 throw new Exception ("No masterinfo files found in current directory");
163 foreach (string file
in master_infos
) {
164 if (file
.EndsWith (name
+ ".info"))
171 const string xpath_init
= "assemblies/assembly/namespaces/namespace[@name='{0}']/classes/class[@name='{1}']";
173 static string GetXPathSearchForType (TypeDefinition type
)
175 TypeDefinition parent_type
= type
;
176 string xpath
= String
.Empty
;
177 while (parent_type
.DeclaringType
!= null) {
178 xpath
= String
.Format ("/classes/class[@name='{0}']", parent_type
.Name
) + xpath
;
179 parent_type
= parent_type
.DeclaringType
;
181 return String
.Format (xpath_init
, parent_type
.Namespace
, parent_type
.Name
) + xpath
;
184 static bool IsInternal (MemberReference member
, bool master_info
)
186 TypeDefinition type
= null;
187 string master_info_file
= null;
189 if (member
is TypeDefinition
) {
190 type
= member
as TypeDefinition
;
192 return (!type
.IsNested
&& !type
.IsPublic
) ||
193 (type
.IsNested
&& (!type
.IsNestedPublic
|| IsInternal (type
.DeclaringType
, false)));
195 master_info_file
= FindMasterInfoFile (type
.Module
.Assembly
.Name
.Name
);
196 if (master_info_file
== null)
197 return IsInternal (member
, false);
199 return !NodeExists (master_info_file
, GetXPathSearchForType (type
));
202 type
= member
.DeclaringType
.Resolve ();
204 if (IsInternal (type
, master_info
))
207 MethodDefinition method
= member
as MethodDefinition
;
208 FieldDefinition field
= member
as FieldDefinition
;
210 if (field
== null && method
== null)
211 throw new System
.NotSupportedException ("Members to scan should be methods or fields");
216 return !method
.IsPublic
;
218 return !field
.IsPublic
;
221 master_info_file
= FindMasterInfoFile (type
.Module
.Assembly
.Name
.Name
);
222 if (master_info_file
== null)
223 return IsInternal (member
, false);
225 string xpath_type
= GetXPathSearchForType (type
);
230 name
= method
.ToString ();
233 name
= WackyOutArgs (WackyCommas (name
.Substring (name
.IndexOf ("::") + 2)
234 .Replace ("/", "+") // nested classes
235 .Replace ('<', '[').Replace ('>', ']'))); //generic params
238 if (field
!= null || !IsPropertyMethod (method
))
239 return !NodeExists (master_info_file
, xpath_type
+ String
.Format ("/*/*[@name='{0}']", name
));
241 return !NodeExists (master_info_file
, xpath_type
+ String
.Format ("/properties/*/*/*[@name='{0}']", name
));
244 //at some point I want to get rid of this method and ask cecil's maintainer to spew commas in a uniform way...
245 static string WackyCommas (string method
)
247 string outstring
= String
.Empty
;
248 bool square_bracket
= false;
249 foreach (char c
in method
) {
251 square_bracket
= true;
253 square_bracket
= false;
255 outstring
= outstring
+ c
;
257 if (c
== ',' && !square_bracket
)
258 outstring
= outstring
+ " ";
263 //ToString() spews & but not 'out' keyword
264 static string WackyOutArgs (string method
)
266 return Regex
.Replace (method
, @"\w+&", delegate (Match m
) { return "out " + m.ToString (); }
);
269 //copied from MarkStep (violating DRY unless I can put this in a better place... Cecil?)
270 static bool IsPropertyMethod (MethodDefinition md
)
272 return (md
.SemanticsAttributes
& MethodSemanticsAttributes
.Getter
) != 0 ||
273 (md
.SemanticsAttributes
& MethodSemanticsAttributes
.Setter
) != 0;
276 //OPTIMIZEME!: maybe hold a dictionary of opened FileStreams?
277 static bool NodeExists (string file
, string xpath
)
279 //Console.WriteLine ("Looking for node {0} in file {1}", xpath, file.Substring (file.LastIndexOf ("/") + 1));
280 using (FileStream stream
= new FileStream (file
, FileMode
.Open
)) {
281 XPathDocument document
= new XPathDocument (stream
);
282 XPathNavigator navigator
= document
.CreateNavigator ();
283 XPathNavigator node
= navigator
.SelectSingleNode (xpath
);
284 return (node
!= null);
288 static IList
/*List<IAnnotationProvider>*/ ExtractUsedProviders (params IList
[] members
)
290 IList used
= new ArrayList ();
291 if (members
== null || members
.Length
== 0)
294 foreach (IList members_list
in members
)
295 foreach (IAnnotationProvider provider
in members_list
)
296 if (Annotations
.IsMarked (provider
))