2 // System.Runtime.Remoting.MetadataServices.MetaDataCodeGenerator
5 // Lluis Sanchez Gual (lluis@ximian.com)
7 // (C) 2003 Novell, Inc
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System
.Collections
;
34 using System
.Reflection
;
35 using System
.Runtime
.Remoting
;
36 using System
.Runtime
.Remoting
.Metadata
;
38 namespace System
.Runtime
.Remoting
.MetadataServices
40 internal class MetaDataCodeGenerator
44 XmlNamespaceManager nsManager
;
47 public void GenerateCode (bool clientProxy
, string outputDirectory
, Stream inputStream
,
48 ArrayList outCodeStreamList
, string proxyUrl
, string proxyNamespace
)
50 doc
= new XmlDocument ();
51 doc
.Load (inputStream
);
53 nsManager
= new XmlNamespaceManager (doc
.NameTable
);
54 nsManager
.AddNamespace ("wsdl", MetaData
.WsdlNamespace
);
55 nsManager
.AddNamespace ("s", MetaData
.SchemaNamespace
);
56 nsManager
.AddNamespace ("suds", MetaData
.SudsNamespace
);
58 if (outputDirectory
== null) outputDirectory
= Directory
.GetCurrentDirectory();
60 CodeFile mainFile
= new CodeFile (outputDirectory
);
62 currentFile
= mainFile
;
66 sudsTypes
= new Hashtable ();
67 XmlNodeList nodes
= doc
.DocumentElement
.SelectNodes ("wsdl:binding/suds:class|wsdl:binding/suds:interface|wsdl:binding/suds:struct", nsManager
);
68 foreach (XmlElement node
in nodes
)
69 sudsTypes
[GetTypeQualifiedName (node
, node
.GetAttribute ("type"))] = node
;
73 nodes
= doc
.SelectNodes ("wsdl:definitions/wsdl:types/s:schema", nsManager
);
74 foreach (XmlElement schema
in nodes
)
75 GenerateSchemaCode (schema
);
79 nodes
= doc
.SelectNodes ("wsdl:definitions/wsdl:service/wsdl:port", nsManager
);
80 foreach (XmlElement port
in nodes
)
81 GeneratePortCode (port
);
84 if (mainFile
.FileName
!= null)
85 outCodeStreamList
.Add (mainFile
.FilePath
);
88 void GeneratePortCode (XmlElement port
)
90 XmlElement binding
= GetBinding (port
.GetAttribute ("binding"));
91 XmlElement type
= null;
92 foreach (XmlNode node
in binding
)
93 if ((node
is XmlElement
) && ((XmlElement
)node
).NamespaceURI
== MetaData
.SudsNamespace
)
94 { type = (XmlElement) node; break; }
96 string rootType
= type
.GetAttribute ("rootType");
97 if (rootType
== "Delegate")
98 GenerateServiceDelegateCode (port
, binding
, type
);
100 GenerateServiceClassCode (port
, binding
, type
);
103 void GenerateServiceDelegateCode (XmlElement port
, XmlElement binding
, XmlElement type
)
105 string typeName
= (type
!= null) ? type
.GetAttribute ("type") : port
.GetAttribute ("name");
106 string portName
= GetNameFromQn (binding
.GetAttribute ("type"));
109 GetTypeQualifiedName (port
, typeName
, out name
, out ns
);
110 currentFile
.SetCurrentNamespace (ns
);
112 XmlElement oper
= (XmlElement
) binding
.SelectSingleNode ("wsdl:operation[@name='Invoke']", nsManager
);
113 if (oper
== null) throw new InvalidOperationException ("Invalid delegate schema");
117 GetParameters (oper
, portName
, "Invoke", out parsDec
, out returnType
);
119 currentFile
.WriteLine ("public delegate " + returnType
+ " " + name
+ " (" + parsDec
+ ");");
120 currentFile
.WriteLine ("");
123 void GenerateServiceClassCode (XmlElement port
, XmlElement binding
, XmlElement type
)
125 string typeName
= (type
!= null) ? type
.GetAttribute ("type") : port
.GetAttribute ("name");
128 GetTypeQualifiedName (port
, typeName
, out name
, out ns
);
129 currentFile
.SetCurrentNamespace (ns
);
131 string cls
= "public " + type
.LocalName
+ " " + name
;
132 string baset
= type
.GetAttribute ("extends");
133 if (baset
!= "") cls
+= ": " + GetTypeQualifiedName (port
, baset
);
137 XmlNodeList interfaces
= type
.SelectNodes ("suds:implements",nsManager
);
138 if (interfaces
.Count
== 0) interfaces
= type
.SelectNodes ("suds:extends",nsManager
);
140 foreach (XmlElement interf
in interfaces
)
142 string iname
= GetTypeQualifiedName (interf
, interf
.GetAttribute ("type"));
143 if (cls
.IndexOf (':') == -1) cls
+= ": " + iname
;
144 else cls
+= ", " + iname
;
147 currentFile
.WriteLine (cls
);
148 currentFile
.WriteLineInd ("{");
150 string portName
= GetNameFromQn (binding
.GetAttribute ("type"));
151 bool isInterface
= type
.LocalName
== "interface";
153 string vis
= isInterface
? "":"public ";
155 ArrayList mets
= GetMethods (portName
, binding
);
156 foreach (MethodData met
in mets
)
160 string prop
= vis
+ met
.ReturnType
+ " ";
161 if (met
.Signature
!= "") prop
+= "this [" + met
.Signature
+ "]";
162 else prop
+= met
.Name
;
167 if (met
.HasGet
) prop
+= "get; ";
168 if (met
.HasSet
) prop
+= "set; ";
170 currentFile
.WriteLine (prop
);
174 currentFile
.WriteLine (prop
);
175 currentFile
.WriteLineInd ("{");
176 if (met
.HasGet
) currentFile
.WriteLine ("get { throw new NotImplementedException (); }");
177 if (met
.HasSet
) currentFile
.WriteLine ("set { throw new NotImplementedException (); }");
178 currentFile
.WriteLineUni ("}");
179 currentFile
.WriteLine ("");
184 currentFile
.WriteLine (vis
+ met
.ReturnType
+ " " + met
.Name
+ " (" + met
.Signature
+ ")" + (isInterface
?";":""));
187 currentFile
.WriteLineInd ("{");
188 currentFile
.WriteLine ("throw new NotImplementedException ();");
189 currentFile
.WriteLineUni ("}");
190 currentFile
.WriteLine ("");
195 currentFile
.WriteLineUni ("}");
196 currentFile
.WriteLine ("");
201 public string ReturnType
;
202 public string Signature
;
207 public bool IsProperty { get { return HasGet || HasSet; }
}
210 ArrayList
GetMethods (string portName
, XmlElement binding
)
212 ArrayList mets
= new ArrayList ();
214 XmlNodeList nodes
= binding
.SelectNodes ("wsdl:operation", nsManager
);
215 foreach (XmlElement oper
in nodes
)
217 MethodData md
= new MethodData ();
218 md
.Name
= oper
.GetAttribute ("name");
220 GetParameters (oper
, portName
, md
.Name
, out md
.Signature
, out md
.ReturnType
);
222 if (md
.Name
.StartsWith ("set_") || md
.Name
.StartsWith ("get_"))
224 string tmp
= ", " + md
.Signature
;
225 if (tmp
.IndexOf (", out ") == -1 && tmp
.IndexOf (", ref ") == -1)
227 bool isSet
= md
.Name
[0]=='s';
228 md
.Name
= md
.Name
.Substring (4);
229 MethodData previousProp
= null;
231 foreach (MethodData fmd
in mets
)
232 if (fmd
.Name
== md
.Name
&& fmd
.IsProperty
)
235 if (previousProp
!= null) {
236 if (isSet
) previousProp
.HasSet
= true;
237 else { previousProp.HasGet = true; previousProp.Signature = md.Signature; }
241 if (isSet
) { md.HasSet = true; md.Signature = ""; }
242 else md
.HasGet
= true;
252 void GetParameters (XmlElement oper
, string portName
, string operName
, out string signature
, out string returnType
)
256 XmlElement portType
= (XmlElement
) doc
.SelectSingleNode ("wsdl:definitions/wsdl:portType[@name='" + portName
+ "']", nsManager
);
257 XmlElement portOper
= (XmlElement
) portType
.SelectSingleNode ("wsdl:operation[@name='" + operName
+ "']", nsManager
);
258 string[] parNames
= portOper
.GetAttribute ("parameterOrder").Split (' ');
260 XmlElement inPortMsg
= (XmlElement
) portOper
.SelectSingleNode ("wsdl:input", nsManager
);
261 XmlElement inMsg
= FindMessageFromPortMessage (inPortMsg
);
263 XmlElement outPortMsg
= (XmlElement
) portOper
.SelectSingleNode ("wsdl:output", nsManager
);
264 XmlElement outMsg
= FindMessageFromPortMessage (outPortMsg
);
267 if (parNames
[0] != "") parameters
= new string [parNames
.Length
];
268 else parameters
= new string [0];
270 foreach (XmlElement part
in inMsg
.SelectNodes ("wsdl:part",nsManager
))
272 int i
= Array
.IndexOf (parNames
, part
.GetAttribute ("name"));
273 string type
= GetTypeQualifiedName (part
, part
.GetAttribute ("type"));
274 parameters
[i
] = type
+ " " + parNames
[i
];
277 foreach (XmlElement part
in outMsg
.SelectNodes ("wsdl:part",nsManager
))
279 string pn
= part
.GetAttribute ("name");
280 string type
= GetTypeQualifiedName (part
, part
.GetAttribute ("type"));
285 int i
= Array
.IndexOf (parNames
, pn
);
286 if (parameters
[i
] != null) parameters
[i
] = "ref " + parameters
[i
];
287 else parameters
[i
] = "out " + type
+ " " + pn
;
291 signature
= string.Join (", ", parameters
);
292 if (returnType
== null) returnType
= "void";
295 XmlElement
FindMessageFromPortMessage (XmlElement portMsg
)
297 string msgName
= portMsg
.GetAttribute ("message");
298 msgName
= GetNameFromQn (msgName
);
299 return (XmlElement
) doc
.SelectSingleNode ("wsdl:definitions/wsdl:message[@name='" + msgName
+ "']", nsManager
);
302 void GenerateSchemaCode (XmlElement schema
)
304 string ns
= schema
.GetAttribute ("targetNamespace");
305 string clrNs
= DecodeNamespace (ns
);
306 currentFile
.SetCurrentNamespace (clrNs
);
308 foreach (XmlNode node
in schema
)
310 XmlElement elem
= node
as XmlElement
;
311 if (elem
== null) continue;
313 if (elem
.LocalName
== "complexType")
314 GenerateClassCode (ns
, elem
);
315 else if (elem
.LocalName
== "simpleType")
316 GenerateEnumCode (ns
, elem
);
320 void GenerateClassCode (string ns
, XmlElement elem
)
322 if (elem
.SelectSingleNode ("s:complexContent/s:restriction", nsManager
) != null) return;
323 string clrNs
= DecodeNamespace (ns
);
324 string typeName
= GetTypeName (elem
.GetAttribute ("name"), ns
);
326 XmlElement sudsType
= (XmlElement
) sudsTypes
[clrNs
+ "." + typeName
];
328 string typetype
= "class";
329 if (sudsType
!= null) typetype
= sudsType
.LocalName
;
331 currentFile
.WriteLine ("[Serializable, SoapType (XmlNamespace = @\"" + ns
+ "\", XmlTypeNamespace = @\"" + ns
+ "\")]");
333 string cls
= "public " + typetype
+ " " + typeName
;
334 string baseType
= elem
.GetAttribute ("base");
335 if (baseType
!= "") cls
+= ": " + GetTypeQualifiedName (elem
, baseType
);
337 bool isSerializable
= (sudsType
.GetAttribute ("rootType") == "ISerializable");
341 if (cls
.IndexOf (':') == -1) cls
+= ": ";
343 cls
+= "System.Runtime.Serialization.ISerializable";
346 currentFile
.WriteLine (cls
);
347 currentFile
.WriteLineInd ("{");
349 XmlNodeList elems
= elem
.GetElementsByTagName ("element", MetaData
.SchemaNamespace
);
350 foreach (XmlElement elemField
in elems
)
351 WriteField (elemField
);
353 elems
= elem
.GetElementsByTagName ("attribute", MetaData
.SchemaNamespace
);
354 foreach (XmlElement elemField
in elems
)
355 WriteField (elemField
);
359 currentFile
.WriteLine ("");
360 currentFile
.WriteLine ("public " + typeName
+ " ()");
361 currentFile
.WriteLineInd ("{");
362 currentFile
.WriteLineUni ("}");
363 currentFile
.WriteLine ("");
365 currentFile
.WriteLine ("public " + typeName
+ " (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)");
366 currentFile
.WriteLineInd ("{");
367 currentFile
.WriteLine ("throw new NotImplementedException ();");
368 currentFile
.WriteLineUni ("}");
369 currentFile
.WriteLine ("");
371 currentFile
.WriteLine ("public void GetObjectData (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)");
372 currentFile
.WriteLineInd ("{");
373 currentFile
.WriteLine ("throw new NotImplementedException ();");
374 currentFile
.WriteLineUni ("}");
377 currentFile
.WriteLineUni ("}");
378 currentFile
.WriteLine ("");
381 void WriteField (XmlElement elemField
)
383 bool isAttr
= elemField
.LocalName
== "attribute";
385 string type
= elemField
.GetAttribute ("type");
388 currentFile
.WriteLine ("[SoapField (UseAttribute = true)]");
389 else if (!IsPrimitive (elemField
, type
))
390 currentFile
.WriteLine ("[SoapField (Embedded = true)]");
391 currentFile
.WriteLine ("public " + GetTypeQualifiedName (elemField
, type
) + " " + elemField
.GetAttribute ("name") + ";");
394 void GenerateEnumCode (string ns
, XmlElement elem
)
396 currentFile
.WriteLine ("public enum " + GetTypeName (elem
.GetAttribute ("name"), ns
));
397 currentFile
.WriteLineInd ("{");
399 XmlNodeList nodes
= elem
.SelectNodes ("s:restriction/s:enumeration/@value", nsManager
);
400 foreach (XmlNode node
in nodes
)
401 currentFile
.WriteLine (node
.Value
+ ",");
403 currentFile
.WriteLineUni ("}");
404 currentFile
.WriteLine ("");
407 bool IsPrimitive (XmlNode node
, string qname
)
409 string name
= GetTypeQualifiedName (node
, qname
);
410 return name
.IndexOf ('.') == -1;
413 string GetTypeName (string localName
, string ns
)
418 void GetTypeQualifiedName (XmlNode node
, string qualifiedName
, out string name
, out string ns
)
420 int i
= qualifiedName
.IndexOf (':');
423 name
= qualifiedName
;
428 string prefix
= qualifiedName
.Substring (0,i
);
429 name
= qualifiedName
.Substring (i
+1);
430 ns
= node
.GetNamespaceOfPrefix (prefix
);
432 string arrayType
= GetArrayType (node
, name
, ns
);
433 if (arrayType
!= null) {
437 else if (ns
!= MetaData
.SchemaNamespace
) {
438 ns
= DecodeNamespace (ns
);
442 name
= GetClrFromXsd (name
);
446 string GetClrFromXsd (string type
)
450 case "boolean": return "bool";
451 case "unsignedByte": return "byte";
452 case "char": return "char";
453 case "dateTime": return "DateTime";
454 case "decimal": return "decimal";
455 case "double": return "double";
456 case "short": return "short";
457 case "int": return "int";
458 case "long": return "long";
459 case "byte": return "sbyte";
460 case "float": return "float";
461 case "unsignedShort": return "ushort";
462 case "unsignedInt": return "uint";
463 case "unsignedLong": return "ulong";
464 case "string": return "string";
465 case "duration": return "TimeSpan";
466 case "anyType": return "object";
468 throw new InvalidOperationException ("Unknown schema type: " + type
);
471 string GetTypeQualifiedName (XmlNode node
, string qualifiedName
)
474 GetTypeQualifiedName (node
, qualifiedName
, out name
, out ns
);
475 if (ns
!= "") return ns
+ "." + name
;
479 string GetTypeNamespace (XmlNode node
, string qualifiedName
)
482 GetTypeQualifiedName (node
, qualifiedName
, out name
, out ns
);
486 string GetArrayType (XmlNode node
, string name
, string ns
)
488 XmlNode anod
= doc
.SelectSingleNode ("wsdl:definitions/wsdl:types/s:schema[@targetNamespace='" + ns
+ "']/s:complexType[@name='" + name
+ "']/s:complexContent/s:restriction/s:attribute/@wsdl:arrayType", nsManager
);
489 if (anod
== null) return null;
491 string atype
= anod
.Value
;
492 int i
= atype
.IndexOf ('[');
493 string itemType
= GetTypeQualifiedName (node
, atype
.Substring (0,i
));
495 return itemType
+ atype
.Substring (i
);
498 XmlElement
GetBinding (string name
)
500 int i
= name
.IndexOf (':');
501 name
= name
.Substring (i
+1);
502 return doc
.SelectSingleNode ("wsdl:definitions/wsdl:binding[@name='" + name
+ "']", nsManager
) as XmlElement
;
505 string DecodeNamespace (string xmlNamespace
)
509 if (!SoapServices
.DecodeXmlNamespaceForClrTypeNamespace (xmlNamespace
, out tns
, out tasm
))
515 string GetLiteral (object ob
)
517 if (ob
== null) return "null";
518 if (ob
is string) return "\"" + ob
.ToString().Replace("\"","\"\"") + "\"";
519 if (ob
is bool) return ((bool)ob
) ? "true" : "false";
520 if (ob
is XmlQualifiedName
) {
521 XmlQualifiedName qn
= (XmlQualifiedName
)ob
;
522 return "new XmlQualifiedName (" + GetLiteral(qn
.Name
) + "," + GetLiteral(qn
.Namespace
) + ")";
524 else return ob
.ToString ();
527 string Params (params string[] pars
)
530 foreach (string p
in pars
)
532 if (res
!= "") res
+= ", ";
538 string GetNameFromQn (string qn
)
540 int i
= qn
.IndexOf (':');
541 if (i
== -1) return qn
;
542 else return qn
.Substring (i
+1);
548 public string FileName
;
549 public string Directory
;
550 public string FilePath
;
551 Hashtable namespaces
= new Hashtable ();
552 public StringWriter writer
;
555 string currentNamespace
;
557 public CodeFile (string directory
)
559 Directory
= directory
;
562 public void SetCurrentNamespace (string ns
)
564 writer
= namespaces
[ns
] as StringWriter
;
568 writer
= new StringWriter ();
569 namespaces
[ns
] = writer
;
570 WriteLine ("namespace " + ns
);
576 if (FileName
== null)
577 FileName
= ns
+ ".cs";
580 public void WriteLineInd (string code
)
586 public void WriteLineUni (string code
)
588 if (indent
> 0) indent
--;
592 public void WriteLine (string code
)
594 if (code
!= "") writer
.Write (new String ('\t',indent
));
595 writer
.WriteLine (code
);
600 if (FileName
== null) return;
602 FilePath
= Path
.Combine (Directory
, FileName
);
603 StreamWriter sw
= new StreamWriter (FilePath
);
605 sw
.WriteLine ("using System;");
606 sw
.WriteLine ("using System.Runtime.Remoting.Metadata;");
609 foreach (StringWriter nsWriter
in namespaces
.Values
)
611 sw
.Write (nsWriter
.ToString ());