**** Merged from MCS ****
[mono-project.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.MetadataServices / MetaDataCodeGenerator.cs
blob637d6ad7520dca16732d108879e61350a79ba886
1 //
2 // System.Runtime.Remoting.MetadataServices.MetaDataCodeGenerator
3 //
4 // Authors:
5 // Lluis Sanchez Gual (lluis@ximian.com)
6 //
7 // (C) 2003 Novell, Inc
8 //
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:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
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;
32 using System.IO;
33 using System.Xml;
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
42 XmlDocument doc;
43 CodeFile currentFile;
44 XmlNamespaceManager nsManager;
45 Hashtable sudsTypes;
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;
65 // Suds types
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;
72 // Data types
74 nodes = doc.SelectNodes ("wsdl:definitions/wsdl:types/s:schema", nsManager);
75 foreach (XmlElement schema in nodes)
76 GenerateSchemaCode (schema);
78 // Services
80 nodes = doc.SelectNodes ("wsdl:definitions/wsdl:service/wsdl:port", nsManager);
81 foreach (XmlElement port in nodes)
82 GeneratePortCode (port);
84 mainFile.Write ();
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);
100 else
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"));
109 string name, ns;
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");
116 string parsDec;
117 string returnType;
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");
128 string name, ns;
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);
136 // Interfaces
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)
159 if (met.IsProperty)
161 string prop = vis + met.ReturnType + " ";
162 if (met.Signature != "") prop += "this [" + met.Signature + "]";
163 else prop += met.Name;
165 if (isInterface)
167 prop += " { ";
168 if (met.HasGet) prop += "get; ";
169 if (met.HasSet) prop += "set; ";
170 prop += "}";
171 currentFile.WriteLine (prop);
173 else
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 ("");
183 else
185 currentFile.WriteLine (vis + met.ReturnType + " " + met.Name + " (" + met.Signature + ")" + (isInterface?";":""));
186 if (!isInterface)
188 currentFile.WriteLineInd ("{");
189 currentFile.WriteLine ("throw new NotImplementedException ();");
190 currentFile.WriteLineUni ("}");
191 currentFile.WriteLine ("");
196 currentFile.WriteLineUni ("}");
197 currentFile.WriteLine ("");
200 class MethodData
202 public string ReturnType;
203 public string Signature;
204 public string Name;
205 public bool HasSet;
206 public bool HasGet;
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)
234 previousProp = fmd;
236 if (previousProp != null) {
237 if (isSet) previousProp.HasSet = true;
238 else { previousProp.HasGet = true; previousProp.Signature = md.Signature; }
239 continue;
241 else {
242 if (isSet) { md.HasSet = true; md.Signature = ""; }
243 else md.HasGet = true;
248 mets.Add (md);
250 return mets;
253 void GetParameters (XmlElement oper, string portName, string operName, out string signature, out string returnType)
255 returnType = null;
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);
267 string[] parameters;
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"));
283 if (pn == "return")
284 returnType = type;
285 else {
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");
340 if (isSerializable)
342 if (cls.IndexOf (':') == -1) cls += ": ";
343 else 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);
358 if (isSerializable)
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");
388 if (isAttr)
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)
416 return localName;
419 void GetTypeQualifiedName (XmlNode node, string qualifiedName, out string name, out string ns)
421 int i = qualifiedName.IndexOf (':');
422 if (i == -1)
424 name = qualifiedName;
425 ns = "";
426 return;
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) {
435 name = arrayType;
436 ns = "";
438 else if (ns != MetaData.SchemaNamespace) {
439 ns = DecodeNamespace (ns);
441 else {
442 ns = "";
443 name = GetClrFromXsd (name);
447 string GetClrFromXsd (string type)
449 switch (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)
474 string name, ns;
475 GetTypeQualifiedName (node, qualifiedName, out name, out ns);
476 if (ns != "") return ns + "." + name;
477 else return name;
480 string GetTypeNamespace (XmlNode node, string qualifiedName)
482 string name, ns;
483 GetTypeQualifiedName (node, qualifiedName, out name, out ns);
484 return 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)
508 string tns, tasm;
510 if (!SoapServices.DecodeXmlNamespaceForClrTypeNamespace (xmlNamespace, out tns, out tasm))
511 tns = xmlNamespace;
513 return tns;
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)
530 string res = "";
531 foreach (string p in pars)
533 if (res != "") res += ", ";
534 res += p;
536 return res;
539 string GetNameFromQn (string qn)
541 int i = qn.IndexOf (':');
542 if (i == -1) return qn;
543 else return qn.Substring (i+1);
547 class CodeFile
549 public string FileName;
550 public string Directory;
551 public string FilePath;
552 Hashtable namespaces = new Hashtable ();
553 public StringWriter writer;
554 int indent;
556 string currentNamespace;
558 public CodeFile (string directory)
560 Directory = directory;
563 public void SetCurrentNamespace (string ns)
565 writer = namespaces [ns] as StringWriter;
566 if (writer == null)
568 indent = 0;
569 writer = new StringWriter ();
570 namespaces [ns] = writer;
571 WriteLine ("namespace " + ns);
572 WriteLineInd ("{");
575 indent = 1;
577 if (FileName == null)
578 FileName = ns + ".cs";
581 public void WriteLineInd (string code)
583 WriteLine (code);
584 indent++;
587 public void WriteLineUni (string code)
589 if (indent > 0) indent--;
590 WriteLine (code);
593 public void WriteLine (string code)
595 if (code != "") writer.Write (new String ('\t',indent));
596 writer.WriteLine (code);
599 public void Write ()
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;");
608 sw.WriteLine ();
610 foreach (StringWriter nsWriter in namespaces.Values)
612 sw.Write (nsWriter.ToString ());
613 sw.WriteLine ("}");
614 sw.WriteLine ();
617 sw.Close ();