XmlSchemaUtil.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  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. public static bool CheckAnyUri(string uri)
  69. {
  70. if (uri.StartsWith ("##"))
  71. return false;
  72. return true;
  73. }
  74. public static bool CheckNormalizedString(string token)
  75. {
  76. return true;
  77. }
  78. public static bool CheckNCName(string name)
  79. {
  80. //check if the string conforms to http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/datatypes.html#NCName
  81. return XmlChar.IsNCName (name);
  82. }
  83. public static bool CheckQName(XmlQualifiedName qname)
  84. {
  85. // What is this doing?
  86. return true;
  87. }
  88. public static XmlParserContext GetParserContext (XmlReader reader)
  89. {
  90. XmlTextReader xtr = reader as XmlTextReader;
  91. if (xtr != null)
  92. return xtr.GetInternalParserContext ();
  93. XmlNodeReader xnr = reader as XmlNodeReader;
  94. if (xnr != null)
  95. return xnr.GetInternalParserContext ();
  96. XmlValidatingReader xvr = reader as XmlValidatingReader;
  97. if (xvr != null)
  98. return xvr.GetInternalParserContext ();
  99. IHasXmlParserContext xctx = reader as IHasXmlParserContext;
  100. if (xctx != null)
  101. return xctx.ParserContext;
  102. throw new NotSupportedException ("XmlParserContext cannot be acquired from this XmlReader.");
  103. }
  104. public static bool IsBuiltInDatatypeName (XmlQualifiedName qname)
  105. {
  106. if (qname.Namespace != XmlSchema.Namespace)
  107. return false;
  108. switch (qname.Name) {
  109. case "anySimpleType":
  110. case "duration": case "dateTime": case "time":
  111. case "date": case "gYearMonth": case "gYear":
  112. case "gMonthDay": case "gDay": case "gMonth":
  113. case "boolean":
  114. case "base64Binary": case "hexBinary":
  115. case "float": case "double":
  116. case "anyURI":
  117. case "QName":
  118. case "NOTATION":
  119. case "string": case "normalizedString": case "token":
  120. case "language": case "Name": case "NCName":
  121. case "ID": case "IDREF": case "IDREFS":
  122. case "ENTITY": case "ENTITIES":
  123. case "NMTOKEN": case "NMTOKENS":
  124. case "decimal": case "integer":
  125. case "nonPositiveInteger": case "negativeInteger":
  126. case "nonNegativeInteger":
  127. case "unsignedLong": case "unsignedInt":
  128. case "unsignedShort": case "unsignedByte":
  129. case "positiveInteger":
  130. case "long": case "int": case "short": case "byte":
  131. return true;
  132. }
  133. return false;
  134. }
  135. public static bool IsSchemaDatatypeEquals (XsdAnySimpleType st1, object v1,
  136. XsdAnySimpleType st2, object v2)
  137. {
  138. if (v1 == null || v2 == null)
  139. return false;
  140. if (st1 == null)
  141. st1 = XmlSchemaSimpleType.AnySimpleType;
  142. if (st2 == null)
  143. st2 = XmlSchemaSimpleType.AnySimpleType;
  144. Type t = st2.GetType ();
  145. if (st1 is XsdFloat) {
  146. return st2 is XsdFloat && Convert.ToSingle (v1) == Convert.ToSingle (v2);
  147. } else if (st1 is XsdDouble) {
  148. return st2 is XsdDouble && Convert.ToDouble (v1) == Convert.ToDouble (v2);
  149. } else if (st1 is XsdDecimal) {
  150. if (!(st2 is XsdDecimal) || Convert.ToDecimal (v1) != Convert.ToDecimal (v2))
  151. return false;
  152. if (st1 is XsdNonPositiveInteger)
  153. return st2 is XsdNonPositiveInteger || t == typeof (XsdDecimal) || t == typeof (XsdInteger);
  154. else if (st1 is XsdPositiveInteger)
  155. return st2 is XsdPositiveInteger || t == typeof (XsdDecimal) ||
  156. t == typeof (XsdInteger) || t == typeof (XsdNonNegativeInteger);
  157. else if (st1 is XsdUnsignedLong)
  158. return st2 is XsdUnsignedLong || t == typeof (XsdDecimal) ||
  159. t == typeof (XsdInteger) || t == typeof (XsdNonNegativeInteger);
  160. else if (st1 is XsdNonNegativeInteger)
  161. return st2 is XsdNonNegativeInteger || t == typeof (XsdDecimal) || t == typeof (XsdInteger);
  162. else if (st1 is XsdLong)
  163. return st2 is XsdLong || t == typeof (XsdDecimal) || t == typeof (XsdInteger);
  164. return true;
  165. }
  166. else if (!v1.Equals (v2))
  167. return false;
  168. if (st1 is XsdString) {
  169. if (!(st2 is XsdString))
  170. return false;
  171. if (st1 is XsdNMToken && (st2 is XsdLanguage || st2 is XsdName))
  172. return false;
  173. if (st2 is XsdNMToken && (st1 is XsdLanguage || st1 is XsdName))
  174. return false;
  175. if (st1 is XsdName && (st2 is XsdLanguage || st2 is XsdNMToken))
  176. return false;
  177. if (st2 is XsdName && (st1 is XsdLanguage || st1 is XsdNMToken))
  178. return false;
  179. if (st1 is XsdID && st2 is XsdIDRef)
  180. return false;
  181. if (st1 is XsdIDRef && st2 is XsdID)
  182. return false;
  183. }
  184. else if (st1 != st2)
  185. return false;
  186. return true;
  187. }
  188. public static bool IsValidQName(string qname)
  189. {
  190. foreach(string part in qname.Split(new char[]{':'},2))
  191. {
  192. if(!CheckNCName(part))
  193. return false;
  194. }
  195. return true;
  196. }
  197. //FIXME: First remove all the multiple instances of whitespace and then return the strings.
  198. //The current method returns empty strings if there are two or more consecutive whitespaces.
  199. public static string[] SplitList(string list)
  200. {
  201. if(list == null || list == string.Empty)
  202. return new string [0];
  203. ArrayList al = null;
  204. int start = 0;
  205. bool wait = true;
  206. for (int i = 0; i < list.Length; i++) {
  207. switch (list [i]) {
  208. case ' ':
  209. case '\r':
  210. case '\n':
  211. case '\t':
  212. if (!wait) {
  213. if (al == null)
  214. al = new ArrayList ();
  215. al.Add (list.Substring (start, i - start));
  216. }
  217. wait = true;
  218. break;
  219. default:
  220. if (wait) {
  221. wait = false;
  222. start = i;
  223. }
  224. break;
  225. }
  226. }
  227. if (!wait && start == 0)
  228. return new string [] {list};
  229. if (!wait && start < list.Length)
  230. al.Add (start == 0 ? list : list.Substring (start));
  231. return al.ToArray (typeof (string)) as string [];
  232. }
  233. public static void ReadUnhandledAttribute(XmlReader reader, XmlSchemaObject xso)
  234. {
  235. if(reader.Prefix == "xmlns")
  236. xso.Namespaces.Add(reader.LocalName, reader.Value);
  237. else if(reader.Name == "xmlns")
  238. xso.Namespaces.Add("",reader.Value);
  239. else
  240. {
  241. if(xso.unhandledAttributeList == null)
  242. xso.unhandledAttributeList = new System.Collections.ArrayList();
  243. XmlAttribute attr = new XmlDocument().CreateAttribute(reader.LocalName,reader.NamespaceURI);
  244. attr.Value = reader.Value;
  245. ParseWsdlArrayType (reader, attr);
  246. xso.unhandledAttributeList.Add(attr);
  247. }
  248. }
  249. static void ParseWsdlArrayType (XmlReader reader, XmlAttribute attr)
  250. {
  251. if (attr.NamespaceURI == XmlSerializer.WsdlNamespace && attr.LocalName == "arrayType")
  252. {
  253. string ns = "", type, dimensions;
  254. TypeTranslator.ParseArrayType (attr.Value, out type, out ns, out dimensions);
  255. if (ns != "") ns = reader.LookupNamespace (ns) + ":";
  256. attr.Value = ns + type + dimensions;
  257. }
  258. }
  259. public static bool ReadBoolAttribute(XmlReader reader, out Exception innerExcpetion)
  260. {
  261. innerExcpetion = null;
  262. try
  263. {
  264. bool val = XmlConvert.ToBoolean(reader.Value);
  265. return val;
  266. }
  267. catch(Exception ex)
  268. {
  269. innerExcpetion = ex;
  270. return false;
  271. }
  272. }
  273. public static decimal ReadDecimalAttribute(XmlReader reader, out Exception innerExcpetion)
  274. {
  275. innerExcpetion = null;
  276. try
  277. {
  278. decimal val = XmlConvert.ToDecimal(reader.Value);
  279. return val;
  280. }
  281. catch(Exception ex)
  282. {
  283. innerExcpetion = ex;
  284. return decimal.Zero;
  285. }
  286. }
  287. // Is some value is read, return it.
  288. // If no values return empty.
  289. // If exception, return none
  290. public static XmlSchemaDerivationMethod ReadDerivationAttribute(XmlReader reader, out Exception innerExcpetion, string name, XmlSchemaDerivationMethod allowed)
  291. {
  292. innerExcpetion = null;
  293. try
  294. {
  295. string list = reader.Value;
  296. string warn = "";
  297. XmlSchemaDerivationMethod val = 0;
  298. if(list.IndexOf("#all") != -1 && list.Trim() != "#all")
  299. {
  300. innerExcpetion = new Exception(list+" is not a valid value for "+ name +". #all if present must be the only value");
  301. return XmlSchemaDerivationMethod.All;
  302. }
  303. foreach(string xsdm in XmlSchemaUtil.SplitList(list))
  304. {
  305. switch(xsdm)
  306. {
  307. case "":
  308. val = AddFlag (val, XmlSchemaDerivationMethod.Empty, allowed); break;
  309. case "#all":
  310. val = AddFlag (val,XmlSchemaDerivationMethod.All, allowed); break;
  311. case "substitution":
  312. val = AddFlag (val,XmlSchemaDerivationMethod.Substitution, allowed); break;
  313. case "extension":
  314. val = AddFlag (val,XmlSchemaDerivationMethod.Extension, allowed); break;
  315. case "restriction":
  316. val = AddFlag (val,XmlSchemaDerivationMethod.Restriction, allowed); break;
  317. case "list":
  318. val = AddFlag (val,XmlSchemaDerivationMethod.List, allowed); break;
  319. case "union":
  320. val = AddFlag (val,XmlSchemaDerivationMethod.Union, allowed); break;
  321. default:
  322. warn += xsdm + " "; break;
  323. }
  324. }
  325. if(warn != "")
  326. innerExcpetion = new Exception(warn + "is/are not valid values for " + name);
  327. return val;
  328. }
  329. catch(Exception ex)
  330. {
  331. innerExcpetion = ex;
  332. return XmlSchemaDerivationMethod.None;
  333. }
  334. }
  335. private static XmlSchemaDerivationMethod AddFlag (XmlSchemaDerivationMethod dst,
  336. XmlSchemaDerivationMethod add, XmlSchemaDerivationMethod allowed)
  337. {
  338. if ((add & allowed) == 0 && allowed != XmlSchemaDerivationMethod.All)
  339. throw new ArgumentException (add + " is not allowed in this attribute.");
  340. if ((dst & add) != 0)
  341. throw new ArgumentException (add + " is already specified in this attribute.");
  342. return dst | add;
  343. }
  344. public static XmlSchemaForm ReadFormAttribute(XmlReader reader, out Exception innerExcpetion)
  345. {
  346. innerExcpetion = null;
  347. XmlSchemaForm val = XmlSchemaForm.None;
  348. switch(reader.Value)
  349. {
  350. case "qualified":
  351. val = XmlSchemaForm.Qualified; break;
  352. case "unqualified":
  353. val = XmlSchemaForm.Unqualified; break;
  354. default:
  355. innerExcpetion = new Exception("only qualified or unqulified is a valid value"); break;
  356. }
  357. return val;
  358. }
  359. public static XmlSchemaContentProcessing ReadProcessingAttribute(XmlReader reader, out Exception innerExcpetion)
  360. {
  361. innerExcpetion = null;
  362. XmlSchemaContentProcessing val = XmlSchemaContentProcessing.None;
  363. switch(reader.Value)
  364. {
  365. case "lax":
  366. val = XmlSchemaContentProcessing.Lax; break;
  367. case "strict":
  368. val = XmlSchemaContentProcessing.Strict; break;
  369. case "skip":
  370. val = XmlSchemaContentProcessing.Skip; break;
  371. default:
  372. innerExcpetion = new Exception("only lax , strict or skip are valid values for processContents");
  373. break;
  374. }
  375. return val;
  376. }
  377. public static XmlSchemaUse ReadUseAttribute(XmlReader reader, out Exception innerExcpetion)
  378. {
  379. innerExcpetion = null;
  380. XmlSchemaUse val = XmlSchemaUse.None;
  381. switch(reader.Value)
  382. {
  383. case "optional":
  384. val = XmlSchemaUse.Optional; break;
  385. case "prohibited":
  386. val = XmlSchemaUse.Prohibited; break;
  387. case "required":
  388. val = XmlSchemaUse.Required; break;
  389. default:
  390. innerExcpetion = new Exception("only optional , prohibited or required are valid values for use");
  391. break;
  392. }
  393. return val;
  394. }
  395. public static XmlQualifiedName ReadQNameAttribute(XmlReader reader, out Exception innerEx)
  396. {
  397. return ToQName(reader, reader.Value, out innerEx);
  398. }
  399. //While Creating a XmlQualifedName, we should check:
  400. // 1. If a prefix is present, its namespace should be resolvable.
  401. // 2. If a prefix is not present, and if the defaultNamespace is set,
  402. public static XmlQualifiedName ToQName(XmlReader reader, string qnamestr, out Exception innerEx)
  403. {
  404. string ns;
  405. string name;
  406. XmlQualifiedName qname;
  407. innerEx = null;
  408. if(!IsValidQName(qnamestr))
  409. {
  410. innerEx = new Exception(qnamestr + " is an invalid QName. Either name or namespace is not a NCName");
  411. return XmlQualifiedName.Empty;
  412. }
  413. string[] values = qnamestr.Split(new char[]{':'},2);
  414. if(values.Length == 2)
  415. {
  416. ns = reader.LookupNamespace(values[0]);
  417. if(ns == null)
  418. {
  419. innerEx = new Exception("Namespace Prefix '"+values[0]+"could not be resolved");
  420. return XmlQualifiedName.Empty;
  421. }
  422. name = values[1];
  423. }
  424. else
  425. {
  426. //Default Namespace
  427. ns = reader.LookupNamespace("");
  428. name = values[0];
  429. }
  430. qname = new XmlQualifiedName(name,ns);
  431. return qname;
  432. }
  433. public static int ValidateAttributesResolved (
  434. XmlSchemaObjectTable attributesResolved,
  435. ValidationEventHandler h,
  436. XmlSchema schema,
  437. XmlSchemaObjectCollection attributes,
  438. XmlSchemaAnyAttribute anyAttribute,
  439. ref XmlSchemaAnyAttribute anyAttributeUse,
  440. XmlSchemaAttributeGroup redefined)
  441. {
  442. int errorCount = 0;
  443. if (anyAttribute != null && anyAttributeUse == null)
  444. anyAttributeUse = anyAttribute;
  445. foreach (XmlSchemaObject xsobj in attributes) {
  446. XmlSchemaAttributeGroupRef grpRef = xsobj as XmlSchemaAttributeGroupRef;
  447. if (grpRef != null) {
  448. // Resolve attributeGroup redefinition.
  449. XmlSchemaAttributeGroup grp = null;
  450. if (redefined != null && grpRef.RefName == redefined.QualifiedName)
  451. grp = redefined;
  452. else
  453. grp = schema.AttributeGroups [grpRef.RefName] as XmlSchemaAttributeGroup;
  454. // otherwise, it might be missing sub components.
  455. if (grp == null) {
  456. if (!schema.missedSubComponents)// && schema.Schemas [grpRef.RefName.Namespace] != null)
  457. grpRef.error (h, "Referenced attribute group " + grpRef.RefName + " was not found in the corresponding schema.");
  458. continue;
  459. }
  460. if (grp.AttributeGroupRecursionCheck) {
  461. grp.error (h, "Attribute group recursion was found: " + grpRef.RefName);
  462. continue;
  463. }
  464. try {
  465. grp.AttributeGroupRecursionCheck = true;
  466. errorCount += grp.Validate (h, schema);
  467. } finally {
  468. grp.AttributeGroupRecursionCheck = false;
  469. }
  470. if (grp.AnyAttributeUse != null) {
  471. if (anyAttribute == null)
  472. anyAttributeUse = grp.AnyAttributeUse;
  473. }
  474. foreach (DictionaryEntry entry in grp.AttributeUses) {
  475. XmlSchemaAttribute attr = (XmlSchemaAttribute) entry.Value;
  476. #if BUGGY_MS_COMPLIANT
  477. if (attr.Use == XmlSchemaUse.Prohibited)
  478. continue;
  479. #endif
  480. if (attr.RefName != null && attr.RefName != XmlQualifiedName.Empty)
  481. AddToTable (attributesResolved, attr, attr.RefName, h);
  482. else
  483. AddToTable (attributesResolved, attr, attr.QualifiedName, h);
  484. }
  485. } else {
  486. XmlSchemaAttribute attr = xsobj as XmlSchemaAttribute;
  487. if (attr != null) {
  488. errorCount += attr.Validate (h, schema);
  489. #if BUGGY_MS_COMPLIANT
  490. if (attr.Use == XmlSchemaUse.Prohibited)
  491. continue;
  492. #endif
  493. if (attr.RefName != null && attr.RefName != XmlQualifiedName.Empty)
  494. AddToTable (attributesResolved, attr, attr.RefName, h);
  495. else
  496. AddToTable (attributesResolved, attr, attr.QualifiedName, h);
  497. } else {
  498. if (anyAttribute == null) {
  499. anyAttributeUse = (XmlSchemaAnyAttribute) xsobj;
  500. anyAttribute.Validate (h, schema);
  501. }
  502. }
  503. }
  504. }
  505. return errorCount;
  506. }
  507. }
  508. }