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 
FillLocation(NvdlElementBase e)29 		internal void FillLocation (NvdlElementBase e)
30 		{
31 			line = e.LineNumber;
32 			column = e.LinePosition;
33 			sourceUri = e.SourceUri;
34 		}
35 
HasLineInfo()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 
SimpleRules(NvdlCompileContext context)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
SimplifyPhase1(NvdlCompileContext ctx)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 
SimplifyPhase2(NvdlCompileContext ctx)136 		private void SimplifyPhase2 (NvdlCompileContext ctx)
137 		{
138 			foreach (SimpleMode mode in modes)
139 				mode.SimplifyPhase2 (ctx);
140 		}
141 
ResolveModes(NvdlCompileContext ctx)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 
SimpleTrigger(NvdlTrigger trigger)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 
Cover(string localName, string ns)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 
SimpleMode(NvdlMode mode, NvdlCompileContext ctx)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 
SimpleMode(string name, NvdlNestedMode mode, NvdlCompileContext ctx)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 
SimpleMode(NvdlIncludedMode mode, NvdlCompileContext ctx)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 
SimplifyPhase1(NvdlModeBase mode, NvdlCompileContext ctx)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 
SimplifyPhase2(NvdlCompileContext ctx)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 
ConsumeIncludes(ArrayList al, NvdlCompileContext ctx)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 
CheckCollision(ArrayList al, ref SimpleRule el, ref SimpleRule attr)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 
ResolveModes(NvdlCompileContext ctx)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 
SimpleRule(NvdlRule rule, bool matchAttributes, NvdlCompileContext ctx)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 
MatchNS(string target)439 		public bool MatchNS (string target)
440 		{
441 			if (isAny)
442 				return true;
443 			return Nvdl.NSMatches (ns, 0, wildcard, target, 0, "");
444 		}
445 
SimplifyPhase1(NvdlRule r, NvdlCompileContext ctx)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 
SimplifyModeUsage( NvdlNoCancelAction nca, NvdlCompileContext ctx)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 
ResolveModes(NvdlCompileContext ctx, SimpleMode current)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 
SimpleAction(NvdlNoCancelAction action)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 
ResolveModes(NvdlCompileContext ctx, SimpleMode current)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 
CreateBuiltInValidate(NvdlAction a)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
SimpleValidate(NvdlAllow allow, NvdlCompileContext ctx)571 		public SimpleValidate (NvdlAllow allow, NvdlCompileContext ctx)
572 			: this (CreateBuiltInValidate (allow), ctx)
573 		{
574 		}
575 
576 		// 6.4.14
SimpleValidate(NvdlReject reject, NvdlCompileContext ctx)577 		public SimpleValidate (NvdlReject reject, NvdlCompileContext ctx)
578 			: this (CreateBuiltInValidate (reject), ctx)
579 		{
580 		}
581 
SimpleValidate( NvdlValidate validate, NvdlCompileContext ctx)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 
CreateValidator(XmlReader reader)600 		public XmlReader CreateValidator (XmlReader reader)
601 		{
602 			return generator.CreateValidator (reader, resolver);
603 		}
604 
ValidateAttributes(XmlReader reader, string ns)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 
SimpleResultAction(NvdlResultAction ra, NvdlCompileContext ctx)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 
SimpleModeUsage(NvdlModeUsage usage)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 
ResolveModes(NvdlCompileContext ctx, SimpleMode current)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 
SimplePath(SimplePathStep [] steps)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 
SimplePathStep(string name, bool descendants)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 
SimpleContext(NvdlContext context)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 
ParsePath(ArrayList al, string path)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 
TrimName(string src)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 
ResolveModes(NvdlCompileContext ctx, SimpleMode current)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 }
802