2004-05-06 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml.Schema / XmlSchemaAttribute.cs
1 //\r
2 // System.Xml.Schema.XmlSchemaAttribute.cs\r
3 //\r
4 // Authors:\r
5 //      Dwivedi, Ajay kumar  Adwiv@Yahoo.com\r
6 //      Enomoto, Atsushi     ginga@kit.hi-ho.ne.jp\r
7 //\r
8 using System;\r
9 using System.Xml;\r
10 using System.ComponentModel;\r
11 using System.Xml.Serialization;\r
12 \r
13 namespace System.Xml.Schema\r
14 {\r
15         /// <summary>\r
16         /// Summary description for XmlSchemaAttribute.\r
17         /// </summary>\r
18         public class XmlSchemaAttribute : XmlSchemaAnnotated\r
19         {\r
20                 private object attributeType;\r
21                 private string defaultValue;\r
22                 private string fixedValue;\r
23                 private string validatedDefaultValue;\r
24                 private string validatedFixedValue;\r
25                 private XmlSchemaForm form;\r
26                 private string name;\r
27                 private string targetNamespace;\r
28                 private XmlQualifiedName qualifiedName;\r
29                 private XmlQualifiedName refName;\r
30                 private XmlSchemaSimpleType schemaType;\r
31                 private XmlQualifiedName schemaTypeName;\r
32                 private XmlSchemaUse use;\r
33                 private XmlSchemaUse validatedUse;\r
34                 //Compilation fields\r
35                 internal bool ParentIsSchema = false;\r
36                 private XmlSchemaAttribute referencedAttribute;\r
37                 const string xmlname = "attribute";\r
38 \r
39                 public XmlSchemaAttribute()\r
40                 {\r
41                         //LAMESPEC: Docs says the default is optional.\r
42                         //Whereas the MS implementation has default None.\r
43                         form    = XmlSchemaForm.None;\r
44                         use             = XmlSchemaUse.None;\r
45                         schemaTypeName  = XmlQualifiedName.Empty;\r
46                         qualifiedName   = XmlQualifiedName.Empty;\r
47                         refName                 = XmlQualifiedName.Empty;\r
48                 }\r
49 \r
50                 // Properties\r
51                 #region Properties\r
52 \r
53                 [DefaultValue(null)]\r
54                 [System.Xml.Serialization.XmlAttribute("default")]\r
55                 public string DefaultValue \r
56                 {\r
57                         get{ return defaultValue;}\r
58                         set\r
59                         { // Default Value and fixed Value are mutually exclusive\r
60                                 fixedValue = null;\r
61                                 defaultValue = value;\r
62                         }\r
63                 }\r
64 \r
65                 [DefaultValue(null)]\r
66                 [System.Xml.Serialization.XmlAttribute("fixed")]\r
67                 public string FixedValue \r
68                 {\r
69                         get{ return fixedValue;}\r
70                         set\r
71                         { // Default Value and fixed Value are mutually exclusive\r
72                                 defaultValue = null;\r
73                                 fixedValue = value;\r
74                         }\r
75                 }\r
76 \r
77                 [DefaultValue(XmlSchemaForm.None)]\r
78                 [System.Xml.Serialization.XmlAttribute("form")]\r
79                 public XmlSchemaForm Form \r
80                 {\r
81                         get{ return form;}\r
82                         set{ form = value;}\r
83                 }\r
84 \r
85                 [System.Xml.Serialization.XmlAttribute("name")]\r
86                 public string Name \r
87                 {\r
88                         get{ return name;}\r
89                         set\r
90                         {\r
91                                 name  = value;\r
92                         }\r
93                 }\r
94 \r
95                 [System.Xml.Serialization.XmlAttribute("ref")]\r
96                 public XmlQualifiedName RefName \r
97                 {\r
98                         get{ return refName;}\r
99                         set\r
100                         {\r
101                                 refName = value; \r
102                         }\r
103                 }\r
104                 \r
105                 [System.Xml.Serialization.XmlAttribute("type")]\r
106                 public XmlQualifiedName SchemaTypeName \r
107                 {\r
108                         get{ return schemaTypeName;}\r
109                         set{ schemaTypeName = value;}\r
110                 }\r
111 \r
112                 [XmlElement("simpleType",Namespace=XmlSchema.Namespace)]\r
113                 public XmlSchemaSimpleType SchemaType \r
114                 {\r
115                         get{ return schemaType;}\r
116                         set{ schemaType = value;}\r
117                 }\r
118 \r
119                 [DefaultValue(XmlSchemaUse.None)]\r
120                 [System.Xml.Serialization.XmlAttribute("use")]\r
121                 public XmlSchemaUse Use \r
122                 {\r
123                         get{ return use;}\r
124                         set{ use = value;}\r
125                 }\r
126 \r
127                 [XmlIgnore]\r
128                 public XmlQualifiedName QualifiedName \r
129                 {\r
130                         get{ return qualifiedName;}\r
131                 }\r
132 \r
133                 [XmlIgnore]\r
134                 public object AttributeType \r
135                 {\r
136                         get{\r
137                                 if (referencedAttribute != null)\r
138                                         return referencedAttribute.AttributeType;\r
139                                 else\r
140                                         return attributeType;\r
141                         }\r
142                 }\r
143 \r
144                 // Post compilation default value (normalized)\r
145                 internal string ValidatedDefaultValue\r
146                 {\r
147                         // DefaultValue can be overriden in case of ref.\r
148                         get { return validatedDefaultValue; }\r
149                 }\r
150 \r
151                 // Post compilation fixed value (normalized)\r
152                 internal string ValidatedFixedValue \r
153                 {\r
154                         // FixedValue can be overriden in case of ref.\r
155                         get { return validatedFixedValue; }\r
156                 }\r
157                 internal XmlSchemaUse ValidatedUse\r
158                 {\r
159                         get { return validatedUse; }\r
160                 }\r
161 \r
162                 #endregion\r
163 \r
164                 /// <remarks>\r
165                 /// For an attribute:\r
166                 ///  a) If the parent is schema \r
167                 ///             1-5             are from <xs:complexType name="topLevelAttribute"> in the Schema for Schema\r
168                 ///             6-8             are from  "Constraints on XML Representations of Attribute Declarations"\r
169                 ///             9-10    are from "Attribute Declaration Schema Component"\r
170                 ///             11-16   are from "Constraints on Attribute Declaration Schema Components"\r
171                 ///             1. ref  must be absent\r
172                 ///             2. form must be absent\r
173                 ///             3. use  must be absent\r
174                 ///             4. name must be present and of type NCName\r
175                 ///             5. *NO CHECK REQUIRED* Only simple types and annotation are allowed as content\r
176                 ///             6. default and fixed must not both be present. \r
177                 ///             7. *NO CHECK REQUIRED* If default and use are both present... (Not possible since use is absent)\r
178                 ///             8. type and <simpleType> must not both be present.\r
179                 ///             9. Target Namespace should be schema's targetnamespace or absent\r
180                 ///             10. Type Definiton coressponds to <simpletype> element, or type value, or absent\r
181                 ///             11. *TO UNDERSTAND* Missing Sub-components\r
182                 ///             12. value constraint must be of the same datatype as of type\r
183                 ///             13. if the type definition is ID then there should be no value constraint.\r
184                 ///             14. name must not be xmlns\r
185                 ///             15. Targetnamespace must not be xsi. This implies the target namespace of schema can't be xsi if toplevel attributes are used.\r
186                 ///             16. *Exception to rule 15* inbuilt attributes: xsi:nil, xsi:type, xsi:schemaLocation, xsi: noNamespaceSchemaLocation\r
187                 ///     b) If the parent is complextype and ref is not set\r
188                 ///             1. name must be present and of type NCName.\r
189                 ///             2. type and <simpleType> must not both be present.\r
190                 ///             3. default and fixed must not both be present. \r
191                 ///     4. If default and use are both present, use must have the Â·actual value· optional.\r
192                 ///             5. name must not be xmlns\r
193                 ///             6. Targetnamespace must not be xsi.\r
194                 ///             7. *Exception to rule 15* inbuilt attributes: xsi:nil, xsi:type, xsi:schemaLocation, xsi: noNamespaceSchemaLocation\r
195                 ///             8. If form has actual value qualified or the schema's formdefault is qualified, targetnamespace\r
196                 ///                is same as schema's target namespace, otherwise absent.\r
197                 ///     c) if the parent is not schema and ref is set\r
198                 ///             1. name must not be present\r
199                 ///             2. all of <simpleType>, form and type must be absent. \r
200                 ///             3. default and fixed must not both be present. \r
201                 ///     4. If default and use are both present, use must have the Â·actual value· optional.\r
202                 /// </remarks>\r
203                 internal override int Compile(ValidationEventHandler h, XmlSchema schema)\r
204                 {\r
205                         // If this is already compiled this time, simply skip.\r
206                         if (this.IsComplied (schema.CompilationId))\r
207                                 return 0;\r
208 \r
209                         errorCount = 0;\r
210                         \r
211                         if(ParentIsSchema || isRedefineChild)//a\r
212                         {\r
213                                 if(RefName!= null && !RefName.IsEmpty) // a.1\r
214                                         error(h,"ref must be absent in the top level <attribute>");\r
215                                 \r
216                                 if(Form != XmlSchemaForm.None)  // a.2\r
217                                         error(h,"form must be absent in the top level <attribute>");\r
218                                 \r
219                                 if(Use != XmlSchemaUse.None)            // a.3\r
220                                         error(h,"use must be absent in the top level <attribute>");\r
221 \r
222                                 targetNamespace = schema.TargetNamespace;\r
223 \r
224                                 CompileCommon (h, schema, true);\r
225                         }\r
226                         else // local\r
227                         {\r
228                                 // Q:How to Use of AttributeFormDefault????\r
229                                 // A:Global attribute cannot be defined locally\r
230                                 if(RefName == null || RefName.IsEmpty)\r
231                                 {\r
232                                         if(form == XmlSchemaForm.Qualified || (form == XmlSchemaForm.None && schema.AttributeFormDefault == XmlSchemaForm.Qualified))\r
233                                                 this.targetNamespace = schema.TargetNamespace;\r
234                                         else\r
235                                                 this.targetNamespace = "";\r
236 \r
237                                         CompileCommon(h, schema, true);\r
238                                 }\r
239                                 else\r
240                                 {\r
241                                         if(this.name != null)\r
242                                                 error(h,"name must be absent if ref is present");\r
243                                         if(this.form != XmlSchemaForm.None)\r
244                                                 error(h,"form must be absent if ref is present");\r
245                                         if(this.schemaType != null)\r
246                                                 error(h,"simpletype must be absent if ref is present");\r
247                                         if(this.schemaTypeName != null && !this.schemaTypeName.IsEmpty)\r
248                                                 error(h,"type must be absent if ref is present");\r
249 \r
250                                         CompileCommon(h, schema, false);\r
251                                 }\r
252                         }\r
253 \r
254                         this.CompilationId = schema.CompilationId;\r
255                         return errorCount;\r
256                 }\r
257                 \r
258                 private void CompileCommon(ValidationEventHandler h, XmlSchema schema, bool refIsNotPresent)\r
259                 {\r
260                         if(refIsNotPresent)\r
261                         {\r
262                                 if(Name == null)        //a.4, b.1, \r
263                                         error(h,"Required attribute name must be present");\r
264                                 else if(!XmlSchemaUtil.CheckNCName(Name)) // a.4.2, b1.2\r
265                                         error(h,"attribute name must be NCName");\r
266                                 else if(Name == "xmlns") // a.14 , b5\r
267                                         error(h,"attribute name must not be xmlns");\r
268                                 else\r
269                                         qualifiedName = new XmlQualifiedName(Name, targetNamespace);\r
270 \r
271                                 if(SchemaType != null)\r
272                                 {\r
273                                         if(SchemaTypeName != null && !SchemaTypeName.IsEmpty) // a.8\r
274                                                 error(h,"attribute can't have both a type and <simpleType> content");\r
275 \r
276                                         errorCount += SchemaType.Compile(h, schema); \r
277                                 }\r
278 \r
279                                 if(SchemaTypeName != null && !XmlSchemaUtil.CheckQName(SchemaTypeName))\r
280                                         error(h,SchemaTypeName+" is not a valid QName");\r
281                         }\r
282                         else\r
283                         {\r
284                                 if(RefName == null || RefName.IsEmpty) \r
285                                         throw new InvalidOperationException ("Error: Should Never Happen. refname must be present");\r
286                                 else\r
287                                         qualifiedName = RefName;\r
288                         }\r
289 \r
290                         if(schema.TargetNamespace == XmlSchema.InstanceNamespace && Name != "nil" && Name != "type" \r
291                                 && Name != "schemaLocation" && Name != "noNamespaceSchemaLocation") // a.15, a.16\r
292                                 error(h,"targetNamespace can't be " + XmlSchema.InstanceNamespace);\r
293 \r
294                         if(DefaultValue != null && FixedValue != null) // a.6, b.3, c.3\r
295                                 error(h,"default and fixed must not both be present in an Attribute");\r
296 \r
297                         if(DefaultValue != null && Use != XmlSchemaUse.None && Use != XmlSchemaUse.Optional)\r
298                                 error(h,"if default is present, use must be optional");\r
299 \r
300                         XmlSchemaUtil.CompileID(Id, this, schema.IDCollection, h);\r
301                 }\r
302 \r
303                 /// <summary>\r
304                 /// Schema Component: \r
305                 ///                     QName, SimpleType, Scope, Default|Fixed, annotation\r
306                 /// </summary>\r
307                 internal override int Validate(ValidationEventHandler h, XmlSchema schema)\r
308                 {\r
309                         if(IsValidated (schema.ValidationId))\r
310                                 return errorCount;\r
311 \r
312                         // -- Attribute Declaration Schema Component --\r
313                         // {name}, {target namespace} -> QualifiedName. Already Compile()d.\r
314                         // {type definition} -> attributeType. From SchemaType or SchemaTypeName.\r
315                         // {scope} -> ParentIsSchema | isRedefineChild.\r
316                         // {value constraint} -> ValidatedFixedValue, ValidatedDefaultValue.\r
317                         // {annotation}\r
318                         // -- Attribute Use Schema Component --\r
319                         // {required}\r
320                         // {attribute declaration}\r
321                         // {value constraint}\r
322 \r
323                         // First, fill type information for type reference\r
324                         if (SchemaType != null) {\r
325                                 SchemaType.Validate (h, schema);\r
326                                 attributeType = SchemaType;\r
327                         }\r
328                         else if (SchemaTypeName != null && SchemaTypeName != XmlQualifiedName.Empty)\r
329                         {\r
330                                 // If type is null, then it is missing sub components .\r
331                                 XmlSchemaType type = schema.SchemaTypes [SchemaTypeName] as XmlSchemaType;\r
332                                 if (type is XmlSchemaComplexType)\r
333                                         error(h,"An attribute can't have complexType Content");\r
334                                 else if (type != null) {        // simple type\r
335                                         errorCount += type.Validate (h, schema);\r
336                                         attributeType = type;\r
337                                 }\r
338                                 else if (SchemaTypeName == XmlSchemaComplexType.AnyTypeName)\r
339                                         attributeType = XmlSchemaComplexType.AnyType;
340                                 else if (XmlSchemaUtil.IsBuiltInDatatypeName (SchemaTypeName)) {\r
341                                         attributeType = XmlSchemaDatatype.FromName (SchemaTypeName);\r
342                                         if (attributeType == null)\r
343                                                 error (h, "Invalid xml schema namespace datatype was specified.");\r
344                                 }\r
345                                 // otherwise, it might be missing sub components.\r
346                                 else if (!schema.IsNamespaceAbsent (SchemaTypeName.Namespace))\r
347                                         error (h, "Referenced schema type " + SchemaTypeName + " was not found in the corresponding schema.");\r
348                         }\r
349 \r
350                         // Then, fill type information for the type references for the referencing attributes\r
351                         if (RefName != null && RefName != XmlQualifiedName.Empty)\r
352                         {\r
353                                 referencedAttribute = schema.Attributes [RefName] as XmlSchemaAttribute;\r
354                                 // If el is null, then it is missing sub components .\r
355                                 if (referencedAttribute != null)\r
356                                         errorCount += referencedAttribute.Validate (h, schema);\r
357                                 // otherwise, it might be missing sub components.\r
358                                 else if (!schema.IsNamespaceAbsent (RefName.Namespace))\r
359                                         error (h, "Referenced attribute " + RefName + " was not found in the corresponding schema.");\r
360                         }\r
361 \r
362                         if (attributeType == null)\r
363                                 attributeType = XmlSchemaSimpleType.AnySimpleType;\r
364 \r
365                         // Validate {value constraints}\r
366                         if (defaultValue != null || fixedValue != null) {\r
367                                 XmlSchemaDatatype datatype = attributeType as XmlSchemaDatatype;\r
368                                 if (datatype == null)\r
369                                         datatype = ((XmlSchemaSimpleType) attributeType).Datatype;\r
370                                 if (datatype.TokenizedType == XmlTokenizedType.QName)\r
371                                         error (h, "By the defection of the W3C XML Schema specification, it is impossible to supply QName default or fixed values.");\r
372                                 else {\r
373                                         try {\r
374                                                 if (defaultValue != null) {\r
375                                                         validatedDefaultValue = datatype.Normalize (defaultValue);\r
376                                                         datatype.ParseValue (validatedDefaultValue, null, null);\r
377                                                 }\r
378                                         } catch (Exception ex) {\r
379                                                 // FIXME: This is not a good way to handle exception.\r
380                                                 error (h, "The Attribute's default value is invalid with its type definition.", ex);\r
381                                         }\r
382                                         try {\r
383                                                 if (fixedValue != null) {\r
384                                                         validatedFixedValue = datatype.Normalize (fixedValue);\r
385                                                         datatype.ParseValue (validatedFixedValue, null, null);\r
386                                                 }\r
387                                         } catch (Exception ex) {\r
388                                                 // FIXME: This is not a good way to handle exception.\r
389                                                 error (h, "The Attribute's fixed value is invalid with its type definition.", ex);\r
390                                         }\r
391                                 }\r
392                         }\r
393                         if (Use == XmlSchemaUse.None)\r
394                                 validatedUse = XmlSchemaUse.Optional;\r
395                         else\r
396                                 validatedUse = Use;\r
397 \r
398                         ValidationId = schema.ValidationId;\r
399                         return errorCount;\r
400                 }\r
401 \r
402                 //<attribute\r
403                 //  default = string\r
404                 //  fixed = string\r
405                 //  form = (qualified | unqualified)\r
406                 //  id = ID\r
407                 //  name = NCName\r
408                 //  ref = QName\r
409                 //  type = QName\r
410                 //  use = (optional | prohibited | required) : optional\r
411                 //  {any attributes with non-schema namespace . . .}>\r
412                 //  Content: (annotation?, (simpleType?))\r
413                 //</attribute>\r
414                 internal static XmlSchemaAttribute Read(XmlSchemaReader reader, ValidationEventHandler h)\r
415                 {\r
416                         XmlSchemaAttribute attribute = new XmlSchemaAttribute();\r
417                         reader.MoveToElement();\r
418 \r
419                         if(reader.NamespaceURI != XmlSchema.Namespace || reader.LocalName != xmlname)\r
420                         {\r
421                                 error(h,"Should not happen :1: XmlSchemaAttribute.Read, name="+reader.Name,null);\r
422                                 reader.SkipToEnd();\r
423                                 return null;\r
424                         }\r
425 \r
426                         attribute.LineNumber = reader.LineNumber;\r
427                         attribute.LinePosition = reader.LinePosition;\r
428                         attribute.SourceUri = reader.BaseURI;\r
429 \r
430                         while(reader.MoveToNextAttribute())\r
431                         {\r
432                                 if(reader.Name == "default")\r
433                                 {\r
434                                         attribute.defaultValue = reader.Value;\r
435                                 }\r
436                                 else if(reader.Name == "fixed")\r
437                                 {\r
438                                         attribute.fixedValue = reader.Value;\r
439                                 }\r
440                                 else if(reader.Name == "form")\r
441                                 {\r
442                                         Exception innerex;\r
443                                         attribute.form = XmlSchemaUtil.ReadFormAttribute(reader,out innerex);\r
444                                         if(innerex != null)\r
445                                                 error(h, reader.Value + " is not a valid value for form attribute", innerex);\r
446                                 }\r
447                                 else if(reader.Name == "id")\r
448                                 {\r
449                                         attribute.Id = reader.Value;\r
450                                 }\r
451                                 else if(reader.Name == "name")\r
452                                 {\r
453                                         attribute.name = reader.Value;\r
454                                 }\r
455                                 else if(reader.Name == "ref")\r
456                                 {\r
457                                         Exception innerex;\r
458                                         attribute.refName = XmlSchemaUtil.ReadQNameAttribute(reader,out innerex);\r
459                                         if(innerex != null)\r
460                                                 error(h, reader.Value + " is not a valid value for ref attribute",innerex);\r
461                                 }\r
462                                 else if(reader.Name == "type")\r
463                                 {\r
464                                         Exception innerex;\r
465                                         attribute.schemaTypeName = XmlSchemaUtil.ReadQNameAttribute(reader,out innerex);\r
466                                         if(innerex != null)\r
467                                                 error(h, reader.Value + " is not a valid value for type attribute",innerex);\r
468                                 }\r
469                                 else if(reader.Name == "use")\r
470                                 {\r
471                                         Exception innerex;\r
472                                         attribute.use = XmlSchemaUtil.ReadUseAttribute(reader,out innerex);\r
473                                         if(innerex != null)\r
474                                                 error(h, reader.Value + " is not a valid value for use attribute", innerex);\r
475                                 }\r
476                                 else if((reader.NamespaceURI == "" && reader.Name != "xmlns") || reader.NamespaceURI == XmlSchema.Namespace)\r
477                                 {\r
478                                         error(h,reader.Name + " is not a valid attribute for attribute",null);\r
479                                 }\r
480                                 else\r
481                                 {\r
482                                         XmlSchemaUtil.ReadUnhandledAttribute(reader,attribute);\r
483                                 }\r
484                         }\r
485                         \r
486                         reader.MoveToElement();\r
487                         if(reader.IsEmptyElement)\r
488                                 return attribute;\r
489 \r
490                         //  Content: (annotation?, (simpleType?))\r
491                         int level = 1;\r
492                         while(reader.ReadNextElement())\r
493                         {\r
494                                 if(reader.NodeType == XmlNodeType.EndElement)\r
495                                 {\r
496                                         if(reader.LocalName != xmlname)\r
497                                                 error(h,"Should not happen :2: XmlSchemaAttribute.Read, name="+reader.Name,null);\r
498                                         break;\r
499                                 }\r
500                                 if(level <= 1 && reader.LocalName == "annotation")\r
501                                 {\r
502                                         level = 2; //Only one annotation\r
503                                         XmlSchemaAnnotation annotation = XmlSchemaAnnotation.Read(reader,h);\r
504                                         if(annotation != null)\r
505                                                 attribute.Annotation = annotation;\r
506                                         continue;\r
507                                 }\r
508                                 if(level <=2 && reader.LocalName == "simpleType")\r
509                                 {\r
510                                         level = 3;\r
511                                         XmlSchemaSimpleType stype = XmlSchemaSimpleType.Read(reader,h);\r
512                                         if(stype != null)\r
513                                                 attribute.schemaType = stype;\r
514                                         continue;\r
515                                 }\r
516                                 reader.RaiseInvalidElementError();\r
517                         }\r
518                         return attribute;\r
519                 }\r
520                 \r
521         }\r
522 }\r