2 using System.Collections;
3 using System.Collections.Specialized;
6 namespace Commons.Xml.Nvdl.Simplified
8 public 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 public 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 public 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 NvdlAllow allow = new NvdlAllow ();
260 allow.SourceUri = this.SourceUri;
261 allow.LineNumber = this.LineNumber;
262 allow.LinePosition = this.LinePosition;
263 ann.Match = NvdlRuleTarget.Attributes;
264 ann.Actions.Add (allow);
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 public 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 public 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 public 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.BuiltInValidationUri);
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);
546 this.resolver = ctx.Config.XmlResolverInternal;
549 string schemaType = validate.SchemaType;
550 if (schemaType == null)
551 schemaType = ctx.Rules.SchemaType;
552 if (schemaType == null && validate.SchemaBody != null && !ElementHasElementChild (validate.SchemaBody))
553 schemaType = validate.SchemaBody.InnerText;
554 if (schemaType == null)
555 schemaType = "text/xml";
557 // FIXME: this part must be totally rewritten.
559 XmlReader schemaReader = null;
560 if (schemaType == "text/xml") {
561 if (validate.SchemaUri != null) {
562 if (validate.SchemaBody != null)
563 throw new NvdlCompileException ("Both 'schema' attribute and 'schema' element are specified in a 'validate' element.", validate);
564 // FIXME: use NvdlConfig
565 schemaReader = new XmlTextReader (validate.SchemaUri);
567 else if (validate.SchemaBody != null) {
568 schemaReader = new XmlNodeReader (validate.SchemaBody);
569 schemaReader.MoveToContent ();
570 schemaReader.Read (); // Skip "schema" element
573 throw new NvdlCompileException ("Neither 'schema' attribute nor 'schema' element is specified in a 'validate' element.", validate);
576 throw new NvdlCompileException (String.Format ("MIME type '{0}' is not supported at this moment.", schemaType), validate);
578 schemaReader.MoveToContent ();
580 NvdlValidationProvider provider =
581 ctx.Config.GetProvider (schemaReader.NamespaceURI);
583 if (provider == null)
584 throw new NvdlCompileException (String.Format ("Schema type '{0}' is not supported in this configuration. Use custom provider that supports this schema type.", schemaType), validate);
586 generator = provider.CreateGenerator (schemaReader, ctx.Config.XmlResolverInternal);
588 foreach (NvdlOption option in validate.Options) {
589 bool mustSupport = option.MustSupport != null ?
590 XmlConvert.ToBoolean (option.MustSupport) : false;
591 if (!generator.AddOption (option.Name,
592 option.Arg) && mustSupport)
593 throw new NvdlCompileException (String.Format ("Option '{0}' with argument '{1}' is not supported for schema type '{2}'.",
594 option.Name, option.Arg,
595 schemaType), validate);
600 internal NvdlValidatorGenerator Generator {
601 get { return generator; }
604 public override bool NoResult {
608 public XmlReader CreateValidator (XmlReader reader)
610 return generator.CreateValidator (reader, resolver);
614 public class SimpleResultAction : SimpleAction
616 readonly NvdlResultType resultType;
618 public SimpleResultAction (NvdlResultAction ra,
619 NvdlCompileContext ctx)
622 this.resultType = ra.ResultType;
625 public override bool NoResult {
626 get { return false; }
629 public NvdlResultType ResultType {
630 get { return resultType; }
634 public class SimpleModeUsage : SimplifiedItem
636 // It will never be used in validation.
637 NvdlModeUsage source; // FIXME: put this into CompileContext
638 readonly SimpleContext [] contexts;
641 public SimpleModeUsage (NvdlModeUsage usage)
644 contexts = new SimpleContext [usage.Contexts.Count];
645 for (int i = 0; i < contexts.Length; i++)
646 contexts [i] = new SimpleContext (
650 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
652 if (source.UseMode != null) {
653 mode = ctx.GetCompiledMode (source.UseMode);
655 else if (source.NestedMode != null)
656 mode = ctx.GetCompiledMode (source);
660 for (int i = 0; i < contexts.Length; i++)
661 contexts [i].ResolveModes (ctx, mode);
663 // FIXME: get location by some way
665 throw new NvdlCompileException (
666 "mode does not contain either referenced modeUsage or nested mode.", null);
669 public SimpleMode UseMode {
673 public SimpleContext [] Contexts {
674 get { return contexts; }
678 internal class SimplePath
680 readonly SimplePathStep [] steps;
682 public SimplePath (SimplePathStep [] steps)
687 public SimplePathStep [] Steps {
688 get { return steps; }
692 internal class SimplePathStep
694 readonly string name;
695 readonly bool descendants;
697 public SimplePathStep (string name, bool descendants)
700 this.descendants = descendants;
707 public bool Descendants {
708 get { return descendants; }
712 public class SimpleContext : SimplifiedItem
714 readonly string useModeName; // It is never used in validation.
718 public SimpleContext (NvdlContext context)
720 FillLocation (context);
722 this.useModeName = context.UseMode;
725 string [] spaths = context.Path.Split ('|');
726 ArrayList al = new ArrayList ();
727 foreach (string spathws in spaths) {
728 string spath = spathws.Trim (
730 if (spath.Length == 0)
732 ParsePath (al, spath);
734 path = (SimplePath []) al.ToArray (
735 typeof (SimplePath));
736 } catch (XmlException ex) {
737 throw new NvdlCompileException (String.Format ("Invalid path string: {0}", path), ex, context);
741 private void ParsePath (ArrayList al, string path)
743 ArrayList steps = new ArrayList ();
746 int idx = path.IndexOf ('/', start);
748 steps.Add (new SimplePathStep (path.Substring (start), false));
751 else if (path.Length > idx + 1 && path [idx + 1] == '/') {
752 steps.Add (new SimplePathStep (path.Substring (start, idx - start), true));
755 steps.Add (new SimplePathStep (path.Substring (start, idx - start), false));
759 } while (start < path.Length);
760 al.Add (new SimplePath (steps.ToArray (typeof (SimplePathStep)) as SimplePathStep []));
763 internal SimplePath [] Path {
767 public SimpleMode UseMode {
768 get { return useMode; }
771 internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
773 if (useModeName != null)
774 useMode = ctx.GetCompiledMode (useModeName);
779 throw new NvdlCompileException (String.Format ("Specified mode '{0}' was not found.",
785 // After simplification, each mode name "shall be different from any
786 // other mode name" (6.4.8)
787 public class SimpleModeTable : DictionaryBase
789 public SimpleModeTable (SimpleMode [] modes)
791 foreach (SimpleMode mode in modes)
792 Dictionary.Add (mode.Name, mode);
795 public SimpleMode this [string name] {
796 get { return (SimpleMode) Dictionary [name]; }
799 public ICollection Keys {
800 get { return Dictionary.Keys; }
803 public ICollection Values {
804 get { return Dictionary.Values; }