545 lines
13 KiB
C#
Raw Normal View History

//
// RELAX NG Compact Syntax writer
//
// Author:
// Atsushi Enomoto <atsushi@ximian.com>
//
// (C)2005 Novell Inc.
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.IO;
using System.Xml;
using Commons.Xml.Relaxng;
using NSResolver = System.Xml.IXmlNamespaceResolver;
namespace Commons.Xml.Relaxng.Rnc
{
internal class RncWriter
{
static readonly XmlNamespaceManager defaultNamespaceManager;
static RncWriter ()
{
XmlNamespaceManager n = new XmlNamespaceManager (
new NameTable ());
n.AddNamespace ("xs", "http://www.w3.org/2001/XMLSchema-datatypes");
defaultNamespaceManager = n;
}
TextWriter w;
NSResolver nsmgr;
NSResolver datansmgr;
public RncWriter (TextWriter writer)
: this (writer, null, defaultNamespaceManager)
{
}
public RncWriter (TextWriter writer, NSResolver nsmgr)
: this (writer, nsmgr, defaultNamespaceManager)
{
}
public RncWriter (TextWriter writer, NSResolver structureNamespaces, NSResolver dataNamespaces)
{
this.w = writer;
this.nsmgr = structureNamespaces;
this.datansmgr = dataNamespaces;
XmlNameTable nt = GetNameTable (nsmgr, datansmgr);
if (nsmgr == null)
nsmgr = new XmlNamespaceManager (nt);
if (datansmgr == null)
datansmgr = new XmlNamespaceManager (nt);
}
#region Utility methods
private XmlNameTable GetNameTable (NSResolver nss1, NSResolver nss2)
{
XmlNameTable nt = null;
if (nss1 is XmlNamespaceManager)
nt = ((XmlNamespaceManager) nss1).NameTable;
if (nss2 is XmlNamespaceManager)
nt = ((XmlNamespaceManager) nss2).NameTable;
if (nt == null)
nt = new NameTable ();
return nt;
}
private bool IsKeyword (string name)
{
switch (name) {
case "attribute":
case "default":
case "datatypes":
case "div":
case "element":
case "empty":
case "external":
case "grammar":
case "include":
case "inherit":
case "list":
case "mixed":
case "namespace":
case "notAllowed":
case "parent":
case "start":
case "string":
case "text":
case "token":
return true;
default:
return false;
}
}
private void WriteNames (RelaxngNameClassList l, bool wrap)
{
switch (l.Count) {
case 0:
throw new RelaxngException ("name choice must contain at least one name class.");
case 1:
l [0].WriteRnc (this);
break;
default:
if (wrap)
w.Write ('(');
l [0].WriteRnc (this);
for (int i = 1; i < l.Count; i++) {
w.Write ('|');
l [i].WriteRnc (this);
}
if (wrap)
w.Write (')');
break;
}
}
private void WritePatterns (RelaxngPatternList l, bool parens)
{
WritePatterns (l, ',', parens);
}
private void WritePatterns (RelaxngPatternList l,
char sep, bool parens)
{
switch (l.Count) {
case 0:
w.Write ("empty");
break;
case 1:
parens = (l [0] is RelaxngBinaryContentPattern
|| l [0] is RelaxngData && ((RelaxngData) l [0]).Except != null);
if (parens)
w.Write ('(');
l [0].WriteRnc (this);
if (parens)
w.Write (')');
break;
default:
if (parens)
w.Write ('(');
l [0].WriteRnc (this);
for (int i = 1; i < l.Count; i++) {
if (sep != ',')
w.Write (' ');
w.Write (sep);
w.Write (' ');
l [i].WriteRnc (this);
}
if (parens)
w.Write (')');
break;
}
}
private void WriteGrammarIncludeContents (
RelaxngGrammarContentList starts,
RelaxngGrammarContentList defines,
RelaxngGrammarContentList divs,
RelaxngGrammarContentList includes)
{
if (includes != null) {
foreach (RelaxngInclude inc in includes)
inc.WriteRnc (this);
w.WriteLine ();
}
if (divs != null) {
foreach (RelaxngDiv div in divs)
div.WriteRnc (this);
w.WriteLine ();
}
if (starts != null) {
foreach (RelaxngStart s in starts)
s.WriteRnc (this);
w.WriteLine ();
}
if (defines != null)
foreach (RelaxngDefine def in defines)
def.WriteRnc (this);
}
private void WriteQName (string name, string ns)
{
WriteQName (name, ns, nsmgr);
}
private void WriteQName (string name, string ns, NSResolver nss)
{
string prefix = String.Empty;
if (ns != null && ns != String.Empty) {
prefix = nss.LookupPrefix (ns);
}
if (prefix == null)
throw new RelaxngException (String.Format ("Namespace '{0}' is not mapped to a prefix in argument XmlNamespaceManager.", ns));
if (prefix != String.Empty) {
w.Write (prefix);
w.Write (':');
}
if (IsKeyword (name))
w.Write ('\\');
w.Write (name);
}
private void WriteLiteral (string value)
{
w.Write ('"');
for (int i = 0; i < value.Length; i++) {
switch (value [i]) {
case '"':
w.Write ("\\x{22}");
break;
case '\r':
w.Write ("\\x{13}");
break;
case '\n':
w.Write ("\\x{10}");
break;
case '\t': // It is not required, but would be better.
w.Write ("\\x{9}");
break;
default:
w.Write (value [i]);
break;
}
}
w.Write ('"');
}
#endregion
public void WriteNamespaces (string defaultNamespace)
{
WriteNamespaces (defaultNamespace, nsmgr, false);
WriteNamespaces (null, datansmgr, true);
}
public void WriteNamespaces (string defaultNamespace, NSResolver nsmgr, bool isData)
{
if (defaultNamespace == null)
defaultNamespace = String.Empty;
if (defaultNamespace.Length > 0)
w.WriteLine ("default namespace = {0}",
defaultNamespace);
if (nsmgr != null) {
foreach (string s in nsmgr.GetNamespacesInScope (
XmlNamespaceScope.All).Keys) {
switch (s) {
case "xml":
case "xmlns":
continue;
case "":
if (defaultNamespace.Length > 0)
w.WriteLine ("default namespace = '{0}'",
nsmgr.LookupNamespace (s).Replace ('\'', '\"'));
break;
default:
w.WriteLine ("{2} {0} = '{1}'",
s,
nsmgr.LookupNamespace (s).Replace ('\'', '\"'),
isData ? "datatypes" : "namespace");
break;
}
}
}
w.WriteLine ();
}
#region Elements
// Note that it might not be used directly when a grammar
// contains more than one "start" (compact syntax does not
// support "combine" attribute).
public void WriteStart (RelaxngStart start)
{
w.Write ("start");
if (start.Combine == null)
w.Write (" = ");
else
w.Write (start.Combine.Trim () == "interleave" ?
" &= " : " |= ");
start.Pattern.WriteRnc (this);
w.WriteLine ();
}
// 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 ();
}
public void WriteInclude (RelaxngInclude include)
{
w.Write ("include ");
w.Write (include.Href);
// FIXME: optInherit?
if (include.Starts.Count > 0 ||
include.Defines.Count > 0 ||
include.Divs.Count > 0) {
w.Write ('(');
WriteGrammarIncludeContents (include.Starts,
include.Defines, include.Divs, null);
w.Write (')');
}
w.WriteLine ();
}
public void WriteDiv (RelaxngDiv div)
{
w.Write ("div { ");
WriteGrammarIncludeContents (div.Starts,
div.Defines, div.Divs, div.Includes);
w.WriteLine ('}');
}
public void WriteNotAllowed (RelaxngNotAllowed na)
{
w.Write ("notAllowed ");
}
public void WriteEmpty (RelaxngEmpty empty)
{
w.Write ("empty ");
}
public void WriteText (RelaxngText text)
{
w.Write ("text ");
}
public void WriteData (RelaxngData data)
{
WriteQName (data.Type, data.DatatypeLibrary, datansmgr);
if (data.ParamList.Count > 0) {
w.Write (" { ");
foreach (RelaxngParam p in data.ParamList)
p.WriteRnc (this);
w.Write (" }");
}
if (data.Except != null)
data.Except.WriteRnc (this);
}
public void WriteValue (RelaxngValue v)
{
WriteQName (v.Type, v.DatatypeLibrary, datansmgr);
w.Write (' ');
WriteLiteral (v.Value);
}
public void WriteList (RelaxngList p)
{
w.Write ("list {");
WritePatterns (p.Patterns, false);
w.Write ("}");
}
public void WriteMixed (RelaxngMixed p)
{
w.Write ("mixed {");
WritePatterns (p.Patterns, false);
w.Write ("}");
}
public void WriteElement (RelaxngElement element)
{
w.Write ("element ");
element.NameClass.WriteRnc (this);
w.Write (" {");
WritePatterns (element.Patterns, false);
w.Write ("}");
}
public void WriteAttribute (RelaxngAttribute attribute)
{
w.Write ("attribute ");
attribute.NameClass.WriteRnc (this);
w.Write (" {");
if (attribute.Pattern == null)
w.Write ("empty");
else
attribute.Pattern.WriteRnc (this);
w.Write (" }");
}
public void WriteRef (RelaxngRef r)
{
if (IsKeyword (r.Name))
w.Write ('\\');
w.Write (r.Name);
}
public void WriteParentRef (RelaxngParentRef r)
{
w.Write ("parent ");
if (IsKeyword (r.Name))
w.Write ('\\');
w.Write (r.Name);
w.Write (' ');
}
public void WriteExternalRef (RelaxngExternalRef r)
{
w.Write ("external ");
w.Write (r.Href);
// FIXME: optInherit?
w.Write (' ');
}
public void WriteOneOrMore (RelaxngOneOrMore p)
{
WritePatterns (p.Patterns, true);
w.Write ('+');
}
public void WriteZeroOrMore (RelaxngZeroOrMore p)
{
WritePatterns (p.Patterns, true);
w.Write ('*');
}
public void WriteOptional (RelaxngOptional p)
{
WritePatterns (p.Patterns, true);
w.Write ('?');
}
public void WriteChoice (RelaxngChoice p)
{
WritePatterns (p.Patterns, '|', false);
}
public void WriteGroup (RelaxngGroup p)
{
WritePatterns (p.Patterns, ',', false);
}
public void WriteInterleave (RelaxngInterleave p)
{
WritePatterns (p.Patterns, '&', false);
}
public void WriteParam (RelaxngParam p)
{
if (IsKeyword (p.Name))
w.Write ('\\');
w.Write (p.Name);
w.Write (" = ");
WriteLiteral (p.Value);
}
public void WriteDataExcept (RelaxngExcept e)
{
w.Write (" - ");
WritePatterns (e.Patterns, true);
}
public void WriteGrammar (RelaxngGrammar g)
{
w.WriteLine ("grammar {");
WriteGrammarIncludeContents (g.Starts,
g.Defines, g.Divs, g.Includes);
w.WriteLine ('}');
}
public void WriteAnyName (RelaxngAnyName n)
{
w.Write ('*');
if (n.Except != null)
n.Except.WriteRnc (this);
}
public void WriteNsName (RelaxngNsName n)
{
WriteQName ("*", n.Namespace);
if (n.Except != null)
n.Except.WriteRnc (this);
}
public void WriteName (RelaxngName n)
{
WriteQName (n.LocalName, n.Namespace);
}
public void WriteNameChoice (RelaxngNameChoice c)
{
WriteNames (c.Children, false);
}
public void WriteNameExcept (RelaxngExceptNameClass e)
{
w.Write (" - ");
WriteNames (e.Names, true);
}
#endregion
}
}