a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
802 lines
20 KiB
C#
802 lines
20 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Specialized;
|
|
using System.Xml;
|
|
|
|
namespace Commons.Xml.Nvdl
|
|
{
|
|
internal class SimplifiedItem : IXmlLineInfo
|
|
{
|
|
int line;
|
|
int column;
|
|
string sourceUri = String.Empty;
|
|
|
|
public int LineNumber {
|
|
get { return line; }
|
|
set { line = value; }
|
|
}
|
|
|
|
public int LinePosition {
|
|
get { return column; }
|
|
set { column = value; }
|
|
}
|
|
|
|
public string SourceUri {
|
|
get { return sourceUri; }
|
|
set { sourceUri = value != null ? value : String.Empty; }
|
|
}
|
|
|
|
internal void FillLocation (NvdlElementBase e)
|
|
{
|
|
line = e.LineNumber;
|
|
column = e.LinePosition;
|
|
sourceUri = e.SourceUri;
|
|
}
|
|
|
|
public bool HasLineInfo ()
|
|
{
|
|
return line != 0;
|
|
}
|
|
|
|
public string Location {
|
|
get { return line != 0 ? String.Format ("{0} ({1},{2})", sourceUri, line, column) : String.Empty; }
|
|
}
|
|
}
|
|
|
|
internal class SimpleRules : SimplifiedItem
|
|
{
|
|
SimpleMode startMode;
|
|
SimpleTrigger [] triggers;
|
|
|
|
// FIXME: It is not used in validation step, so move it to
|
|
// compile context
|
|
SimpleMode [] modes;
|
|
|
|
public SimpleRules (NvdlCompileContext context)
|
|
{
|
|
FillLocation (context.Rules);
|
|
SimplifyPhase1 (context); // 6.4.1 - 10.
|
|
SimplifyPhase2 (context); // 6.4.11 - 14.
|
|
ResolveModes (context); // 6.4.15.
|
|
}
|
|
|
|
public SimpleMode StartMode {
|
|
get { return startMode; }
|
|
}
|
|
|
|
public SimpleTrigger [] Triggers {
|
|
get { return triggers; }
|
|
}
|
|
|
|
#region Simplification
|
|
private void SimplifyPhase1 (NvdlCompileContext ctx)
|
|
{
|
|
NvdlRules rules = ctx.Rules;
|
|
// 6.4.1 : just ignore "Foreign" property.
|
|
// 6.4.2 : already ignored on reading nvdl.
|
|
// 6.4.3 : done in SOM
|
|
// 6.4.4 : FIXME: must be done.
|
|
// 6.4.5 : FIXME: considered in compiler.
|
|
// 6.4.6 : FIXME: considered in compiler.
|
|
// 6.4.7 : FIXME: considered in compiler.
|
|
|
|
// 6.4.8 : here
|
|
NvdlModeList list = rules.Modes;
|
|
NvdlMode startMode = null;
|
|
|
|
if (rules.Modes.Count > 0) {
|
|
if (rules.Rules.Count > 0)
|
|
throw new NvdlCompileException ("Modes and rules cannot coexist in 'rules' element.", rules);
|
|
else if (rules.StartMode == null)
|
|
throw new NvdlCompileException ("startMode is missing in 'rules' element when modes are specified.", rules);
|
|
foreach (NvdlMode m in rules.Modes) {
|
|
if (m.Name == rules.StartMode) {
|
|
startMode = m;
|
|
break;
|
|
}
|
|
}
|
|
if (startMode == null)
|
|
throw new NvdlCompileException ("Matching 'mode' element specified by 'startMode' does not exist.", rules);
|
|
} else {
|
|
if (rules.Rules.Count == 0)
|
|
throw new NvdlCompileException ("Neither modes nor rules exists in 'rules' element.", rules);
|
|
list = new NvdlModeList ();
|
|
startMode = new NvdlMode ();
|
|
startMode.SourceUri = rules.SourceUri;
|
|
startMode.LineNumber = rules.LineNumber;
|
|
startMode.LinePosition = rules.LinePosition;
|
|
startMode.Name = "(startMode)";
|
|
list.Add (startMode);
|
|
foreach (NvdlRule rule in rules.Rules)
|
|
startMode.Rules.Add (rule);
|
|
}
|
|
|
|
// 6.4.9 : done in SimpleMode.ctor() and
|
|
// SimpleRule.ctor(), using ctx.CompiledModes.
|
|
foreach (NvdlMode m in list) {
|
|
SimpleMode sm = new SimpleMode (m, ctx);
|
|
ctx.AddCompiledMode (sm.Name, sm);
|
|
if (m == startMode)
|
|
this.startMode = sm;
|
|
}
|
|
|
|
// 6.4.10 : done in SimpleRule.Simplify
|
|
|
|
ArrayList tl = new ArrayList ();
|
|
for (int i = 0; i < rules.Triggers.Count; i++)
|
|
tl.Add (new SimpleTrigger (rules.Triggers [i]));
|
|
triggers = (SimpleTrigger []) tl.ToArray (
|
|
typeof (SimpleTrigger));
|
|
|
|
modes = (SimpleMode [])
|
|
new ArrayList (ctx.GetCompiledModes ())
|
|
.ToArray (typeof (SimpleMode));
|
|
}
|
|
|
|
private void SimplifyPhase2 (NvdlCompileContext ctx)
|
|
{
|
|
foreach (SimpleMode mode in modes)
|
|
mode.SimplifyPhase2 (ctx);
|
|
}
|
|
|
|
private void ResolveModes (NvdlCompileContext ctx)
|
|
{
|
|
foreach (SimpleMode mode in modes)
|
|
mode.ResolveModes (ctx);
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
internal class SimpleTrigger : SimplifiedItem
|
|
{
|
|
XmlQualifiedName [] names;
|
|
|
|
public SimpleTrigger (NvdlTrigger trigger)
|
|
{
|
|
FillLocation (trigger);
|
|
|
|
ArrayList al = new ArrayList ();
|
|
foreach (string ss in trigger.NameList.Split (' ')) {
|
|
string s = ss.Trim ();
|
|
if (s.Length == 0)
|
|
continue;
|
|
al.Add (new XmlQualifiedName (s, trigger.NS));
|
|
}
|
|
names = (XmlQualifiedName []) al.ToArray (
|
|
typeof (XmlQualifiedName));
|
|
}
|
|
|
|
public XmlQualifiedName [] Names {
|
|
get { return names; }
|
|
}
|
|
|
|
public bool Cover (string localName, string ns)
|
|
{
|
|
for (int i = 0; i < Names.Length; i++) {
|
|
XmlQualifiedName q = Names [i];
|
|
if (q.Name == localName && q.Namespace == ns)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal class SimpleMode : SimplifiedItem
|
|
{
|
|
string name;
|
|
SimpleRule [] rules;
|
|
|
|
// They are available only after complete simplification.
|
|
SimpleRule [] elementRules;
|
|
SimpleRule [] attributeRules;
|
|
|
|
public SimpleMode (NvdlMode mode, NvdlCompileContext ctx)
|
|
{
|
|
FillLocation (mode);
|
|
|
|
if (mode.Name == null)
|
|
throw new NvdlCompileException (
|
|
"'mode' element must have a name.", mode);
|
|
this.name = mode.Name;
|
|
SimplifyPhase1 (mode, ctx);
|
|
}
|
|
|
|
public SimpleMode (string name, NvdlNestedMode mode,
|
|
NvdlCompileContext ctx)
|
|
{
|
|
FillLocation (mode);
|
|
|
|
this.name = name;
|
|
SimplifyPhase1 (mode, ctx);
|
|
}
|
|
|
|
public SimpleMode (NvdlIncludedMode mode, NvdlCompileContext ctx)
|
|
{
|
|
FillLocation (mode);
|
|
|
|
// name doesn't matter here.
|
|
SimplifyPhase1 (mode, ctx);
|
|
}
|
|
|
|
public string Name {
|
|
get { return name; }
|
|
}
|
|
|
|
public SimpleRule [] ElementRules {
|
|
get { return elementRules; }
|
|
}
|
|
|
|
public SimpleRule [] AttributeRules {
|
|
get { return attributeRules; }
|
|
}
|
|
|
|
private void SimplifyPhase1 (NvdlModeBase mode,
|
|
NvdlCompileContext ctx)
|
|
{
|
|
NvdlModeCompileContext mctx =
|
|
new NvdlModeCompileContext (mode);
|
|
ctx.AddModeContext (this, mctx);
|
|
ArrayList al = new ArrayList ();
|
|
foreach (NvdlRule r in mode.Rules) {
|
|
switch (r.Match) {
|
|
case NvdlRuleTarget.Both:
|
|
al.Add (new SimpleRule (r, true, ctx));
|
|
al.Add (new SimpleRule (r, false, ctx));
|
|
break;
|
|
case NvdlRuleTarget.None:
|
|
case NvdlRuleTarget.Elements:
|
|
al.Add (new SimpleRule (r, false, ctx));
|
|
break;
|
|
case NvdlRuleTarget.Attributes:
|
|
al.Add (new SimpleRule (r, true, ctx));
|
|
break;
|
|
}
|
|
}
|
|
foreach (NvdlIncludedMode inc in mode.IncludedModes)
|
|
mctx.Included.Add (new SimpleMode (inc, ctx));
|
|
// The rule table is just a dummy store that might
|
|
// erase because of removal of inclusion.
|
|
rules = (SimpleRule []) al.ToArray (typeof (SimpleRule));
|
|
}
|
|
|
|
internal void SimplifyPhase2 (NvdlCompileContext ctx)
|
|
{
|
|
ArrayList al = new ArrayList ();
|
|
ConsumeIncludes (al, ctx);
|
|
SimpleRule anyElement = null;
|
|
SimpleRule anyAttribute = null;
|
|
// 6.4.12 + part of 6.4.13
|
|
CheckCollision (al, ref anyElement, ref anyAttribute);
|
|
// 6.4.13
|
|
if (anyElement == null) {
|
|
NvdlAnyNamespace ann = new NvdlAnyNamespace ();
|
|
ann.SourceUri = this.SourceUri;
|
|
ann.LineNumber = this.LineNumber;
|
|
ann.LinePosition = this.LinePosition;
|
|
|
|
NvdlReject reject = new NvdlReject ();
|
|
reject.SourceUri = this.SourceUri;
|
|
reject.LineNumber = this.LineNumber;
|
|
reject.LinePosition = this.LinePosition;
|
|
ann.Actions.Add (reject);
|
|
ann.Match = NvdlRuleTarget.Elements;
|
|
|
|
al.Add (new SimpleRule (ann, false, ctx));
|
|
}
|
|
if (anyAttribute == null) {
|
|
NvdlAnyNamespace ann = new NvdlAnyNamespace ();
|
|
ann.SourceUri = this.SourceUri;
|
|
ann.LineNumber = this.LineNumber;
|
|
ann.LinePosition = this.LinePosition;
|
|
|
|
NvdlAttach attach = new NvdlAttach ();
|
|
attach.SourceUri = this.SourceUri;
|
|
attach.LineNumber = this.LineNumber;
|
|
attach.LinePosition = this.LinePosition;
|
|
ann.Match = NvdlRuleTarget.Attributes;
|
|
ann.Actions.Add (attach);
|
|
|
|
al.Add (new SimpleRule (ann, true, ctx));
|
|
}
|
|
rules = (SimpleRule []) al.ToArray (typeof (SimpleRule));
|
|
}
|
|
|
|
private void ConsumeIncludes (ArrayList al,
|
|
NvdlCompileContext ctx)
|
|
{
|
|
// The reason why we limit the check to current count
|
|
// is to add invalid siblings (according to 6.4.12).
|
|
int checkMax = al.Count;
|
|
NvdlModeCompileContext mctx = ctx.GetModeContext (this);
|
|
foreach (SimpleRule rule in rules) {
|
|
// Don't skip cancelled rules here. They are
|
|
// needed to filter overriden rules out
|
|
// (according to 6.4.10)
|
|
//if (ctx.CancelledRules [rule] != null)
|
|
// continue;
|
|
|
|
bool exclude = false;
|
|
for (int i = 0; i < checkMax; i++) {
|
|
SimpleRule r = (SimpleRule) al [i];
|
|
if (rule.IsAny == r.IsAny &&
|
|
rule.MatchAttributes == r.MatchAttributes &&
|
|
rule.NS == r.NS &&
|
|
rule.Wildcard == r.Wildcard) {
|
|
exclude = true;
|
|
break;
|
|
}
|
|
}
|
|
if (exclude)
|
|
break;
|
|
al.Add (rule);
|
|
}
|
|
foreach (SimpleMode mode in mctx.Included)
|
|
mode.ConsumeIncludes (al, ctx);
|
|
|
|
// remove cancelled rules at this stage.
|
|
for (int i = 0; i < al.Count; ) {
|
|
if (ctx.CancelledRules [(SimpleRule) al [i]] != null)
|
|
al.RemoveAt (i);
|
|
else
|
|
i++;
|
|
}
|
|
}
|
|
|
|
private void CheckCollision (ArrayList al, ref SimpleRule el, ref SimpleRule attr)
|
|
{
|
|
for (int i = 0; i < al.Count; i++) {
|
|
SimpleRule r1 = (SimpleRule) al [i];
|
|
if (r1.IsAny) {
|
|
if (r1.MatchAttributes)
|
|
attr = r1;
|
|
else
|
|
el = r1;
|
|
}
|
|
for (int j = i + 1; j < al.Count; j++) {
|
|
SimpleRule r2 = (SimpleRule) al [j];
|
|
if (r1.MatchAttributes != r2.MatchAttributes)
|
|
continue;
|
|
if (r1.IsAny && r2.IsAny)
|
|
throw new NvdlCompileException ("collision in mode was found. Two anyNamespace elements.", this);
|
|
if (r1.IsAny || r2.IsAny)
|
|
continue;
|
|
if (Nvdl.NSMatches (r1.NS, 0, r1.Wildcard,
|
|
r2.NS, 0, r2.Wildcard))
|
|
throw new NvdlCompileException ("collision in mode was found.", this);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void ResolveModes (NvdlCompileContext ctx)
|
|
{
|
|
// Resolve moces and fill element/attributeRules.
|
|
ArrayList e = new ArrayList ();
|
|
ArrayList a = new ArrayList ();
|
|
foreach (SimpleRule rule in rules) {
|
|
rule.ResolveModes (ctx, this);
|
|
if (rule.MatchAttributes)
|
|
a.Add (rule);
|
|
else
|
|
e.Add (rule);
|
|
}
|
|
|
|
elementRules = (SimpleRule []) e.ToArray (typeof (SimpleRule));
|
|
attributeRules = (SimpleRule []) a.ToArray (typeof (SimpleRule));
|
|
}
|
|
}
|
|
|
|
internal class SimpleRule : SimplifiedItem
|
|
{
|
|
bool matchAttributes;
|
|
SimpleAction [] actions;
|
|
|
|
readonly string ns;
|
|
readonly string wildcard;
|
|
bool isAny;
|
|
|
|
public SimpleRule (NvdlRule rule, bool matchAttributes,
|
|
NvdlCompileContext ctx)
|
|
{
|
|
FillLocation (rule);
|
|
|
|
this.matchAttributes = matchAttributes;
|
|
NvdlNamespace nss = rule as NvdlNamespace;
|
|
if (nss == null)
|
|
this.isAny = true;
|
|
else {
|
|
this.ns = nss.NS;
|
|
if (nss.Wildcard == null)
|
|
wildcard = "*";
|
|
else if (nss.Wildcard.Length > 1)
|
|
throw new NvdlCompileException ("'wildCard' attribute can specify at most one character string.", rule);
|
|
else
|
|
wildcard = nss.Wildcard;
|
|
}
|
|
|
|
SimplifyPhase1 (rule, ctx);
|
|
}
|
|
|
|
public bool MatchAttributes {
|
|
get { return matchAttributes; }
|
|
}
|
|
|
|
public SimpleAction [] Actions {
|
|
get { return actions; }
|
|
}
|
|
|
|
public string NS {
|
|
get { return ns; }
|
|
}
|
|
|
|
public string Wildcard {
|
|
get { return wildcard; }
|
|
}
|
|
|
|
public bool IsAny {
|
|
get { return isAny; }
|
|
}
|
|
|
|
public bool MatchNS (string target)
|
|
{
|
|
if (isAny)
|
|
return true;
|
|
return Nvdl.NSMatches (ns, 0, wildcard, target, 0, "");
|
|
}
|
|
|
|
private void SimplifyPhase1 (NvdlRule r, NvdlCompileContext ctx)
|
|
{
|
|
ctx.AddRuleContext (this, r);
|
|
// 6.4.9
|
|
ArrayList al = new ArrayList ();
|
|
foreach (NvdlAction a in r.Actions) {
|
|
NvdlNoCancelAction nca =
|
|
a as NvdlNoCancelAction;
|
|
if (nca != null) {
|
|
if (nca.ModeUsage != null)
|
|
SimplifyModeUsage (nca, ctx);
|
|
NvdlResultAction ra = nca as NvdlResultAction;
|
|
if (ra != null)
|
|
al.Add (new SimpleResultAction (ra, ctx));
|
|
else if (nca is NvdlValidate)
|
|
al.Add (new SimpleValidate (
|
|
(NvdlValidate) nca, ctx));
|
|
else if (nca is NvdlAllow)
|
|
al.Add (new SimpleValidate (
|
|
(NvdlAllow) nca, ctx));
|
|
else
|
|
al.Add (new SimpleValidate (
|
|
(NvdlReject) nca, ctx));
|
|
}
|
|
else if (nca == null)
|
|
ctx.CancelledRules.Add (this, this);
|
|
}
|
|
actions = (SimpleAction []) al.ToArray (
|
|
typeof (SimpleAction));
|
|
}
|
|
|
|
private void SimplifyModeUsage (
|
|
NvdlNoCancelAction nca, NvdlCompileContext ctx)
|
|
{
|
|
NvdlModeUsage usage = nca.ModeUsage;
|
|
if (usage.NestedMode != null && ctx.GetCompiledMode (usage) == null) {
|
|
SimpleMode sm = new SimpleMode (String.Empty,
|
|
usage.NestedMode, ctx);
|
|
ctx.AddCompiledMode (usage, sm);
|
|
}
|
|
foreach (NvdlContext c in usage.Contexts) {
|
|
if (c.NestedMode != null) {
|
|
SimpleMode sm = new SimpleMode (
|
|
String.Empty, c.NestedMode, ctx);
|
|
ctx.AddCompiledMode (c, sm);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
|
|
{
|
|
foreach (SimpleAction a in actions)
|
|
a.ResolveModes (ctx, current);
|
|
}
|
|
}
|
|
|
|
internal abstract class SimpleAction : SimplifiedItem
|
|
{
|
|
readonly ListDictionary messages;
|
|
readonly SimpleModeUsage modeUsage;
|
|
SimpleMode mode;
|
|
|
|
protected SimpleAction (NvdlNoCancelAction action)
|
|
{
|
|
FillLocation (action);
|
|
|
|
if (action.ModeUsage != null)
|
|
modeUsage = new SimpleModeUsage (action.ModeUsage);
|
|
messages = new ListDictionary ();
|
|
if (action.SimpleMessage != null)
|
|
messages.Add ("", action.SimpleMessage);
|
|
foreach (NvdlMessage msg in action.Messages)
|
|
messages.Add (msg.XmlLang, msg.Text);
|
|
}
|
|
|
|
public abstract bool NoResult { get; }
|
|
|
|
public ListDictionary Messages {
|
|
get { return messages; }
|
|
}
|
|
|
|
public SimpleMode DefaultMode {
|
|
get { return mode; }
|
|
}
|
|
|
|
public SimpleContext [] Contexts {
|
|
get { return modeUsage.Contexts; }
|
|
}
|
|
|
|
internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
|
|
{
|
|
if (modeUsage != null) {
|
|
modeUsage.ResolveModes (ctx, current);
|
|
mode = modeUsage.UseMode;
|
|
}
|
|
if (mode == null)
|
|
mode = current;
|
|
}
|
|
}
|
|
|
|
internal class SimpleValidate : SimpleAction
|
|
{
|
|
readonly NvdlValidatorGenerator generator;
|
|
XmlResolver resolver;
|
|
|
|
static NvdlValidate CreateBuiltInValidate (NvdlAction a)
|
|
{
|
|
bool allow = a is NvdlAllow;
|
|
NvdlValidate v = new NvdlValidate ();
|
|
v.SourceUri = a.SourceUri;
|
|
v.LineNumber = a.LineNumber;
|
|
v.LinePosition = a.LinePosition;
|
|
v.ModeUsage = new NvdlModeUsage ();
|
|
XmlDocument doc = new XmlDocument ();
|
|
XmlElement el = doc.CreateElement (
|
|
allow ? "allow" : "reject",
|
|
Nvdl.BuiltInValidationNamespace);
|
|
doc.AppendChild (doc.CreateElement ("schema",
|
|
Nvdl.Namespace));
|
|
doc.DocumentElement.AppendChild (el);
|
|
v.SchemaBody = doc.DocumentElement;
|
|
return v;
|
|
}
|
|
|
|
// 6.4.14
|
|
public SimpleValidate (NvdlAllow allow, NvdlCompileContext ctx)
|
|
: this (CreateBuiltInValidate (allow), ctx)
|
|
{
|
|
}
|
|
|
|
// 6.4.14
|
|
public SimpleValidate (NvdlReject reject, NvdlCompileContext ctx)
|
|
: this (CreateBuiltInValidate (reject), ctx)
|
|
{
|
|
}
|
|
|
|
public SimpleValidate (
|
|
NvdlValidate validate,
|
|
NvdlCompileContext ctx)
|
|
: base (validate)
|
|
{
|
|
// 6.4.7
|
|
generator = ctx.Config.GetGenerator (validate,
|
|
ctx.Rules.SchemaType);
|
|
}
|
|
|
|
internal NvdlValidatorGenerator Generator {
|
|
get { return generator; }
|
|
}
|
|
|
|
public override bool NoResult {
|
|
get { return true; }
|
|
}
|
|
|
|
public XmlReader CreateValidator (XmlReader reader)
|
|
{
|
|
return generator.CreateValidator (reader, resolver);
|
|
}
|
|
|
|
public void ValidateAttributes (XmlReader reader, string ns)
|
|
{
|
|
XmlDocument doc = new XmlDocument ();
|
|
XmlElement el = doc.CreateElement ("virtualElement",
|
|
Nvdl.InstanceNamespace);
|
|
for (int i = 0; i < reader.AttributeCount; i++) {
|
|
reader.MoveToAttribute (i);
|
|
if (reader.NamespaceURI != ns)
|
|
continue;
|
|
el.SetAttribute (reader.LocalName,
|
|
reader.NamespaceURI, reader.Value);
|
|
}
|
|
reader.MoveToElement ();
|
|
XmlReader r = generator.CreateAttributeValidator (
|
|
new XmlNodeReader (el), resolver);
|
|
while (!r.EOF)
|
|
r.Read ();
|
|
}
|
|
}
|
|
|
|
internal class SimpleResultAction : SimpleAction
|
|
{
|
|
readonly NvdlResultType resultType;
|
|
|
|
public SimpleResultAction (NvdlResultAction ra,
|
|
NvdlCompileContext ctx)
|
|
: base (ra)
|
|
{
|
|
this.resultType = ra.ResultType;
|
|
}
|
|
|
|
public override bool NoResult {
|
|
get { return false; }
|
|
}
|
|
|
|
public NvdlResultType ResultType {
|
|
get { return resultType; }
|
|
}
|
|
}
|
|
|
|
internal class SimpleModeUsage : SimplifiedItem
|
|
{
|
|
// It will never be used in validation.
|
|
NvdlModeUsage source; // FIXME: put this into CompileContext
|
|
readonly SimpleContext [] contexts;
|
|
SimpleMode mode;
|
|
|
|
public SimpleModeUsage (NvdlModeUsage usage)
|
|
{
|
|
this.source = usage;
|
|
contexts = new SimpleContext [usage.Contexts.Count];
|
|
for (int i = 0; i < contexts.Length; i++)
|
|
contexts [i] = new SimpleContext (
|
|
usage.Contexts [i]);
|
|
}
|
|
|
|
internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
|
|
{
|
|
if (source.UseMode != null) {
|
|
mode = ctx.GetCompiledMode (source.UseMode);
|
|
}
|
|
else if (source.NestedMode != null)
|
|
mode = ctx.GetCompiledMode (source);
|
|
else
|
|
mode = current;
|
|
|
|
for (int i = 0; i < contexts.Length; i++)
|
|
contexts [i].ResolveModes (ctx, mode);
|
|
|
|
// FIXME: get location by some way
|
|
if (mode == null)
|
|
throw new NvdlCompileException (
|
|
"mode does not contain either referenced modeUsage or nested mode.", null);
|
|
}
|
|
|
|
public SimpleMode UseMode {
|
|
get { return mode; }
|
|
}
|
|
|
|
public SimpleContext [] Contexts {
|
|
get { return contexts; }
|
|
}
|
|
}
|
|
|
|
internal class SimplePath
|
|
{
|
|
readonly SimplePathStep [] steps;
|
|
|
|
public SimplePath (SimplePathStep [] steps)
|
|
{
|
|
this.steps = steps;
|
|
}
|
|
|
|
public SimplePathStep [] Steps {
|
|
get { return steps; }
|
|
}
|
|
}
|
|
|
|
internal class SimplePathStep
|
|
{
|
|
readonly string name;
|
|
readonly bool descendants;
|
|
|
|
public SimplePathStep (string name, bool descendants)
|
|
{
|
|
this.name = name;
|
|
this.descendants = descendants;
|
|
}
|
|
|
|
public string Name {
|
|
get { return name; }
|
|
}
|
|
|
|
public bool Descendants {
|
|
get { return descendants; }
|
|
}
|
|
}
|
|
|
|
internal class SimpleContext : SimplifiedItem
|
|
{
|
|
readonly NvdlContext source;
|
|
readonly string useModeName; // It is never used in validation.
|
|
SimpleMode useMode;
|
|
SimplePath [] path;
|
|
|
|
public SimpleContext (NvdlContext context)
|
|
{
|
|
source = context;
|
|
FillLocation (context);
|
|
|
|
this.useModeName = context.UseMode;
|
|
|
|
try {
|
|
string [] spaths = context.Path.Split ('|');
|
|
ArrayList al = new ArrayList ();
|
|
foreach (string spathws in spaths) {
|
|
string spath = spathws.Trim (
|
|
Nvdl.Whitespaces);
|
|
if (spath.Length == 0)
|
|
continue;
|
|
ParsePath (al, TrimName (spath));
|
|
}
|
|
path = (SimplePath []) al.ToArray (
|
|
typeof (SimplePath));
|
|
} catch (XmlException ex) {
|
|
throw new NvdlCompileException (String.Format ("Invalid path string: {0}", path), ex, context);
|
|
}
|
|
}
|
|
|
|
private void ParsePath (ArrayList al, string path)
|
|
{
|
|
ArrayList steps = new ArrayList ();
|
|
int start = path.Length > 0 && path [0] == '/' ? 1 : 0;
|
|
do {
|
|
int idx = path.IndexOf ('/', start);
|
|
if (idx < 0) {
|
|
steps.Add (new SimplePathStep (TrimName (path.Substring (start)), false));
|
|
start = path.Length;
|
|
} else if (path.Length > idx + 1 && path [idx + 1] == '/') {
|
|
steps.Add (new SimplePathStep (TrimName (path.Substring (start, idx - start)), true));
|
|
start = idx + 2;
|
|
} else {
|
|
steps.Add (new SimplePathStep (TrimName (path.Substring (start, idx - start)), false));
|
|
start = idx + 1;
|
|
}
|
|
} while (start < path.Length);
|
|
al.Add (new SimplePath (steps.ToArray (typeof (SimplePathStep)) as SimplePathStep []));
|
|
}
|
|
|
|
string TrimName (string src)
|
|
{
|
|
return src.TrimStart (Nvdl.Whitespaces).TrimEnd (Nvdl.Whitespaces);
|
|
}
|
|
|
|
internal SimplePath [] Path {
|
|
get { return path; }
|
|
}
|
|
|
|
public SimpleMode UseMode {
|
|
get { return useMode; }
|
|
}
|
|
|
|
internal void ResolveModes (NvdlCompileContext ctx, SimpleMode current)
|
|
{
|
|
if (useModeName != null)
|
|
useMode = ctx.GetCompiledMode (useModeName);
|
|
else if (source.NestedMode != null)
|
|
useMode = ctx.GetCompiledMode (source);
|
|
else
|
|
useMode = current;
|
|
|
|
if (useMode == null)
|
|
throw new NvdlCompileException (String.Format ("Specified mode '{0}' was not found.",
|
|
useModeName), this);
|
|
}
|
|
}
|
|
}
|