1 // Copyright 2006 Alp Toker <alp@atoker.com>
2 // This software is made available under the MIT License
3 // See COPYING for details
6 using System
.Collections
.Generic
;
10 using System
.Reflection
;
14 //TODO: complete this class
17 const string NAMESPACE
= "http://www.freedesktop.org/standards/dbus";
18 const string PUBLIC_IDENTIFIER
= "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN";
19 const string SYSTEM_IDENTIFIER
= "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd";
21 public StringBuilder sb
;
23 public ObjectPath root_path
= ObjectPath
.Root
;
25 protected XmlWriter writer
;
27 public Introspector ()
29 XmlWriterSettings settings
= new XmlWriterSettings ();
30 settings
.Indent
= true;
31 settings
.IndentChars
= (" ");
32 settings
.OmitXmlDeclaration
= true;
34 sb
= new StringBuilder ();
36 writer
= XmlWriter
.Create (sb
, settings
);
39 static string GetProductDescription ()
43 Assembly assembly
= Assembly
.GetExecutingAssembly ();
44 AssemblyName aname
= assembly
.GetName ();
46 AssemblyInformationalVersionAttribute iversion
= Attribute
.GetCustomAttribute (assembly
, typeof (AssemblyInformationalVersionAttribute
)) as AssemblyInformationalVersionAttribute
;
49 version
= iversion
.InformationalVersion
;
51 version
= aname
.Version
.ToString ();
53 return aname
.Name
+ " " + version
;
56 public void WriteStart ()
58 writer
.WriteDocType ("node", PUBLIC_IDENTIFIER
, SYSTEM_IDENTIFIER
, null);
60 writer
.WriteComment (" " + GetProductDescription () + " ");
62 //the root node element
63 writer
.WriteStartElement ("node");
66 public void WriteNode (string name
)
68 writer
.WriteStartElement ("node");
69 writer
.WriteAttributeString ("name", name
);
70 writer
.WriteEndElement ();
73 public void WriteEnd ()
76 WriteEnum (typeof (org.freedesktop.DBus.NameFlag));
77 WriteEnum (typeof (org.freedesktop.DBus.NameReply));
78 WriteEnum (typeof (org.freedesktop.DBus.ReleaseNameReply));
79 WriteEnum (typeof (org.freedesktop.DBus.StartReply));
80 WriteInterface (typeof (org.freedesktop.DBus.IBus));
83 writer
.WriteEndElement ();
89 //public void WriteNode ()
90 public void WriteType (Type target_type
)
92 //writer.WriteStartElement ("node");
94 //TODO: non-well-known introspection has paths as well, which we don't do yet. read the spec again
95 //hackishly just remove the root '/' to make the path relative for now
96 //writer.WriteAttributeString ("name", target_path.Value.Substring (1));
97 //writer.WriteAttributeString ("name", "test");
99 //reflect our own interface manually
100 WriteInterface (typeof (org
.freedesktop
.DBus
.Introspectable
));
102 //reflect the target interface
103 if (target_type
!= null) {
104 WriteInterface (target_type
);
106 foreach (Type ifType
in target_type
.GetInterfaces ())
107 WriteInterface (ifType
);
110 //TODO: review recursion of interfaces and inheritance hierarchy
112 //writer.WriteEndElement ();
115 public void WriteArg (ParameterInfo pi
)
117 WriteArg (pi
.ParameterType
, Mapper
.GetArgumentName (pi
), pi
.IsOut
, false);
120 public void WriteArgReverse (ParameterInfo pi
)
122 WriteArg (pi
.ParameterType
, Mapper
.GetArgumentName (pi
), pi
.IsOut
, true);
125 //TODO: clean up and get rid of reverse (or argIsOut) parm
126 public void WriteArg (Type argType
, string argName
, bool argIsOut
, bool reverse
)
128 argType
= argIsOut
? argType
.GetElementType () : argType
;
129 if (argType
== typeof (void))
132 writer
.WriteStartElement ("arg");
134 if (!String
.IsNullOrEmpty (argName
))
135 writer
.WriteAttributeString ("name", argName
);
137 //we can't rely on the default direction (qt-dbus requires a direction at time of writing), so we use a boolean to reverse the parameter direction and make it explicit
140 writer
.WriteAttributeString ("direction", !reverse
? "out" : "in");
142 writer
.WriteAttributeString ("direction", !reverse
? "in" : "out");
144 Signature sig
= Signature
.GetSig (argType
);
146 //TODO: avoid writing null (DType.Invalid) to the XML stream
147 writer
.WriteAttributeString ("type", sig
.Value
);
149 //annotations aren't valid in an arg element, so this is disabled
150 //if (argType.IsEnum)
151 // WriteAnnotation ("org.ndesk.DBus.Enum", Mapper.GetInterfaceName (argType));
153 writer
.WriteEndElement ();
156 public void WriteMethod (MethodInfo mi
)
158 writer
.WriteStartElement ("method");
159 writer
.WriteAttributeString ("name", mi
.Name
);
161 foreach (ParameterInfo pi
in mi
.GetParameters ())
164 //Mono <= 1.1.13 doesn't support MethodInfo.ReturnParameter, so avoid it
165 //WriteArgReverse (mi.ReturnParameter);
166 WriteArg (mi
.ReturnType
, Mapper
.GetArgumentName (mi
.ReturnTypeCustomAttributes
, "ret"), false, true);
168 WriteAnnotations (mi
);
170 writer
.WriteEndElement ();
173 public void WriteProperty (PropertyInfo pri
)
175 //expose properties as dbus properties
176 writer
.WriteStartElement ("property");
177 writer
.WriteAttributeString ("name", pri
.Name
);
178 writer
.WriteAttributeString ("type", Signature
.GetSig (pri
.PropertyType
).Value
);
179 string access
= (pri
.CanRead
? "read" : String
.Empty
) + (pri
.CanWrite
? "write" : String
.Empty
);
180 writer
.WriteAttributeString ("access", access
);
181 WriteAnnotations (pri
);
182 writer
.WriteEndElement ();
184 //expose properties as methods also
185 //it may not be worth doing this in the long run
188 writer.WriteStartElement ("method");
189 writer.WriteAttributeString ("name", "Get" + pri.Name);
190 WriteArgReverse (pri.GetGetMethod ().ReturnParameter);
191 writer.WriteEndElement ();
195 writer.WriteStartElement ("method");
196 writer.WriteAttributeString ("name", "Set" + pri.Name);
197 foreach (ParameterInfo pi in pri.GetSetMethod ().GetParameters ())
199 writer.WriteEndElement ();
204 public void WriteSignal (EventInfo ei
)
206 writer
.WriteStartElement ("signal");
207 writer
.WriteAttributeString ("name", ei
.Name
);
209 foreach (ParameterInfo pi
in ei
.EventHandlerType
.GetMethod ("Invoke").GetParameters ())
210 WriteArgReverse (pi
);
212 WriteAnnotations (ei
);
214 //no need to consider the delegate return value as dbus doesn't support it
215 writer
.WriteEndElement ();
218 const BindingFlags relevantBindingFlags
= BindingFlags
.Public
| BindingFlags
.Instance
| BindingFlags
.DeclaredOnly
;
220 public void WriteInterface (Type type
)
225 //TODO: this is unreliable, fix it
226 if (!Mapper
.IsPublic (type
))
229 writer
.WriteStartElement ("interface");
231 writer
.WriteAttributeString ("name", Mapper
.GetInterfaceName (type
));
234 foreach (MemberInfo mbi in type.GetMembers (relevantBindingFlags)) {
235 switch (mbi.MemberType) {
236 case MemberTypes.Method:
237 if (!((MethodInfo)mbi).IsSpecialName)
238 WriteMethod ((MethodInfo)mbi);
240 case MemberTypes.Event:
241 WriteSignal ((EventInfo)mbi);
243 case MemberTypes.Property:
244 WriteProperty ((PropertyInfo)mbi);
247 Console.Error.WriteLine ("Warning: Unhandled MemberType '{0}' encountered while introspecting {1}", mbi.MemberType, type.FullName);
253 foreach (MethodInfo mi
in type
.GetMethods (relevantBindingFlags
))
254 if (!mi
.IsSpecialName
)
257 foreach (EventInfo ei
in type
.GetEvents (relevantBindingFlags
))
260 foreach (PropertyInfo pri
in type
.GetProperties (relevantBindingFlags
))
265 //TODO: attributes as annotations?
267 writer
.WriteEndElement ();
269 //this recursion seems somewhat inelegant
270 WriteInterface (type
.BaseType
);
273 public void WriteAnnotations (ICustomAttributeProvider attrProvider
)
275 if (Mapper
.IsDeprecated (attrProvider
))
276 WriteAnnotation ("org.freedesktop.DBus.Deprecated", "true");
279 public void WriteAnnotation (string name
, string value)
281 writer
.WriteStartElement ("annotation");
283 writer
.WriteAttributeString ("name", name
);
284 writer
.WriteAttributeString ("value", value);
286 writer
.WriteEndElement ();
289 //this is not in the spec, and is not finalized
290 public void WriteEnum (Type type
)
292 writer
.WriteStartElement ("enum");
293 writer
.WriteAttributeString ("name", Mapper
.GetInterfaceName (type
));
294 writer
.WriteAttributeString ("type", Signature
.GetSig (type
.GetElementType ()).Value
);
295 writer
.WriteAttributeString ("flags", (type
.IsDefined (typeof (FlagsAttribute
), false)) ? "true" : "false");
297 string[] names
= Enum
.GetNames (type
);
300 foreach (Enum val
in Enum
.GetValues (type
)) {
301 writer
.WriteStartElement ("element");
302 writer
.WriteAttributeString ("name", names
[i
++]);
303 writer
.WriteAttributeString ("value", val
.ToString ("d"));
304 writer
.WriteEndElement ();
307 writer
.WriteEndElement ();