281 lines
7.0 KiB
C#
Raw Normal View History

using System;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using Commons.Xml.Relaxng;
using Commons.Xml.Relaxng.Rnc;
using BF = System.Reflection.BindingFlags;
namespace Mono.XmlTools
{
public class Dtd2Rng
{
public static int Main (string [] args)
{
if (args.Length == 0) {
Usage ();
return 1;
}
return new Dtd2Rng ().Process (args);
}
static void Usage ()
{
Console.Error.WriteLine (@"
Usage dtd2rng [options] dtdfile [ns]
options:
--help : show this message.
--compact, -c : output compact syntax.
");
}
public int Process (string [] args)
{
string file = null;
bool compact = false;
string ns = String.Empty;
foreach (string arg in args) {
if (arg == "--help") {
Usage ();
return 1;
}
if (arg == "--compact" || arg == "-c")
compact = true;
else if (file == null)
file = arg;
else if (ns != String.Empty) {
Usage ();
Console.Error.WriteLine ("Extra command line argument.");
return 1;
}
else
ns = arg;
}
XmlTextReader xtr;
if (file.EndsWith (".dtd")) {
xtr = new XmlTextReader (
"<!DOCTYPE dummy SYSTEM '" + file + "'>",
XmlNodeType.Document, null);
} else {
xtr = new XmlTextReader (file);
}
xtr.Read ();
if (xtr.NodeType == XmlNodeType.XmlDeclaration)
xtr.Read ();
XmlSchema xsd = GetXmlSchema (xtr);
RelaxngPattern rng = DtdXsd2Rng (xsd, ns);
if (compact)
rng.WriteCompact (Console.Out);
else {
XmlTextWriter w = new XmlTextWriter (Console.Out);
w.Formatting = Formatting.Indented;
rng.Write (w);
w.Close ();
}
return 0;
}
XmlSchema GetXmlSchema (XmlTextReader xtr)
{
// Hacky reflection part
object impl = xtr;
BF flag = BF.NonPublic | BF.Instance;
// In Mono NET_2_0 XmlTextReader is just a wrapper which
// does not contain DTD directly.
FieldInfo fi = typeof (XmlTextReader).GetField ("source", flag);
if (fi != null)
impl = fi.GetValue (xtr);
PropertyInfo pi = impl.GetType ().GetProperty ("DTD", flag);
object dtd = pi.GetValue (impl, null);
MethodInfo mi =
dtd.GetType ().GetMethod ("CreateXsdSchema", flag);
object o = mi.Invoke (dtd, null);
return (XmlSchema) o;
}
RelaxngGrammar g;
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;
}
RelaxngDefine DefineElement (XmlSchemaElement el)
{
RelaxngDefine def = new RelaxngDefine ();
def.Patterns.Add (CreateElement (el));
def.Name = el.Name;
return def;
}
RelaxngPattern CreateElement (XmlSchemaElement xse)
{
if (xse.RefName != XmlQualifiedName.Empty) {
RelaxngRef r = new RelaxngRef ();
r.Name = xse.RefName.Name;
// namespace means nothing here.
return r;
}
RelaxngElement re = new RelaxngElement ();
RelaxngName name = new RelaxngName ();
name.LocalName = xse.Name;
re.NameClass = name;
XmlSchemaComplexType ct = xse.SchemaType as XmlSchemaComplexType;
foreach (XmlSchemaAttribute a in ct.Attributes)
re.Patterns.Add (CreateAttribute (a));
RelaxngPattern rpart;
if (ct.Particle == null)
rpart = new RelaxngEmpty ();
else
rpart = CreatePatternFromParticle (ct.Particle);
if (ct.IsMixed) {
if (rpart.PatternType != RelaxngPatternType.Empty) {
RelaxngMixed mixed = new RelaxngMixed ();
mixed.Patterns.Add (rpart);
rpart = mixed;
} else {
rpart = new RelaxngText ();
}
}
re.Patterns.Add (rpart);
return re;
}
RelaxngPattern CreateAttribute (XmlSchemaAttribute attr)
{
RelaxngAttribute ra = new RelaxngAttribute ();
RelaxngName name = new RelaxngName ();
name.LocalName = attr.Name;
ra.NameClass = name;
ra.Pattern = attr.SchemaType != null ?
CreatePatternFromType (attr.SchemaType) :
CreatePatternFromTypeName (attr.SchemaTypeName);
RelaxngPattern ret = ra;
if (attr.Use == XmlSchemaUse.Optional) {
RelaxngOptional opt = new RelaxngOptional ();
opt.Patterns.Add (ra);
ret = opt;
}
return ret;
}
RelaxngPattern CreatePatternFromParticle (XmlSchemaParticle xsdp)
{
RelaxngSingleContentPattern rngp = null;
if (xsdp.MinOccurs == 0 && xsdp.MaxOccursString == "unbounded")
rngp = new RelaxngZeroOrMore ();
else if (xsdp.MinOccurs == 1 && xsdp.MaxOccursString == "unbounded")
rngp = new RelaxngOneOrMore ();
else if (xsdp.MinOccurs == 0)
rngp = new RelaxngOptional ();
RelaxngPattern child = CreatePatternFromParticleCore (xsdp);
if (rngp == null)
return child;
rngp.Patterns.Add (child);
return rngp;
}
RelaxngPattern CreatePatternFromParticleCore (XmlSchemaParticle xsdp)
{
XmlSchemaGroupBase gb = xsdp as XmlSchemaGroupBase;
if (xsdp is XmlSchemaAny) {
RelaxngRef r = new RelaxngRef ();
r.Name = "anyType";
return r;
}
if (gb is XmlSchemaSequence) {
RelaxngGroup grp = new RelaxngGroup ();
foreach (XmlSchemaParticle xsdc in gb.Items)
grp.Patterns.Add (CreatePatternFromParticle (xsdc));
return grp;
}
if (gb is XmlSchemaChoice) {
RelaxngChoice rc = new RelaxngChoice ();
foreach (XmlSchemaParticle xsdc in gb.Items)
rc.Patterns.Add (CreatePatternFromParticle (xsdc));
return rc;
}
return CreateElement ((XmlSchemaElement) xsdp);
}
RelaxngPattern CreatePatternFromType (XmlSchemaType type)
{
XmlSchemaSimpleType st = type as XmlSchemaSimpleType;
if (st == null)
throw new NotSupportedException ("Complex types are not supported as an attribute type.");
XmlSchemaSimpleTypeRestriction r =
st.Content as XmlSchemaSimpleTypeRestriction;
if (r == null)
throw new NotSupportedException ("Only simple type restriction is supported as an attribute type.");
RelaxngChoice c = new RelaxngChoice ();
foreach (XmlSchemaFacet f in r.Facets) {
XmlSchemaEnumerationFacet en =
f as XmlSchemaEnumerationFacet;
if (en == null)
throw new NotSupportedException ("Only enumeration facet is supported.");
RelaxngValue v = new RelaxngValue ();
v.Type = r.BaseTypeName.Name;
v.DatatypeLibrary = RemapDatatypeLibrary (
r.BaseTypeName.Namespace);
v.Value = en.Value;
c.Patterns.Add (v);
}
return c;
}
RelaxngPattern CreatePatternFromTypeName (XmlQualifiedName name)
{
if (name == XmlQualifiedName.Empty)
return new RelaxngText ();
RelaxngData data = new RelaxngData ();
data.Type = name.Name;
data.DatatypeLibrary = RemapDatatypeLibrary (
name.Namespace);
return data;
}
string RemapDatatypeLibrary (string ns)
{
return ns == XmlSchema.Namespace ?
"http://www.w3.org/2001/XMLSchema-datatypes" :
ns;
}
}
}