1 //------------------------------------------------------------------------------
2 // <copyright file="ConstraintStruct.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
8 namespace System.Xml.Schema {
11 using System.Collections;
12 using System.Globalization;
13 using System.Diagnostics;
14 using System.Xml.XPath;
15 using MS.Internal.Xml.XPath;
17 internal sealed class ConstraintStruct {
18 // for each constraint
19 internal CompiledIdentityConstraint constraint; // pointer to constraint
20 internal SelectorActiveAxis axisSelector;
21 internal ArrayList axisFields; // Add tableDim * LocatedActiveAxis in a loop
22 internal Hashtable qualifiedTable; // Checking confliction
23 internal Hashtable keyrefTable; // several keyref tables having connections to this one is possible
24 private int tableDim; // dimension of table = numbers of fields;
26 internal int TableDim {
27 get { return this.tableDim; }
30 internal ConstraintStruct (CompiledIdentityConstraint constraint) {
31 this.constraint = constraint;
32 this.tableDim = constraint.Fields.Length;
33 this.axisFields = new ArrayList(); // empty fields
34 this.axisSelector = new SelectorActiveAxis (constraint.Selector, this);
35 if (this.constraint.Role != CompiledIdentityConstraint.ConstraintRole.Keyref) {
36 this.qualifiedTable = new Hashtable();
42 // ActiveAxis plus the location plus the state of matching in the constraint table : only for field
43 internal class LocatedActiveAxis : ActiveAxis {
44 private int column; // the column in the table (the field sequence)
45 internal bool isMatched; // if it's matched, then fill value in the validator later
46 internal KeySequence Ks; // associated with a keysequence it will fills in
49 get { return this.column; }
52 internal LocatedActiveAxis (Asttree astfield, KeySequence ks, int column) : base (astfield) {
55 this.isMatched = false;
58 internal void Reactivate(KeySequence ks) {
65 // exist for optimization purpose
67 // 1. overload endelement function from parent to return result
68 // 2. combine locatedactiveaxis and keysequence more closely
69 // 3. enable locatedactiveaxis reusing (the most important optimization point)
70 // 4. enable ks adding to hashtable right after moving out selector node (to enable 3)
71 // 5. will modify locatedactiveaxis class accordingly
72 // 6. taking care of updating ConstraintStruct.axisFields
73 // 7. remove constraintTable from ConstraintStruct
74 // 8. still need centralized locatedactiveaxis for movetoattribute purpose
75 internal class SelectorActiveAxis : ActiveAxis {
76 private ConstraintStruct cs; // pointer of constraintstruct, to enable 6
77 private ArrayList KSs; // stack of KSStruct, will not become less
78 private int KSpointer = 0; // indicate current stack top (next available element);
80 public bool EmptyStack {
81 get { return KSpointer == 0; }
84 public int lastDepth {
85 get { return (KSpointer == 0) ? -1 : ((KSStruct) KSs[KSpointer - 1]).depth; }
88 public SelectorActiveAxis(Asttree axisTree, ConstraintStruct cs) : base(axisTree) {
89 this.KSs = new ArrayList();
93 public override bool EndElement(string localname, string URN) {
94 base.EndElement(localname, URN);
95 if (KSpointer > 0 && this.CurrentDepth == lastDepth) {
97 // next step PopPS, and insert into hash
102 // update constraintStruct.axisFields as well, if it's new LocatedActiveAxis
103 public int PushKS (int errline, int errcol) {
104 // new KeySequence each time
105 KeySequence ks = new KeySequence(cs.TableDim, errline, errcol);
107 // needs to clear KSStruct before using
109 if (KSpointer < KSs.Count) {
110 // reuse, clear up KSs.KSpointer
111 kss = (KSStruct) KSs[KSpointer];
113 // reactivate LocatedActiveAxis
114 for (int i = 0; i < cs.TableDim; i ++) {
115 kss.fields[i].Reactivate(ks); // reassociate key sequence
119 kss = new KSStruct(ks, cs.TableDim);
120 for (int i = 0; i < cs.TableDim; i ++) {
121 kss.fields[i] = new LocatedActiveAxis (cs.constraint.Fields[i], ks, i);
122 cs.axisFields.Add (kss.fields[i]); // new, add to axisFields
127 kss.depth = this.CurrentDepth - 1;
129 return (KSpointer ++);
132 public KeySequence PopKS () {
133 return ((KSStruct)KSs[-- KSpointer]).ks;
138 internal class KSStruct {
139 public int depth; // depth of selector when it matches
140 public KeySequence ks; // ks of selector when it matches and assigned -- needs to new each time
141 public LocatedActiveAxis[] fields; // array of fields activeaxis when it matches and assigned
143 public KSStruct(KeySequence ks, int dim) {
145 this.fields = new LocatedActiveAxis[dim];
149 internal class TypedObject {
151 private class DecimalStruct {
152 bool isDecimal = false; // rare case it will be used...
153 decimal[] dvalue; // to accelerate equals operation. array <-> list
155 public bool IsDecimal {
156 get { return this.isDecimal; }
157 set { this.isDecimal = value; }
160 public decimal[] Dvalue {
161 get { return this.dvalue; }
164 public DecimalStruct () {
165 this.dvalue = new decimal[1];
168 public DecimalStruct (int dim) {
169 this.dvalue = new decimal[dim];
173 DecimalStruct dstruct = null;
175 string svalue; // only for output
176 XmlSchemaDatatype xsdtype;
181 get { return this.dim; }
185 get { return this.isList; }
188 public bool IsDecimal {
190 Debug.Assert (this.dstruct != null);
191 return this.dstruct.IsDecimal;
194 public decimal[] Dvalue {
196 Debug.Assert (this.dstruct != null);
197 return this.dstruct.Dvalue;
201 public object Value {
202 get {return ovalue; }
203 set {ovalue = value; }
206 public XmlSchemaDatatype Type {
207 get {return xsdtype; }
208 set {xsdtype = value; }
211 public TypedObject (object obj, string svalue, XmlSchemaDatatype xsdtype) {
213 this.svalue = svalue;
214 this.xsdtype = xsdtype;
215 if (xsdtype.Variety == XmlSchemaDatatypeVariety.List ||
216 xsdtype is Datatype_base64Binary ||
217 xsdtype is Datatype_hexBinary) {
219 this.dim = ((Array)obj).Length;
223 public override string ToString() {
224 // only for exception
228 public void SetDecimal () {
230 if (this.dstruct != null) {
234 // Debug.Assert(!this.IsDecimal);
235 switch(xsdtype.TypeCode) {
236 case XmlTypeCode.Byte:
237 case XmlTypeCode.UnsignedByte:
238 case XmlTypeCode.Short:
239 case XmlTypeCode.UnsignedShort:
240 case XmlTypeCode.Int:
241 case XmlTypeCode.UnsignedInt:
242 case XmlTypeCode.Long:
243 case XmlTypeCode.UnsignedLong:
244 case XmlTypeCode.Decimal:
245 case XmlTypeCode.Integer:
246 case XmlTypeCode.PositiveInteger:
247 case XmlTypeCode.NonNegativeInteger:
248 case XmlTypeCode.NegativeInteger:
249 case XmlTypeCode.NonPositiveInteger:
252 this.dstruct = new DecimalStruct(this.dim);
253 for (int i = 0; i < this.dim; i ++) {
254 this.dstruct.Dvalue[i] = Convert.ToDecimal (((Array) this.ovalue).GetValue(i),NumberFormatInfo.InvariantInfo);
258 this.dstruct = new DecimalStruct();
259 //possibility of list of length 1.
260 this.dstruct.Dvalue[0] = Convert.ToDecimal (this.ovalue, NumberFormatInfo.InvariantInfo);
262 this.dstruct.IsDecimal = true;
267 this.dstruct = new DecimalStruct(this.dim);
270 this.dstruct = new DecimalStruct();
277 private bool ListDValueEquals (TypedObject other) {
278 for (int i = 0; i < this.Dim; i ++) {
279 if (this.Dvalue[i] != other.Dvalue[i]) {
286 public bool Equals (TypedObject other) {
287 // ? one is list with one member, another is not list -- still might be equal
288 if (this.Dim != other.Dim) {
292 if (this.Type != other.Type) {
293 //Check if types are comparable
294 if (! (this.Type.IsComparable(other.Type)) ) {
297 other.SetDecimal(); // can't use cast and other.Type.IsEqual (value1, value2)
299 if (this.IsDecimal && other.IsDecimal) { //Both are decimal / derived types
300 return this.ListDValueEquals(other);
304 // not-Decimal derivation or type equal
306 if (other.IsList) { //Both are lists and values are XmlAtomicValue[] or clrvalue[]. So use Datatype_List.Compare
307 return this.Type.Compare(this.Value, other.Value) == 0;
309 else { //this is a list and other is a single value
310 Array arr1 = this.Value as System.Array;
311 XmlAtomicValue[] atomicValues1 = arr1 as XmlAtomicValue[];
312 if (atomicValues1 != null) { // this is a list of union
313 return atomicValues1.Length == 1 && atomicValues1.GetValue(0).Equals(other.Value);
316 return arr1.Length == 1 && arr1.GetValue(0).Equals(other.Value);
320 else if (other.IsList) {
321 Array arr2 = other.Value as System.Array;
322 XmlAtomicValue[] atomicValues2 = arr2 as XmlAtomicValue[];
323 if (atomicValues2 != null) { // other is a list of union
324 return atomicValues2.Length == 1 && atomicValues2.GetValue(0).Equals(this.Value);
327 return arr2.Length == 1 && arr2.GetValue(0).Equals(this.Value);
330 else { //Both are not lists
331 return this.Value.Equals(other.Value);
336 internal class KeySequence {
340 int posline, poscol; // for error reporting
342 internal KeySequence (int dim, int line, int col) {
343 Debug.Assert(dim > 0);
345 this.ks = new TypedObject[dim];
351 get { return this.posline; }
355 get { return this.poscol; }
358 public KeySequence(TypedObject[] ks) {
360 this.dim = ks.Length;
361 this.posline = this.poscol = 0;
364 public object this[int index] {
366 object result = ks[index];
370 ks[index] = (TypedObject) value;
374 // return true if no null field
375 internal bool IsQualified() {
376 for (int i = 0; i < this.ks.Length; ++i) {
377 if ((this.ks[i] == null) || (this.ks[i].Value == null)) return false;
382 // it's not directly suit for hashtable, because it's always calculating address
383 public override int GetHashCode() {
384 if (hashcode != -1) {
387 hashcode = 0; // indicate it's changed. even the calculated hashcode below is 0
388 for (int i = 0; i < this.ks.Length; i ++) {
389 // extract its primitive value to calculate hashcode
390 // decimal is handled differently to enable among different CLR types
391 this.ks[i].SetDecimal();
392 if (this.ks[i].IsDecimal) {
393 for (int j = 0 ; j < this.ks[i].Dim ; j ++) {
394 hashcode += this.ks[i].Dvalue[j].GetHashCode();
399 Array arr = this.ks[i].Value as System.Array;
401 XmlAtomicValue[] atomicValues = arr as XmlAtomicValue[];
402 if (atomicValues != null) {
403 for (int j = 0 ; j < atomicValues.Length ; j ++) {
404 hashcode += ((XmlAtomicValue)atomicValues.GetValue(j)).TypedValue.GetHashCode();
408 for (int j = 0 ; j < ((Array) this.ks[i].Value).Length ; j ++) {
409 hashcode += ((Array) this.ks[i].Value).GetValue(j).GetHashCode();
414 hashcode += this.ks[i].Value.GetHashCode();
421 // considering about derived type
422 public override bool Equals(object other) {
423 // each key sequence member can have different type
424 KeySequence keySequence = (KeySequence)other;
425 for (int i = 0; i < this.ks.Length; i ++) {
426 if (! this.ks[i].Equals (keySequence.ks[i])) {
433 public override string ToString() {
434 StringBuilder sb = new StringBuilder();
435 sb.Append(this.ks[0].ToString());
436 for (int i = 1; i < this.ks.Length; i ++) {
438 sb.Append(this.ks[i].ToString());
440 return sb.ToString();