b84cd7fb539aaefc9881e4a64e39f28c20f779f3
[mono.git] / mcs / class / Commons.Xml.Relaxng / Commons.Xml.Nvdl / NvdlSimplified.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Specialized;
4 using System.Xml;
5
6 namespace Commons.Xml.Nvdl.Simplified
7 {
8         public class SimplifiedItem : IXmlLineInfo
9         {
10                 int line;
11                 int column;
12                 string sourceUri = String.Empty;
13
14                 public int LineNumber {
15                         get { return line; }
16                         set { line = value; }
17                 }
18
19                 public int LinePosition {
20                         get { return column; }
21                         set { column = value; }
22                 }
23
24                 public string SourceUri {
25                         get { return sourceUri; }
26                         set { sourceUri = value != null ? value : String.Empty; }
27                 }
28
29                 internal void FillLocation (NvdlElementBase e)
30                 {
31                         line = e.LineNumber;
32                         column = e.LinePosition;
33                         sourceUri = e.SourceUri;
34                 }
35
36                 public bool HasLineInfo ()
37                 {
38                         return line != 0;
39                 }
40
41                 public string Location {
42                         get { return line != 0 ? String.Format ("{0} ({1},{2})", sourceUri, line, column) : String.Empty; }
43                 }
44         }
45
46         public class SimpleRules : SimplifiedItem
47         {
48                 SimpleMode startMode;
49                 XmlQualifiedName [] triggers;
50
51                 // FIXME: It is not used in validation step, so move it to
52                 // compile context
53                 SimpleMode [] modes;
54
55                 public SimpleRules (NvdlCompileContext context)
56                 {
57                         FillLocation (context.Rules);
58                         SimplifyPhase1 (context); // 6.4.1 - 10.
59                         SimplifyPhase2 (context); // 6.4.11 - 14.
60                         ResolveModes (context); // 6.4.15.
61                 }
62
63                 public SimpleMode StartMode {
64                         get { return startMode; }
65                 }
66
67                 public XmlQualifiedName [] Triggers {
68                         get { return triggers; }
69                 }
70
71                 #region Simplification
72                 private void SimplifyPhase1 (NvdlCompileContext ctx)
73                 {
74                         NvdlRules rules = ctx.Rules;
75                         // 6.4.1 : just ignore "Foreign" property.
76                         // 6.4.2 : already ignored on reading nvdl.
77                         // 6.4.3 : done in SOM
78                         // 6.4.4 : FIXME: must be done.
79                         // 6.4.5 : FIXME: considered in compiler.
80                         // 6.4.6 : FIXME: considered in compiler.
81                         // 6.4.7 : FIXME: considered in compiler.
82
83                         // 6.4.8 : here
84                         NvdlModeList list = rules.Modes;
85                         NvdlMode startMode = null;
86
87                         if (rules.Modes.Count > 0) {
88                                 if (rules.Rules.Count > 0)
89                                         throw new NvdlCompileException ("Modes and rules cannot coexist in 'rules' element.", rules);
90                                 else if (rules.StartMode == null)
91                                         throw new NvdlCompileException ("startMode is missing in 'rules' element when modes are specified.", rules);
92                                 foreach (NvdlMode m in rules.Modes) {
93                                         if (m.Name == rules.StartMode) {
94                                                 startMode = m;
95                                                 break;
96                                         }
97                                 }
98                                 if (startMode == null)
99                                         throw new NvdlCompileException ("Matching 'mode' element specified by 'startMode' does not exist.", rules);
100                         } else {
101                                 if (rules.Rules.Count == 0)
102                                         throw new NvdlCompileException ("Neither modes nor rules exists in 'rules' element.", rules);
103                                 list = new NvdlModeList ();
104                                 startMode = new NvdlMode ();
105                                 startMode.SourceUri = rules.SourceUri;
106                                 startMode.LineNumber = rules.LineNumber;
107                                 startMode.LinePosition = rules.LinePosition;
108                                 startMode.Name = "(startMode)";
109                                 list.Add (startMode);
110                                 foreach (NvdlRule rule in rules.Rules)
111                                         startMode.Rules.Add (rule);
112                         }
113
114                         // 6.4.9 : done in SimpleMode.ctor() and
115                         // SimpleRule.ctor(), using ctx.CompiledModes.
116                         foreach (NvdlMode m in list) {
117                                 SimpleMode sm = new SimpleMode (m, ctx);
118                                 ctx.AddCompiledMode (sm.Name, sm);
119                                 if (m == startMode)
120                                         this.startMode = sm;
121                         }
122
123                         // 6.4.10 : done in SimpleRule.Simplify
124
125                         triggers = new XmlQualifiedName [rules.Triggers.Count];
126                         for (int i = 0; i < triggers.Length; i++) {
127                                 NvdlTrigger t = rules.Triggers [i];
128                                 triggers [i] = new XmlQualifiedName (
129                                         t.Name, t.NS);
130                         }
131
132                         modes = (SimpleMode [])
133                                 new ArrayList (ctx.GetCompiledModes ())
134                                 .ToArray (typeof (SimpleMode));
135                 }
136
137                 private void SimplifyPhase2 (NvdlCompileContext ctx)
138                 {
139                         foreach (SimpleMode mode in modes)
140                                 mode.SimplifyPhase2 (ctx);
141                 }
142
143                 private void ResolveModes (NvdlCompileContext ctx)
144                 {
145                         foreach (SimpleMode mode in modes)
146                                 mode.ResolveModes (ctx);
147                 }
148                 #endregion
149         }
150
151         public class SimpleMode : SimplifiedItem
152         {
153                 string name;
154                 SimpleRule [] rules;
155
156                 // They are available only after complete simplification.
157                 SimpleRule [] elementRules;
158                 SimpleRule [] attributeRules;
159
160                 public SimpleMode (NvdlMode mode, NvdlCompileContext ctx)
161                 {
162                         FillLocation (mode);
163
164                         if (mode.Name == null)
165                                 throw new NvdlCompileException (
166                                         "'mode' element must have a name.", mode);
167                         this.name = mode.Name;
168                         SimplifyPhase1 (mode, ctx);
169                 }
170
171                 public SimpleMode (string name, NvdlNestedMode mode,
172                         NvdlCompileContext ctx)
173                 {
174                         FillLocation (mode);
175
176                         this.name = name;
177                         SimplifyPhase1 (mode, ctx);
178                 }
179
180                 public SimpleMode (NvdlIncludedMode mode, NvdlCompileContext ctx)
181                 {
182                         FillLocation (mode);
183
184                         // name doesn't matter here.
185                         SimplifyPhase1 (mode, ctx);
186                 }
187
188                 public string Name {
189                         get { return name; }
190                 }
191
192                 public SimpleRule [] ElementRules {
193                         get { return elementRules; }
194                 }
195
196                 public SimpleRule [] AttributeRules {
197                         get { return attributeRules; }
198                 }
199
200                 private void SimplifyPhase1 (NvdlModeBase mode,
201                         NvdlCompileContext ctx)
202                 {
203                         NvdlModeCompileContext mctx =
204                                 new NvdlModeCompileContext (mode);
205                         ctx.AddModeContext (this, mctx);
206                         ArrayList al = new ArrayList ();
207                         foreach (NvdlRule r in mode.Rules) {
208                                 switch (r.Match) {
209                                 case NvdlRuleTarget.Both:
210                                         al.Add (new SimpleRule (r, true, ctx));
211                                         al.Add (new SimpleRule (r, false, ctx));
212                                         break;
213                                 case NvdlRuleTarget.None:
214                                 case NvdlRuleTarget.Elements:
215                                         al.Add (new SimpleRule (r, false, ctx));
216                                         break;
217                                 case NvdlRuleTarget.Attributes:
218                                         al.Add (new SimpleRule (r, true, ctx));
219                                         break;
220                                 }
221                         }
222                         foreach (NvdlIncludedMode inc in mode.IncludedModes)
223                                 mctx.Included.Add (new SimpleMode (inc, ctx));
224                         // The rule table is just a dummy store that might
225                         // erase because of removal of inclusion.
226                         rules = (SimpleRule []) al.ToArray (typeof (SimpleRule));
227                 }
228
229                 internal void SimplifyPhase2 (NvdlCompileContext ctx)
230                 {
231                         ArrayList al = new ArrayList ();
232                         ConsumeIncludes (al, ctx);
233                         SimpleRule anyElement = null;
234                         SimpleRule anyAttribute = null;
235                         // 6.4.12 + part of 6.4.13
236                         CheckCollision (al, ref anyElement, ref anyAttribute);
237                         // 6.4.13
238                         if (anyElement == null) {
239                                 NvdlAnyNamespace ann = new NvdlAnyNamespace ();
240                                 ann.SourceUri = this.SourceUri;
241                                 ann.LineNumber = this.LineNumber;
242                                 ann.LinePosition = this.LinePosition;
243
244                                 NvdlReject reject = new NvdlReject ();
245                                 reject.SourceUri = this.SourceUri;
246                                 reject.LineNumber = this.LineNumber;
247                                 reject.LinePosition = this.LinePosition;
248                                 ann.Actions.Add (reject);
249                                 ann.Match = NvdlRuleTarget.Elements;
250
251                                 al.Add (new SimpleRule (ann, false, ctx));
252                         }
253                         if (anyAttribute == null) {
254                                 NvdlAnyNamespace ann = new NvdlAnyNamespace ();
255                                 ann.SourceUri = this.SourceUri;
256                                 ann.LineNumber = this.LineNumber;
257                                 ann.LinePosition = this.LinePosition;
258
259                                 NvdlAllow allow = new NvdlAllow ();
260                                 allow.SourceUri = this.SourceUri;
261                                 allow.LineNumber = this.LineNumber;
262                                 allow.LinePosition = this.LinePosition;
263                                 ann.Match = NvdlRuleTarget.Attributes;
264                                 ann.Actions.Add (allow);
265
266                                 al.Add (new SimpleRule (ann, true, ctx));
267                         }
268                         rules = (SimpleRule []) al.ToArray (typeof (SimpleRule));
269                 }
270
271                 private void ConsumeIncludes (ArrayList al,
272                         NvdlCompileContext ctx)
273                 {
274                         // The reason why we limit the check to current count
275                         // is to add invalid siblings (according to 6.4.12).
276                         int checkMax = al.Count;
277                         NvdlModeCompileContext mctx = ctx.GetModeContext (this);
278                         foreach (SimpleRule rule in rules) {
279                                 if (ctx.CancelledRules [rule] != null)
280                                         continue;
281                                 bool exclude = false;
282                                 for (int i = 0; i < checkMax; i++) {
283                                         SimpleRule r = (SimpleRule) al [i];
284                                         if (rule.IsAny == r.IsAny &&
285                                                 rule.MatchAttributes == r.MatchAttributes &&
286                                                 rule.NS == r.NS &&
287                                                 rule.Wildcard == r.Wildcard) {
288                                                 exclude = true;
289                                                 break;
290                                         }
291                                 }
292                                 if (exclude)
293                                         break;
294                                 al.Add (rule);
295                         }
296                         foreach (SimpleMode mode in mctx.Included)
297                                 mode.ConsumeIncludes (al, ctx);
298                 }
299
300                 private void CheckCollision (ArrayList al, ref SimpleRule el, ref SimpleRule attr)
301                 {
302                         for (int i = 0; i < al.Count; i++) {
303                                 SimpleRule r1 = (SimpleRule) al [i];
304                                 if (r1.IsAny) {
305                                         if (r1.MatchAttributes)
306                                                 attr = r1;
307                                         else
308                                                 el = r1;
309                                 }
310                                 for (int j = i + 1; j < al.Count; j++) {
311                                         SimpleRule r2 = (SimpleRule) al [j];
312                                         if (r1.MatchAttributes != r2.MatchAttributes)
313                                                 continue;
314                                         if (r1.IsAny && r2.IsAny)
315                                                 throw new NvdlCompileException ("collision in mode was found. Two anyNamespace elements.", this);
316                                         if (r1.IsAny || r2.IsAny)
317                                                 continue;
318                                         if (Nvdl.NSMatches (r1.NS, 0, r1.Wildcard,
319                                                 r2.NS, 0, r2.Wildcard))
320                                                 throw new NvdlCompileException ("collision in mode was found.", this);
321                                 }
322                         }
323                 }
324
325                 internal void ResolveModes (NvdlCompileContext ctx)
326                 {
327                         // Resolve moces and fill element/attributeRules.
328                         ArrayList e = new ArrayList ();
329                         ArrayList a = new ArrayList ();
330                         foreach (SimpleRule rule in rules) {
331                                 rule.ResolveModes (ctx, this);
332                                 if (rule.MatchAttributes)
333                                         a.Add (rule);
334                                 else
335                                         e.Add (rule);
336                         }
337
338                         elementRules = (SimpleRule []) e.ToArray (typeof (SimpleRule));
339                         attributeRules = (SimpleRule []) a.ToArray (typeof (SimpleRule));
340                 }
341         }
342
343         public class SimpleRule : SimplifiedItem
344         {
345                 bool matchAttributes;
346                 SimpleAction [] actions;
347
348                 readonly string ns;
349                 readonly string wildcard;
350                 bool isAny;
351
352                 public SimpleRule (NvdlRule rule, bool matchAttributes,
353                         NvdlCompileContext ctx)
354                 {
355                         FillLocation (rule);
356
357                         this.matchAttributes = matchAttributes;
358                         NvdlNamespace nss = rule as NvdlNamespace;
359                         if (nss == null)
360                                 this.isAny = true;
361                         else {
362                                 this.ns = nss.NS;
363                                 if (nss.Wildcard == null)
364                                         wildcard = "*";
365                                 else if (nss.Wildcard.Length > 1)
366                                         throw new NvdlCompileException ("'wildcard' attribute can specify at most one character string.", rule);
367                                 else
368                                         wildcard = nss.Wildcard;
369                         }
370
371                         SimplifyPhase1 (rule, ctx);
372                 }
373
374                 public bool MatchAttributes {
375                         get { return matchAttributes; }
376                 }
377
378                 public SimpleAction [] Actions {
379                         get { return actions; }
380                 }
381
382                 public string NS {
383                         get { return ns; }
384                 }
385
386                 public string Wildcard {
387                         get { return wildcard; }
388                 }
389
390                 public bool IsAny {
391                         get { return isAny; }
392                 }
393
394                 public bool MatchNS (string target)
395                 {
396                         if (isAny)
397                                 return true;
398                         return Nvdl.NSMatches (ns, 0, wildcard, target, 0, "");
399                 }
400
401                 private void SimplifyPhase1 (NvdlRule r, NvdlCompileContext ctx)
402                 {
403                         ctx.AddRuleContext (this, r);
404                         // 6.4.9
405                         ArrayList al = new ArrayList ();
406                         foreach (NvdlAction a in r.Actions) {
407                                 NvdlNoCancelAction nca =
408                                         a as NvdlNoCancelAction;
409                                 if (nca != null) {
410                                         if (nca.ModeUsage != null)
411                                                 SimplifyModeUsage (nca, ctx);
412                                         NvdlResultAction ra = nca as NvdlResultAction;
413                                         if (ra != null)
414                                                 al.Add (new SimpleResultAction (ra, ctx));
415                                         else if (nca is NvdlValidate)
416                                                 al.Add (new SimpleValidate (
417                                                         (NvdlValidate) nca, ctx));
418                                         else if (nca is NvdlAllow)
419                                                 al.Add (new SimpleValidate (
420                                                         (NvdlAllow) nca, ctx));
421                                         else
422                                                 al.Add (new SimpleValidate (
423                                                         (NvdlReject) nca, ctx));
424                                 }
425                                 else if (nca == null)
426                                         ctx.CancelledRules.Add (this, this);
427                         }
428                         actions = (SimpleAction []) al.ToArray (
429                                 typeof (SimpleAction));
430                 }
431
432                 private void SimplifyModeUsage (
433                         NvdlNoCancelAction nca, NvdlCompileContext ctx)
434                 {
435                         NvdlModeUsage usage = nca.ModeUsage;
436                         if (usage.NestedMode != null && ctx.GetCompiledMode (usage) == null) {
437                                 SimpleMode sm = new SimpleMode (String.Empty,
438                                         usage.NestedMode, ctx);
439                                 ctx.AddCompiledMode (usage, sm);
440                         }
441                         foreach (NvdlContext c in usage.Contexts) {
442                                 if (c.NestedMode != null) {
443                                         SimpleMode sm = new SimpleMode (
444                                                 String.Empty, c.NestedMode, ctx);
445                                         ctx.AddCompiledMode (c, sm);
446                                 }
447                         }
448                 }
449
450                 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
451                 {
452                         foreach (SimpleAction a in actions)
453                                 a.ResolveModes (ctx, current);
454                 }
455         }
456
457         public abstract class SimpleAction : SimplifiedItem
458         {
459                 readonly ListDictionary messages;
460                 readonly SimpleModeUsage modeUsage;
461                 SimpleMode mode;
462
463                 protected SimpleAction (NvdlNoCancelAction action)
464                 {
465                         FillLocation (action);
466
467                         if (action.ModeUsage != null)
468                                 modeUsage = new SimpleModeUsage (action.ModeUsage);
469                         messages = new ListDictionary ();
470                         if (action.SimpleMessage != null)
471                                 messages.Add ("", action.SimpleMessage);
472                         foreach (NvdlMessage msg in action.Messages)
473                                 messages.Add (msg.XmlLang, msg.Text);
474                 }
475
476                 public abstract bool NoResult { get; }
477
478                 public ListDictionary Messages {
479                         get { return messages; }
480                 }
481
482                 public SimpleMode DefaultMode {
483                         get { return mode; }
484                 }
485
486                 public SimpleContext [] Contexts {
487                         get { return modeUsage.Contexts; }
488                 }
489
490                 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
491                 {
492                         if (modeUsage != null) {
493                                 modeUsage.ResolveModes (ctx, current);
494                                 mode = modeUsage.UseMode;
495                         }
496                         if (mode == null)
497                                 mode = current;
498                 }
499         }
500
501         public class SimpleValidate : SimpleAction
502         {
503                 readonly NvdlValidatorGenerator generator;
504                 XmlResolver resolver;
505
506                 static NvdlValidate CreateBuiltInValidate (NvdlAction a)
507                 {
508                         bool allow = a is NvdlAllow;
509                         NvdlValidate v = new NvdlValidate ();
510                         v.SourceUri = a.SourceUri;
511                         v.LineNumber = a.LineNumber;
512                         v.LinePosition = a.LinePosition;
513                         v.ModeUsage = new NvdlModeUsage ();
514                         XmlDocument doc = new XmlDocument ();
515                         XmlElement el = doc.CreateElement (
516                                 allow ? "allow" : "reject",
517                                 Nvdl.BuiltInValidationUri);
518                         doc.AppendChild (doc.CreateElement ("schema",
519                                 Nvdl.Namespace));
520                         doc.DocumentElement.AppendChild (el);
521                         v.SchemaBody = doc.DocumentElement;
522                         return v;
523                 }
524
525                 // 6.4.14
526                 public SimpleValidate (NvdlAllow allow, NvdlCompileContext ctx)
527                         : this (CreateBuiltInValidate (allow), ctx)
528                 {
529                 }
530
531                 // 6.4.14
532                 public SimpleValidate (NvdlReject reject, NvdlCompileContext ctx)
533                         : this (CreateBuiltInValidate (reject), ctx)
534                 {
535                 }
536
537                 public SimpleValidate (
538                         NvdlValidate validate,
539                         NvdlCompileContext ctx)
540                         : base (validate)
541                 {
542                         // 6.4.7
543                         generator = ctx.Config.GetGenerator (validate,
544                                 ctx.Rules.SchemaType);
545 /*
546                         this.resolver = ctx.Config.XmlResolverInternal;
547
548                         // 6.4.7
549                         string schemaType = validate.SchemaType;
550                         if (schemaType == null)
551                                 schemaType = ctx.Rules.SchemaType;
552                         if (schemaType == null && validate.SchemaBody != null && !ElementHasElementChild (validate.SchemaBody))
553                                 schemaType = validate.SchemaBody.InnerText;
554                         if (schemaType == null)
555                                 schemaType = "text/xml";
556
557                         // FIXME: this part must be totally rewritten.
558
559                         XmlReader schemaReader = null;
560                         if (schemaType == "text/xml") {
561                                 if (validate.SchemaUri != null) {
562                                         if (validate.SchemaBody != null)
563                                                 throw new NvdlCompileException ("Both 'schema' attribute and 'schema' element are specified in a 'validate' element.", validate);
564                                         // FIXME: use NvdlConfig
565                                         schemaReader = new XmlTextReader (validate.SchemaUri);
566                                 }
567                                 else if (validate.SchemaBody != null) {
568                                         schemaReader = new XmlNodeReader (validate.SchemaBody);
569                                         schemaReader.MoveToContent ();
570                                         schemaReader.Read (); // Skip "schema" element
571                                 }
572                                 else
573                                         throw new NvdlCompileException ("Neither 'schema' attribute nor 'schema' element is specified in a 'validate' element.", validate);
574                         }
575                         else
576                                 throw new NvdlCompileException (String.Format ("MIME type '{0}' is not supported at this moment.", schemaType), validate);
577
578                         schemaReader.MoveToContent ();
579
580                         NvdlValidationProvider provider =
581                                 ctx.Config.GetProvider (schemaReader.NamespaceURI);
582
583                         if (provider == null)
584                                 throw new NvdlCompileException (String.Format ("Schema type '{0}' is not supported in this configuration. Use custom provider that supports this schema type.", schemaType), validate);
585
586                         generator = provider.CreateGenerator (schemaReader, ctx.Config.XmlResolverInternal);
587
588                         foreach (NvdlOption option in validate.Options) {
589                                 bool mustSupport = option.MustSupport != null ?
590                                         XmlConvert.ToBoolean (option.MustSupport) : false;
591                                 if (!generator.AddOption (option.Name,
592                                         option.Arg) && mustSupport)
593                                         throw new NvdlCompileException (String.Format ("Option '{0}' with argument '{1}' is not supported for schema type '{2}'.",
594                                                 option.Name, option.Arg,
595                                                 schemaType), validate);
596                         }
597 */
598                 }
599
600                 internal NvdlValidatorGenerator Generator {
601                         get { return generator; }
602                 }
603
604                 public override bool NoResult {
605                         get { return true; }
606                 }
607
608                 public XmlReader CreateValidator (XmlReader reader)
609                 {
610                         return generator.CreateValidator (reader, resolver);
611                 }
612         }
613
614         public class SimpleResultAction : SimpleAction
615         {
616                 readonly NvdlResultType resultType;
617
618                 public SimpleResultAction (NvdlResultAction ra,
619                         NvdlCompileContext ctx)
620                         : base (ra)
621                 {
622                         this.resultType = ra.ResultType;
623                 }
624
625                 public override bool NoResult {
626                         get { return false; }
627                 }
628
629                 public NvdlResultType ResultType {
630                         get { return resultType; }
631                 }
632         }
633
634         public class SimpleModeUsage : SimplifiedItem
635         {
636                 // It will never be used in validation.
637                 NvdlModeUsage source; // FIXME: put this into CompileContext
638                 readonly SimpleContext [] contexts;
639                 SimpleMode mode;
640
641                 public SimpleModeUsage (NvdlModeUsage usage)
642                 {
643                         this.source = usage;
644                         contexts = new SimpleContext [usage.Contexts.Count];
645                         for (int i = 0; i < contexts.Length; i++)
646                                 contexts [i] = new SimpleContext (
647                                         usage.Contexts [i]);
648                 }
649
650                 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
651                 {
652                         if (source.UseMode != null) {
653                                 mode = ctx.GetCompiledMode (source.UseMode);
654                         }
655                         else if (source.NestedMode != null)
656                                 mode = ctx.GetCompiledMode (source);
657                         else
658                                 mode = current;
659
660                         for (int i = 0; i < contexts.Length; i++)
661                                 contexts [i].ResolveModes (ctx, mode);
662
663                         // FIXME: get location by some way
664                         if (mode == null)
665                                 throw new NvdlCompileException (
666                                         "mode does not contain either referenced modeUsage or nested mode.", null);
667                 }
668
669                 public SimpleMode UseMode {
670                         get { return mode; }
671                 }
672
673                 public SimpleContext [] Contexts {
674                         get { return contexts; }
675                 }
676         }
677
678         internal class SimplePath
679         {
680                 readonly SimplePathStep [] steps;
681
682                 public SimplePath (SimplePathStep [] steps)
683                 {
684                         this.steps = steps;
685                 }
686
687                 public SimplePathStep [] Steps {
688                         get { return steps; }
689                 }
690         }
691
692         internal class SimplePathStep
693         {
694                 readonly string name;
695                 readonly bool descendants;
696
697                 public SimplePathStep (string name, bool descendants)
698                 {
699                         this.name = name;
700                         this.descendants = descendants;
701                 }
702
703                 public string Name {
704                         get { return name; }
705                 }
706
707                 public bool Descendants {
708                         get { return descendants; }
709                 }
710         }
711
712         public class SimpleContext : SimplifiedItem
713         {
714                 readonly string useModeName; // It is never used in validation.
715                 SimpleMode useMode;
716                 SimplePath [] path;
717
718                 public SimpleContext (NvdlContext context)
719                 {
720                         FillLocation (context);
721
722                         this.useModeName = context.UseMode;
723
724                         try {
725                                 string [] spaths = context.Path.Split ('|');
726                                 ArrayList al = new ArrayList ();
727                                 foreach (string spathws in spaths) {
728                                         string spath = spathws.Trim (
729                                                 Nvdl.Whitespaces);
730                                         if (spath.Length == 0)
731                                                 continue;
732                                         ParsePath (al, spath);
733                                 }
734                                 path = (SimplePath []) al.ToArray (
735                                         typeof (SimplePath));
736                         } catch (XmlException ex) {
737                                 throw new NvdlCompileException (String.Format ("Invalid path string: {0}", path), ex, context);
738                         }
739                 }
740
741                 private void ParsePath (ArrayList al, string path)
742                 {
743                         ArrayList steps = new ArrayList ();
744                         int start = 0;
745                         do {
746                                 int idx = path.IndexOf ('/', start);
747                                 if (idx < 0) {
748                                         steps.Add (new SimplePathStep (path.Substring (start), false));
749                                         start = path.Length;
750                                 }
751                                 else if (path.Length > idx + 1 && path [idx + 1] == '/') {
752                                         steps.Add (new SimplePathStep (path.Substring (start, idx - start), true));
753                                         start = idx + 2;
754                                 } else {
755                                         steps.Add (new SimplePathStep (path.Substring (start, idx - start), false));
756                                         start = idx + 1;
757                                 }
758                                 
759                         } while (start < path.Length);
760                         al.Add (new SimplePath (steps.ToArray (typeof (SimplePathStep)) as SimplePathStep []));
761                 }
762
763                 internal SimplePath [] Path {
764                         get { return path; }
765                 }
766
767                 public SimpleMode UseMode {
768                         get { return useMode; }
769                 }
770
771                 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
772                 {
773                         if (useModeName != null)
774                                 useMode = ctx.GetCompiledMode (useModeName);
775                         else
776                                 useMode = current;
777
778                         if (useMode == null)
779                                 throw new NvdlCompileException (String.Format ("Specified mode '{0}' was not found.",
780                                         useModeName), this);
781                 }
782         }
783
784 /*
785         // After simplification, each mode name "shall be different from any
786         // other mode name" (6.4.8)
787         public class SimpleModeTable : DictionaryBase
788         {
789                 public SimpleModeTable (SimpleMode [] modes)
790                 {
791                         foreach (SimpleMode mode in modes)
792                                 Dictionary.Add (mode.Name, mode);
793                 }
794
795                 public SimpleMode this [string name] {
796                         get { return (SimpleMode) Dictionary [name]; }
797                 }
798
799                 public ICollection Keys {
800                         get { return Dictionary.Keys; }
801                 }
802
803                 public ICollection Values {
804                         get { return Dictionary.Values; }
805                 }
806         }
807 */
808 }