Merge pull request #5714 from alexischr/update_bockbuild
[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
7 {
8         internal 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         internal class SimpleRules : SimplifiedItem
47         {
48                 SimpleMode startMode;
49                 SimpleTrigger [] 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 SimpleTrigger [] 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                         ArrayList tl = new ArrayList ();
126                         for (int i = 0; i < rules.Triggers.Count; i++)
127                                 tl.Add (new SimpleTrigger (rules.Triggers [i]));
128                         triggers = (SimpleTrigger []) tl.ToArray (
129                                 typeof (SimpleTrigger));
130
131                         modes = (SimpleMode [])
132                                 new ArrayList (ctx.GetCompiledModes ())
133                                 .ToArray (typeof (SimpleMode));
134                 }
135
136                 private void SimplifyPhase2 (NvdlCompileContext ctx)
137                 {
138                         foreach (SimpleMode mode in modes)
139                                 mode.SimplifyPhase2 (ctx);
140                 }
141
142                 private void ResolveModes (NvdlCompileContext ctx)
143                 {
144                         foreach (SimpleMode mode in modes)
145                                 mode.ResolveModes (ctx);
146                 }
147                 #endregion
148         }
149
150         internal class SimpleTrigger : SimplifiedItem
151         {
152                 XmlQualifiedName [] names;
153
154                 public SimpleTrigger (NvdlTrigger trigger)
155                 {
156                         FillLocation (trigger);
157
158                         ArrayList al = new ArrayList ();
159                         foreach (string ss in trigger.NameList.Split (' ')) {
160                                 string s = ss.Trim ();
161                                 if (s.Length == 0)
162                                         continue;
163                                 al.Add (new XmlQualifiedName (s, trigger.NS));
164                         }
165                         names = (XmlQualifiedName []) al.ToArray (
166                                 typeof (XmlQualifiedName));
167                 }
168
169                 public XmlQualifiedName [] Names {
170                         get { return names; }
171                 }
172
173                 public bool Cover (string localName, string ns)
174                 {
175                         for (int i = 0; i < Names.Length; i++) {
176                                 XmlQualifiedName q = Names [i];
177                                 if (q.Name == localName && q.Namespace == ns)
178                                         return true;
179                         }
180                         return false;
181                 }
182         }
183
184         internal class SimpleMode : SimplifiedItem
185         {
186                 string name;
187                 SimpleRule [] rules;
188
189                 // They are available only after complete simplification.
190                 SimpleRule [] elementRules;
191                 SimpleRule [] attributeRules;
192
193                 public SimpleMode (NvdlMode mode, NvdlCompileContext ctx)
194                 {
195                         FillLocation (mode);
196
197                         if (mode.Name == null)
198                                 throw new NvdlCompileException (
199                                         "'mode' element must have a name.", mode);
200                         this.name = mode.Name;
201                         SimplifyPhase1 (mode, ctx);
202                 }
203
204                 public SimpleMode (string name, NvdlNestedMode mode,
205                         NvdlCompileContext ctx)
206                 {
207                         FillLocation (mode);
208
209                         this.name = name;
210                         SimplifyPhase1 (mode, ctx);
211                 }
212
213                 public SimpleMode (NvdlIncludedMode mode, NvdlCompileContext ctx)
214                 {
215                         FillLocation (mode);
216
217                         // name doesn't matter here.
218                         SimplifyPhase1 (mode, ctx);
219                 }
220
221                 public string Name {
222                         get { return name; }
223                 }
224
225                 public SimpleRule [] ElementRules {
226                         get { return elementRules; }
227                 }
228
229                 public SimpleRule [] AttributeRules {
230                         get { return attributeRules; }
231                 }
232
233                 private void SimplifyPhase1 (NvdlModeBase mode,
234                         NvdlCompileContext ctx)
235                 {
236                         NvdlModeCompileContext mctx =
237                                 new NvdlModeCompileContext (mode);
238                         ctx.AddModeContext (this, mctx);
239                         ArrayList al = new ArrayList ();
240                         foreach (NvdlRule r in mode.Rules) {
241                                 switch (r.Match) {
242                                 case NvdlRuleTarget.Both:
243                                         al.Add (new SimpleRule (r, true, ctx));
244                                         al.Add (new SimpleRule (r, false, ctx));
245                                         break;
246                                 case NvdlRuleTarget.None:
247                                 case NvdlRuleTarget.Elements:
248                                         al.Add (new SimpleRule (r, false, ctx));
249                                         break;
250                                 case NvdlRuleTarget.Attributes:
251                                         al.Add (new SimpleRule (r, true, ctx));
252                                         break;
253                                 }
254                         }
255                         foreach (NvdlIncludedMode inc in mode.IncludedModes)
256                                 mctx.Included.Add (new SimpleMode (inc, ctx));
257                         // The rule table is just a dummy store that might
258                         // erase because of removal of inclusion.
259                         rules = (SimpleRule []) al.ToArray (typeof (SimpleRule));
260                 }
261
262                 internal void SimplifyPhase2 (NvdlCompileContext ctx)
263                 {
264                         ArrayList al = new ArrayList ();
265                         ConsumeIncludes (al, ctx);
266                         SimpleRule anyElement = null;
267                         SimpleRule anyAttribute = null;
268                         // 6.4.12 + part of 6.4.13
269                         CheckCollision (al, ref anyElement, ref anyAttribute);
270                         // 6.4.13
271                         if (anyElement == null) {
272                                 NvdlAnyNamespace ann = new NvdlAnyNamespace ();
273                                 ann.SourceUri = this.SourceUri;
274                                 ann.LineNumber = this.LineNumber;
275                                 ann.LinePosition = this.LinePosition;
276
277                                 NvdlReject reject = new NvdlReject ();
278                                 reject.SourceUri = this.SourceUri;
279                                 reject.LineNumber = this.LineNumber;
280                                 reject.LinePosition = this.LinePosition;
281                                 ann.Actions.Add (reject);
282                                 ann.Match = NvdlRuleTarget.Elements;
283
284                                 al.Add (new SimpleRule (ann, false, ctx));
285                         }
286                         if (anyAttribute == null) {
287                                 NvdlAnyNamespace ann = new NvdlAnyNamespace ();
288                                 ann.SourceUri = this.SourceUri;
289                                 ann.LineNumber = this.LineNumber;
290                                 ann.LinePosition = this.LinePosition;
291
292                                 NvdlAttach attach = new NvdlAttach ();
293                                 attach.SourceUri = this.SourceUri;
294                                 attach.LineNumber = this.LineNumber;
295                                 attach.LinePosition = this.LinePosition;
296                                 ann.Match = NvdlRuleTarget.Attributes;
297                                 ann.Actions.Add (attach);
298
299                                 al.Add (new SimpleRule (ann, true, ctx));
300                         }
301                         rules = (SimpleRule []) al.ToArray (typeof (SimpleRule));
302                 }
303
304                 private void ConsumeIncludes (ArrayList al,
305                         NvdlCompileContext ctx)
306                 {
307                         // The reason why we limit the check to current count
308                         // is to add invalid siblings (according to 6.4.12).
309                         int checkMax = al.Count;
310                         NvdlModeCompileContext mctx = ctx.GetModeContext (this);
311                         foreach (SimpleRule rule in rules) {
312                                 // Don't skip cancelled rules here. They are
313                                 // needed to filter overriden rules out
314                                 // (according to 6.4.10)
315                                 //if (ctx.CancelledRules [rule] != null)
316                                 //      continue;
317
318                                 bool exclude = false;
319                                 for (int i = 0; i < checkMax; i++) {
320                                         SimpleRule r = (SimpleRule) al [i];
321                                         if (rule.IsAny == r.IsAny &&
322                                                 rule.MatchAttributes == r.MatchAttributes &&
323                                                 rule.NS == r.NS &&
324                                                 rule.Wildcard == r.Wildcard) {
325                                                 exclude = true;
326                                                 break;
327                                         }
328                                 }
329                                 if (exclude)
330                                         break;
331                                 al.Add (rule);
332                         }
333                         foreach (SimpleMode mode in mctx.Included)
334                                 mode.ConsumeIncludes (al, ctx);
335
336                         // remove cancelled rules at this stage.
337                         for (int i = 0; i < al.Count; ) {
338                                 if (ctx.CancelledRules [(SimpleRule) al [i]] != null)
339                                         al.RemoveAt (i);
340                                 else
341                                         i++;
342                         }
343                 }
344
345                 private void CheckCollision (ArrayList al, ref SimpleRule el, ref SimpleRule attr)
346                 {
347                         for (int i = 0; i < al.Count; i++) {
348                                 SimpleRule r1 = (SimpleRule) al [i];
349                                 if (r1.IsAny) {
350                                         if (r1.MatchAttributes)
351                                                 attr = r1;
352                                         else
353                                                 el = r1;
354                                 }
355                                 for (int j = i + 1; j < al.Count; j++) {
356                                         SimpleRule r2 = (SimpleRule) al [j];
357                                         if (r1.MatchAttributes != r2.MatchAttributes)
358                                                 continue;
359                                         if (r1.IsAny && r2.IsAny)
360                                                 throw new NvdlCompileException ("collision in mode was found. Two anyNamespace elements.", this);
361                                         if (r1.IsAny || r2.IsAny)
362                                                 continue;
363                                         if (Nvdl.NSMatches (r1.NS, 0, r1.Wildcard,
364                                                 r2.NS, 0, r2.Wildcard))
365                                                 throw new NvdlCompileException ("collision in mode was found.", this);
366                                 }
367                         }
368                 }
369
370                 internal void ResolveModes (NvdlCompileContext ctx)
371                 {
372                         // Resolve moces and fill element/attributeRules.
373                         ArrayList e = new ArrayList ();
374                         ArrayList a = new ArrayList ();
375                         foreach (SimpleRule rule in rules) {
376                                 rule.ResolveModes (ctx, this);
377                                 if (rule.MatchAttributes)
378                                         a.Add (rule);
379                                 else
380                                         e.Add (rule);
381                         }
382
383                         elementRules = (SimpleRule []) e.ToArray (typeof (SimpleRule));
384                         attributeRules = (SimpleRule []) a.ToArray (typeof (SimpleRule));
385                 }
386         }
387
388         internal class SimpleRule : SimplifiedItem
389         {
390                 bool matchAttributes;
391                 SimpleAction [] actions;
392
393                 readonly string ns;
394                 readonly string wildcard;
395                 bool isAny;
396
397                 public SimpleRule (NvdlRule rule, bool matchAttributes,
398                         NvdlCompileContext ctx)
399                 {
400                         FillLocation (rule);
401
402                         this.matchAttributes = matchAttributes;
403                         NvdlNamespace nss = rule as NvdlNamespace;
404                         if (nss == null)
405                                 this.isAny = true;
406                         else {
407                                 this.ns = nss.NS;
408                                 if (nss.Wildcard == null)
409                                         wildcard = "*";
410                                 else if (nss.Wildcard.Length > 1)
411                                         throw new NvdlCompileException ("'wildCard' attribute can specify at most one character string.", rule);
412                                 else
413                                         wildcard = nss.Wildcard;
414                         }
415
416                         SimplifyPhase1 (rule, ctx);
417                 }
418
419                 public bool MatchAttributes {
420                         get { return matchAttributes; }
421                 }
422
423                 public SimpleAction [] Actions {
424                         get { return actions; }
425                 }
426
427                 public string NS {
428                         get { return ns; }
429                 }
430
431                 public string Wildcard {
432                         get { return wildcard; }
433                 }
434
435                 public bool IsAny {
436                         get { return isAny; }
437                 }
438
439                 public bool MatchNS (string target)
440                 {
441                         if (isAny)
442                                 return true;
443                         return Nvdl.NSMatches (ns, 0, wildcard, target, 0, "");
444                 }
445
446                 private void SimplifyPhase1 (NvdlRule r, NvdlCompileContext ctx)
447                 {
448                         ctx.AddRuleContext (this, r);
449                         // 6.4.9
450                         ArrayList al = new ArrayList ();
451                         foreach (NvdlAction a in r.Actions) {
452                                 NvdlNoCancelAction nca =
453                                         a as NvdlNoCancelAction;
454                                 if (nca != null) {
455                                         if (nca.ModeUsage != null)
456                                                 SimplifyModeUsage (nca, ctx);
457                                         NvdlResultAction ra = nca as NvdlResultAction;
458                                         if (ra != null)
459                                                 al.Add (new SimpleResultAction (ra, ctx));
460                                         else if (nca is NvdlValidate)
461                                                 al.Add (new SimpleValidate (
462                                                         (NvdlValidate) nca, ctx));
463                                         else if (nca is NvdlAllow)
464                                                 al.Add (new SimpleValidate (
465                                                         (NvdlAllow) nca, ctx));
466                                         else
467                                                 al.Add (new SimpleValidate (
468                                                         (NvdlReject) nca, ctx));
469                                 }
470                                 else if (nca == null)
471                                         ctx.CancelledRules.Add (this, this);
472                         }
473                         actions = (SimpleAction []) al.ToArray (
474                                 typeof (SimpleAction));
475                 }
476
477                 private void SimplifyModeUsage (
478                         NvdlNoCancelAction nca, NvdlCompileContext ctx)
479                 {
480                         NvdlModeUsage usage = nca.ModeUsage;
481                         if (usage.NestedMode != null && ctx.GetCompiledMode (usage) == null) {
482                                 SimpleMode sm = new SimpleMode (String.Empty,
483                                         usage.NestedMode, ctx);
484                                 ctx.AddCompiledMode (usage, sm);
485                         }
486                         foreach (NvdlContext c in usage.Contexts) {
487                                 if (c.NestedMode != null) {
488                                         SimpleMode sm = new SimpleMode (
489                                                 String.Empty, c.NestedMode, ctx);
490                                         ctx.AddCompiledMode (c, sm);
491                                 }
492                         }
493                 }
494
495                 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
496                 {
497                         foreach (SimpleAction a in actions)
498                                 a.ResolveModes (ctx, current);
499                 }
500         }
501
502         internal abstract class SimpleAction : SimplifiedItem
503         {
504                 readonly ListDictionary messages;
505                 readonly SimpleModeUsage modeUsage;
506                 SimpleMode mode;
507
508                 protected SimpleAction (NvdlNoCancelAction action)
509                 {
510                         FillLocation (action);
511
512                         if (action.ModeUsage != null)
513                                 modeUsage = new SimpleModeUsage (action.ModeUsage);
514                         messages = new ListDictionary ();
515                         if (action.SimpleMessage != null)
516                                 messages.Add ("", action.SimpleMessage);
517                         foreach (NvdlMessage msg in action.Messages)
518                                 messages.Add (msg.XmlLang, msg.Text);
519                 }
520
521                 public abstract bool NoResult { get; }
522
523                 public ListDictionary Messages {
524                         get { return messages; }
525                 }
526
527                 public SimpleMode DefaultMode {
528                         get { return mode; }
529                 }
530
531                 public SimpleContext [] Contexts {
532                         get { return modeUsage.Contexts; }
533                 }
534
535                 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
536                 {
537                         if (modeUsage != null) {
538                                 modeUsage.ResolveModes (ctx, current);
539                                 mode = modeUsage.UseMode;
540                         }
541                         if (mode == null)
542                                 mode = current;
543                 }
544         }
545
546         internal class SimpleValidate : SimpleAction
547         {
548                 readonly NvdlValidatorGenerator generator;
549                 XmlResolver resolver;
550
551                 static NvdlValidate CreateBuiltInValidate (NvdlAction a)
552                 {
553                         bool allow = a is NvdlAllow;
554                         NvdlValidate v = new NvdlValidate ();
555                         v.SourceUri = a.SourceUri;
556                         v.LineNumber = a.LineNumber;
557                         v.LinePosition = a.LinePosition;
558                         v.ModeUsage = new NvdlModeUsage ();
559                         XmlDocument doc = new XmlDocument ();
560                         XmlElement el = doc.CreateElement (
561                                 allow ? "allow" : "reject",
562                                 Nvdl.BuiltInValidationNamespace);
563                         doc.AppendChild (doc.CreateElement ("schema",
564                                 Nvdl.Namespace));
565                         doc.DocumentElement.AppendChild (el);
566                         v.SchemaBody = doc.DocumentElement;
567                         return v;
568                 }
569
570                 // 6.4.14
571                 public SimpleValidate (NvdlAllow allow, NvdlCompileContext ctx)
572                         : this (CreateBuiltInValidate (allow), ctx)
573                 {
574                 }
575
576                 // 6.4.14
577                 public SimpleValidate (NvdlReject reject, NvdlCompileContext ctx)
578                         : this (CreateBuiltInValidate (reject), ctx)
579                 {
580                 }
581
582                 public SimpleValidate (
583                         NvdlValidate validate,
584                         NvdlCompileContext ctx)
585                         : base (validate)
586                 {
587                         // 6.4.7
588                         generator = ctx.Config.GetGenerator (validate,
589                                 ctx.Rules.SchemaType);
590                 }
591
592                 internal NvdlValidatorGenerator Generator {
593                         get { return generator; }
594                 }
595
596                 public override bool NoResult {
597                         get { return true; }
598                 }
599
600                 public XmlReader CreateValidator (XmlReader reader)
601                 {
602                         return generator.CreateValidator (reader, resolver);
603                 }
604
605                 public void ValidateAttributes (XmlReader reader, string ns)
606                 {
607                         XmlDocument doc = new XmlDocument ();
608                         XmlElement el = doc.CreateElement ("virtualElement",
609                                 Nvdl.InstanceNamespace);
610                         for (int i = 0; i < reader.AttributeCount; i++) {
611                                 reader.MoveToAttribute (i);
612                                 if (reader.NamespaceURI != ns)
613                                         continue;
614                                 el.SetAttribute (reader.LocalName,
615                                         reader.NamespaceURI, reader.Value);
616                         }
617                         reader.MoveToElement ();
618                         XmlReader r = generator.CreateAttributeValidator (
619                                 new XmlNodeReader (el), resolver);
620                         while (!r.EOF)
621                                 r.Read ();
622                 }
623         }
624
625         internal class SimpleResultAction : SimpleAction
626         {
627                 readonly NvdlResultType resultType;
628
629                 public SimpleResultAction (NvdlResultAction ra,
630                         NvdlCompileContext ctx)
631                         : base (ra)
632                 {
633                         this.resultType = ra.ResultType;
634                 }
635
636                 public override bool NoResult {
637                         get { return false; }
638                 }
639
640                 public NvdlResultType ResultType {
641                         get { return resultType; }
642                 }
643         }
644
645         internal class SimpleModeUsage : SimplifiedItem
646         {
647                 // It will never be used in validation.
648                 NvdlModeUsage source; // FIXME: put this into CompileContext
649                 readonly SimpleContext [] contexts;
650                 SimpleMode mode;
651
652                 public SimpleModeUsage (NvdlModeUsage usage)
653                 {
654                         this.source = usage;
655                         contexts = new SimpleContext [usage.Contexts.Count];
656                         for (int i = 0; i < contexts.Length; i++)
657                                 contexts [i] = new SimpleContext (
658                                         usage.Contexts [i]);
659                 }
660
661                 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
662                 {
663                         if (source.UseMode != null) {
664                                 mode = ctx.GetCompiledMode (source.UseMode);
665                         }
666                         else if (source.NestedMode != null)
667                                 mode = ctx.GetCompiledMode (source);
668                         else
669                                 mode = current;
670
671                         for (int i = 0; i < contexts.Length; i++)
672                                 contexts [i].ResolveModes (ctx, mode);
673
674                         // FIXME: get location by some way
675                         if (mode == null)
676                                 throw new NvdlCompileException (
677                                         "mode does not contain either referenced modeUsage or nested mode.", null);
678                 }
679
680                 public SimpleMode UseMode {
681                         get { return mode; }
682                 }
683
684                 public SimpleContext [] Contexts {
685                         get { return contexts; }
686                 }
687         }
688
689         internal class SimplePath
690         {
691                 readonly SimplePathStep [] steps;
692
693                 public SimplePath (SimplePathStep [] steps)
694                 {
695                         this.steps = steps;
696                 }
697
698                 public SimplePathStep [] Steps {
699                         get { return steps; }
700                 }
701         }
702
703         internal class SimplePathStep
704         {
705                 readonly string name;
706                 readonly bool descendants;
707
708                 public SimplePathStep (string name, bool descendants)
709                 {
710                         this.name = name;
711                         this.descendants = descendants;
712                 }
713
714                 public string Name {
715                         get { return name; }
716                 }
717
718                 public bool Descendants {
719                         get { return descendants; }
720                 }
721         }
722
723         internal class SimpleContext : SimplifiedItem
724         {
725                 readonly NvdlContext source;
726                 readonly string useModeName; // It is never used in validation.
727                 SimpleMode useMode;
728                 SimplePath [] path;
729
730                 public SimpleContext (NvdlContext context)
731                 {
732                         source = context;
733                         FillLocation (context);
734
735                         this.useModeName = context.UseMode;
736
737                         try {
738                                 string [] spaths = context.Path.Split ('|');
739                                 ArrayList al = new ArrayList ();
740                                 foreach (string spathws in spaths) {
741                                         string spath = spathws.Trim (
742                                                 Nvdl.Whitespaces);
743                                         if (spath.Length == 0)
744                                                 continue;
745                                         ParsePath (al, TrimName (spath));
746                                 }
747                                 path = (SimplePath []) al.ToArray (
748                                         typeof (SimplePath));
749                         } catch (XmlException ex) {
750                                 throw new NvdlCompileException (String.Format ("Invalid path string: {0}", path), ex, context);
751                         }
752                 }
753
754                 private void ParsePath (ArrayList al, string path)
755                 {
756                         ArrayList steps = new ArrayList ();
757                         int start = path.Length > 0 && path [0] == '/' ? 1 : 0;
758                         do {
759                                 int idx = path.IndexOf ('/', start);
760                                 if (idx < 0) {
761                                         steps.Add (new SimplePathStep (TrimName (path.Substring (start)), false));
762                                         start = path.Length;
763                                 } else if (path.Length > idx + 1 && path [idx + 1] == '/') {
764                                         steps.Add (new SimplePathStep (TrimName (path.Substring (start, idx - start)), true));
765                                         start = idx + 2;
766                                 } else {
767                                         steps.Add (new SimplePathStep (TrimName (path.Substring (start, idx - start)), false));
768                                         start = idx + 1;
769                                 }
770                         } while (start < path.Length);
771                         al.Add (new SimplePath (steps.ToArray (typeof (SimplePathStep)) as SimplePathStep []));
772                 }
773
774                 string TrimName (string src)
775                 {
776                         return src.TrimStart (Nvdl.Whitespaces).TrimEnd (Nvdl.Whitespaces);
777                 }
778
779                 internal SimplePath [] Path {
780                         get { return path; }
781                 }
782
783                 public SimpleMode UseMode {
784                         get { return useMode; }
785                 }
786
787                 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
788                 {
789                         if (useModeName != null)
790                                 useMode = ctx.GetCompiledMode (useModeName);
791                         else if (source.NestedMode != null)
792                                 useMode = ctx.GetCompiledMode (source);
793                         else
794                                 useMode = current;
795
796                         if (useMode == null)
797                                 throw new NvdlCompileException (String.Format ("Specified mode '{0}' was not found.",
798                                         useModeName), this);
799                 }
800         }
801 }