// It returns RelaxngAttribute for local attribute, and // RelaxngRef for global attribute. private RelaxngPattern InferNewAttribute( QName attrName, bool isNewTypeDefinition) { RelaxngPattern p = null; bool mergedRequired = false; if (attrName.Namespace.Length > 0) { // global attribute; might be already defined. // (Actually RELAX NG has no concept of "global // attributes" but it is still useful to // represent attributes in global scope. RelaxngDefine attr = GetGlobalAttribute( attrName); if (attr == null) { attr = CreateGlobalAttribute(attrName); attr.Patterns.Add(CreateSimplePattern( InferSimpleType(source.Value))); } else { RelaxngAttribute a = attr.Patterns [0] as RelaxngAttribute; if (a != null) { mergedRequired = true; } else { RelaxngOptional opt = (RelaxngOptional)attr.Patterns [0]; a = (RelaxngAttribute)opt.Patterns [0]; } InferMergedAttribute(a); } RelaxngRef r = new RelaxngRef(); r.Name = attr.Name; p = r; } else { // local attribute RelaxngAttribute a = new RelaxngAttribute(); a.NameClass = new RelaxngName( attrName.Name, attrName.Namespace); a.Pattern = CreateSimplePattern( InferSimpleType(source.Value)); p = a; } // optional if (laxOccurence || (!isNewTypeDefinition && !mergedRequired)) { RelaxngOptional opt = new RelaxngOptional(); opt.Patterns.Add(p); p = opt; } return(p); }
private void InferElement(RelaxngDefine el, bool isNew) { RelaxngElement ct = (RelaxngElement)el.Patterns [0]; // Attributes if (source.MoveToFirstAttribute()) { InferAttributes(ct, isNew); source.MoveToElement(); } // Content if (source.IsEmptyElement) { InferAsEmptyElement(ct, isNew); source.Read(); source.MoveToContent(); } else { InferContent(ct, isNew); source.ReadEndElement(); } if (GetElementContent(ct) == null) { el.Patterns.Add(new RelaxngEmpty()); } }
private void Run() { // move to top-level element source.MoveToContent(); int depth = source.Depth; if (source.NodeType != XmlNodeType.Element) { throw new ArgumentException("Argument XmlReader content is expected to be an element."); } QName qname = new QName(source.LocalName, source.NamespaceURI); RelaxngDefine el = GetGlobalElement(qname); if (el == null) { el = CreateGlobalElement(qname); InferElement(el, true); } else { InferElement(el, false); } RelaxngStart start = new RelaxngStart(); start.Combine = "choice"; RelaxngRef topRef = new RelaxngRef(); topRef.Name = el.Name; start.Pattern = topRef; grammar.Starts.Add(start); }
// Note that it might not be used directly when a grammar // contains more than one "define" for an identical name // (compact syntax does not support "combine" attribute). public void WriteDefine(RelaxngDefine define) { if (IsKeyword(define.Name)) { w.Write('\\'); } w.Write(define.Name); if (define.Combine == null) { w.Write(" = "); } else { w.Write(define.Combine.Trim() == "interleave" ? " &= " : " |= "); } if (define.Patterns.Count == 0) { w.Write("empty"); } else { define.Patterns [0].WriteRnc(this); for (int i = 1; i < define.Patterns.Count; i++) { w.Write(","); define.Patterns [i].WriteRnc(this); } } w.WriteLine(); w.WriteLine(); }
private bool ElementMatches(RelaxngRef el) { RelaxngDefine def = elements [new QName( source.LocalName, source.NamespaceURI)] as RelaxngDefine; return(def != null && def.Name == el.Name); }
RelaxngDefine DefineElement(XmlSchemaElement el) { RelaxngDefine def = new RelaxngDefine(); def.Patterns.Add(CreateElement(el)); def.Name = el.Name; return(def); }
private RelaxngDefine CreateGlobalAttribute(QName name) { RelaxngDefine def = new RelaxngDefine(); def.Name = CreateUniqueName(name.Name + "-attr"); RelaxngAttribute attr = new RelaxngAttribute(); attr.NameClass = new RelaxngName( name.Name, name.Namespace); def.Patterns.Add(attr); attributes.Add(name, def); grammar.Defines.Add(def); return(def); }
// Already relaxed. private RelaxngDefine CreateGlobalElement(QName name) { RelaxngDefine def = new RelaxngDefine(); def.Name = CreateUniqueName(name.Name); RelaxngElement el = new RelaxngElement(); el.NameClass = new RelaxngName(name.Name, name.Namespace); def.Patterns.Add(el); elements.Add(name, def); grammar.Defines.Add(def); return(def); }
// validate string value agains attr and // if invalid, then relax the type. private void InferMergedAttribute(RelaxngPattern ap) { switch (ap.PatternType) { case RelaxngPatternType.Ref: string refName = ((RelaxngRef)ap).Name; RelaxngDefine def = GetDefine(refName); InferMergedAttribute(def.Patterns [0]); return; case RelaxngPatternType.Optional: InferMergedAttribute( ((RelaxngOptional)ap).Patterns [0]); return; } RelaxngAttribute attr = (RelaxngAttribute)ap; RelaxngPattern p = attr.Pattern; if (p is RelaxngText) { return; // We could do nothing anymore. } if (p is RelaxngEmpty) { if (source.Value.Length == 0) { return; // We can keep empty. } // We still could infer a choice of empty and // data, but it's being too complicated. So // here we just set text. attr.Pattern = new RelaxngText(); return; } RelaxngData data = p as RelaxngData; if (data == null) { throw Error(p, "This inference implementation only allows text, empty and data for an attribute."); } attr.Pattern = CreateSimplePattern( InferMergedType(source.Value, new QName(data.Type, data.DatatypeLibrary))); }
private void ProcessLax(RelaxngSingleContentPattern scp) { RelaxngChoice c = (RelaxngChoice)scp.Patterns [0]; foreach (RelaxngPattern p in c.Patterns) { RelaxngRef el = p as RelaxngRef; if (el == null) { RelaxngOneOrMore oom = (RelaxngOneOrMore)p; el = (RelaxngRef)oom.Patterns [0]; } if (el == null) { throw Error(c, String.Format("Target pattern contains unacceptable child pattern {0}. Only ref is allowed here.")); } if (ElementMatches(el)) { InferElement(el, false); return; } } // append a new element particle to lax term. QName qname = new QName( source.LocalName, source.NamespaceURI); RelaxngDefine def = GetGlobalElement(qname); if (def == null) { def = CreateGlobalElement(qname); // used to be CreateElement(). InferElement(def, true); } else { InferElement(def, false); } RelaxngRef nel = new RelaxngRef(); nel.Name = def.Name; c.Patterns.Add(nel); }
RelaxngGrammar DtdXsd2Rng(XmlSchema xsd, string ns) { g = new RelaxngGrammar(); g.DefaultNamespace = ns; RelaxngStart start = new RelaxngStart(); g.Starts.Add(start); RelaxngChoice choice = new RelaxngChoice(); start.Pattern = choice; // There are only elements. foreach (XmlSchemaElement el in xsd.Items) { RelaxngDefine def = DefineElement(el); g.Defines.Add(def); RelaxngRef dref = new RelaxngRef(); dref.Name = def.Name; choice.Patterns.Add(dref); } return(g); }
private void ProcessSequence(RelaxngElement ct, RelaxngGroup s, ref int position, ref bool consumed, bool isNew) { RelaxngMixed m = s.Patterns.Count > 0 ? s.Patterns [0] as RelaxngMixed : null; RelaxngPatternList pl = m != null ? m.Patterns : s.Patterns; for (int i = 0; i < position; i++) { RelaxngPattern p = pl [i]; RelaxngRef iel = p as RelaxngRef; if (iel == null) { RelaxngOneOrMore oom = p as RelaxngOneOrMore; iel = (RelaxngRef)oom.Patterns [0]; } if (ElementMatches(iel)) { // Sequence element type violation // might happen (might not, but we // cannot backtrack here). So switch // to sequence of choice* here. ProcessLax(ToSequenceOfChoice(ct, s)); return; } } if (pl.Count <= position) { QName name = new QName(source.LocalName, source.NamespaceURI); RelaxngDefine nel = GetGlobalElement(name); if (nel != null) { InferElement(nel, false); } else { nel = CreateGlobalElement(name); // used to be CreateElement(). InferElement(nel, true); } RelaxngRef re = new RelaxngRef(); re.Name = nel.Name; pl.Add(re); consumed = true; return; } RelaxngPattern c = pl [position]; RelaxngRef el = c as RelaxngRef; if (el == null) { RelaxngOneOrMore oom = c as RelaxngOneOrMore; el = (RelaxngRef)oom.Patterns [0]; } if (el == null) { throw Error(s, String.Format("Target complex type content sequence has an unacceptable type of particle {0}", s.Patterns [position])); } bool matches = ElementMatches(el); if (matches) { if (consumed && c is RelaxngRef) { RelaxngOneOrMore oom = new RelaxngOneOrMore(); oom.Patterns.Add(el); pl [position] = oom; } InferElement(el, false); source.MoveToContent(); switch (source.NodeType) { case XmlNodeType.None: if (source.NodeType == XmlNodeType.Element) { goto case XmlNodeType.Element; } else if (source.NodeType == XmlNodeType.EndElement) { goto case XmlNodeType.EndElement; } break; case XmlNodeType.Element: ProcessSequence(ct, s, ref position, ref consumed, isNew); break; case XmlNodeType.Text: case XmlNodeType.CDATA: case XmlNodeType.SignificantWhitespace: MarkAsMixed(ct); source.ReadString(); goto case XmlNodeType.None; case XmlNodeType.Whitespace: source.ReadString(); goto case XmlNodeType.None; case XmlNodeType.EndElement: return; default: source.Read(); break; } } else { if (consumed) { position++; consumed = false; ProcessSequence(ct, s, ref position, ref consumed, isNew); } else { ProcessLax(ToSequenceOfChoice(ct, s)); } } }
private void InferAttributes(RelaxngElement ct, bool isNew) { RelaxngInterleave attList = null; Hashtable table = null; do { if (source.NamespaceURI == NamespaceXmlns) { continue; } if (table == null) { attList = GetAttributes(ct); table = CollectAttrTable(attList); } QName attrName = new QName( source.LocalName, source.NamespaceURI); RelaxngPattern attr = table [attrName] as RelaxngPattern; if (attr == null) { if (attList == null) { attList = new RelaxngInterleave(); ct.Patterns.Insert(0, attList); } attList.Patterns.Add( InferNewAttribute( attrName, isNew)); } else { table.Remove(attrName); if (attrName.Namespace.Length > 0) { RelaxngDefine ga = GetGlobalAttribute(attrName); InferMergedAttribute( ga.Patterns [0]); } else { InferMergedAttribute(attr); } } } while (source.MoveToNextAttribute()); // mark all attr definitions that did not appear // as optional. if (table != null) { foreach (RelaxngPattern attr in table.Values) { if (attr is RelaxngOptional) { continue; } attList.Patterns.Remove(attr); RelaxngOptional opt = new RelaxngOptional(); opt.Patterns.Add(attr); attList.Patterns.Add(opt); } } }
private void InferElement(RelaxngRef r, bool isNew) { RelaxngDefine body = GetDefine(r.Name); InferElement(body, isNew); }