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
);
61 CodeFile interopFile
= new CodeFile (outputDirectory
);
63 currentFile
= mainFile
;
67 sudsTypes
= new Hashtable ();
68 XmlNodeList nodes
= doc
.DocumentElement
.SelectNodes ("wsdl:binding/suds:class|wsdl:binding/suds:interface|wsdl:binding/suds:struct", nsManager
);
69 foreach (XmlElement node
in nodes
)
70 sudsTypes
[GetTypeQualifiedName (node
, node
.GetAttribute ("type"))] = node
;
74 nodes
= doc
.SelectNodes ("wsdl:definitions/wsdl:types/s:schema", nsManager
);
75 foreach (XmlElement schema
in nodes
)
76 GenerateSchemaCode (schema
);
80 nodes
= doc
.SelectNodes ("wsdl:definitions/wsdl:service/wsdl:port", nsManager
);
81 foreach (XmlElement port
in nodes
)
82 GeneratePortCode (port
);
85 if (mainFile
.FileName
!= null)
86 outCodeStreamList
.Add (mainFile
.FilePath
);
89 void GeneratePortCode (XmlElement port
)
91 XmlElement binding
= GetBinding (port
.GetAttribute ("binding"));
92 XmlElement type
= null;
93 foreach (XmlNode node
in binding
)
94 if ((node
is XmlElement
) && ((XmlElement
)node
).NamespaceURI
== MetaData
.SudsNamespace
)
95 { type = (XmlElement) node; break; }
97 string rootType
= type
.GetAttribute ("rootType");
98 if (rootType
== "Delegate")
99 GenerateServiceDelegateCode (port
, binding
, type
);
101 GenerateServiceClassCode (port
, binding
, type
);
104 void GenerateServiceDelegateCode (XmlElement port
, XmlElement binding
, XmlElement type
)
106 string typeName
= (type
!= null) ? type
.GetAttribute ("type") : port
.GetAttribute ("name");
107 string portName
= GetNameFromQn (binding
.GetAttribute ("type"));
110 GetTypeQualifiedName (port
, typeName
, out name
, out ns
);
111 currentFile
.SetCurrentNamespace (ns
);
113 XmlElement oper
= (XmlElement
) binding
.SelectSingleNode ("wsdl:operation[@name='Invoke']", nsManager
);
114 if (oper
== null) throw new InvalidOperationException ("Invalid delegate schema");
118 GetParameters (oper
, portName
, "Invoke", out parsDec
, out returnType
);
120 currentFile
.WriteLine ("public delegate " + returnType
+ " " + name
+ " (" + parsDec
+ ");");
121 currentFile
.WriteLine ("");
124 void GenerateServiceClassCode (XmlElement port
, XmlElement binding
, XmlElement type
)
126 string typeName
= (type
!= null) ? type
.GetAttribute ("type") : port
.GetAttribute ("name");
129 GetTypeQualifiedName (port
, typeName
, out name
, out ns
);
130 currentFile
.SetCurrentNamespace (ns
);
132 string cls
= "public " + type
.LocalName
+ " " + name
;
133 string baset
= type
.GetAttribute ("extends");
134 if (baset
!= "") cls
+= ": " + GetTypeQualifiedName (port
, baset
);
138 XmlNodeList interfaces
= type
.SelectNodes ("suds:implements",nsManager
);
139 if (interfaces
.Count
== 0) interfaces
= type
.SelectNodes ("suds:extends",nsManager
);
141 foreach (XmlElement interf
in interfaces
)
143 string iname
= GetTypeQualifiedName (interf
, interf
.GetAttribute ("type"));
144 if (cls
.IndexOf (':') == -1) cls
+= ": " + iname
;
145 else cls
+= ", " + iname
;
148 currentFile
.WriteLine (cls
);
149 currentFile
.WriteLineInd ("{");
151 string portName
= GetNameFromQn (binding
.GetAttribute ("type"));
152 bool isInterface
= type
.LocalName
== "interface";
154 string vis
= isInterface
? "":"public ";
156 ArrayList mets
= GetMethods (portName
, binding
);
157 foreach (MethodData met
in mets
)
161 string prop
= vis
+ met
.ReturnType
+ " ";
162 if (met
.Signature
!= "") prop
+= "this [" + met
.Signature
+ "]";
163 else prop
+= met
.Name
;
168 if (met
.HasGet
) prop
+= "get; ";
169 if (met
.HasSet
) prop
+= "set; ";
171 currentFile
.WriteLine (prop
);
175 currentFile
.WriteLine (prop
);
176 currentFile
.WriteLineInd ("{");
177 if (met
.HasGet
) currentFile
.WriteLine ("get { throw new NotImplementedException (); }");
178 if (met
.HasSet
) currentFile
.WriteLine ("set { throw new NotImplementedException (); }");
179 currentFile
.WriteLineUni ("}");
180 currentFile
.WriteLine ("");
185 currentFile
.WriteLine (vis
+ met
.ReturnType
+ " " + met
.Name
+ " (" + met
.Signature
+ ")" + (isInterface
?";":""));
188 currentFile
.WriteLineInd ("{");
189 currentFile
.WriteLine ("throw new NotImplementedException ();");
190 currentFile
.WriteLineUni ("}");
191 currentFile
.WriteLine ("");
196 currentFile
.WriteLineUni ("}");
197 currentFile
.WriteLine ("");
202 public string ReturnType
;
203 public string Signature
;
208 public bool IsProperty { get { return HasGet || HasSet; }
}
211 ArrayList
GetMethods (string portName
, XmlElement binding
)
213 ArrayList mets
= new ArrayList ();
215 XmlNodeList nodes
= binding
.SelectNodes ("wsdl:operation", nsManager
);
216 foreach (XmlElement oper
in nodes
)
218 MethodData md
= new MethodData ();
219 md
.Name
= oper
.GetAttribute ("name");
221 GetParameters (oper
, portName
, md
.Name
, out md
.Signature
, out md
.ReturnType
);
223 if (md
.Name
.StartsWith ("set_") || md
.Name
.StartsWith ("get_"))
225 string tmp
= ", " + md
.Signature
;
226 if (tmp
.IndexOf (", out ") == -1 && tmp
.IndexOf (", ref ") == -1)
228 bool isSet
= md
.Name
[0]=='s';
229 md
.Name
= md
.Name
.Substring (4);
230 MethodData previousProp
= null;
232 foreach (MethodData fmd
in mets
)
233 if (fmd
.Name
== md
.Name
&& fmd
.IsProperty
)
236 if (previousProp
!= null) {
237 if (isSet
) previousProp
.HasSet
= true;
238 else { previousProp.HasGet = true; previousProp.Signature = md.Signature; }
242 if (isSet
) { md.HasSet = true; md.Signature = ""; }
243 else md
.HasGet
= true;
253 void GetParameters (XmlElement oper
, string portName
, string operName
, out string signature
, out string returnType
)
257 XmlElement portType
= (XmlElement
) doc
.SelectSingleNode ("wsdl:definitions/wsdl:portType[@name='" + portName
+ "']", nsManager
);
258 XmlElement portOper
= (XmlElement
) portType
.SelectSingleNode ("wsdl:operation[@name='" + operName
+ "']", nsManager
);
259 string[] parNames
= portOper
.GetAttribute ("parameterOrder").Split (' ');
261 XmlElement inPortMsg
= (XmlElement
) portOper
.SelectSingleNode ("wsdl:input", nsManager
);
262 XmlElement inMsg
= FindMessageFromPortMessage (inPortMsg
);
264 XmlElement outPortMsg
= (XmlElement
) portOper
.SelectSingleNode ("wsdl:output", nsManager
);
265 XmlElement outMsg
= FindMessageFromPortMessage (outPortMsg
);
268 if (parNames
[0] != "") parameters
= new string [parNames
.Length
];
269 else parameters
= new string [0];
271 foreach (XmlElement part
in inMsg
.SelectNodes ("wsdl:part",nsManager
))
273 int i
= Array
.IndexOf (parNames
, part
.GetAttribute ("name"));
274 string type
= GetTypeQualifiedName (part
, part
.GetAttribute ("type"));
275 parameters
[i
] = type
+ " " + parNames
[i
];
278 foreach (XmlElement part
in outMsg
.SelectNodes ("wsdl:part",nsManager
))
280 string pn
= part
.GetAttribute ("name");
281 string type
= GetTypeQualifiedName (part
, part
.GetAttribute ("type"));
286 int i
= Array
.IndexOf (parNames
, pn
);
287 if (parameters
[i
] != null) parameters
[i
] = "ref " + parameters
[i
];
288 else parameters
[i
] = "out " + type
+ " " + pn
;
292 signature
= string.Join (", ", parameters
);
293 if (returnType
== null) returnType
= "void";
296 XmlElement
FindMessageFromPortMessage (XmlElement portMsg
)
298 string msgName
= portMsg
.GetAttribute ("message");
299 msgName
= GetNameFromQn (msgName
);
300 return (XmlElement
) doc
.SelectSingleNode ("wsdl:definitions/wsdl:message[@name='" + msgName
+ "']", nsManager
);
303 void GenerateSchemaCode (XmlElement schema
)
305 string ns
= schema
.GetAttribute ("targetNamespace");
306 string clrNs
= DecodeNamespace (ns
);
307 currentFile
.SetCurrentNamespace (clrNs
);
309 foreach (XmlNode node
in schema
)
311 XmlElement elem
= node
as XmlElement
;
312 if (elem
== null) continue;
314 if (elem
.LocalName
== "complexType")
315 GenerateClassCode (ns
, elem
);
316 else if (elem
.LocalName
== "simpleType")
317 GenerateEnumCode (ns
, elem
);
321 void GenerateClassCode (string ns
, XmlElement elem
)
323 if (elem
.SelectSingleNode ("s:complexContent/s:restriction", nsManager
) != null) return;
324 string clrNs
= DecodeNamespace (ns
);
325 string typeName
= GetTypeName (elem
.GetAttribute ("name"), ns
);
327 XmlElement sudsType
= (XmlElement
) sudsTypes
[clrNs
+ "." + typeName
];
329 string typetype
= "class";
330 if (sudsType
!= null) typetype
= sudsType
.LocalName
;
332 currentFile
.WriteLine ("[Serializable, SoapType (XmlNamespace = @\"" + ns
+ "\", XmlTypeNamespace = @\"" + ns
+ "\")]");
334 string cls
= "public " + typetype
+ " " + typeName
;
335 string baseType
= elem
.GetAttribute ("base");
336 if (baseType
!= "") cls
+= ": " + GetTypeQualifiedName (elem
, baseType
);
338 bool isSerializable
= (sudsType
.GetAttribute ("rootType") == "ISerializable");
342 if (cls
.IndexOf (':') == -1) cls
+= ": ";
344 cls
+= "System.Runtime.Serialization.ISerializable";
347 currentFile
.WriteLine (cls
);
348 currentFile
.WriteLineInd ("{");
350 XmlNodeList elems
= elem
.GetElementsByTagName ("element", MetaData
.SchemaNamespace
);
351 foreach (XmlElement elemField
in elems
)
352 WriteField (elemField
);
354 elems
= elem
.GetElementsByTagName ("attribute", MetaData
.SchemaNamespace
);
355 foreach (XmlElement elemField
in elems
)
356 WriteField (elemField
);
360 currentFile
.WriteLine ("");
361 currentFile
.WriteLine ("public " + typeName
+ " ()");
362 currentFile
.WriteLineInd ("{");
363 currentFile
.WriteLineUni ("}");
364 currentFile
.WriteLine ("");
366 currentFile
.WriteLine ("public " + typeName
+ " (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)");
367 currentFile
.WriteLineInd ("{");
368 currentFile
.WriteLine ("throw new NotImplementedException ();");
369 currentFile
.WriteLineUni ("}");
370 currentFile
.WriteLine ("");
372 currentFile
.WriteLine ("public void GetObjectData (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)");
373 currentFile
.WriteLineInd ("{");
374 currentFile
.WriteLine ("throw new NotImplementedException ();");
375 currentFile
.WriteLineUni ("}");
378 currentFile
.WriteLineUni ("}");
379 currentFile
.WriteLine ("");
382 void WriteField (XmlElement elemField
)
384 bool isAttr
= elemField
.LocalName
== "attribute";
386 string type
= elemField
.GetAttribute ("type");
389 currentFile
.WriteLine ("[SoapField (UseAttribute = true)]");
390 else if (!IsPrimitive (elemField
, type
))
391 currentFile
.WriteLine ("[SoapField (Embedded = true)]");
392 currentFile
.WriteLine ("public " + GetTypeQualifiedName (elemField
, type
) + " " + elemField
.GetAttribute ("name") + ";");
395 void GenerateEnumCode (string ns
, XmlElement elem
)
397 currentFile
.WriteLine ("public enum " + GetTypeName (elem
.GetAttribute ("name"), ns
));
398 currentFile
.WriteLineInd ("{");
400 XmlNodeList nodes
= elem
.SelectNodes ("s:restriction/s:enumeration/@value", nsManager
);
401 foreach (XmlNode node
in nodes
)
402 currentFile
.WriteLine (node
.Value
+ ",");
404 currentFile
.WriteLineUni ("}");
405 currentFile
.WriteLine ("");
408 bool IsPrimitive (XmlNode node
, string qname
)
410 string name
= GetTypeQualifiedName (node
, qname
);
411 return name
.IndexOf ('.') == -1;
414 string GetTypeName (string localName
, string ns
)
419 void GetTypeQualifiedName (XmlNode node
, string qualifiedName
, out string name
, out string ns
)
421 int i
= qualifiedName
.IndexOf (':');
424 name
= qualifiedName
;
429 string prefix
= qualifiedName
.Substring (0,i
);
430 name
= qualifiedName
.Substring (i
+1);
431 ns
= node
.GetNamespaceOfPrefix (prefix
);
433 string arrayType
= GetArrayType (node
, name
, ns
);
434 if (arrayType
!= null) {
438 else if (ns
!= MetaData
.SchemaNamespace
) {
439 ns
= DecodeNamespace (ns
);
443 name
= GetClrFromXsd (name
);
447 string GetClrFromXsd (string type
)
451 case "boolean": return "bool";
452 case "unsignedByte": return "byte";
453 case "char": return "char";
454 case "dateTime": return "DateTime";
455 case "decimal": return "decimal";
456 case "double": return "double";
457 case "short": return "short";
458 case "int": return "int";
459 case "long": return "long";
460 case "byte": return "sbyte";
461 case "float": return "float";
462 case "unsignedShort": return "ushort";
463 case "unsignedInt": return "uint";
464 case "unsignedLong": return "ulong";
465 case "string": return "string";
466 case "duration": return "TimeSpan";
467 case "anyType": return "object";
469 throw new InvalidOperationException ("Unknown schema type: " + type
);
472 string GetTypeQualifiedName (XmlNode node
, string qualifiedName
)
475 GetTypeQualifiedName (node
, qualifiedName
, out name
, out ns
);
476 if (ns
!= "") return ns
+ "." + name
;
480 string GetTypeNamespace (XmlNode node
, string qualifiedName
)
483 GetTypeQualifiedName (node
, qualifiedName
, out name
, out ns
);
487 string GetArrayType (XmlNode node
, string name
, string ns
)
489 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
);
490 if (anod
== null) return null;
492 string atype
= anod
.Value
;
493 int i
= atype
.IndexOf ('[');
494 string itemType
= GetTypeQualifiedName (node
, atype
.Substring (0,i
));
496 return itemType
+ atype
.Substring (i
);
499 XmlElement
GetBinding (string name
)
501 int i
= name
.IndexOf (':');
502 name
= name
.Substring (i
+1);
503 return doc
.SelectSingleNode ("wsdl:definitions/wsdl:binding[@name='" + name
+ "']", nsManager
) as XmlElement
;
506 string DecodeNamespace (string xmlNamespace
)
510 if (!SoapServices
.DecodeXmlNamespaceForClrTypeNamespace (xmlNamespace
, out tns
, out tasm
))
516 string GetLiteral (object ob
)
518 if (ob
== null) return "null";
519 if (ob
is string) return "\"" + ob
.ToString().Replace("\"","\"\"") + "\"";
520 if (ob
is bool) return ((bool)ob
) ? "true" : "false";
521 if (ob
is XmlQualifiedName
) {
522 XmlQualifiedName qn
= (XmlQualifiedName
)ob
;
523 return "new XmlQualifiedName (" + GetLiteral(qn
.Name
) + "," + GetLiteral(qn
.Namespace
) + ")";
525 else return ob
.ToString ();
528 string Params (params string[] pars
)
531 foreach (string p
in pars
)
533 if (res
!= "") res
+= ", ";
539 string GetNameFromQn (string qn
)
541 int i
= qn
.IndexOf (':');
542 if (i
== -1) return qn
;
543 else return qn
.Substring (i
+1);
549 public string FileName
;
550 public string Directory
;
551 public string FilePath
;
552 Hashtable namespaces
= new Hashtable ();
553 public StringWriter writer
;
556 string currentNamespace
;
558 public CodeFile (string directory
)
560 Directory
= directory
;
563 public void SetCurrentNamespace (string ns
)
565 writer
= namespaces
[ns
] as StringWriter
;
569 writer
= new StringWriter ();
570 namespaces
[ns
] = writer
;
571 WriteLine ("namespace " + ns
);
577 if (FileName
== null)
578 FileName
= ns
+ ".cs";
581 public void WriteLineInd (string code
)
587 public void WriteLineUni (string code
)
589 if (indent
> 0) indent
--;
593 public void WriteLine (string code
)
595 if (code
!= "") writer
.Write (new String ('\t',indent
));
596 writer
.WriteLine (code
);
601 if (FileName
== null) return;
603 FilePath
= Path
.Combine (Directory
, FileName
);
604 StreamWriter sw
= new StreamWriter (FilePath
);
606 sw
.WriteLine ("using System;");
607 sw
.WriteLine ("using System.Runtime.Remoting.Metadata;");
610 foreach (StringWriter nsWriter
in namespaces
.Values
)
612 sw
.Write (nsWriter
.ToString ());