Initial commit
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / Schema / ConstraintStruct.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="ConstraintStruct.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">[....]</owner>                                                                
6 //------------------------------------------------------------------------------
7
8 namespace System.Xml.Schema {
9     using System;
10     using System.Text;
11     using System.Collections;
12     using System.Globalization;
13     using System.Diagnostics;
14     using System.Xml.XPath;
15     using MS.Internal.Xml.XPath;
16
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;
25
26         internal int TableDim {
27             get { return this.tableDim; }
28         }
29
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();
37             }
38         }
39
40     } 
41
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
47
48         internal int Column {
49             get { return this.column; }
50         }
51
52         internal LocatedActiveAxis (Asttree astfield, KeySequence ks, int column) : base (astfield) {
53             this.Ks = ks;
54             this.column = column;
55             this.isMatched = false;
56         }
57
58         internal void Reactivate(KeySequence ks) {
59             Reactivate();
60             this.Ks = ks;
61         }
62         
63     }
64
65     // exist for optimization purpose
66     // ActiveAxis plus
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);
79         
80         public bool EmptyStack {
81             get { return KSpointer == 0; }
82         }
83         
84         public int lastDepth {
85             get { return (KSpointer == 0) ? -1 : ((KSStruct) KSs[KSpointer - 1]).depth; } 
86         }
87         
88         public SelectorActiveAxis(Asttree axisTree, ConstraintStruct cs) : base(axisTree) {
89             this.KSs = new ArrayList();
90             this.cs = cs;
91         }
92         
93         public override bool EndElement(string localname, string URN) {
94             base.EndElement(localname, URN);
95             if (KSpointer > 0 && this.CurrentDepth == lastDepth) {
96                 return true;
97                 // next step PopPS, and insert into hash
98             }
99             return false;
100         }
101         
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);
106
107             // needs to clear KSStruct before using
108             KSStruct kss;
109             if (KSpointer < KSs.Count) {
110                 // reuse, clear up KSs.KSpointer
111                 kss = (KSStruct) KSs[KSpointer];
112                 kss.ks = ks;
113                 // reactivate LocatedActiveAxis
114                 for (int i = 0; i < cs.TableDim; i ++) {
115                     kss.fields[i].Reactivate(ks);               // reassociate key sequence
116                 }
117             }
118             else { // "==", new
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
123                 }
124                 KSs.Add(kss);
125             }
126             
127             kss.depth = this.CurrentDepth - 1;
128             
129             return (KSpointer ++);
130         }
131     
132         public KeySequence PopKS () {
133             return ((KSStruct)KSs[-- KSpointer]).ks;
134         }
135         
136     }
137     
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
142         
143         public KSStruct(KeySequence ks, int dim) {
144             this.ks = ks;
145             this.fields = new LocatedActiveAxis[dim];
146         }
147     }
148     
149     internal class TypedObject {
150
151         private class DecimalStruct {
152             bool isDecimal = false;         // rare case it will be used...
153             decimal[] dvalue;               // to accelerate equals operation.  array <-> list
154
155             public bool IsDecimal {
156                 get { return this.isDecimal; }
157                 set { this.isDecimal = value; }
158             }
159
160             public decimal[] Dvalue {
161                 get { return this.dvalue; }
162             }
163
164             public DecimalStruct () {
165                 this.dvalue = new decimal[1];
166             }
167             //list
168             public DecimalStruct (int dim) {
169                 this.dvalue = new decimal[dim];
170             }
171         }
172
173         DecimalStruct dstruct = null; 
174         object ovalue;
175         string svalue;      // only for output
176         XmlSchemaDatatype xsdtype;
177         int dim = 1; 
178         bool isList = false;
179
180         public int Dim {
181             get { return this.dim; }
182         }
183
184         public bool IsList {
185             get { return this.isList; }
186         }
187
188         public bool IsDecimal {
189             get { 
190                 Debug.Assert (this.dstruct != null);
191                 return this.dstruct.IsDecimal; 
192             }
193         }
194         public decimal[] Dvalue {
195             get {
196                 Debug.Assert (this.dstruct != null);
197                 return this.dstruct.Dvalue; 
198             }
199         }
200         
201         public object Value {
202             get {return ovalue; }
203             set {ovalue = value; }
204         }
205
206         public XmlSchemaDatatype Type {
207             get {return xsdtype; }
208             set {xsdtype = value; }
209         }
210
211         public TypedObject (object obj, string svalue, XmlSchemaDatatype xsdtype) {
212             this.ovalue = obj;
213             this.svalue = svalue;
214             this.xsdtype = xsdtype;
215             if (xsdtype.Variety == XmlSchemaDatatypeVariety.List ||
216                 xsdtype is Datatype_base64Binary ||
217                 xsdtype is Datatype_hexBinary) {
218                 this.isList = true;
219                 this.dim = ((Array)obj).Length;
220             }
221         }
222
223         public override string ToString() {
224             // only for exception
225             return this.svalue;
226         }
227
228         public void SetDecimal () {
229
230             if (this.dstruct != null) {
231                 return; 
232             }
233         
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:
250
251                     if (this.isList) {
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);
255                         }
256                     }
257                     else { //not list
258                         this.dstruct = new DecimalStruct();
259                         //possibility of list of length 1.
260                         this.dstruct.Dvalue[0] = Convert.ToDecimal (this.ovalue, NumberFormatInfo.InvariantInfo);
261                     }
262                     this.dstruct.IsDecimal = true;
263                     break;
264
265                 default:
266                     if (this.isList) {
267                         this.dstruct = new DecimalStruct(this.dim);
268                     }
269                     else {
270                         this.dstruct = new DecimalStruct();
271                     }
272                     break;
273
274             }
275         }
276
277         private bool ListDValueEquals (TypedObject other) {
278             for (int i = 0; i < this.Dim; i ++) {
279                 if (this.Dvalue[i] != other.Dvalue[i]) {
280                     return false;
281                 }                
282             }
283             return true;
284         }
285
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) {
289                 return false;
290             }
291
292             if (this.Type != other.Type) {
293                 //Check if types are comparable
294                 if (! (this.Type.IsComparable(other.Type)) ) {       
295                     return false;
296                 }
297                 other.SetDecimal(); // can't use cast and other.Type.IsEqual (value1, value2)
298                 this.SetDecimal();
299                 if (this.IsDecimal && other.IsDecimal) { //Both are decimal / derived types 
300                     return this.ListDValueEquals(other);
301                 }
302             }
303
304             // not-Decimal derivation or type equal
305             if (this.IsList) {
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;
308                 }
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);
314                     }
315                     else {
316                         return arr1.Length == 1 && arr1.GetValue(0).Equals(other.Value);
317                     }
318                 }
319             }
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);
325                 }
326                 else {
327                     return arr2.Length == 1 && arr2.GetValue(0).Equals(this.Value);
328                 }
329             }
330             else { //Both are not lists
331                 return this.Value.Equals(other.Value);
332             }
333         }
334     }
335
336     internal class KeySequence {
337         TypedObject[] ks;
338         int dim;
339         int hashcode = -1;
340         int posline, poscol;            // for error reporting
341
342         internal KeySequence (int dim, int line, int col) {
343             Debug.Assert(dim > 0);
344             this.dim = dim;
345             this.ks = new TypedObject[dim];
346             this.posline = line;
347             this.poscol = col;
348         }
349
350         public int PosLine {
351             get { return this.posline; }
352         }
353
354         public int PosCol {
355             get { return this.poscol; }
356         }
357
358         public KeySequence(TypedObject[] ks) {
359             this.ks = ks;
360             this.dim = ks.Length;
361             this.posline = this.poscol = 0;
362         }
363
364         public object this[int index] {
365             get {
366                 object result = ks[index];
367                 return result;
368             }
369             set {
370                 ks[index] = (TypedObject) value;
371             } 
372         }
373
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;
378             }
379             return true;
380         }
381
382         // it's not directly suit for hashtable, because it's always calculating address
383         public override int GetHashCode() {
384             if (hashcode != -1) {
385                 return hashcode;
386             }
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();
395                     }
396                 }
397                 // 
398                 else {
399                     Array arr = this.ks[i].Value as System.Array;
400                     if (arr != null) {
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();
405                             }
406                         }
407                         else {
408                             for (int j = 0 ; j < ((Array) this.ks[i].Value).Length ; j ++) {
409                                 hashcode += ((Array) this.ks[i].Value).GetValue(j).GetHashCode();
410                             }
411                         }
412                     }
413                     else { //not a list
414                         hashcode += this.ks[i].Value.GetHashCode();
415                     }
416                 }
417             }
418             return hashcode;
419         }
420
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])) {
427                     return false;
428                 }
429             }
430             return true;
431         }
432
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 ++) {
437                 sb.Append(" ");
438                 sb.Append(this.ks[i].ToString());
439             }
440             return sb.ToString();
441         }
442     }
443
444 }