2004-05-06 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml.Schema / XmlSchemaChoice.cs
1 //\r
2 // System.Xml.Schema.XmlSchemaChoice.cs\r
3 //\r
4 // Author:\r
5 //      Dwivedi, Ajay kumar  Adwiv@Yahoo.com\r
6 //      Atsushi Enomoto  ginga@kit.hi-ho.ne.jp\r
7 //\r
8 using System;\r
9 using System.Collections;\r
10 using System.Xml.Serialization;\r
11 using System.Xml;\r
12 \r
13 namespace System.Xml.Schema\r
14 {\r
15         public class XmlSchemaChoice : XmlSchemaGroupBase\r
16         {\r
17                 private XmlSchemaObjectCollection items;\r
18                 const string xmlname = "choice";\r
19                 private decimal minEffectiveTotalRange = -1;\r
20 \r
21                 public XmlSchemaChoice ()\r
22                 {\r
23                         items = new XmlSchemaObjectCollection();\r
24                 }\r
25 \r
26                 [XmlElement("element",typeof(XmlSchemaElement),Namespace=XmlSchema.Namespace)]\r
27                 [XmlElement("group",typeof(XmlSchemaGroupRef),Namespace=XmlSchema.Namespace)]\r
28                 [XmlElement("choice",typeof(XmlSchemaChoice),Namespace=XmlSchema.Namespace)]\r
29                 [XmlElement("sequence",typeof(XmlSchemaSequence),Namespace=XmlSchema.Namespace)]\r
30                 [XmlElement("any",typeof(XmlSchemaAny),Namespace=XmlSchema.Namespace)]\r
31                 public override XmlSchemaObjectCollection Items \r
32                 {\r
33                         get{ return items; }\r
34                 }\r
35 \r
36                 internal override int Compile(ValidationEventHandler h, XmlSchema schema)\r
37                 {\r
38                         // If this is already compiled this time, simply skip.\r
39                         if (this.IsComplied (schema.CompilationId))\r
40                                 return 0;\r
41 \r
42                         XmlSchemaUtil.CompileID(Id, this, schema.IDCollection, h);\r
43                         CompileOccurence (h, schema);\r
44 \r
45                         if (Items.Count == 0)\r
46                                 this.warn (h, "Empty choice is unsatisfiable if minOccurs not equals to 0");\r
47 \r
48                         foreach(XmlSchemaObject obj in Items)\r
49                         {\r
50                                 if(obj is XmlSchemaElement ||\r
51                                         obj is XmlSchemaGroupRef ||\r
52                                         obj is XmlSchemaChoice ||\r
53                                         obj is XmlSchemaSequence ||\r
54                                         obj is XmlSchemaAny)\r
55                                 {\r
56                                         errorCount += obj.Compile(h,schema);\r
57                                 }\r
58                                 else\r
59                                         error(h, "Invalid schema object was specified in the particles of the choice model group.");\r
60                         }\r
61                         this.CompilationId = schema.CompilationId;\r
62                         return errorCount;\r
63                 }\r
64 \r
65                 internal override XmlSchemaParticle GetOptimizedParticle (bool isTop)\r
66                 {\r
67                         if (OptimizedParticle != null)\r
68                                 return OptimizedParticle;\r
69 \r
70                         if (Items.Count == 0 || ValidatedMaxOccurs == 0)\r
71                                 OptimizedParticle = XmlSchemaParticle.Empty;\r
72                         // FIXME: Regardless of isTop, it should remove pointless particle.\r
73                         else if (!isTop && Items.Count == 1 && ValidatedMinOccurs == 1 && ValidatedMaxOccurs == 1)\r
74                                 OptimizedParticle = ((XmlSchemaParticle) Items [0]).GetOptimizedParticle (false);\r
75                         else {\r
76                                 XmlSchemaChoice c = new XmlSchemaChoice ();\r
77                                 CopyInfo (c);\r
78                                 for (int i = 0; i < Items.Count; i++) {\r
79                                         XmlSchemaParticle p = Items [i] as XmlSchemaParticle;\r
80                                         p = p.GetOptimizedParticle (false);\r
81                                         if (p == XmlSchemaParticle.Empty)\r
82                                                 continue;\r
83                                         else if (p is XmlSchemaChoice && p.ValidatedMinOccurs == 1 && p.ValidatedMaxOccurs == 1) {\r
84                                                 XmlSchemaChoice pc = p as XmlSchemaChoice;\r
85                                                 for (int ci = 0; ci < pc.Items.Count; ci++) {\r
86                                                         c.Items.Add (pc.Items [ci]);\r
87                                                         c.CompiledItems.Add (pc.Items [ci]);\r
88                                                 }\r
89                                         }\r
90                                         else {\r
91                                                 c.Items.Add (p);\r
92                                                 c.CompiledItems.Add (p);\r
93                                         }\r
94                                 }\r
95                                 if (c.Items.Count == 0)\r
96                                         OptimizedParticle = XmlSchemaParticle.Empty;\r
97                                 else\r
98                                         OptimizedParticle = c;\r
99                         }\r
100                         return OptimizedParticle;\r
101                 }\r
102 \r
103                 internal override int Validate (ValidationEventHandler h, XmlSchema schema)\r
104                 {\r
105                         if (IsValidated (schema.CompilationId))\r
106                                 return errorCount;\r
107 \r
108                         CompiledItems.Clear ();\r
109                         foreach (XmlSchemaParticle p in Items) {\r
110                                 errorCount += p.Validate (h, schema); // This is basically extraneous for pointless item, but needed to check validation error.\r
111                                 CompiledItems.Add (p);\r
112                         }\r
113 \r
114                         ValidationId = schema.ValidationId;\r
115                         return errorCount;\r
116                 }\r
117 \r
118                 internal override bool ValidateDerivationByRestriction (XmlSchemaParticle baseParticle,\r
119                         ValidationEventHandler h, XmlSchema schema, bool raiseError)\r
120                 {\r
121                         XmlSchemaAny any = baseParticle as XmlSchemaAny;\r
122                         if (any != null) {\r
123                                 // NSRecurseCheckCardinality\r
124                                 return ValidateNSRecurseCheckCardinality (any, h, schema, raiseError);\r
125                         }\r
126 \r
127                         XmlSchemaChoice choice = baseParticle as XmlSchemaChoice;\r
128                         if (choice != null) {\r
129                                 // RecurseLax\r
130                                 if (!ValidateOccurenceRangeOK (choice, h, schema, raiseError))\r
131                                         return false;\r
132 \r
133                                 // If it is totally optional, then ignore their contents.\r
134                                 if (choice.ValidatedMinOccurs == 0 && choice.ValidatedMaxOccurs == 0 &&\r
135                                         this.ValidatedMinOccurs == 0 && this.ValidatedMaxOccurs == 0)\r
136                                         return true;\r
137 //                              return ValidateRecurseLax (choice, h, schema, raiseError);\r
138                                 return this.ValidateSeqRecurseMapSumCommon (choice, h, schema, true, false, raiseError);\r
139                         }\r
140 \r
141                         if (raiseError)\r
142                                 error (h, "Invalid choice derivation by restriction was found.");\r
143                         return false;\r
144                 }\r
145 \r
146                 private bool ValidateRecurseLax (XmlSchemaGroupBase baseGroup,\r
147                         ValidationEventHandler h, XmlSchema schema, bool raiseError)\r
148                 {\r
149                         int index = 0;\r
150                         for (int i = 0; i < baseGroup.CompiledItems.Count; i++) {\r
151                                 XmlSchemaParticle pb = (XmlSchemaParticle) baseGroup.CompiledItems [i];\r
152                                 pb = pb.GetOptimizedParticle (false);\r
153                                 if (pb == XmlSchemaParticle.Empty)\r
154                                         continue;\r
155                                 XmlSchemaParticle pd = null;\r
156                                 while (this.CompiledItems.Count > index) {\r
157                                         pd = (XmlSchemaParticle) this.CompiledItems [index];\r
158                                         pd = pd.GetOptimizedParticle (false);\r
159                                         index++;\r
160                                         if (pd != XmlSchemaParticle.Empty)\r
161                                                 break;\r
162                                 }\r
163                                 if (!ValidateParticleSection (ref index, pd, pb, h, schema, raiseError))\r
164                                         continue;\r
165                         }\r
166                         if (this.CompiledItems.Count > 0 && index != this.CompiledItems.Count) {\r
167                                 if (raiseError)\r
168                                         error (h, "Invalid particle derivation by restriction was found. Extraneous derived particle was found.");\r
169                                 return false;\r
170                         }\r
171                         return true;\r
172                 }\r
173 \r
174                 private bool ValidateParticleSection (ref int index, XmlSchemaParticle pd, XmlSchemaParticle pb, ValidationEventHandler h, XmlSchema schema, bool raiseError)\r
175                 {\r
176                         if (pd == pb) // they are same particle\r
177                                 return true;\r
178 \r
179                         if (pd != null) {\r
180                                 XmlSchemaElement el = pd as XmlSchemaElement;\r
181                                 XmlSchemaParticle pdx = pd;\r
182 //                              if (el != null && el.SubstitutingElements.Count > 0)\r
183 //                                      pdx = el.SubstitutingChoice;\r
184 \r
185                                 if (!pdx.ValidateDerivationByRestriction (pb, h, schema, false)) {\r
186                                         if (!pb.ValidateIsEmptiable ()) {\r
187                                                 if (raiseError)\r
188                                                         error (h, "Invalid particle derivation by restriction was found. Invalid sub-particle derivation was found.");\r
189                                                 return false;\r
190                                         }\r
191                                         else {\r
192                                                 index--; // try the same derived particle and next base particle.\r
193                                                 return false;\r
194                                         }\r
195                                 }\r
196                         } else if (!pb.ValidateIsEmptiable ()) {\r
197                                 if (raiseError)\r
198                                         error (h, "Invalid particle derivation by restriction was found. Base schema particle has non-emptiable sub particle that is not mapped to the derived particle.");\r
199                                 return false;\r
200                         }\r
201 \r
202                         return true;\r
203                 }\r
204 \r
205                 internal override decimal GetMinEffectiveTotalRange ()\r
206                 {\r
207                         if (minEffectiveTotalRange >= 0)\r
208                                 return minEffectiveTotalRange;\r
209 \r
210                         decimal product = 0; //this.ValidatedMinOccurs;\r
211                         if (Items.Count == 0)\r
212                                 product = 0;\r
213                         else {\r
214                                 foreach (XmlSchemaParticle p in this.Items) {\r
215                                         decimal got = p.GetMinEffectiveTotalRange ();\r
216                                         if (product > got)\r
217                                                 product= got;\r
218                                 }\r
219                         }\r
220                         minEffectiveTotalRange = product;\r
221                         return product;\r
222                 }\r
223 \r
224                 internal override void ValidateUniqueParticleAttribution (XmlSchemaObjectTable qnames, ArrayList nsNames,\r
225                         ValidationEventHandler h, XmlSchema schema)\r
226                 {\r
227                         foreach (XmlSchemaParticle p in this.Items)\r
228                                 p.ValidateUniqueParticleAttribution (qnames, nsNames, h, schema);\r
229                 }\r
230 \r
231                 internal override void ValidateUniqueTypeAttribution (XmlSchemaObjectTable labels,\r
232                         ValidationEventHandler h, XmlSchema schema)\r
233                 {\r
234                         foreach (XmlSchemaParticle p in this.Items)\r
235                                 p.ValidateUniqueTypeAttribution (labels, h, schema);\r
236                 }\r
237 \r
238                 //<choice\r
239                 //  id = ID\r
240                 //  maxOccurs =  (nonNegativeInteger | unbounded)  : 1\r
241                 //  minOccurs = nonNegativeInteger : 1\r
242                 //  {any attributes with non-schema namespace . . .}>\r
243                 //  Content: (annotation?, (element | group | choice | sequence | any)*)\r
244                 //</choice>\r
245                 internal static XmlSchemaChoice Read(XmlSchemaReader reader, ValidationEventHandler h)\r
246                 {\r
247                         XmlSchemaChoice choice = new XmlSchemaChoice();\r
248                         reader.MoveToElement();\r
249 \r
250                         if(reader.NamespaceURI != XmlSchema.Namespace || reader.LocalName != xmlname)\r
251                         {\r
252                                 error(h,"Should not happen :1: XmlSchemaChoice.Read, name="+reader.Name,null);\r
253                                 reader.SkipToEnd();\r
254                                 return null;\r
255                         }\r
256 \r
257                         choice.LineNumber = reader.LineNumber;\r
258                         choice.LinePosition = reader.LinePosition;\r
259                         choice.SourceUri = reader.BaseURI;\r
260 \r
261                         while(reader.MoveToNextAttribute())\r
262                         {\r
263                                 if(reader.Name == "id")\r
264                                 {\r
265                                         choice.Id = reader.Value;\r
266                                 }\r
267                                 else if(reader.Name == "maxOccurs")\r
268                                 {\r
269                                         try\r
270                                         {\r
271                                                 choice.MaxOccursString = reader.Value;\r
272                                         }\r
273                                         catch(Exception e)\r
274                                         {\r
275                                                 error(h,reader.Value + " is an invalid value for maxOccurs",e);\r
276                                         }\r
277                                 }\r
278                                 else if(reader.Name == "minOccurs")\r
279                                 {\r
280                                         try\r
281                                         {\r
282                                                 choice.MinOccursString = reader.Value;\r
283                                         }\r
284                                         catch(Exception e)\r
285                                         {\r
286                                                 error(h,reader.Value + " is an invalid value for minOccurs",e);\r
287                                         }\r
288                                 }\r
289                                 else if((reader.NamespaceURI == "" && reader.Name != "xmlns") || reader.NamespaceURI == XmlSchema.Namespace)\r
290                                 {\r
291                                         error(h,reader.Name + " is not a valid attribute for choice",null);\r
292                                 }\r
293                                 else\r
294                                 {\r
295                                         XmlSchemaUtil.ReadUnhandledAttribute(reader,choice);\r
296                                 }\r
297                         }\r
298                         \r
299                         reader.MoveToElement();\r
300                         if(reader.IsEmptyElement)\r
301                                 return choice;\r
302 \r
303                         //  Content: (annotation?, (element | group | choice | sequence | any)*)\r
304                         int level = 1;\r
305                         while(reader.ReadNextElement())\r
306                         {\r
307                                 if(reader.NodeType == XmlNodeType.EndElement)\r
308                                 {\r
309                                         if(reader.LocalName != xmlname)\r
310                                                 error(h,"Should not happen :2: XmlSchemaChoice.Read, name="+reader.Name,null);\r
311                                         break;\r
312                                 }\r
313                                 if(level <= 1 && reader.LocalName == "annotation")\r
314                                 {\r
315                                         level = 2; //Only one annotation\r
316                                         XmlSchemaAnnotation annotation = XmlSchemaAnnotation.Read(reader,h);\r
317                                         if(annotation != null)\r
318                                                 choice.Annotation = annotation;\r
319                                         continue;\r
320                                 }\r
321                                 if(level <=2)\r
322                                 {\r
323                                         if(reader.LocalName == "element")\r
324                                         {\r
325                                                 level = 2;\r
326                                                 XmlSchemaElement element = XmlSchemaElement.Read(reader,h);\r
327                                                 if(element != null)\r
328                                                         choice.items.Add(element);\r
329                                                 continue;\r
330                                         }\r
331                                         if(reader.LocalName == "group")\r
332                                         {\r
333                                                 level = 2;\r
334                                                 XmlSchemaGroupRef group = XmlSchemaGroupRef.Read(reader,h);\r
335                                                 if(group != null)\r
336                                                         choice.items.Add(group);\r
337                                                 continue;\r
338                                         }\r
339                                         if(reader.LocalName == "choice")\r
340                                         {\r
341                                                 level = 2;\r
342                                                 XmlSchemaChoice ch = XmlSchemaChoice.Read(reader,h);\r
343                                                 if(ch != null)\r
344                                                         choice.items.Add(ch);\r
345                                                 continue;\r
346                                         }\r
347                                         if(reader.LocalName == "sequence")\r
348                                         {\r
349                                                 level = 2;\r
350                                                 XmlSchemaSequence sequence = XmlSchemaSequence.Read(reader,h);\r
351                                                 if(sequence != null)\r
352                                                         choice.items.Add(sequence);\r
353                                                 continue;\r
354                                         }\r
355                                         if(reader.LocalName == "any")\r
356                                         {\r
357                                                 level = 2;\r
358                                                 XmlSchemaAny any = XmlSchemaAny.Read(reader,h);\r
359                                                 if(any != null)\r
360                                                         choice.items.Add(any);\r
361                                                 continue;\r
362                                         }\r
363                                 }\r
364                                 reader.RaiseInvalidElementError();\r
365                         }\r
366                         return choice;\r
367                 }\r
368         }\r
369 }\r