XmlSchemaUtil.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. //
  2. // Permission is hereby granted, free of charge, to any person obtaining
  3. // a copy of this software and associated documentation files (the
  4. // "Software"), to deal in the Software without restriction, including
  5. // without limitation the rights to use, copy, modify, merge, publish,
  6. // distribute, sublicense, and/or sell copies of the Software, and to
  7. // permit persons to whom the Software is furnished to do so, subject to
  8. // the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be
  11. // included in all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  14. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  15. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  16. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  17. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  18. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  19. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  20. //
  21. using System;
  22. using System.Xml;
  23. using System.Collections;
  24. using Mono.Xml;
  25. using Mono.Xml.Schema;
  26. using System.Xml.Serialization;
  27. namespace System.Xml.Schema
  28. {
  29. /// <summary>
  30. /// All Methods in this class should use XmlConvert. Some Methods are not present in the
  31. /// MS Implementation. We should provide them.
  32. /// </summary>
  33. internal class XmlSchemaUtil
  34. {
  35. static XmlSchemaUtil ()
  36. {
  37. FinalAllowed = XmlSchemaDerivationMethod.Restriction |
  38. XmlSchemaDerivationMethod.Extension;
  39. ComplexTypeBlockAllowed = FinalAllowed;
  40. ElementBlockAllowed = XmlSchemaDerivationMethod.Substitution |
  41. FinalAllowed;
  42. }
  43. internal static XmlSchemaDerivationMethod FinalAllowed;
  44. internal static XmlSchemaDerivationMethod ElementBlockAllowed;
  45. internal static XmlSchemaDerivationMethod ComplexTypeBlockAllowed;
  46. public static void AddToTable (XmlSchemaObjectTable table, XmlSchemaObject obj,
  47. XmlQualifiedName qname, ValidationEventHandler h)
  48. {
  49. if (table.Contains (qname)) {
  50. // FIXME: This logic unexpectedly allows
  51. // one redefining item and two or more redefining items.
  52. // FIXME: redefining item is not simple replacement,
  53. // but much more complex stuff.
  54. if (obj.isRedefineChild) { // take precedence.
  55. if (obj.redefinedObject != null)
  56. obj.error (h, String.Format ("Named item {0} was already contained in the schema object table.", qname));
  57. else
  58. obj.redefinedObject = table [qname];
  59. table.Set (qname, obj);
  60. }
  61. else if (table [qname].isRedefineChild) {
  62. if (table [qname].redefinedObject != null)
  63. obj.error (h, String.Format ("Named item {0} was already contained in the schema object table.", qname));
  64. else
  65. table [qname].redefinedObject = obj;
  66. return; // never add to the table.
  67. }
  68. else
  69. obj.error (h, String.Format ("Named item {0} was already contained in the schema object table.", qname));
  70. }
  71. else
  72. table.Set (qname, obj);
  73. }
  74. public static void CompileID(string id, XmlSchemaObject xso, Hashtable idCollection, ValidationEventHandler h)
  75. {
  76. //check if the string conforms to http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/datatypes.html#ID
  77. // 1. ID must be a NCName
  78. // 2. ID must be unique in the schema
  79. if(id == null)
  80. return;
  81. if(!CheckNCName(id))
  82. xso.error(h,id+" is not a valid id attribute");
  83. else if(idCollection.ContainsKey(id))
  84. xso.error(h,"Duplicate id attribute "+id);
  85. else
  86. idCollection.Add(id,xso);
  87. }
  88. public static bool CheckAnyUri(string uri)
  89. {
  90. if (uri.StartsWith ("##"))
  91. return false;
  92. return true;
  93. }
  94. public static bool CheckNormalizedString(string token)
  95. {
  96. return true;
  97. }
  98. public static bool CheckNCName(string name)
  99. {
  100. //check if the string conforms to http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/datatypes.html#NCName
  101. return XmlChar.IsNCName (name);
  102. }
  103. public static bool CheckQName(XmlQualifiedName qname)
  104. {
  105. // What is this doing?
  106. return true;
  107. }
  108. public static XmlParserContext GetParserContext (XmlReader reader)
  109. {
  110. XmlTextReader xtr = reader as XmlTextReader;
  111. if (xtr != null)
  112. return xtr.GetInternalParserContext ();
  113. XmlNodeReader xnr = reader as XmlNodeReader;
  114. if (xnr != null)
  115. return xnr.GetInternalParserContext ();
  116. XmlValidatingReader xvr = reader as XmlValidatingReader;
  117. if (xvr != null)
  118. return xvr.GetInternalParserContext ();
  119. IHasXmlParserContext xctx = reader as IHasXmlParserContext;
  120. if (xctx != null)
  121. return xctx.ParserContext;
  122. throw new NotSupportedException ("XmlParserContext cannot be acquired from this XmlReader.");
  123. }
  124. public static bool IsBuiltInDatatypeName (XmlQualifiedName qname)
  125. {
  126. if (qname.Namespace == XmlSchema.XdtNamespace) {
  127. switch (qname.Name) {
  128. case "anyAtomicType":
  129. case "untypedAtomic":
  130. case "dayTimeDuration":
  131. case "yearMonthDuration":
  132. return true;
  133. default:
  134. return false;
  135. }
  136. }
  137. if (qname.Namespace != XmlSchema.Namespace)
  138. return false;
  139. switch (qname.Name) {
  140. case "anySimpleType":
  141. case "duration": case "dateTime": case "time":
  142. case "date": case "gYearMonth": case "gYear":
  143. case "gMonthDay": case "gDay": case "gMonth":
  144. case "boolean":
  145. case "base64Binary": case "hexBinary":
  146. case "float": case "double":
  147. case "anyURI":
  148. case "QName":
  149. case "NOTATION":
  150. case "string": case "normalizedString": case "token":
  151. case "language": case "Name": case "NCName":
  152. case "ID": case "IDREF": case "IDREFS":
  153. case "ENTITY": case "ENTITIES":
  154. case "NMTOKEN": case "NMTOKENS":
  155. case "decimal": case "integer":
  156. case "nonPositiveInteger": case "negativeInteger":
  157. case "nonNegativeInteger":
  158. case "unsignedLong": case "unsignedInt":
  159. case "unsignedShort": case "unsignedByte":
  160. case "positiveInteger":
  161. case "long": case "int": case "short": case "byte":
  162. return true;
  163. }
  164. return false;
  165. }
  166. public static bool IsSchemaDatatypeEquals (XsdAnySimpleType st1, object v1,
  167. XsdAnySimpleType st2, object v2)
  168. {
  169. if (v1 == null || v2 == null)
  170. return false;
  171. if (st1 == null)
  172. st1 = XmlSchemaSimpleType.AnySimpleType;
  173. if (st2 == null)
  174. st2 = XmlSchemaSimpleType.AnySimpleType;
  175. Type t = st2.GetType ();
  176. if (st1 is XsdFloat) {
  177. return st2 is XsdFloat && Convert.ToSingle (v1) == Convert.ToSingle (v2);
  178. } else if (st1 is XsdDouble) {
  179. return st2 is XsdDouble && Convert.ToDouble (v1) == Convert.ToDouble (v2);
  180. } else if (st1 is XsdDecimal) {
  181. if (!(st2 is XsdDecimal) || Convert.ToDecimal (v1) != Convert.ToDecimal (v2))
  182. return false;
  183. if (st1 is XsdNonPositiveInteger)
  184. return st2 is XsdNonPositiveInteger || t == typeof (XsdDecimal) || t == typeof (XsdInteger);
  185. else if (st1 is XsdPositiveInteger)
  186. return st2 is XsdPositiveInteger || t == typeof (XsdDecimal) ||
  187. t == typeof (XsdInteger) || t == typeof (XsdNonNegativeInteger);
  188. else if (st1 is XsdUnsignedLong)
  189. return st2 is XsdUnsignedLong || t == typeof (XsdDecimal) ||
  190. t == typeof (XsdInteger) || t == typeof (XsdNonNegativeInteger);
  191. else if (st1 is XsdNonNegativeInteger)
  192. return st2 is XsdNonNegativeInteger || t == typeof (XsdDecimal) || t == typeof (XsdInteger);
  193. else if (st1 is XsdLong)
  194. return st2 is XsdLong || t == typeof (XsdDecimal) || t == typeof (XsdInteger);
  195. return true;
  196. }
  197. else if (!v1.Equals (v2))
  198. return false;
  199. if (st1 is XsdString) {
  200. if (!(st2 is XsdString))
  201. return false;
  202. if (st1 is XsdNMToken && (st2 is XsdLanguage || st2 is XsdName))
  203. return false;
  204. if (st2 is XsdNMToken && (st1 is XsdLanguage || st1 is XsdName))
  205. return false;
  206. if (st1 is XsdName && (st2 is XsdLanguage || st2 is XsdNMToken))
  207. return false;
  208. if (st2 is XsdName && (st1 is XsdLanguage || st1 is XsdNMToken))
  209. return false;
  210. if (st1 is XsdID && st2 is XsdIDRef)
  211. return false;
  212. if (st1 is XsdIDRef && st2 is XsdID)
  213. return false;
  214. }
  215. else if (st1 != st2)
  216. return false;
  217. return true;
  218. }
  219. public static bool IsValidQName(string qname)
  220. {
  221. foreach(string part in qname.Split(new char[]{':'},2))
  222. {
  223. if(!CheckNCName(part))
  224. return false;
  225. }
  226. return true;
  227. }
  228. //FIXME: First remove all the multiple instances of whitespace and then return the strings.
  229. //The current method returns empty strings if there are two or more consecutive whitespaces.
  230. public static string[] SplitList(string list)
  231. {
  232. if(list == null || list == string.Empty)
  233. return new string [0];
  234. ArrayList al = null;
  235. int start = 0;
  236. bool wait = true;
  237. for (int i = 0; i < list.Length; i++) {
  238. switch (list [i]) {
  239. case ' ':
  240. case '\r':
  241. case '\n':
  242. case '\t':
  243. if (!wait) {
  244. if (al == null)
  245. al = new ArrayList ();
  246. al.Add (list.Substring (start, i - start));
  247. }
  248. wait = true;
  249. break;
  250. default:
  251. if (wait) {
  252. wait = false;
  253. start = i;
  254. }
  255. break;
  256. }
  257. }
  258. if (!wait && start == 0)
  259. return new string [] {list};
  260. if (!wait && start < list.Length)
  261. al.Add (start == 0 ? list : list.Substring (start));
  262. return al.ToArray (typeof (string)) as string [];
  263. }
  264. public static void ReadUnhandledAttribute(XmlReader reader, XmlSchemaObject xso)
  265. {
  266. if(reader.Prefix == "xmlns")
  267. xso.Namespaces.Add(reader.LocalName, reader.Value);
  268. else if(reader.Name == "xmlns")
  269. xso.Namespaces.Add("",reader.Value);
  270. else
  271. {
  272. if(xso.unhandledAttributeList == null)
  273. xso.unhandledAttributeList = new System.Collections.ArrayList();
  274. XmlAttribute attr = new XmlDocument().CreateAttribute(reader.LocalName,reader.NamespaceURI);
  275. attr.Value = reader.Value;
  276. ParseWsdlArrayType (reader, attr);
  277. xso.unhandledAttributeList.Add(attr);
  278. }
  279. }
  280. static void ParseWsdlArrayType (XmlReader reader, XmlAttribute attr)
  281. {
  282. if (attr.NamespaceURI == XmlSerializer.WsdlNamespace && attr.LocalName == "arrayType")
  283. {
  284. string ns = "", type, dimensions;
  285. TypeTranslator.ParseArrayType (attr.Value, out type, out ns, out dimensions);
  286. if (ns != "") ns = reader.LookupNamespace (ns) + ":";
  287. attr.Value = ns + type + dimensions;
  288. }
  289. }
  290. public static bool ReadBoolAttribute(XmlReader reader, out Exception innerExcpetion)
  291. {
  292. innerExcpetion = null;
  293. try
  294. {
  295. bool val = XmlConvert.ToBoolean(reader.Value);
  296. return val;
  297. }
  298. catch(Exception ex)
  299. {
  300. innerExcpetion = ex;
  301. return false;
  302. }
  303. }
  304. public static decimal ReadDecimalAttribute(XmlReader reader, out Exception innerExcpetion)
  305. {
  306. innerExcpetion = null;
  307. try
  308. {
  309. decimal val = XmlConvert.ToDecimal(reader.Value);
  310. return val;
  311. }
  312. catch(Exception ex)
  313. {
  314. innerExcpetion = ex;
  315. return decimal.Zero;
  316. }
  317. }
  318. // Is some value is read, return it.
  319. // If no values return empty.
  320. // If exception, return none
  321. public static XmlSchemaDerivationMethod ReadDerivationAttribute(XmlReader reader, out Exception innerExcpetion, string name, XmlSchemaDerivationMethod allowed)
  322. {
  323. innerExcpetion = null;
  324. try
  325. {
  326. string list = reader.Value;
  327. string warn = "";
  328. XmlSchemaDerivationMethod val = 0;
  329. if(list.IndexOf("#all") != -1 && list.Trim() != "#all")
  330. {
  331. innerExcpetion = new Exception(list+" is not a valid value for "+ name +". #all if present must be the only value");
  332. return XmlSchemaDerivationMethod.All;
  333. }
  334. foreach(string xsdm in XmlSchemaUtil.SplitList(list))
  335. {
  336. switch(xsdm)
  337. {
  338. case "":
  339. val = AddFlag (val, XmlSchemaDerivationMethod.Empty, allowed); break;
  340. case "#all":
  341. val = AddFlag (val,XmlSchemaDerivationMethod.All, allowed); break;
  342. case "substitution":
  343. val = AddFlag (val,XmlSchemaDerivationMethod.Substitution, allowed); break;
  344. case "extension":
  345. val = AddFlag (val,XmlSchemaDerivationMethod.Extension, allowed); break;
  346. case "restriction":
  347. val = AddFlag (val,XmlSchemaDerivationMethod.Restriction, allowed); break;
  348. case "list":
  349. val = AddFlag (val,XmlSchemaDerivationMethod.List, allowed); break;
  350. case "union":
  351. val = AddFlag (val,XmlSchemaDerivationMethod.Union, allowed); break;
  352. default:
  353. warn += xsdm + " "; break;
  354. }
  355. }
  356. if(warn != "")
  357. innerExcpetion = new Exception(warn + "is/are not valid values for " + name);
  358. return val;
  359. }
  360. catch(Exception ex)
  361. {
  362. innerExcpetion = ex;
  363. return XmlSchemaDerivationMethod.None;
  364. }
  365. }
  366. private static XmlSchemaDerivationMethod AddFlag (XmlSchemaDerivationMethod dst,
  367. XmlSchemaDerivationMethod add, XmlSchemaDerivationMethod allowed)
  368. {
  369. if ((add & allowed) == 0 && allowed != XmlSchemaDerivationMethod.All)
  370. throw new ArgumentException (add + " is not allowed in this attribute.");
  371. if ((dst & add) != 0)
  372. throw new ArgumentException (add + " is already specified in this attribute.");
  373. return dst | add;
  374. }
  375. public static XmlSchemaForm ReadFormAttribute(XmlReader reader, out Exception innerExcpetion)
  376. {
  377. innerExcpetion = null;
  378. XmlSchemaForm val = XmlSchemaForm.None;
  379. switch(reader.Value)
  380. {
  381. case "qualified":
  382. val = XmlSchemaForm.Qualified; break;
  383. case "unqualified":
  384. val = XmlSchemaForm.Unqualified; break;
  385. default:
  386. innerExcpetion = new Exception("only qualified or unqulified is a valid value"); break;
  387. }
  388. return val;
  389. }
  390. public static XmlSchemaContentProcessing ReadProcessingAttribute(XmlReader reader, out Exception innerExcpetion)
  391. {
  392. innerExcpetion = null;
  393. XmlSchemaContentProcessing val = XmlSchemaContentProcessing.None;
  394. switch(reader.Value)
  395. {
  396. case "lax":
  397. val = XmlSchemaContentProcessing.Lax; break;
  398. case "strict":
  399. val = XmlSchemaContentProcessing.Strict; break;
  400. case "skip":
  401. val = XmlSchemaContentProcessing.Skip; break;
  402. default:
  403. innerExcpetion = new Exception("only lax , strict or skip are valid values for processContents");
  404. break;
  405. }
  406. return val;
  407. }
  408. public static XmlSchemaUse ReadUseAttribute(XmlReader reader, out Exception innerExcpetion)
  409. {
  410. innerExcpetion = null;
  411. XmlSchemaUse val = XmlSchemaUse.None;
  412. switch(reader.Value)
  413. {
  414. case "optional":
  415. val = XmlSchemaUse.Optional; break;
  416. case "prohibited":
  417. val = XmlSchemaUse.Prohibited; break;
  418. case "required":
  419. val = XmlSchemaUse.Required; break;
  420. default:
  421. innerExcpetion = new Exception("only optional , prohibited or required are valid values for use");
  422. break;
  423. }
  424. return val;
  425. }
  426. public static XmlQualifiedName ReadQNameAttribute(XmlReader reader, out Exception innerEx)
  427. {
  428. return ToQName(reader, reader.Value, out innerEx);
  429. }
  430. //While Creating a XmlQualifedName, we should check:
  431. // 1. If a prefix is present, its namespace should be resolvable.
  432. // 2. If a prefix is not present, and if the defaultNamespace is set,
  433. public static XmlQualifiedName ToQName(XmlReader reader, string qnamestr, out Exception innerEx)
  434. {
  435. string ns;
  436. string name;
  437. XmlQualifiedName qname;
  438. innerEx = null;
  439. if(!IsValidQName(qnamestr))
  440. {
  441. innerEx = new Exception(qnamestr + " is an invalid QName. Either name or namespace is not a NCName");
  442. return XmlQualifiedName.Empty;
  443. }
  444. string[] values = qnamestr.Split(new char[]{':'},2);
  445. if(values.Length == 2)
  446. {
  447. ns = reader.LookupNamespace(values[0]);
  448. if(ns == null)
  449. {
  450. innerEx = new Exception("Namespace Prefix '"+values[0]+"could not be resolved");
  451. return XmlQualifiedName.Empty;
  452. }
  453. name = values[1];
  454. }
  455. else
  456. {
  457. //Default Namespace
  458. ns = reader.LookupNamespace("");
  459. name = values[0];
  460. }
  461. qname = new XmlQualifiedName(name,ns);
  462. return qname;
  463. }
  464. public static int ValidateAttributesResolved (
  465. XmlSchemaObjectTable attributesResolved,
  466. ValidationEventHandler h,
  467. XmlSchema schema,
  468. XmlSchemaObjectCollection attributes,
  469. XmlSchemaAnyAttribute anyAttribute,
  470. ref XmlSchemaAnyAttribute anyAttributeUse,
  471. XmlSchemaAttributeGroup redefined)
  472. {
  473. int errorCount = 0;
  474. if (anyAttribute != null && anyAttributeUse == null)
  475. anyAttributeUse = anyAttribute;
  476. foreach (XmlSchemaObject xsobj in attributes) {
  477. XmlSchemaAttributeGroupRef grpRef = xsobj as XmlSchemaAttributeGroupRef;
  478. if (grpRef != null) {
  479. // Resolve attributeGroup redefinition.
  480. XmlSchemaAttributeGroup grp = null;
  481. if (redefined != null && grpRef.RefName == redefined.QualifiedName)
  482. grp = redefined;
  483. else
  484. grp = schema.AttributeGroups [grpRef.RefName] as XmlSchemaAttributeGroup;
  485. // otherwise, it might be missing sub components.
  486. if (grp == null) {
  487. if (!schema.missedSubComponents)// && schema.Schemas [grpRef.RefName.Namespace] != null)
  488. grpRef.error (h, "Referenced attribute group " + grpRef.RefName + " was not found in the corresponding schema.");
  489. continue;
  490. }
  491. if (grp.AttributeGroupRecursionCheck) {
  492. grp.error (h, "Attribute group recursion was found: " + grpRef.RefName);
  493. continue;
  494. }
  495. try {
  496. grp.AttributeGroupRecursionCheck = true;
  497. errorCount += grp.Validate (h, schema);
  498. } finally {
  499. grp.AttributeGroupRecursionCheck = false;
  500. }
  501. if (grp.AnyAttributeUse != null) {
  502. if (anyAttribute == null)
  503. anyAttributeUse = grp.AnyAttributeUse;
  504. }
  505. foreach (DictionaryEntry entry in grp.AttributeUses) {
  506. XmlSchemaAttribute attr = (XmlSchemaAttribute) entry.Value;
  507. #if BUGGY_MS_COMPLIANT
  508. if (attr.Use == XmlSchemaUse.Prohibited)
  509. continue;
  510. #endif
  511. if (attr.RefName != null && attr.RefName != XmlQualifiedName.Empty)
  512. AddToTable (attributesResolved, attr, attr.RefName, h);
  513. else
  514. AddToTable (attributesResolved, attr, attr.QualifiedName, h);
  515. }
  516. } else {
  517. XmlSchemaAttribute attr = xsobj as XmlSchemaAttribute;
  518. if (attr != null) {
  519. errorCount += attr.Validate (h, schema);
  520. #if BUGGY_MS_COMPLIANT
  521. if (attr.Use == XmlSchemaUse.Prohibited)
  522. continue;
  523. #endif
  524. if (attr.RefName != null && attr.RefName != XmlQualifiedName.Empty)
  525. AddToTable (attributesResolved, attr, attr.RefName, h);
  526. else
  527. AddToTable (attributesResolved, attr, attr.QualifiedName, h);
  528. } else {
  529. if (anyAttribute == null) {
  530. anyAttributeUse = (XmlSchemaAnyAttribute) xsobj;
  531. anyAttribute.Validate (h, schema);
  532. }
  533. }
  534. }
  535. }
  536. return errorCount;
  537. }
  538. }
  539. }