XmlSchemaUtil.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. using System;
  2. using System.Xml;
  3. using System.Collections;
  4. using Mono.Xml;
  5. using Mono.Xml.Schema;
  6. using System.Xml.Serialization;
  7. namespace System.Xml.Schema
  8. {
  9. /// <summary>
  10. /// All Methods in this class should use XmlConvert. Some Methods are not present in the
  11. /// MS Implementation. We should provide them.
  12. /// </summary>
  13. internal class XmlSchemaUtil
  14. {
  15. static XmlSchemaUtil()
  16. {
  17. FinalAllowed = XmlSchemaDerivationMethod.Restriction |
  18. XmlSchemaDerivationMethod.Extension;
  19. ComplexTypeBlockAllowed = FinalAllowed;
  20. ElementBlockAllowed = XmlSchemaDerivationMethod.Substitution |
  21. FinalAllowed;
  22. }
  23. internal static XmlSchemaDerivationMethod FinalAllowed;
  24. internal static XmlSchemaDerivationMethod ElementBlockAllowed;
  25. internal static XmlSchemaDerivationMethod ComplexTypeBlockAllowed;
  26. public static void AddToTable (XmlSchemaObjectTable table, XmlSchemaObject obj,
  27. XmlQualifiedName qname, ValidationEventHandler h)
  28. {
  29. if (table.Contains (qname)) {
  30. // FIXME: This logic unexpectedly allows
  31. // one redefining item and two or more redefining items.
  32. // FIXME: redefining item is not simple replacement,
  33. // but much more complex stuff.
  34. if (obj.isRedefineChild) { // take precedence.
  35. if (obj.redefinedObject != null)
  36. obj.error (h, "Named item " + qname + " was already contained in the schema object table.");
  37. else
  38. obj.redefinedObject = table [qname];
  39. table.Set (qname, obj);
  40. }
  41. else if (table [qname].isRedefineChild) {
  42. if (table [qname].redefinedObject != null)
  43. obj.error (h, "Named item " + qname + " was already contained in the schema object table.");
  44. else
  45. table [qname].redefinedObject = obj;
  46. return; // never add to the table.
  47. }
  48. else
  49. obj.error (h, "Named item " + qname + " was already contained in the schema object table.");
  50. }
  51. else
  52. table.Set (qname, obj);
  53. }
  54. public static void CompileID(string id, XmlSchemaObject xso, Hashtable idCollection, ValidationEventHandler h)
  55. {
  56. //check if the string conforms to http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/datatypes.html#ID
  57. // 1. ID must be a NCName
  58. // 2. ID must be unique in the schema
  59. if(id == null)
  60. return;
  61. if(!CheckNCName(id))
  62. xso.error(h,id+" is not a valid id attribute");
  63. else if(idCollection.ContainsKey(id))
  64. xso.error(h,"Duplicate id attribute "+id);
  65. else
  66. idCollection.Add(id,xso);
  67. }
  68. [MonoTODO]
  69. public static bool CheckAnyUri(string uri)
  70. {
  71. if (uri.StartsWith ("##"))
  72. return false;
  73. return true;
  74. }
  75. public static bool CheckToken(string token)
  76. {
  77. //check if the string conforms to http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/datatypes.html#token
  78. return true;
  79. }
  80. public static bool CheckNormalizedString(string token)
  81. {
  82. return true;
  83. }
  84. public static bool CheckLanguage(string lang)
  85. {
  86. //check if the string conforms to http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/datatypes.html#language
  87. return true;
  88. }
  89. public static bool CheckNCName(string name)
  90. {
  91. //check if the string conforms to http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/datatypes.html#NCName
  92. try
  93. {
  94. XmlConvert.VerifyNCName(name);
  95. return true;
  96. }
  97. catch(Exception ex)
  98. {
  99. return false;
  100. }
  101. }
  102. public static bool CheckQName(XmlQualifiedName qname)
  103. {
  104. // What is this doing?
  105. return true;
  106. }
  107. public static XmlParserContext GetParserContext (XmlReader reader)
  108. {
  109. XmlTextReader xtr = reader as XmlTextReader;
  110. if (xtr != null)
  111. return xtr.GetInternalParserContext ();
  112. XmlNodeReader xnr = reader as XmlNodeReader;
  113. if (xnr != null)
  114. return xnr.GetInternalParserContext ();
  115. XmlValidatingReader xvr = reader as XmlValidatingReader;
  116. if (xvr != null)
  117. return xvr.GetInternalParserContext ();
  118. IHasXmlParserContext xctx = reader as IHasXmlParserContext;
  119. if (xctx != null)
  120. return xctx.ParserContext;
  121. throw new NotSupportedException ("XmlParserContext cannot be acquired from this XmlReader.");
  122. }
  123. public static bool IsSchemaDatatypeEquals (XsdAnySimpleType st1, object v1,
  124. XsdAnySimpleType st2, object v2)
  125. {
  126. if (v1 == null || v2 == null)
  127. return false;
  128. if (st1 == null)
  129. st1 = XmlSchemaSimpleType.AnySimpleType;
  130. if (st2 == null)
  131. st2 = XmlSchemaSimpleType.AnySimpleType;
  132. Type t = st2.GetType ();
  133. if (st1 is XsdFloat) {
  134. return st2 is XsdFloat && Convert.ToSingle (v1) == Convert.ToSingle (v2);
  135. } else if (st1 is XsdDouble) {
  136. return st2 is XsdDouble && Convert.ToDouble (v1) == Convert.ToDouble (v2);
  137. } else if (st1 is XsdDecimal) {
  138. if (!(st2 is XsdDecimal) || Convert.ToDecimal (v1) != Convert.ToDecimal (v2))
  139. return false;
  140. if (st1 is XsdNonPositiveInteger)
  141. return st2 is XsdNonPositiveInteger || t == typeof (XsdDecimal) || t == typeof (XsdInteger);
  142. else if (st1 is XsdPositiveInteger)
  143. return st2 is XsdPositiveInteger || t == typeof (XsdDecimal) ||
  144. t == typeof (XsdInteger) || t == typeof (XsdNonNegativeInteger);
  145. else if (st1 is XsdUnsignedLong)
  146. return st2 is XsdUnsignedLong || t == typeof (XsdDecimal) ||
  147. t == typeof (XsdInteger) || t == typeof (XsdNonNegativeInteger);
  148. else if (st1 is XsdNonNegativeInteger)
  149. return st2 is XsdNonNegativeInteger || t == typeof (XsdDecimal) || t == typeof (XsdInteger);
  150. else if (st1 is XsdLong)
  151. return st2 is XsdLong || t == typeof (XsdDecimal) || t == typeof (XsdInteger);
  152. return true;
  153. }
  154. else if (!v1.Equals (v2))
  155. return false;
  156. if (st1 is XsdString) {
  157. if (!(st2 is XsdString))
  158. return false;
  159. if (st1 is XsdNMToken && (st2 is XsdLanguage || st2 is XsdName))
  160. return false;
  161. if (st2 is XsdNMToken && (st1 is XsdLanguage || st1 is XsdName))
  162. return false;
  163. if (st1 is XsdName && (st2 is XsdLanguage || st2 is XsdNMToken))
  164. return false;
  165. if (st2 is XsdName && (st1 is XsdLanguage || st1 is XsdNMToken))
  166. return false;
  167. }
  168. else if (st1 != st2)
  169. return false;
  170. return true;
  171. }
  172. public static bool IsValidQName(string qname)
  173. {
  174. foreach(string part in qname.Split(new char[]{':'},2))
  175. {
  176. if(!CheckNCName(part))
  177. return false;
  178. }
  179. return true;
  180. }
  181. //FIXME: First remove all the multiple instances of whitespace and then return the strings.
  182. //The current method returns empty strings if there are two or more consecutive whitespaces.
  183. public static string[] SplitList(string list)
  184. {
  185. if(list == null || list == string.Empty)
  186. return new string [0];
  187. string[] listarr = list.Split(new char[]{' ','\t','\n'});
  188. int pos=0;
  189. int i = 0;
  190. for(i=0;i<listarr.Length;i++)
  191. {
  192. if(listarr[i] != null && listarr[i] != String.Empty)
  193. {
  194. listarr[pos++] = listarr[i];
  195. }
  196. }
  197. if(pos == i)
  198. return listarr;
  199. string[] retarr = new String[pos];
  200. if(pos!=0)
  201. Array.Copy(listarr, retarr, pos);
  202. return retarr;
  203. }
  204. public static void ReadUnhandledAttribute(XmlReader reader, XmlSchemaObject xso)
  205. {
  206. if(reader.Prefix == "xmlns")
  207. xso.Namespaces.Add(reader.LocalName, reader.Value);
  208. else if(reader.Name == "xmlns")
  209. xso.Namespaces.Add("",reader.Value);
  210. else
  211. {
  212. if(xso.unhandledAttributeList == null)
  213. xso.unhandledAttributeList = new System.Collections.ArrayList();
  214. XmlAttribute attr = new XmlDocument().CreateAttribute(reader.LocalName,reader.NamespaceURI);
  215. attr.Value = reader.Value;
  216. ParseWsdlArrayType (reader, attr);
  217. xso.unhandledAttributeList.Add(attr);
  218. }
  219. }
  220. static void ParseWsdlArrayType (XmlReader reader, XmlAttribute attr)
  221. {
  222. if (attr.NamespaceURI == XmlSerializer.WsdlNamespace && attr.LocalName == "arrayType")
  223. {
  224. string ns = "", type, dimensions;
  225. TypeTranslator.ParseArrayType (attr.Value, out type, out ns, out dimensions);
  226. if (ns != "") ns = reader.LookupNamespace (ns) + ":";
  227. attr.Value = ns + type + dimensions;
  228. }
  229. }
  230. public static bool ReadBoolAttribute(XmlReader reader, out Exception innerExcpetion)
  231. {
  232. innerExcpetion = null;
  233. try
  234. {
  235. bool val = XmlConvert.ToBoolean(reader.Value);
  236. return val;
  237. }
  238. catch(Exception ex)
  239. {
  240. innerExcpetion = ex;
  241. return false;
  242. }
  243. }
  244. public static decimal ReadDecimalAttribute(XmlReader reader, out Exception innerExcpetion)
  245. {
  246. innerExcpetion = null;
  247. try
  248. {
  249. decimal val = XmlConvert.ToDecimal(reader.Value);
  250. return val;
  251. }
  252. catch(Exception ex)
  253. {
  254. innerExcpetion = ex;
  255. return decimal.Zero;
  256. }
  257. }
  258. // Is some value is read, return it.
  259. // If no values return empty.
  260. // If exception, return none
  261. public static XmlSchemaDerivationMethod ReadDerivationAttribute(XmlReader reader, out Exception innerExcpetion, string name, XmlSchemaDerivationMethod allowed)
  262. {
  263. innerExcpetion = null;
  264. try
  265. {
  266. string list = reader.Value;
  267. string warn = "";
  268. XmlSchemaDerivationMethod val = 0;
  269. if(list.IndexOf("#all") != -1 && list.Trim() != "#all")
  270. {
  271. innerExcpetion = new Exception(list+" is not a valid value for "+ name +". #all if present must be the only value");
  272. return XmlSchemaDerivationMethod.All;
  273. }
  274. foreach(string xsdm in XmlSchemaUtil.SplitList(list))
  275. {
  276. switch(xsdm)
  277. {
  278. case "":
  279. val = AddFlag (val, XmlSchemaDerivationMethod.Empty, allowed); break;
  280. case "#all":
  281. val = AddFlag (val,XmlSchemaDerivationMethod.All, allowed); break;
  282. case "substitution":
  283. val = AddFlag (val,XmlSchemaDerivationMethod.Substitution, allowed); break;
  284. case "extension":
  285. val = AddFlag (val,XmlSchemaDerivationMethod.Extension, allowed); break;
  286. case "restriction":
  287. val = AddFlag (val,XmlSchemaDerivationMethod.Restriction, allowed); break;
  288. case "list":
  289. val = AddFlag (val,XmlSchemaDerivationMethod.List, allowed); break;
  290. case "union":
  291. val = AddFlag (val,XmlSchemaDerivationMethod.Union, allowed); break;
  292. default:
  293. warn += xsdm + " "; break;
  294. }
  295. }
  296. if(warn != "")
  297. innerExcpetion = new Exception(warn + "is/are not valid values for " + name);
  298. return val;
  299. }
  300. catch(Exception ex)
  301. {
  302. innerExcpetion = ex;
  303. return XmlSchemaDerivationMethod.None;
  304. }
  305. }
  306. private static XmlSchemaDerivationMethod AddFlag (XmlSchemaDerivationMethod dst,
  307. XmlSchemaDerivationMethod add, XmlSchemaDerivationMethod allowed)
  308. {
  309. if ((add & allowed) == 0 && allowed != XmlSchemaDerivationMethod.All)
  310. throw new ArgumentException (add + " is not allowed in this attribute.");
  311. if ((dst & add) != 0)
  312. throw new ArgumentException (add + " is already specified in this attribute.");
  313. return dst | add;
  314. }
  315. public static XmlSchemaForm ReadFormAttribute(XmlReader reader, out Exception innerExcpetion)
  316. {
  317. innerExcpetion = null;
  318. XmlSchemaForm val = XmlSchemaForm.None;
  319. switch(reader.Value)
  320. {
  321. case "qualified":
  322. val = XmlSchemaForm.Qualified; break;
  323. case "unqualified":
  324. val = XmlSchemaForm.Unqualified; break;
  325. default:
  326. innerExcpetion = new Exception("only qualified or unqulified is a valid value"); break;
  327. }
  328. return val;
  329. }
  330. public static XmlSchemaContentProcessing ReadProcessingAttribute(XmlReader reader, out Exception innerExcpetion)
  331. {
  332. innerExcpetion = null;
  333. XmlSchemaContentProcessing val = XmlSchemaContentProcessing.None;
  334. switch(reader.Value)
  335. {
  336. case "lax":
  337. val = XmlSchemaContentProcessing.Lax; break;
  338. case "strict":
  339. val = XmlSchemaContentProcessing.Strict; break;
  340. case "skip":
  341. val = XmlSchemaContentProcessing.Skip; break;
  342. default:
  343. innerExcpetion = new Exception("only lax , strict or skip are valid values for processContents");
  344. break;
  345. }
  346. return val;
  347. }
  348. public static XmlSchemaUse ReadUseAttribute(XmlReader reader, out Exception innerExcpetion)
  349. {
  350. innerExcpetion = null;
  351. XmlSchemaUse val = XmlSchemaUse.None;
  352. switch(reader.Value)
  353. {
  354. case "optional":
  355. val = XmlSchemaUse.Optional; break;
  356. case "prohibited":
  357. val = XmlSchemaUse.Prohibited; break;
  358. case "required":
  359. val = XmlSchemaUse.Required; break;
  360. default:
  361. innerExcpetion = new Exception("only optional , prohibited or required are valid values for use");
  362. break;
  363. }
  364. return val;
  365. }
  366. public static XmlQualifiedName ReadQNameAttribute(XmlReader reader, out Exception innerEx)
  367. {
  368. return ToQName(reader, reader.Value, out innerEx);
  369. }
  370. //While Creating a XmlQualifedName, we should check:
  371. // 1. If a prefix is present, its namespace should be resolvable.
  372. // 2. If a prefix is not present, and if the defaultNamespace is set,
  373. public static XmlQualifiedName ToQName(XmlReader reader, string qnamestr, out Exception innerEx)
  374. {
  375. string ns;
  376. string name;
  377. XmlQualifiedName qname;
  378. innerEx = null;
  379. if(!IsValidQName(qnamestr))
  380. {
  381. innerEx = new Exception(qnamestr + " is an invalid QName. Either name or namespace is not a NCName");
  382. return XmlQualifiedName.Empty;
  383. }
  384. string[] values = qnamestr.Split(new char[]{':'},2);
  385. if(values.Length == 2)
  386. {
  387. ns = reader.LookupNamespace(values[0]);
  388. if(ns == null)
  389. {
  390. innerEx = new Exception("Namespace Prefix '"+values[0]+"could not be resolved");
  391. return XmlQualifiedName.Empty;
  392. }
  393. name = values[1];
  394. }
  395. else
  396. {
  397. //Default Namespace
  398. ns = reader.LookupNamespace("");
  399. name = values[0];
  400. }
  401. qname = new XmlQualifiedName(name,ns);
  402. return qname;
  403. }
  404. public static int ValidateAttributesResolved (
  405. XmlSchemaObjectTable attributesResolved,
  406. ValidationEventHandler h,
  407. XmlSchema schema,
  408. XmlSchemaObjectCollection attributes,
  409. XmlSchemaAnyAttribute anyAttribute,
  410. ref XmlSchemaAnyAttribute anyAttributeUse,
  411. XmlSchemaAttributeGroup redefined)
  412. {
  413. int errorCount = 0;
  414. if (anyAttribute != null && anyAttributeUse == null)
  415. anyAttributeUse = anyAttribute;
  416. foreach (XmlSchemaObject xsobj in attributes) {
  417. XmlSchemaAttributeGroupRef grpRef = xsobj as XmlSchemaAttributeGroupRef;
  418. if (grpRef != null) {
  419. // Resolve attributeGroup redefinition.
  420. XmlSchemaAttributeGroup grp = null;
  421. if (redefined != null && grpRef.RefName == redefined.QualifiedName)
  422. grp = redefined;
  423. else
  424. grp = schema.AttributeGroups [grpRef.RefName] as XmlSchemaAttributeGroup;
  425. // otherwise, it might be missing sub components.
  426. if (grp == null) {
  427. if (!schema.missedSubComponents)// && schema.Schemas [grpRef.RefName.Namespace] != null)
  428. grpRef.error (h, "Referenced attribute group " + grpRef.RefName + " was not found in the corresponding schema.");
  429. continue;
  430. }
  431. if (grp.AttributeGroupRecursionCheck) {
  432. grp.error (h, "Attribute group recursion was found: " + grpRef.RefName);
  433. continue;
  434. }
  435. try {
  436. grp.AttributeGroupRecursionCheck = true;
  437. errorCount += grp.Validate (h, schema);
  438. } finally {
  439. grp.AttributeGroupRecursionCheck = false;
  440. }
  441. if (grp.AnyAttributeUse != null) {
  442. // FIXME: should validate wildcard subset validity. See spec 3.10.6
  443. if (anyAttribute == null)
  444. anyAttributeUse = grp.AnyAttributeUse;
  445. }
  446. foreach (XmlSchemaAttribute attr in grp.AttributeUses) {
  447. if (attr.RefName != null && attr.RefName != XmlQualifiedName.Empty)
  448. AddToTable (attributesResolved, attr, attr.RefName, h);
  449. else
  450. AddToTable (attributesResolved, attr, attr.QualifiedName, h);
  451. }
  452. } else {
  453. XmlSchemaAttribute attr = xsobj as XmlSchemaAttribute;
  454. if (attr != null) {
  455. errorCount += attr.Validate (h, schema);
  456. if (attr.RefName != null && attr.RefName != XmlQualifiedName.Empty)
  457. AddToTable (attributesResolved, attr, attr.RefName, h);
  458. else
  459. AddToTable (attributesResolved, attr, attr.QualifiedName, h);
  460. } else {
  461. if (anyAttribute == null) {
  462. anyAttributeUse = (XmlSchemaAnyAttribute) xsobj;
  463. anyAttribute.Validate (h, schema);
  464. }
  465. }
  466. }
  467. }
  468. return errorCount;
  469. }
  470. }
  471. }