2 // Mono.Xml.Schema.XsdIdentityState.cs
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 // (C)2003 Atsushi Enomoto
9 // These classses represents XML Schema's identity constraints validation state,
10 // created by xs:key, xs:keyref, xs:unique in xs:element.
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System
.Collections
;
36 using System
.Collections
.Specialized
;
37 using System
.Globalization
;
39 using System
.Xml
.Schema
;
41 namespace Mono
.Xml
.Schema
43 internal class XsdKeyEntryField
46 XsdIdentityField field
;
48 public XsdKeyEntryField (XsdKeyEntry entry
, XsdIdentityField field
)
54 public XsdIdentityField Field
{
58 public bool FieldFound
;
59 public int FieldLineNumber
;
60 public int FieldLinePosition
;
61 public bool FieldHasLineInfo
;
62 public XsdAnySimpleType FieldType
;
64 public object Identity
;
67 public int FieldFoundDepth
;
68 public XsdIdentityPath FieldFoundPath
;
70 public bool Consuming
;
73 // Return false if there is already the same key.
74 public bool SetIdentityField (object identity
, bool isXsiNil
, XsdAnySimpleType type
, XsdValidatingReader reader
)
76 IXmlLineInfo li
= reader
as IXmlLineInfo
;
78 FieldFoundDepth
= reader
.Depth
;
81 FieldFound
|= isXsiNil
;
85 if (li
!= null && li
.HasLineInfo ()) {
86 FieldHasLineInfo
= true;
87 FieldLineNumber
= li
.LineNumber
;
88 FieldLinePosition
= li
.LinePosition
;
91 if (!(this.entry
.KeySequence
.SourceSchemaIdentity
is XmlSchemaKeyref
)) {
92 for (int i
= 0; i
< entry
.KeySequence
.FinishedEntries
.Count
; i
++) {
93 XsdKeyEntry other
= (XsdKeyEntry
) entry
.KeySequence
.FinishedEntries
[i
];
94 XsdKeyEntryField of
= other
.KeyFields
[field
.Index
];
95 if (this.entry
.CompareIdentity (other
))
103 // It might throw Exception including other than XmlSchemaException (ReadTypedValue).
104 internal XsdIdentityPath
FieldMatches (ArrayList qnameStack
, XsdValidatingReader reader
)
106 for (int i
= 0; i
< field
.Paths
.Length
; i
++) {
107 XsdIdentityPath path
= field
.Paths
[i
];
108 if (FieldFound
&& (reader
.Depth
> this.FieldFoundDepth
&& this.FieldFoundPath
== path
))
112 if (path
.OrderedSteps
.Length
== 0) {
113 if (reader
.Depth
== entry
.StartDepth
)
118 // It does not hit as yet (too shallow to hit).
119 if (reader
.Depth
- entry
.StartDepth
< path
.OrderedSteps
.Length
- 1)
122 bool isAttributePath
= false;
123 int iter
= path
.OrderedSteps
.Length
;
124 if (path
.OrderedSteps
[iter
-1].IsAttribute
) {
125 isAttributePath
= true;
128 if (path
.Descendants
&& reader
.Depth
< entry
.StartDepth
+ iter
)
130 else if (!path
.Descendants
&& reader
.Depth
!= entry
.StartDepth
+ iter
)
135 XsdIdentityStep step
;
136 for (; iter
>= 0; iter
--) {
137 step
= path
.OrderedSteps
[iter
];
140 XmlQualifiedName qname
= (XmlQualifiedName
) qnameStack
[entry
.StartDepth
+ iter
+ 1];
141 if (step
.NsName
!= null && qname
.Namespace
== step
.NsName
)
143 if ((step
.Name
== "*" || step
.Name
== qname
.Name
) &&
144 step
.Namespace
== qname
.Namespace
)
149 if (iter
>= 0) // i.e. did not match against the path.
151 if (!isAttributePath
)
153 step
= path
.OrderedSteps
[path
.OrderedSteps
.Length
- 1];
154 if (step
.IsAnyName
|| step
.NsName
!= null) {
156 while (reader
.MoveToNextAttribute ()) {
157 if (reader
.NamespaceURI
== XmlSchema
.InstanceNamespace
)
159 if (step
.IsAnyName
|| reader
.NamespaceURI
== step
.NsName
) {
160 this.FillAttributeFieldValue (reader
);
164 reader
.MoveToElement ();
166 if (this.Identity
!= null)
171 if (reader
.MoveToAttribute (step
.Name
, step
.Namespace
)) {
172 this.FillAttributeFieldValue (reader
);
173 reader
.MoveToElement ();
182 private void FillAttributeFieldValue (XsdValidatingReader reader
)
185 throw new XmlSchemaException ("The key value was was already found."
186 + (this.FieldHasLineInfo
?
187 String
.Format (CultureInfo
.InvariantCulture
, " At line {0}, position {1}.", FieldLineNumber
, FieldLinePosition
) :
189 reader
, reader
.BaseURI
, entry
.KeySequence
.SourceSchemaIdentity
, null);
190 XmlSchemaDatatype dt
= reader
.SchemaType
as XmlSchemaDatatype
;
191 XmlSchemaSimpleType st
= reader
.SchemaType
as XmlSchemaSimpleType
;
192 if (dt
== null && st
!= null)
194 object identity
= reader
.ReadTypedValue ();
195 if (identity
== null)
196 identity
= reader
.Value
;
197 if (!this.SetIdentityField (identity
, reader
.Depth
- 1 == reader
.XsiNilDepth
, dt
as XsdAnySimpleType
, reader
))
198 throw new XmlSchemaException ("Two or more identical field was found.",
199 reader
, reader
.BaseURI
, entry
.KeySequence
.SourceSchemaIdentity
, null);
200 // HACK: This is not logical. Attributes never be cosuming,
201 // so I used it as a temporary mark to sign it is *just* validated now.
202 this.Consuming
= true;
203 this.FieldFound
= true;
207 internal class XsdKeyEntryFieldCollection
: IList
209 ArrayList al
= new ArrayList ();
212 public bool IsReadOnly
{
213 get { return false; }
216 object IList
.this [int i
] {
217 get { return al [i]; }
218 set { al [i] = value; }
221 public XsdKeyEntryField
this [int i
] {
222 get { return (XsdKeyEntryField) al [i]; }
223 set { al [i] = value; }
226 public void RemoveAt (int i
)
231 public void Insert (int i
, object value)
233 al
.Insert (i
, value);
236 public void Remove (object value)
241 public bool Contains (object value)
243 return al
.Contains (value);
251 public int IndexOf (object value)
253 return al
.IndexOf (value);
256 public int Add (object value)
258 return al
.Add (value);
261 public bool IsFixedSize
{
262 get { return false; }
269 public bool IsSynchronized
{
270 get { return al.IsSynchronized; }
274 get { return al.Count; }
277 public void CopyTo(Array array
, int i
)
279 al
.CopyTo (array
, i
);
282 public object SyncRoot
{
283 get { return al.SyncRoot; }
290 public IEnumerator
GetEnumerator ()
292 return ((IEnumerable
) al
).GetEnumerator ();
299 // Created per field/key pair, created per selector-matched element.
300 internal class XsdKeyEntry
302 public int StartDepth
;
303 public int CurrentStep
;
305 public bool ConsumptionTargetIsKey
;
307 public int SelectorLineNumber
;
308 public int SelectorLinePosition
;
309 public bool SelectorHasLineInfo
;
311 public XsdKeyEntryFieldCollection KeyFields
;
313 public bool KeyRefFound
;
314 public int KeyRefSelectorLineNumber
;
315 public int KeyRefSelectorLinePosition
;
316 public bool KeyRefSelectorHasLineInfo
;
318 public XsdKeyTable KeySequence
;
319 private bool keyFound
= false;
321 public XsdKeyEntry (XsdKeyTable keyseq
, XmlReader reader
)
323 Init (keyseq
, reader
);
326 public bool KeyFound
{
330 for (int i
= 0; i
< KeyFields
.Count
; i
++) {
331 XsdKeyEntryField kf
= KeyFields
[i
];
341 private void Init (XsdKeyTable keyseq
, XmlReader reader
)
343 KeySequence
= keyseq
;
344 KeyFields
= new XsdKeyEntryFieldCollection ();
345 for (int i
= 0; i
< keyseq
.Selector
.Fields
.Length
; i
++)
346 KeyFields
.Add (new XsdKeyEntryField (this, keyseq
.Selector
.Fields
[i
]));
347 StartDepth
= reader
.Depth
;
348 IXmlLineInfo li
= reader
as IXmlLineInfo
;
350 if (li
.HasLineInfo ()) {
351 this.SelectorHasLineInfo
= true;
352 this.SelectorLineNumber
= li
.LineNumber
;
353 this.SelectorLinePosition
= li
.LinePosition
;
358 public bool CompareIdentity (XsdKeyEntry other
)
360 for (int i
= 0; i
< KeyFields
.Count
; i
++) {
361 XsdKeyEntryField f
= this.KeyFields
[i
];
362 XsdKeyEntryField of
= other
.KeyFields
[i
];
363 if (f
.IsXsiNil
&& !of
.IsXsiNil
|| !f
.IsXsiNil
&& of
.IsXsiNil
)
365 if (!XmlSchemaUtil
.IsSchemaDatatypeEquals (
366 of
.FieldType
, of
.Identity
, f
.FieldType
, f
.Identity
))
367 return false; // does not match
369 return true; // matches
372 // In this method, attributes are ignored.
373 // It might throw Exception including non-XmlSchemaException.
374 public void FieldMatches (ArrayList qnameStack
, XsdValidatingReader reader
)
376 for (int i
= 0; i
< KeyFields
.Count
; i
++) {
377 XsdKeyEntryField keyField
= KeyFields
[i
];
378 XsdIdentityField fieldDef
= keyField
.Field
;
379 XsdIdentityPath path
= keyField
.FieldMatches (qnameStack
, reader
);
381 if (keyField
.FieldFound
) {
382 // HACK: This is not logical by nature. Attributes never be cosuming,
383 // so I used it as a temporary mark to sign it is *just* validated now.
384 if (!keyField
.Consuming
)
385 throw new XmlSchemaException ("Two or more matching field was found.",
386 reader
, reader
.BaseURI
, this.KeySequence
.SourceSchemaIdentity
, null);
388 keyField
.Consuming
= false;
390 if (!keyField
.Consumed
) {
391 if (reader
.XsiNilDepth
== reader
.Depth
&& !keyField
.SetIdentityField (Guid
.Empty
, true, XsdAnySimpleType
.Instance
, reader
))
392 throw new XmlSchemaException ("Two or more identical field was found.", reader
, reader
.BaseURI
, KeySequence
.SourceSchemaIdentity
, null);
394 XmlSchemaComplexType ct
= reader
.SchemaType
as XmlSchemaComplexType
;
396 (ct
.ContentType
== XmlSchemaContentType
.Empty
|| ct
.ContentType
== XmlSchemaContentType
.ElementOnly
) &&
397 reader
.SchemaType
!= XmlSchemaComplexType
.AnyType
)
398 throw new XmlSchemaException ("Specified schema type is complex type, which is not allowed for identity constraints.", reader
, reader
.BaseURI
, KeySequence
.SourceSchemaIdentity
, null);
399 keyField
.FieldFound
= true;
400 keyField
.FieldFoundPath
= path
;
401 keyField
.FieldFoundDepth
= reader
.Depth
;
402 keyField
.Consuming
= true;
403 IXmlLineInfo li
= reader
as IXmlLineInfo
;
404 if (li
!= null && li
.HasLineInfo ()) {
405 keyField
.FieldHasLineInfo
= true;
406 keyField
.FieldLineNumber
= li
.LineNumber
;
407 keyField
.FieldLinePosition
= li
.LinePosition
;
409 reader
.CurrentKeyFieldConsumers
.Add (keyField
);