2 using System.Collections;
3 using System.Collections.Specialized;
6 namespace Commons.Xml.Nvdl
8 internal class SimplifiedItem : IXmlLineInfo
12 string sourceUri = String.Empty;
14 public int LineNumber {
19 public int LinePosition {
20 get { return column; }
21 set { column = value; }
24 public string SourceUri {
25 get { return sourceUri; }
26 set { sourceUri = value != null ? value : String.Empty; }
29 internal void FillLocation (NvdlElementBase e)
32 column = e.LinePosition;
33 sourceUri = e.SourceUri;
36 public bool HasLineInfo ()
41 public string Location {
42 get { return line != 0 ? String.Format ("{0} ({1},{2})", sourceUri, line, column) : String.Empty; }
46 internal class SimpleRules : SimplifiedItem
49 SimpleTrigger [] triggers;
51 // FIXME: It is not used in validation step, so move it to
55 public SimpleRules (NvdlCompileContext context)
57 FillLocation (context.Rules);
58 SimplifyPhase1 (context); // 6.4.1 - 10.
59 SimplifyPhase2 (context); // 6.4.11 - 14.
60 ResolveModes (context); // 6.4.15.
63 public SimpleMode StartMode {
64 get { return startMode; }
67 public SimpleTrigger [] Triggers {
68 get { return triggers; }
71 #region Simplification
72 private void SimplifyPhase1 (NvdlCompileContext ctx)
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.
84 NvdlModeList list = rules.Modes;
85 NvdlMode startMode = null;
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) {
98 if (startMode == null)
99 throw new NvdlCompileException ("Matching 'mode' element specified by 'startMode' does not exist.", rules);
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);
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);
123 // 6.4.10 : done in SimpleRule.Simplify
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));
131 modes = (SimpleMode [])
132 new ArrayList (ctx.GetCompiledModes ())
133 .ToArray (typeof (SimpleMode));
136 private void SimplifyPhase2 (NvdlCompileContext ctx)
138 foreach (SimpleMode mode in modes)
139 mode.SimplifyPhase2 (ctx);
142 private void ResolveModes (NvdlCompileContext ctx)
144 foreach (SimpleMode mode in modes)
145 mode.ResolveModes (ctx);
150 internal class SimpleTrigger : SimplifiedItem
152 XmlQualifiedName [] names;
154 public SimpleTrigger (NvdlTrigger trigger)
156 FillLocation (trigger);
158 ArrayList al = new ArrayList ();
159 foreach (string ss in trigger.NameList.Split (' ')) {
160 string s = ss.Trim ();
163 al.Add (new XmlQualifiedName (s, trigger.NS));
165 names = (XmlQualifiedName []) al.ToArray (
166 typeof (XmlQualifiedName));
169 public XmlQualifiedName [] Names {
170 get { return names; }
173 public bool Cover (string localName, string ns)
175 for (int i = 0; i < Names.Length; i++) {
176 XmlQualifiedName q = Names [i];
177 if (q.Name == localName && q.Namespace == ns)
184 internal class SimpleMode : SimplifiedItem
189 // They are available only after complete simplification.
190 SimpleRule [] elementRules;
191 SimpleRule [] attributeRules;
193 public SimpleMode (NvdlMode mode, NvdlCompileContext ctx)
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);
204 public SimpleMode (string name, NvdlNestedMode mode,
205 NvdlCompileContext ctx)
210 SimplifyPhase1 (mode, ctx);
213 public SimpleMode (NvdlIncludedMode mode, NvdlCompileContext ctx)
217 // name doesn't matter here.
218 SimplifyPhase1 (mode, ctx);
225 public SimpleRule [] ElementRules {
226 get { return elementRules; }
229 public SimpleRule [] AttributeRules {
230 get { return attributeRules; }
233 private void SimplifyPhase1 (NvdlModeBase mode,
234 NvdlCompileContext ctx)
236 NvdlModeCompileContext mctx =
237 new NvdlModeCompileContext (mode);
238 ctx.AddModeContext (this, mctx);
239 ArrayList al = new ArrayList ();
240 foreach (NvdlRule r in mode.Rules) {
242 case NvdlRuleTarget.Both:
243 al.Add (new SimpleRule (r, true, ctx));
244 al.Add (new SimpleRule (r, false, ctx));
246 case NvdlRuleTarget.None:
247 case NvdlRuleTarget.Elements:
248 al.Add (new SimpleRule (r, false, ctx));
250 case NvdlRuleTarget.Attributes:
251 al.Add (new SimpleRule (r, true, ctx));
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));
262 internal void SimplifyPhase2 (NvdlCompileContext ctx)
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);
271 if (anyElement == null) {
272 NvdlAnyNamespace ann = new NvdlAnyNamespace ();
273 ann.SourceUri = this.SourceUri;
274 ann.LineNumber = this.LineNumber;
275 ann.LinePosition = this.LinePosition;
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;
284 al.Add (new SimpleRule (ann, false, ctx));
286 if (anyAttribute == null) {
287 NvdlAnyNamespace ann = new NvdlAnyNamespace ();
288 ann.SourceUri = this.SourceUri;
289 ann.LineNumber = this.LineNumber;
290 ann.LinePosition = this.LinePosition;
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);
299 al.Add (new SimpleRule (ann, true, ctx));
301 rules = (SimpleRule []) al.ToArray (typeof (SimpleRule));
304 private void ConsumeIncludes (ArrayList al,
305 NvdlCompileContext ctx)
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)
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 &&
324 rule.Wildcard == r.Wildcard) {
333 foreach (SimpleMode mode in mctx.Included)
334 mode.ConsumeIncludes (al, ctx);
336 // remove cancelled rules at this stage.
337 for (int i = 0; i < al.Count; ) {
338 if (ctx.CancelledRules [(SimpleRule) al [i]] != null)
345 private void CheckCollision (ArrayList al, ref SimpleRule el, ref SimpleRule attr)
347 for (int i = 0; i < al.Count; i++) {
348 SimpleRule r1 = (SimpleRule) al [i];
350 if (r1.MatchAttributes)
355 for (int j = i + 1; j < al.Count; j++) {
356 SimpleRule r2 = (SimpleRule) al [j];
357 if (r1.MatchAttributes != r2.MatchAttributes)
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)
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);
370 internal void ResolveModes (NvdlCompileContext ctx)
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)
383 elementRules = (SimpleRule []) e.ToArray (typeof (SimpleRule));
384 attributeRules = (SimpleRule []) a.ToArray (typeof (SimpleRule));
388 internal class SimpleRule : SimplifiedItem
390 bool matchAttributes;
391 SimpleAction [] actions;
394 readonly string wildcard;
397 public SimpleRule (NvdlRule rule, bool matchAttributes,
398 NvdlCompileContext ctx)
402 this.matchAttributes = matchAttributes;
403 NvdlNamespace nss = rule as NvdlNamespace;
408 if (nss.Wildcard == null)
410 else if (nss.Wildcard.Length > 1)
411 throw new NvdlCompileException ("'wildCard' attribute can specify at most one character string.", rule);
413 wildcard = nss.Wildcard;
416 SimplifyPhase1 (rule, ctx);
419 public bool MatchAttributes {
420 get { return matchAttributes; }
423 public SimpleAction [] Actions {
424 get { return actions; }
431 public string Wildcard {
432 get { return wildcard; }
436 get { return isAny; }
439 public bool MatchNS (string target)
443 return Nvdl.NSMatches (ns, 0, wildcard, target, 0, "");
446 private void SimplifyPhase1 (NvdlRule r, NvdlCompileContext ctx)
448 ctx.AddRuleContext (this, r);
450 ArrayList al = new ArrayList ();
451 foreach (NvdlAction a in r.Actions) {
452 NvdlNoCancelAction nca =
453 a as NvdlNoCancelAction;
455 if (nca.ModeUsage != null)
456 SimplifyModeUsage (nca, ctx);
457 NvdlResultAction ra = nca as NvdlResultAction;
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));
467 al.Add (new SimpleValidate (
468 (NvdlReject) nca, ctx));
470 else if (nca == null)
471 ctx.CancelledRules.Add (this, this);
473 actions = (SimpleAction []) al.ToArray (
474 typeof (SimpleAction));
477 private void SimplifyModeUsage (
478 NvdlNoCancelAction nca, NvdlCompileContext ctx)
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);
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);
495 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
497 foreach (SimpleAction a in actions)
498 a.ResolveModes (ctx, current);
502 internal abstract class SimpleAction : SimplifiedItem
504 readonly ListDictionary messages;
505 readonly SimpleModeUsage modeUsage;
508 protected SimpleAction (NvdlNoCancelAction action)
510 FillLocation (action);
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);
521 public abstract bool NoResult { get; }
523 public ListDictionary Messages {
524 get { return messages; }
527 public SimpleMode DefaultMode {
531 public SimpleContext [] Contexts {
532 get { return modeUsage.Contexts; }
535 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
537 if (modeUsage != null) {
538 modeUsage.ResolveModes (ctx, current);
539 mode = modeUsage.UseMode;
546 internal class SimpleValidate : SimpleAction
548 readonly NvdlValidatorGenerator generator;
549 XmlResolver resolver;
551 static NvdlValidate CreateBuiltInValidate (NvdlAction a)
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",
565 doc.DocumentElement.AppendChild (el);
566 v.SchemaBody = doc.DocumentElement;
571 public SimpleValidate (NvdlAllow allow, NvdlCompileContext ctx)
572 : this (CreateBuiltInValidate (allow), ctx)
577 public SimpleValidate (NvdlReject reject, NvdlCompileContext ctx)
578 : this (CreateBuiltInValidate (reject), ctx)
582 public SimpleValidate (
583 NvdlValidate validate,
584 NvdlCompileContext ctx)
588 generator = ctx.Config.GetGenerator (validate,
589 ctx.Rules.SchemaType);
592 internal NvdlValidatorGenerator Generator {
593 get { return generator; }
596 public override bool NoResult {
600 public XmlReader CreateValidator (XmlReader reader)
602 return generator.CreateValidator (reader, resolver);
605 public void ValidateAttributes (XmlReader reader, string ns)
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)
614 el.SetAttribute (reader.LocalName,
615 reader.NamespaceURI, reader.Value);
617 reader.MoveToElement ();
618 XmlReader r = generator.CreateAttributeValidator (
619 new XmlNodeReader (el), resolver);
625 internal class SimpleResultAction : SimpleAction
627 readonly NvdlResultType resultType;
629 public SimpleResultAction (NvdlResultAction ra,
630 NvdlCompileContext ctx)
633 this.resultType = ra.ResultType;
636 public override bool NoResult {
637 get { return false; }
640 public NvdlResultType ResultType {
641 get { return resultType; }
645 internal class SimpleModeUsage : SimplifiedItem
647 // It will never be used in validation.
648 NvdlModeUsage source; // FIXME: put this into CompileContext
649 readonly SimpleContext [] contexts;
652 public SimpleModeUsage (NvdlModeUsage usage)
655 contexts = new SimpleContext [usage.Contexts.Count];
656 for (int i = 0; i < contexts.Length; i++)
657 contexts [i] = new SimpleContext (
661 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
663 if (source.UseMode != null) {
664 mode = ctx.GetCompiledMode (source.UseMode);
666 else if (source.NestedMode != null)
667 mode = ctx.GetCompiledMode (source);
671 for (int i = 0; i < contexts.Length; i++)
672 contexts [i].ResolveModes (ctx, mode);
674 // FIXME: get location by some way
676 throw new NvdlCompileException (
677 "mode does not contain either referenced modeUsage or nested mode.", null);
680 public SimpleMode UseMode {
684 public SimpleContext [] Contexts {
685 get { return contexts; }
689 internal class SimplePath
691 readonly SimplePathStep [] steps;
693 public SimplePath (SimplePathStep [] steps)
698 public SimplePathStep [] Steps {
699 get { return steps; }
703 internal class SimplePathStep
705 readonly string name;
706 readonly bool descendants;
708 public SimplePathStep (string name, bool descendants)
711 this.descendants = descendants;
718 public bool Descendants {
719 get { return descendants; }
723 internal class SimpleContext : SimplifiedItem
725 readonly NvdlContext source;
726 readonly string useModeName; // It is never used in validation.
730 public SimpleContext (NvdlContext context)
733 FillLocation (context);
735 this.useModeName = context.UseMode;
738 string [] spaths = context.Path.Split ('|');
739 ArrayList al = new ArrayList ();
740 foreach (string spathws in spaths) {
741 string spath = spathws.Trim (
743 if (spath.Length == 0)
745 ParsePath (al, TrimName (spath));
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);
754 private void ParsePath (ArrayList al, string path)
756 ArrayList steps = new ArrayList ();
757 int start = path.Length > 0 && path [0] == '/' ? 1 : 0;
759 int idx = path.IndexOf ('/', start);
761 steps.Add (new SimplePathStep (TrimName (path.Substring (start)), false));
763 } else if (path.Length > idx + 1 && path [idx + 1] == '/') {
764 steps.Add (new SimplePathStep (TrimName (path.Substring (start, idx - start)), true));
767 steps.Add (new SimplePathStep (TrimName (path.Substring (start, idx - start)), false));
770 } while (start < path.Length);
771 al.Add (new SimplePath (steps.ToArray (typeof (SimplePathStep)) as SimplePathStep []));
774 string TrimName (string src)
776 return src.TrimStart (Nvdl.Whitespaces).TrimEnd (Nvdl.Whitespaces);
779 internal SimplePath [] Path {
783 public SimpleMode UseMode {
784 get { return useMode; }
787 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
789 if (useModeName != null)
790 useMode = ctx.GetCompiledMode (useModeName);
791 else if (source.NestedMode != null)
792 useMode = ctx.GetCompiledMode (source);
797 throw new NvdlCompileException (String.Format ("Specified mode '{0}' was not found.",