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 XmlQualifiedName [] 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 XmlQualifiedName [] 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 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 (
132 modes = (SimpleMode [])
133 new ArrayList (ctx.GetCompiledModes ())
134 .ToArray (typeof (SimpleMode));
137 private void SimplifyPhase2 (NvdlCompileContext ctx)
139 foreach (SimpleMode mode in modes)
140 mode.SimplifyPhase2 (ctx);
143 private void ResolveModes (NvdlCompileContext ctx)
145 foreach (SimpleMode mode in modes)
146 mode.ResolveModes (ctx);
151 internal class SimpleMode : SimplifiedItem
156 // They are available only after complete simplification.
157 SimpleRule [] elementRules;
158 SimpleRule [] attributeRules;
160 public SimpleMode (NvdlMode mode, NvdlCompileContext ctx)
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);
171 public SimpleMode (string name, NvdlNestedMode mode,
172 NvdlCompileContext ctx)
177 SimplifyPhase1 (mode, ctx);
180 public SimpleMode (NvdlIncludedMode mode, NvdlCompileContext ctx)
184 // name doesn't matter here.
185 SimplifyPhase1 (mode, ctx);
192 public SimpleRule [] ElementRules {
193 get { return elementRules; }
196 public SimpleRule [] AttributeRules {
197 get { return attributeRules; }
200 private void SimplifyPhase1 (NvdlModeBase mode,
201 NvdlCompileContext ctx)
203 NvdlModeCompileContext mctx =
204 new NvdlModeCompileContext (mode);
205 ctx.AddModeContext (this, mctx);
206 ArrayList al = new ArrayList ();
207 foreach (NvdlRule r in mode.Rules) {
209 case NvdlRuleTarget.Both:
210 al.Add (new SimpleRule (r, true, ctx));
211 al.Add (new SimpleRule (r, false, ctx));
213 case NvdlRuleTarget.None:
214 case NvdlRuleTarget.Elements:
215 al.Add (new SimpleRule (r, false, ctx));
217 case NvdlRuleTarget.Attributes:
218 al.Add (new SimpleRule (r, true, ctx));
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));
229 internal void SimplifyPhase2 (NvdlCompileContext ctx)
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);
238 if (anyElement == null) {
239 NvdlAnyNamespace ann = new NvdlAnyNamespace ();
240 ann.SourceUri = this.SourceUri;
241 ann.LineNumber = this.LineNumber;
242 ann.LinePosition = this.LinePosition;
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;
251 al.Add (new SimpleRule (ann, false, ctx));
253 if (anyAttribute == null) {
254 NvdlAnyNamespace ann = new NvdlAnyNamespace ();
255 ann.SourceUri = this.SourceUri;
256 ann.LineNumber = this.LineNumber;
257 ann.LinePosition = this.LinePosition;
259 NvdlAttach attach = new NvdlAttach ();
260 attach.SourceUri = this.SourceUri;
261 attach.LineNumber = this.LineNumber;
262 attach.LinePosition = this.LinePosition;
263 ann.Match = NvdlRuleTarget.Attributes;
264 ann.Actions.Add (attach);
266 al.Add (new SimpleRule (ann, true, ctx));
268 rules = (SimpleRule []) al.ToArray (typeof (SimpleRule));
271 private void ConsumeIncludes (ArrayList al,
272 NvdlCompileContext ctx)
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)
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 &&
287 rule.Wildcard == r.Wildcard) {
296 foreach (SimpleMode mode in mctx.Included)
297 mode.ConsumeIncludes (al, ctx);
300 private void CheckCollision (ArrayList al, ref SimpleRule el, ref SimpleRule attr)
302 for (int i = 0; i < al.Count; i++) {
303 SimpleRule r1 = (SimpleRule) al [i];
305 if (r1.MatchAttributes)
310 for (int j = i + 1; j < al.Count; j++) {
311 SimpleRule r2 = (SimpleRule) al [j];
312 if (r1.MatchAttributes != r2.MatchAttributes)
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)
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);
325 internal void ResolveModes (NvdlCompileContext ctx)
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)
338 elementRules = (SimpleRule []) e.ToArray (typeof (SimpleRule));
339 attributeRules = (SimpleRule []) a.ToArray (typeof (SimpleRule));
343 internal class SimpleRule : SimplifiedItem
345 bool matchAttributes;
346 SimpleAction [] actions;
349 readonly string wildcard;
352 public SimpleRule (NvdlRule rule, bool matchAttributes,
353 NvdlCompileContext ctx)
357 this.matchAttributes = matchAttributes;
358 NvdlNamespace nss = rule as NvdlNamespace;
363 if (nss.Wildcard == null)
365 else if (nss.Wildcard.Length > 1)
366 throw new NvdlCompileException ("'wildcard' attribute can specify at most one character string.", rule);
368 wildcard = nss.Wildcard;
371 SimplifyPhase1 (rule, ctx);
374 public bool MatchAttributes {
375 get { return matchAttributes; }
378 public SimpleAction [] Actions {
379 get { return actions; }
386 public string Wildcard {
387 get { return wildcard; }
391 get { return isAny; }
394 public bool MatchNS (string target)
398 return Nvdl.NSMatches (ns, 0, wildcard, target, 0, "");
401 private void SimplifyPhase1 (NvdlRule r, NvdlCompileContext ctx)
403 ctx.AddRuleContext (this, r);
405 ArrayList al = new ArrayList ();
406 foreach (NvdlAction a in r.Actions) {
407 NvdlNoCancelAction nca =
408 a as NvdlNoCancelAction;
410 if (nca.ModeUsage != null)
411 SimplifyModeUsage (nca, ctx);
412 NvdlResultAction ra = nca as NvdlResultAction;
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));
422 al.Add (new SimpleValidate (
423 (NvdlReject) nca, ctx));
425 else if (nca == null)
426 ctx.CancelledRules.Add (this, this);
428 actions = (SimpleAction []) al.ToArray (
429 typeof (SimpleAction));
432 private void SimplifyModeUsage (
433 NvdlNoCancelAction nca, NvdlCompileContext ctx)
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);
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);
450 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
452 foreach (SimpleAction a in actions)
453 a.ResolveModes (ctx, current);
457 internal abstract class SimpleAction : SimplifiedItem
459 readonly ListDictionary messages;
460 readonly SimpleModeUsage modeUsage;
463 protected SimpleAction (NvdlNoCancelAction action)
465 FillLocation (action);
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);
476 public abstract bool NoResult { get; }
478 public ListDictionary Messages {
479 get { return messages; }
482 public SimpleMode DefaultMode {
486 public SimpleContext [] Contexts {
487 get { return modeUsage.Contexts; }
490 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
492 if (modeUsage != null) {
493 modeUsage.ResolveModes (ctx, current);
494 mode = modeUsage.UseMode;
501 internal class SimpleValidate : SimpleAction
503 readonly NvdlValidatorGenerator generator;
504 XmlResolver resolver;
506 static NvdlValidate CreateBuiltInValidate (NvdlAction a)
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.BuiltInValidationNamespace);
518 doc.AppendChild (doc.CreateElement ("schema",
520 doc.DocumentElement.AppendChild (el);
521 v.SchemaBody = doc.DocumentElement;
526 public SimpleValidate (NvdlAllow allow, NvdlCompileContext ctx)
527 : this (CreateBuiltInValidate (allow), ctx)
532 public SimpleValidate (NvdlReject reject, NvdlCompileContext ctx)
533 : this (CreateBuiltInValidate (reject), ctx)
537 public SimpleValidate (
538 NvdlValidate validate,
539 NvdlCompileContext ctx)
543 generator = ctx.Config.GetGenerator (validate,
544 ctx.Rules.SchemaType);
547 internal NvdlValidatorGenerator Generator {
548 get { return generator; }
551 public override bool NoResult {
555 public XmlReader CreateValidator (XmlReader reader)
557 return generator.CreateValidator (reader, resolver);
560 public void ValidateAttributes (XmlReader reader, string ns)
562 XmlDocument doc = new XmlDocument ();
563 XmlElement el = doc.CreateElement ("virtualElement",
564 Nvdl.InstanceNamespace);
565 for (int i = 0; i < reader.AttributeCount; i++) {
566 reader.MoveToAttribute (i);
567 if (reader.NamespaceURI != ns)
569 el.SetAttribute (reader.LocalName,
570 reader.NamespaceURI, reader.Value);
572 reader.MoveToElement ();
573 XmlReader r = generator.CreateAttributeValidator (
574 new XmlNodeReader (el), resolver);
580 internal class SimpleResultAction : SimpleAction
582 readonly NvdlResultType resultType;
584 public SimpleResultAction (NvdlResultAction ra,
585 NvdlCompileContext ctx)
588 this.resultType = ra.ResultType;
591 public override bool NoResult {
592 get { return false; }
595 public NvdlResultType ResultType {
596 get { return resultType; }
600 internal class SimpleModeUsage : SimplifiedItem
602 // It will never be used in validation.
603 NvdlModeUsage source; // FIXME: put this into CompileContext
604 readonly SimpleContext [] contexts;
607 public SimpleModeUsage (NvdlModeUsage usage)
610 contexts = new SimpleContext [usage.Contexts.Count];
611 for (int i = 0; i < contexts.Length; i++)
612 contexts [i] = new SimpleContext (
616 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
618 if (source.UseMode != null) {
619 mode = ctx.GetCompiledMode (source.UseMode);
621 else if (source.NestedMode != null)
622 mode = ctx.GetCompiledMode (source);
626 for (int i = 0; i < contexts.Length; i++)
627 contexts [i].ResolveModes (ctx, mode);
629 // FIXME: get location by some way
631 throw new NvdlCompileException (
632 "mode does not contain either referenced modeUsage or nested mode.", null);
635 public SimpleMode UseMode {
639 public SimpleContext [] Contexts {
640 get { return contexts; }
644 internal class SimplePath
646 readonly SimplePathStep [] steps;
648 public SimplePath (SimplePathStep [] steps)
653 public SimplePathStep [] Steps {
654 get { return steps; }
658 internal class SimplePathStep
660 readonly string name;
661 readonly bool descendants;
663 public SimplePathStep (string name, bool descendants)
666 this.descendants = descendants;
673 public bool Descendants {
674 get { return descendants; }
678 internal class SimpleContext : SimplifiedItem
680 readonly string useModeName; // It is never used in validation.
684 public SimpleContext (NvdlContext context)
686 FillLocation (context);
688 this.useModeName = context.UseMode;
691 string [] spaths = context.Path.Split ('|');
692 ArrayList al = new ArrayList ();
693 foreach (string spathws in spaths) {
694 string spath = spathws.Trim (
696 if (spath.Length == 0)
698 ParsePath (al, spath);
700 path = (SimplePath []) al.ToArray (
701 typeof (SimplePath));
702 } catch (XmlException ex) {
703 throw new NvdlCompileException (String.Format ("Invalid path string: {0}", path), ex, context);
707 private void ParsePath (ArrayList al, string path)
709 ArrayList steps = new ArrayList ();
712 int idx = path.IndexOf ('/', start);
714 steps.Add (new SimplePathStep (path.Substring (start), false));
717 else if (path.Length > idx + 1 && path [idx + 1] == '/') {
718 steps.Add (new SimplePathStep (path.Substring (start, idx - start), true));
721 steps.Add (new SimplePathStep (path.Substring (start, idx - start), false));
725 } while (start < path.Length);
726 al.Add (new SimplePath (steps.ToArray (typeof (SimplePathStep)) as SimplePathStep []));
729 internal SimplePath [] Path {
733 public SimpleMode UseMode {
734 get { return useMode; }
737 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
739 if (useModeName != null)
740 useMode = ctx.GetCompiledMode (useModeName);
745 throw new NvdlCompileException (String.Format ("Specified mode '{0}' was not found.",