XmlSchemaImporter.cs 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  1. //
  2. // System.Xml.Serialization.XmlSchemaImporter
  3. //
  4. // Author:
  5. // Tim Coleman ([email protected])
  6. //
  7. // Copyright (C) Tim Coleman, 2002
  8. //
  9. using System.Xml;
  10. using System.Xml.Schema;
  11. using System.Collections;
  12. namespace System.Xml.Serialization {
  13. public class XmlSchemaImporter {
  14. #region Fields
  15. XmlSchemas schemas;
  16. CodeIdentifiers typeIdentifiers;
  17. CodeIdentifiers elemIdentifiers = new CodeIdentifiers ();
  18. Hashtable mappedTypes = new Hashtable ();
  19. Hashtable dataMappedTypes = new Hashtable ();
  20. Queue pendingMaps = new Queue ();
  21. static readonly XmlQualifiedName anyType = new XmlQualifiedName ("anyType",XmlSchema.Namespace);
  22. XmlSchemaElement anyElement = null;
  23. class MapFixup
  24. {
  25. public XmlTypeMapping Map;
  26. public XmlSchemaComplexType SchemaType;
  27. public XmlQualifiedName TypeName;
  28. }
  29. #endregion
  30. #region Constructors
  31. public XmlSchemaImporter (XmlSchemas schemas)
  32. {
  33. this.schemas = schemas;
  34. typeIdentifiers = new CodeIdentifiers ();
  35. }
  36. public XmlSchemaImporter (XmlSchemas schemas, CodeIdentifiers typeIdentifiers)
  37. : this (schemas)
  38. {
  39. this.typeIdentifiers = typeIdentifiers;
  40. }
  41. #endregion // Constructors
  42. #region Methods
  43. [MonoTODO]
  44. public XmlMembersMapping ImportAnyType (XmlQualifiedName typeName, string elementName)
  45. {
  46. throw new NotImplementedException ();
  47. }
  48. [MonoTODO]
  49. public XmlTypeMapping ImportDerivedTypeMapping (XmlQualifiedName name, Type baseType)
  50. {
  51. throw new NotImplementedException ();
  52. }
  53. [MonoTODO]
  54. public XmlTypeMapping ImportDerivedTypeMapping (XmlQualifiedName name, bool baseTypeCanBeIndirect)
  55. {
  56. throw new NotImplementedException ();
  57. }
  58. [MonoTODO]
  59. public XmlTypeMapping ImportDerivedTypeMapping (XmlQualifiedName name,
  60. Type baseType,
  61. bool baseTypeCanBeIndirect)
  62. {
  63. throw new NotImplementedException ();
  64. }
  65. [MonoTODO]
  66. public XmlMembersMapping ImportMembersMapping (XmlQualifiedName name)
  67. {
  68. throw new NotImplementedException ();
  69. }
  70. [MonoTODO]
  71. public XmlMembersMapping ImportMembersMapping (XmlQualifiedName[] names)
  72. {
  73. throw new NotImplementedException ();
  74. }
  75. [MonoTODO]
  76. public XmlMembersMapping ImportMembersMapping (XmlQualifiedName[] names, Type baseType, bool baseTypeCanBeIndirect)
  77. {
  78. throw new NotImplementedException ();
  79. }
  80. public XmlTypeMapping ImportTypeMapping (XmlQualifiedName name)
  81. {
  82. XmlSchemaElement elem = (XmlSchemaElement) schemas.Find (name, typeof (XmlSchemaElement));
  83. if (elem == null) return null;
  84. // The root element must be an element with complex type
  85. XmlQualifiedName qname;
  86. XmlSchemaType stype;
  87. if (elem.SchemaType != null)
  88. {
  89. stype = elem.SchemaType;
  90. qname = name;
  91. }
  92. else
  93. {
  94. if (elem.SchemaTypeName.IsEmpty) return null;
  95. if (elem.SchemaTypeName.Namespace == XmlSchema.Namespace) return null;
  96. object type = schemas.Find (elem.SchemaTypeName, typeof (XmlSchemaComplexType));
  97. if (type == null) type = schemas.Find (elem.SchemaTypeName, typeof (XmlSchemaSimpleType));
  98. if (type == null) throw new InvalidOperationException ("Schema type '" + elem.SchemaTypeName + "' not found");
  99. stype = (XmlSchemaType) type;
  100. qname = stype.QualifiedName;
  101. }
  102. if (stype is XmlSchemaSimpleType) return null;
  103. XmlTypeMapping map = ImportType (qname, (XmlSchemaComplexType)stype, name);
  104. BuildPendingMaps ();
  105. return map;
  106. }
  107. public XmlTypeMapping ImportType (XmlQualifiedName name, XmlQualifiedName root)
  108. {
  109. XmlTypeMapping map = GetRegisteredTypeMapping (name);
  110. if (map != null) return map;
  111. XmlSchemaType type = (XmlSchemaType) schemas.Find (name, typeof (XmlSchemaComplexType));
  112. if (type == null) type = (XmlSchemaType) schemas.Find (name, typeof (XmlSchemaSimpleType));
  113. return ImportType (name, type, root);
  114. }
  115. XmlTypeMapping ImportType (XmlQualifiedName name, XmlSchemaType stype, XmlQualifiedName root)
  116. {
  117. XmlTypeMapping map = GetRegisteredTypeMapping (name);
  118. if (map != null) return map;
  119. if (stype is XmlSchemaComplexType)
  120. return ImportClassComplexType (name, (XmlSchemaComplexType) stype, root);
  121. else if (stype is XmlSchemaSimpleType)
  122. return ImportClassSimpleType (name, (XmlSchemaSimpleType) stype, root);
  123. throw new NotSupportedException ("Schema type not supported: " + stype.GetType ());
  124. }
  125. XmlTypeMapping ImportClassComplexType (XmlQualifiedName typeQName, XmlSchemaComplexType stype, XmlQualifiedName root)
  126. {
  127. // if (root != null) typeQName = root;
  128. XmlTypeMapping map;
  129. // The need for fixups: If the complex type is an array, then to get the type of the
  130. // array we need first to get the type of the items of the array.
  131. // But if one of the item types or its children has a referece to this type array,
  132. // then we enter in an infinite loop. This does not happen with class types because
  133. // the class map is registered before parsing the children. We can't do the same
  134. // with the array type because to register the array map we need the type of the array.
  135. if (root == null && CanBeArray (typeQName, stype))
  136. {
  137. TypeData typeData;
  138. ListMap listMap = BuildArrayMap (typeQName, stype, out typeData);
  139. if (listMap != null)
  140. {
  141. map = CreateArrayTypeMapping (typeQName, typeData);
  142. map.ObjectMap = listMap;
  143. return map;
  144. }
  145. // After all, it is not an array. Create a class map then.
  146. }
  147. else if (CanBeAnyElement (stype))
  148. {
  149. return GetTypeMapping (TypeTranslator.GetTypeData(typeof(XmlElement)));
  150. }
  151. else if (CanBeIXmlSerializable (stype))
  152. {
  153. return GetTypeMapping (TypeTranslator.GetTypeData(typeof(object)));
  154. }
  155. // Register the map right now but do not build it,
  156. // This will avoid loops.
  157. map = CreateTypeMapping (typeQName, SchemaTypes.Class, root);
  158. RegisterMapFixup (map, typeQName, stype);
  159. return map;
  160. }
  161. void RegisterMapFixup (XmlTypeMapping map, XmlQualifiedName typeQName, XmlSchemaComplexType stype)
  162. {
  163. MapFixup fixup = new MapFixup ();
  164. fixup.Map = map;
  165. fixup.SchemaType = stype;
  166. fixup.TypeName = typeQName;
  167. pendingMaps.Enqueue (fixup);
  168. }
  169. void BuildPendingMaps ()
  170. {
  171. while (pendingMaps.Count > 0) {
  172. MapFixup fixup = (MapFixup) pendingMaps.Dequeue ();
  173. if (fixup.Map.ObjectMap == null) {
  174. BuildClassMap (fixup.Map, fixup.TypeName, fixup.SchemaType);
  175. if (fixup.Map.ObjectMap == null) pendingMaps.Enqueue (fixup);
  176. }
  177. }
  178. }
  179. void BuildPendingMap (XmlTypeMapping map)
  180. {
  181. if (map.ObjectMap != null) return;
  182. foreach (MapFixup fixup in pendingMaps)
  183. {
  184. if (fixup.Map == map) {
  185. BuildClassMap (fixup.Map, fixup.TypeName, fixup.SchemaType);
  186. return;
  187. }
  188. }
  189. throw new InvalidOperationException ("Can't complete map of type " + map.XmlType + " : " + map.Namespace);
  190. }
  191. void BuildClassMap (XmlTypeMapping map, XmlQualifiedName typeQName, XmlSchemaComplexType stype)
  192. {
  193. CodeIdentifiers classIds = new CodeIdentifiers();
  194. classIds.AddReserved (map.TypeData.TypeName);
  195. ClassMap cmap = new ClassMap ();
  196. map.ObjectMap = cmap;
  197. bool isMixed = stype.IsMixed;
  198. if (stype.Particle != null)
  199. {
  200. ImportParticleComplexContent (typeQName, cmap, stype.Particle, classIds, isMixed);
  201. }
  202. else
  203. {
  204. if (stype.ContentModel is XmlSchemaSimpleContent) {
  205. ImportSimpleContent (typeQName, map, (XmlSchemaSimpleContent)stype.ContentModel, classIds, isMixed);
  206. }
  207. else if (stype.ContentModel is XmlSchemaComplexContent) {
  208. ImportComplexContent (typeQName, map, (XmlSchemaComplexContent)stype.ContentModel, classIds, isMixed);
  209. }
  210. }
  211. ImportAttributes (typeQName, cmap, stype.Attributes, stype.AnyAttribute, classIds);
  212. }
  213. void ImportAttributes (XmlQualifiedName typeQName, ClassMap cmap, XmlSchemaObjectCollection atts, XmlSchemaAnyAttribute anyat, CodeIdentifiers classIds)
  214. {
  215. if (anyat != null)
  216. {
  217. XmlTypeMapMemberAnyAttribute member = new XmlTypeMapMemberAnyAttribute ();
  218. member.Name = classIds.AddUnique ("AnyAttribute", member);
  219. member.TypeData = TypeTranslator.GetTypeData (typeof(XmlAttribute[]));
  220. cmap.AddMember (member);
  221. }
  222. foreach (XmlSchemaObject at in atts)
  223. {
  224. if (at is XmlSchemaAttribute)
  225. {
  226. string ns;
  227. XmlSchemaAttribute attr = (XmlSchemaAttribute)at;
  228. XmlSchemaAttribute refAttr = GetRefAttribute (typeQName, attr, out ns);
  229. XmlTypeMapMemberAttribute member = new XmlTypeMapMemberAttribute ();
  230. member.Name = classIds.AddUnique (CodeIdentifier.MakeValid (refAttr.Name), member);
  231. member.AttributeName = refAttr.Name;
  232. member.Namespace = ns;
  233. member.Form = refAttr.Form;
  234. member.TypeData = GetAttributeTypeData (typeQName, attr);
  235. if (refAttr.DefaultValue != null) member.DefaultValue = refAttr.DefaultValue;
  236. if (member.TypeData.IsComplexType)
  237. member.MappedType = GetTypeMapping (member.TypeData);
  238. cmap.AddMember (member);
  239. }
  240. else if (at is XmlSchemaAttributeGroupRef)
  241. {
  242. XmlSchemaAttributeGroupRef gref = (XmlSchemaAttributeGroupRef)at;
  243. XmlSchemaAttributeGroup grp = (XmlSchemaAttributeGroup) schemas.Find (gref.RefName, typeof(XmlSchemaAttributeGroup));
  244. ImportAttributes (typeQName, cmap, grp.Attributes, grp.AnyAttribute, classIds);
  245. }
  246. }
  247. }
  248. ListMap BuildArrayMap (XmlQualifiedName typeQName, XmlSchemaComplexType stype, out TypeData arrayTypeData)
  249. {
  250. ClassMap cmap = new ClassMap ();
  251. CodeIdentifiers classIds = new CodeIdentifiers();
  252. ImportParticleComplexContent (typeQName, cmap, stype.Particle, classIds, false);
  253. XmlTypeMapMemberFlatList list = (cmap.AllMembers.Count == 1) ? cmap.AllMembers[0] as XmlTypeMapMemberFlatList : null;
  254. if (list != null && list.ChoiceMember == null)
  255. {
  256. arrayTypeData = list.TypeData;
  257. return list.ListMap;
  258. }
  259. else
  260. {
  261. arrayTypeData = null;
  262. return null;
  263. }
  264. }
  265. void ImportParticleComplexContent (XmlQualifiedName typeQName, ClassMap cmap, XmlSchemaParticle particle, CodeIdentifiers classIds, bool isMixed)
  266. {
  267. ImportParticleContent (typeQName, cmap, particle, classIds, false, ref isMixed);
  268. if (isMixed)
  269. {
  270. XmlTypeMapMemberFlatList member = new XmlTypeMapMemberFlatList ();
  271. member.Name = classIds.AddUnique ("Text", member);
  272. member.TypeData = TypeTranslator.GetTypeData (typeof(string[]));
  273. member.ElementInfo.Add (CreateTextElementInfo (typeQName.Namespace, member, member.TypeData.ListItemTypeData));
  274. member.IsXmlTextCollector = true;
  275. member.ListMap = new ListMap ();
  276. member.ListMap.ItemInfo = member.ElementInfo;
  277. cmap.AddMember (member);
  278. }
  279. }
  280. void ImportParticleContent (XmlQualifiedName typeQName, ClassMap cmap, XmlSchemaParticle particle, CodeIdentifiers classIds, bool multiValue, ref bool isMixed)
  281. {
  282. if (particle is XmlSchemaGroupRef)
  283. particle = GetRefGroupParticle ((XmlSchemaGroupRef)particle);
  284. if (particle.MaxOccurs > 1) multiValue = true;
  285. if (particle is XmlSchemaSequence) {
  286. ImportSequenceContent (typeQName, cmap, ((XmlSchemaSequence)particle).Items, classIds, multiValue, ref isMixed);
  287. }
  288. else if (particle is XmlSchemaChoice) {
  289. if (((XmlSchemaChoice)particle).Items.Count == 1)
  290. ImportSequenceContent (typeQName, cmap, ((XmlSchemaChoice)particle).Items, classIds, multiValue, ref isMixed);
  291. else
  292. ImportChoiceContent (typeQName, cmap, (XmlSchemaChoice)particle, classIds, multiValue);
  293. }
  294. else if (particle is XmlSchemaAll) {
  295. ImportSequenceContent (typeQName, cmap, ((XmlSchemaAll)particle).Items, classIds, multiValue, ref isMixed);
  296. }
  297. }
  298. void ImportSequenceContent (XmlQualifiedName typeQName, ClassMap cmap, XmlSchemaObjectCollection items, CodeIdentifiers classIds, bool multiValue, ref bool isMixed)
  299. {
  300. foreach (XmlSchemaObject item in items)
  301. {
  302. XmlTypeMapMember mapMember;
  303. if (item is XmlSchemaElement)
  304. {
  305. string ns;
  306. XmlSchemaElement elem = (XmlSchemaElement) item;
  307. TypeData typeData = GetElementTypeData (typeQName, elem);
  308. XmlSchemaElement refElem = GetRefElement (typeQName, elem, out ns);
  309. if (elem.MaxOccurs == 1 && !multiValue)
  310. {
  311. XmlTypeMapMemberElement member = null;
  312. if (typeData.SchemaType != SchemaTypes.Array)
  313. {
  314. member = new XmlTypeMapMemberElement ();
  315. if (refElem.DefaultValue != null) member.DefaultValue = refElem.DefaultValue;
  316. }
  317. else if (GetTypeMapping (typeData).IsSimpleType)
  318. {
  319. // It is a simple list (space separated list).
  320. // Since this is not supported, map as a single item value
  321. // TODO: improve this
  322. member = new XmlTypeMapMemberElement ();
  323. typeData = typeData.ListItemTypeData;
  324. }
  325. else
  326. member = new XmlTypeMapMemberList ();
  327. member.Name = classIds.AddUnique(CodeIdentifier.MakeValid(refElem.Name), member);
  328. member.TypeData = typeData;
  329. member.ElementInfo.Add (CreateElementInfo (ns, member, refElem.Name, typeData, refElem.IsNillable));
  330. cmap.AddMember (member);
  331. }
  332. else
  333. {
  334. XmlTypeMapMemberFlatList member = new XmlTypeMapMemberFlatList ();
  335. member.ListMap = new ListMap ();
  336. member.Name = classIds.AddUnique(CodeIdentifier.MakeValid(refElem.Name), member);
  337. member.TypeData = typeData.ListTypeData;
  338. member.ElementInfo.Add (CreateElementInfo (ns, member, refElem.Name, typeData, refElem.IsNillable));
  339. member.ListMap.ItemInfo = member.ElementInfo;
  340. cmap.AddMember (member);
  341. }
  342. }
  343. else if (item is XmlSchemaAny)
  344. {
  345. XmlSchemaAny elem = (XmlSchemaAny) item;
  346. XmlTypeMapMemberAnyElement member = new XmlTypeMapMemberAnyElement ();
  347. member.Name = classIds.AddUnique ("Any", member);
  348. Type ctype;
  349. if (elem.MaxOccurs > 1 || multiValue)
  350. ctype = isMixed ? typeof(XmlNode[]) : typeof(XmlElement[]);
  351. else
  352. ctype = isMixed ? typeof(XmlNode) : typeof(XmlElement);
  353. member.TypeData = TypeTranslator.GetTypeData (ctype);
  354. XmlTypeMapElementInfo einfo = new XmlTypeMapElementInfo (member, member.TypeData);
  355. einfo.IsUnnamedAnyElement = true;
  356. member.ElementInfo.Add (einfo);
  357. if (isMixed)
  358. {
  359. einfo = CreateTextElementInfo (typeQName.Namespace, member, member.TypeData);
  360. member.ElementInfo.Add (einfo);
  361. member.IsXmlTextCollector = true;
  362. isMixed = false; //Allow only one XmlTextAttribute
  363. }
  364. cmap.AddMember (member);
  365. }
  366. else if (item is XmlSchemaParticle) {
  367. ImportParticleContent (typeQName, cmap, (XmlSchemaParticle)item, classIds, multiValue, ref isMixed);
  368. }
  369. }
  370. }
  371. void ImportChoiceContent (XmlQualifiedName typeQName, ClassMap cmap, XmlSchemaChoice choice, CodeIdentifiers classIds, bool multiValue)
  372. {
  373. XmlTypeMapElementInfoList choices = new XmlTypeMapElementInfoList ();
  374. multiValue = ImportChoices (typeQName, null, choices, choice.Items) || multiValue;
  375. if (choices.Count == 0) return;
  376. if (choice.MaxOccurs > 1) multiValue = true;
  377. XmlTypeMapMemberElement member;
  378. if (multiValue)
  379. {
  380. member = new XmlTypeMapMemberFlatList ();
  381. member.Name = classIds.AddUnique ("Items", member);
  382. ListMap listMap = new ListMap ();
  383. listMap.ItemInfo = choices;
  384. ((XmlTypeMapMemberFlatList)member).ListMap = listMap;
  385. }
  386. else
  387. {
  388. member = new XmlTypeMapMemberElement ();
  389. member.Name = classIds.AddUnique ("Item", member);
  390. }
  391. // If all choices have the same type, use that type for the member.
  392. // If not use System.Object.
  393. // If there are at least two choices with the same type, use a choice
  394. // identifier attribute
  395. TypeData typeData = null;
  396. bool twoEqual = false;
  397. bool allEqual = true;
  398. Hashtable types = new Hashtable ();
  399. foreach (XmlTypeMapElementInfo einfo in choices)
  400. {
  401. if (types.ContainsKey (einfo.TypeData)) twoEqual = true;
  402. else types.Add (einfo.TypeData, einfo);
  403. TypeData choiceType = einfo.TypeData;
  404. if (choiceType.SchemaType == SchemaTypes.Class)
  405. {
  406. // When comparing class types, use the most generic class in the
  407. // inheritance hierarchy
  408. XmlTypeMapping choiceMap = GetTypeMapping (choiceType);
  409. BuildPendingMap (choiceMap);
  410. while (choiceMap.BaseMap != null) {
  411. choiceMap = choiceMap.BaseMap;
  412. BuildPendingMap (choiceMap);
  413. choiceType = choiceMap.TypeData;
  414. }
  415. }
  416. if (typeData == null) typeData = choiceType;
  417. else if (typeData != choiceType) allEqual = false;
  418. }
  419. if (!allEqual)
  420. typeData = TypeTranslator.GetTypeData (typeof(object));
  421. if (twoEqual)
  422. {
  423. // Create the choice member
  424. XmlTypeMapMemberElement choiceMember = new XmlTypeMapMemberElement ();
  425. choiceMember.Name = classIds.AddUnique (member.Name + "ElementName", choiceMember);
  426. member.ChoiceMember = choiceMember.Name;
  427. // Create the choice enum
  428. XmlTypeMapping enumMap = CreateTypeMapping (new XmlQualifiedName (member.Name + "ChoiceType", typeQName.Namespace), SchemaTypes.Enum, null);
  429. CodeIdentifiers codeIdents = new CodeIdentifiers ();
  430. EnumMap.EnumMapMember[] members = new EnumMap.EnumMapMember [choices.Count];
  431. for (int n=0; n<choices.Count; n++)
  432. {
  433. XmlTypeMapElementInfo it =(XmlTypeMapElementInfo) choices[n];
  434. string xmlName = (it.Namespace != null && it.Namespace != "") ? it.Namespace + ":" + it.ElementName : it.ElementName;
  435. string enumName = codeIdents.AddUnique (CodeIdentifier.MakeValid (it.ElementName), it);
  436. members [n] = new EnumMap.EnumMapMember (xmlName, enumName);
  437. }
  438. enumMap.ObjectMap = new EnumMap (members, false);
  439. choiceMember.TypeData = multiValue ? enumMap.TypeData.ListTypeData : enumMap.TypeData;
  440. choiceMember.ElementInfo.Add (CreateElementInfo (typeQName.Namespace, choiceMember, choiceMember.Name, choiceMember.TypeData, false));
  441. cmap.AddMember (choiceMember);
  442. }
  443. if (multiValue)
  444. typeData = typeData.ListTypeData;
  445. member.ElementInfo = choices;
  446. member.TypeData = typeData;
  447. cmap.AddMember (member);
  448. }
  449. bool ImportChoices (XmlQualifiedName typeQName, XmlTypeMapMember member, XmlTypeMapElementInfoList choices, XmlSchemaObjectCollection items)
  450. {
  451. bool multiValue = false;
  452. foreach (XmlSchemaObject titem in items)
  453. {
  454. XmlSchemaObject item = titem;
  455. if (item is XmlSchemaGroupRef)
  456. item = GetRefGroupParticle ((XmlSchemaGroupRef)item);
  457. if (item is XmlSchemaElement)
  458. {
  459. string ns;
  460. XmlSchemaElement elem = (XmlSchemaElement) item;
  461. TypeData typeData = GetElementTypeData (typeQName, elem);
  462. XmlSchemaElement refElem = GetRefElement (typeQName, elem, out ns);
  463. choices.Add (CreateElementInfo (ns, member, refElem.Name, typeData, refElem.IsNillable));
  464. if (elem.MaxOccurs > 1) multiValue = true;
  465. }
  466. else if (item is XmlSchemaAny)
  467. {
  468. XmlTypeMapElementInfo einfo = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(typeof(XmlElement)));
  469. einfo.IsUnnamedAnyElement = true;
  470. choices.Add (einfo);
  471. }
  472. else if (item is XmlSchemaChoice) {
  473. multiValue = ImportChoices (typeQName, member, choices, ((XmlSchemaChoice)item).Items) || multiValue;
  474. }
  475. else if (item is XmlSchemaSequence) {
  476. multiValue = ImportChoices (typeQName, member, choices, ((XmlSchemaSequence)item).Items) || multiValue;
  477. }
  478. }
  479. return multiValue;
  480. }
  481. void ImportSimpleContent (XmlQualifiedName typeQName, XmlTypeMapping map, XmlSchemaSimpleContent content, CodeIdentifiers classIds, bool isMixed)
  482. {
  483. ClassMap cmap = (ClassMap)map.ObjectMap;
  484. XmlQualifiedName qname = GetContentBaseType (content.Content);
  485. XmlTypeMapMemberElement member = new XmlTypeMapMemberElement ();
  486. member.Name = classIds.AddUnique("Value", member);
  487. member.TypeData = FindBuiltInType (qname);
  488. member.ElementInfo.Add (CreateTextElementInfo (typeQName.Namespace, member, member.TypeData));
  489. member.IsXmlTextCollector = true;
  490. cmap.AddMember (member);
  491. XmlSchemaSimpleContentExtension ext = content.Content as XmlSchemaSimpleContentExtension;
  492. if (ext != null)
  493. ImportAttributes (typeQName, cmap, ext.Attributes, ext.AnyAttribute, classIds);
  494. }
  495. TypeData FindBuiltInType (XmlQualifiedName qname)
  496. {
  497. if (qname.Namespace == XmlSchema.Namespace)
  498. return TypeTranslator.GetPrimitiveTypeData (qname.Name);
  499. XmlSchemaComplexType ct = (XmlSchemaComplexType) schemas.Find (qname, typeof(XmlSchemaComplexType));
  500. if (ct != null)
  501. {
  502. XmlSchemaSimpleContent sc = ct.ContentModel as XmlSchemaSimpleContent;
  503. if (sc == null) throw new InvalidOperationException ("Invalid schema");
  504. return FindBuiltInType (GetContentBaseType (sc.Content));
  505. }
  506. XmlSchemaSimpleType st = (XmlSchemaSimpleType) schemas.Find (qname, typeof(XmlSchemaSimpleType));
  507. if (st != null)
  508. return FindBuiltInType (qname, st);
  509. throw new InvalidOperationException ("Definition of type " + qname + " not found");
  510. }
  511. TypeData FindBuiltInType (XmlQualifiedName qname, XmlSchemaSimpleType st)
  512. {
  513. if (CanBeEnum (st))
  514. return ImportType (qname, null).TypeData;
  515. if (st.Content is XmlSchemaSimpleTypeRestriction) {
  516. return FindBuiltInType (GetContentBaseType (st.Content));
  517. }
  518. else if (st.Content is XmlSchemaSimpleTypeList) {
  519. return FindBuiltInType (GetContentBaseType (st.Content)).ListTypeData;
  520. }
  521. else if (st.Content is XmlSchemaSimpleTypeUnion)
  522. {
  523. // Check if all types of the union are equal. If not, then will use anyType.
  524. XmlSchemaSimpleTypeUnion uni = (XmlSchemaSimpleTypeUnion) st.Content;
  525. TypeData utype = null;
  526. // Anonymous types are unique
  527. if (uni.BaseTypes.Count != 0 && uni.MemberTypes.Length != 0)
  528. return FindBuiltInType (anyType);
  529. foreach (XmlQualifiedName mt in uni.MemberTypes)
  530. {
  531. TypeData qn = FindBuiltInType (mt);
  532. if (utype != null && qn != utype) return FindBuiltInType (anyType);
  533. else utype = qn;
  534. }
  535. return utype;
  536. }
  537. else
  538. return null;
  539. }
  540. XmlQualifiedName GetContentBaseType (XmlSchemaObject ob)
  541. {
  542. if (ob is XmlSchemaSimpleContentExtension)
  543. return ((XmlSchemaSimpleContentExtension)ob).BaseTypeName;
  544. else if (ob is XmlSchemaSimpleContentRestriction)
  545. return ((XmlSchemaSimpleContentRestriction)ob).BaseTypeName;
  546. else if (ob is XmlSchemaSimpleTypeRestriction)
  547. return ((XmlSchemaSimpleTypeRestriction)ob).BaseTypeName;
  548. else if (ob is XmlSchemaSimpleTypeList)
  549. return ((XmlSchemaSimpleTypeList)ob).ItemTypeName;
  550. else
  551. return null;
  552. }
  553. void ImportComplexContent (XmlQualifiedName typeQName, XmlTypeMapping map, XmlSchemaComplexContent content, CodeIdentifiers classIds, bool isMixed)
  554. {
  555. XmlSchemaComplexContentExtension ext = content.Content as XmlSchemaComplexContentExtension;
  556. ClassMap cmap = (ClassMap)map.ObjectMap;
  557. XmlQualifiedName qname;
  558. if (ext != null) qname = ext.BaseTypeName;
  559. else qname = ((XmlSchemaComplexContentRestriction)content.Content).BaseTypeName;
  560. // Add base map members to this map
  561. XmlTypeMapping baseMap = ImportType (qname, null);
  562. BuildPendingMap (baseMap);
  563. ClassMap baseClassMap = (ClassMap)baseMap.ObjectMap;
  564. foreach (XmlTypeMapMember member in baseClassMap.AllMembers)
  565. cmap.AddMember (member);
  566. if (baseClassMap.XmlTextCollector != null) isMixed = false;
  567. else if (content.IsMixed) isMixed = true;
  568. map.BaseMap = baseMap;
  569. baseMap.DerivedTypes.Add (map);
  570. if (ext != null) {
  571. // Add the members of this map
  572. ImportParticleComplexContent (typeQName, cmap, ext.Particle, classIds, isMixed);
  573. ImportAttributes (typeQName, cmap, ext.Attributes, ext.AnyAttribute, classIds);
  574. }
  575. else {
  576. if (isMixed) ImportParticleComplexContent (typeQName, cmap, null, classIds, true);
  577. }
  578. }
  579. XmlTypeMapping ImportClassSimpleType (XmlQualifiedName typeQName, XmlSchemaSimpleType stype, XmlQualifiedName root)
  580. {
  581. if (CanBeEnum (stype))
  582. {
  583. // Create an enum map
  584. CodeIdentifiers codeIdents = new CodeIdentifiers ();
  585. XmlSchemaSimpleTypeRestriction rest = (XmlSchemaSimpleTypeRestriction)stype.Content;
  586. XmlTypeMapping enumMap = CreateTypeMapping (typeQName, SchemaTypes.Enum, null);
  587. codeIdents.AddReserved (enumMap.TypeData.TypeName);
  588. EnumMap.EnumMapMember[] members = new EnumMap.EnumMapMember [rest.Facets.Count];
  589. for (int n=0; n<rest.Facets.Count; n++)
  590. {
  591. XmlSchemaEnumerationFacet enu = (XmlSchemaEnumerationFacet) rest.Facets[n];
  592. string enumName = codeIdents.AddUnique(CodeIdentifier.MakeValid (enu.Value), enu);
  593. members [n] = new EnumMap.EnumMapMember (enu.Value, enumName);
  594. }
  595. enumMap.ObjectMap = new EnumMap (members, false);
  596. enumMap.IsSimpleType = true;
  597. return enumMap;
  598. }
  599. if (stype.Content is XmlSchemaSimpleTypeList)
  600. {
  601. XmlSchemaSimpleTypeList slist = (XmlSchemaSimpleTypeList)stype.Content;
  602. TypeData arrayTypeData = FindBuiltInType (slist.ItemTypeName, stype);
  603. ListMap listMap = new ListMap ();
  604. listMap.ItemInfo = new XmlTypeMapElementInfoList ();
  605. listMap.ItemInfo.Add (CreateElementInfo (typeQName.Namespace, null, "Item", arrayTypeData.ListItemTypeData, false));
  606. XmlTypeMapping map = CreateArrayTypeMapping (typeQName, arrayTypeData);
  607. map.ObjectMap = listMap;
  608. map.IsSimpleType = true;
  609. return map;
  610. }
  611. // It is an extension of a primitive or known type
  612. TypeData typeData = FindBuiltInType (typeQName, stype);
  613. return GetTypeMapping (typeData);
  614. }
  615. bool CanBeEnum (XmlSchemaSimpleType stype)
  616. {
  617. if (stype.Content is XmlSchemaSimpleTypeRestriction)
  618. {
  619. XmlSchemaSimpleTypeRestriction rest = (XmlSchemaSimpleTypeRestriction)stype.Content;
  620. foreach (object ob in rest.Facets)
  621. if (!(ob is XmlSchemaEnumerationFacet)) return false;
  622. return true;
  623. }
  624. return false;
  625. }
  626. bool CanBeArray (XmlQualifiedName typeQName, XmlSchemaComplexType stype)
  627. {
  628. return !stype.IsMixed && CanBeArray (typeQName, stype.Particle, false);
  629. }
  630. bool CanBeArray (XmlQualifiedName typeQName, XmlSchemaParticle particle, bool multiValue)
  631. {
  632. // To be an array, there can't be a direct child of type typeQName
  633. if (particle == null) return false;
  634. multiValue = multiValue || particle.MaxOccurs > 1;
  635. if (particle is XmlSchemaGroupRef)
  636. return CanBeArray (typeQName, GetRefGroupParticle ((XmlSchemaGroupRef)particle), multiValue);
  637. if (particle is XmlSchemaElement)
  638. {
  639. XmlSchemaElement elem = (XmlSchemaElement)particle;
  640. if (!elem.RefName.IsEmpty)
  641. return CanBeArray (typeQName, FindRefElement (elem), multiValue);
  642. else
  643. return multiValue && !typeQName.Equals (((XmlSchemaElement)particle).SchemaTypeName);
  644. }
  645. if (particle is XmlSchemaAny)
  646. return multiValue;
  647. if (particle is XmlSchemaSequence)
  648. {
  649. XmlSchemaSequence seq = particle as XmlSchemaSequence;
  650. if (seq.Items.Count != 1) return false;
  651. return CanBeArray (typeQName, (XmlSchemaParticle)seq.Items[0], multiValue);
  652. }
  653. if (particle is XmlSchemaChoice)
  654. {
  655. // Can be array if all choices have different types
  656. ArrayList types = new ArrayList ();
  657. if(!CheckChoiceType (typeQName, particle, types, ref multiValue)) return false;
  658. return multiValue;
  659. }
  660. return false;
  661. }
  662. bool CheckChoiceType (XmlQualifiedName typeQName, XmlSchemaParticle particle, ArrayList types, ref bool multiValue)
  663. {
  664. XmlQualifiedName type = null;
  665. multiValue = multiValue || particle.MaxOccurs > 1;
  666. if (particle is XmlSchemaGroupRef)
  667. return CheckChoiceType (typeQName, GetRefGroupParticle ((XmlSchemaGroupRef)particle), types, ref multiValue);
  668. if (particle is XmlSchemaElement) {
  669. string ns;
  670. XmlSchemaElement elem = (XmlSchemaElement)particle;
  671. XmlSchemaElement refElem = GetRefElement (typeQName, elem, out ns);
  672. if (refElem.SchemaType != null) return true;
  673. type = refElem.SchemaTypeName;
  674. }
  675. else if (particle is XmlSchemaAny) {
  676. type = anyType;
  677. }
  678. else if (particle is XmlSchemaSequence)
  679. {
  680. XmlSchemaSequence seq = particle as XmlSchemaSequence;
  681. foreach (XmlSchemaParticle par in seq.Items)
  682. if (!CheckChoiceType (typeQName, par, types, ref multiValue)) return false;
  683. return true;
  684. }
  685. else if (particle is XmlSchemaChoice)
  686. {
  687. foreach (XmlSchemaParticle choice in ((XmlSchemaChoice)particle).Items)
  688. if (!CheckChoiceType (typeQName, choice, types, ref multiValue)) return false;
  689. return true;
  690. }
  691. if (typeQName.Equals (type)) return false;
  692. // For primitive types, compare using CLR types, since several
  693. // xml types can be mapped to a single CLR type
  694. string t;
  695. if (type.Namespace == XmlSchema.Namespace)
  696. t = TypeTranslator.GetPrimitiveTypeData (type.Name).FullTypeName + ":" + type.Namespace;
  697. else
  698. t = type.Name + ":" + type.Namespace;
  699. if (types.Contains (t)) return false;
  700. types.Add (t);
  701. return true;
  702. }
  703. bool CanBeAnyElement (XmlSchemaComplexType stype)
  704. {
  705. XmlSchemaSequence seq = stype.Particle as XmlSchemaSequence;
  706. return (seq != null) && (seq.Items.Count == 1) && (seq.Items[0] is XmlSchemaAny);
  707. }
  708. bool CanBeIXmlSerializable (XmlSchemaComplexType stype)
  709. {
  710. XmlSchemaSequence seq = stype.Particle as XmlSchemaSequence;
  711. if (seq == null) return false;
  712. if (seq.Items.Count != 2) return false;
  713. XmlSchemaElement elem = seq.Items[0] as XmlSchemaElement;
  714. if (elem == null) return false;
  715. if (elem.RefName != new XmlQualifiedName ("schema",XmlSchema.Namespace)) return false;
  716. return (seq.Items[1] is XmlSchemaAny);
  717. }
  718. XmlTypeMapElementInfo CreateElementInfo (string ns, XmlTypeMapMember member, string name, TypeData typeData, bool isNillable)
  719. {
  720. XmlTypeMapElementInfo einfo = new XmlTypeMapElementInfo (member, typeData);
  721. einfo.ElementName = name;
  722. einfo.Namespace = ns;
  723. einfo.IsNullable = isNillable;
  724. if (einfo.TypeData.IsComplexType)
  725. einfo.MappedType = GetTypeMapping (typeData);
  726. return einfo;
  727. }
  728. XmlTypeMapElementInfo CreateTextElementInfo (string ns, XmlTypeMapMember member, TypeData typeData)
  729. {
  730. XmlTypeMapElementInfo einfo = new XmlTypeMapElementInfo (member, typeData);
  731. einfo.IsTextElement = true;
  732. einfo.WrappedElement = false;
  733. if (typeData.IsComplexType)
  734. einfo.MappedType = GetTypeMapping (typeData);
  735. return einfo;
  736. }
  737. XmlTypeMapping CreateTypeMapping (XmlQualifiedName typeQName, SchemaTypes schemaType, XmlQualifiedName root)
  738. {
  739. string typeName = CodeIdentifier.MakeValid (typeQName.Name);
  740. typeName = typeIdentifiers.AddUnique (typeName, null);
  741. TypeData typeData = new TypeData (typeName, typeName, typeName, schemaType, null);
  742. XmlQualifiedName elemName = (root != null) ? root : typeQName;
  743. XmlTypeMapping map = new XmlTypeMapping (elemName.Name, elemName.Namespace, typeData, typeQName.Name, typeQName.Namespace);
  744. mappedTypes [typeQName] = map;
  745. dataMappedTypes [typeData] = map;
  746. return map;
  747. }
  748. XmlTypeMapping CreateArrayTypeMapping (XmlQualifiedName typeQName, TypeData arrayTypeData)
  749. {
  750. XmlTypeMapping map = new XmlTypeMapping (arrayTypeData.XmlType, typeQName.Namespace, arrayTypeData, arrayTypeData.XmlType, typeQName.Namespace);
  751. mappedTypes [typeQName] = map;
  752. dataMappedTypes [arrayTypeData] = map;
  753. return map;
  754. }
  755. XmlSchemaElement GetRefElement (XmlQualifiedName typeQName, XmlSchemaElement elem, out string ns)
  756. {
  757. if (!elem.RefName.IsEmpty)
  758. {
  759. ns = elem.RefName.Namespace;
  760. return FindRefElement (elem);
  761. }
  762. else
  763. {
  764. ns = typeQName.Namespace;
  765. return elem;
  766. }
  767. }
  768. XmlSchemaAttribute GetRefAttribute (XmlQualifiedName typeQName, XmlSchemaAttribute attr, out string ns)
  769. {
  770. if (!attr.RefName.IsEmpty)
  771. {
  772. ns = attr.RefName.Namespace;
  773. return (XmlSchemaAttribute) schemas.Find (attr.RefName, typeof(XmlSchemaAttribute));
  774. }
  775. else
  776. {
  777. ns = typeQName.Namespace;
  778. return attr;
  779. }
  780. }
  781. TypeData GetElementTypeData (XmlQualifiedName typeQName, XmlSchemaElement elem)
  782. {
  783. if (!elem.RefName.IsEmpty) {
  784. XmlSchemaElement refElem = FindRefElement (elem);
  785. if (refElem == null) throw new InvalidOperationException ("Ref type not found: " + elem.RefName);
  786. return GetElementTypeData (typeQName, refElem);
  787. }
  788. if (!elem.SchemaTypeName.IsEmpty) return GetTypeData (elem.SchemaTypeName);
  789. else if (elem.SchemaType == null) return TypeTranslator.GetTypeData (typeof(object));
  790. else return GetTypeData (elem.SchemaType, typeQName, elem.Name);
  791. }
  792. TypeData GetAttributeTypeData (XmlQualifiedName typeQName, XmlSchemaAttribute attr)
  793. {
  794. if (!attr.RefName.IsEmpty)
  795. return GetAttributeTypeData (typeQName, (XmlSchemaAttribute)schemas.Find (attr.RefName, typeof (XmlSchemaAttribute)));
  796. if (!attr.SchemaTypeName.IsEmpty) return GetTypeData (attr.SchemaTypeName);
  797. else return GetTypeData (attr.SchemaType, typeQName, attr.Name);
  798. }
  799. TypeData GetTypeData (XmlQualifiedName typeQName)
  800. {
  801. if (typeQName.Namespace == XmlSchema.Namespace)
  802. return TypeTranslator.GetPrimitiveTypeData (typeQName.Name);
  803. return ImportType (typeQName, null).TypeData;
  804. }
  805. TypeData GetTypeData (XmlSchemaType stype, XmlQualifiedName typeQNname, string propertyName)
  806. {
  807. string baseName = typeQNname.Name + typeIdentifiers.MakeRightCase (propertyName);
  808. baseName = elemIdentifiers.AddUnique (baseName, stype);
  809. XmlQualifiedName newName;
  810. newName = new XmlQualifiedName (baseName, typeQNname.Namespace);
  811. XmlTypeMapping map = ImportType (newName, stype, null);
  812. return map.TypeData;
  813. }
  814. XmlTypeMapping GetTypeMapping (TypeData typeData)
  815. {
  816. XmlTypeMapping map = (XmlTypeMapping) dataMappedTypes [typeData];
  817. if (map != null) return map;
  818. if (map == null && typeData.IsListType)
  819. {
  820. // Create an array map for the type
  821. XmlTypeMapping itemMap = GetTypeMapping (typeData.ListItemTypeData);
  822. map = new XmlTypeMapping (typeData.XmlType, itemMap.Namespace, typeData, typeData.XmlType, itemMap.Namespace);
  823. ListMap listMap = new ListMap ();
  824. listMap.ItemInfo = new XmlTypeMapElementInfoList();
  825. listMap.ItemInfo.Add (CreateElementInfo (itemMap.Namespace, null, typeData.ListItemTypeData.XmlType, typeData.ListItemTypeData, false));
  826. map.ObjectMap = listMap;
  827. mappedTypes [new XmlQualifiedName(map.ElementName, map.Namespace)] = map;
  828. dataMappedTypes [typeData] = map;
  829. return map;
  830. }
  831. else if (typeData.SchemaType == SchemaTypes.Primitive || typeData.Type == typeof(object) || typeof(XmlNode).IsAssignableFrom(typeData.Type))
  832. {
  833. map = new XmlTypeMapping (typeData.XmlType, XmlSchema.Namespace, typeData, typeData.XmlType, XmlSchema.Namespace);
  834. dataMappedTypes [typeData] = map;
  835. return map;
  836. }
  837. throw new InvalidOperationException ("Map for type " + typeData.TypeName + " not found");
  838. }
  839. XmlTypeMapping GetRegisteredTypeMapping (XmlQualifiedName typeQName)
  840. {
  841. return (XmlTypeMapping) mappedTypes [typeQName];
  842. }
  843. XmlSchemaParticle GetRefGroupParticle (XmlSchemaGroupRef refGroup)
  844. {
  845. XmlSchemaGroup grp = (XmlSchemaGroup) schemas.Find (refGroup.RefName, typeof (XmlSchemaGroup));
  846. return grp.Particle;
  847. }
  848. XmlSchemaElement FindRefElement (XmlSchemaElement elem)
  849. {
  850. if (elem.RefName.Namespace == XmlSchema.Namespace)
  851. {
  852. if (anyElement != null) return anyElement;
  853. anyElement = new XmlSchemaElement ();
  854. anyElement.Name = "any";
  855. anyElement.SchemaTypeName = anyType;
  856. return anyElement;
  857. }
  858. return (XmlSchemaElement) schemas.Find (elem.RefName, typeof(XmlSchemaElement));
  859. }
  860. #endregion // Methods
  861. }
  862. }