2010-06-04 Jb Evain <jbevain@novell.com>
[mcs.git] / tools / dtd2rng / dtd2rng.cs
blob38b9987a98aedecc6f410b7a0f8f8f0ea6176986
1 using System;
2 using System.Reflection;
3 using System.Xml;
4 using System.Xml.Schema;
5 using Commons.Xml.Relaxng;
6 using Commons.Xml.Relaxng.Rnc;
8 using BF = System.Reflection.BindingFlags;
10 namespace Mono.XmlTools
12 public class Dtd2Rng
14 public static int Main (string [] args)
16 if (args.Length == 0) {
17 Usage ();
18 return 1;
21 return new Dtd2Rng ().Process (args);
24 static void Usage ()
26 Console.Error.WriteLine (@"
27 Usage dtd2rng [options] dtdfile [ns]
29 options:
30 --help : show this message.
31 --compact, -c : output compact syntax.
32 ");
35 public int Process (string [] args)
37 string file = null;
38 bool compact = false;
39 string ns = String.Empty;
40 foreach (string arg in args) {
41 if (arg == "--help") {
42 Usage ();
43 return 1;
45 if (arg == "--compact" || arg == "-c")
46 compact = true;
47 else if (file == null)
48 file = arg;
49 else if (ns != String.Empty) {
50 Usage ();
51 Console.Error.WriteLine ("Extra command line argument.");
52 return 1;
54 else
55 ns = arg;
58 XmlTextReader xtr;
59 if (file.EndsWith (".dtd")) {
60 xtr = new XmlTextReader (
61 "<!DOCTYPE dummy SYSTEM '" + file + "'>",
62 XmlNodeType.Document, null);
63 } else {
64 xtr = new XmlTextReader (file);
66 xtr.Read ();
67 if (xtr.NodeType == XmlNodeType.XmlDeclaration)
68 xtr.Read ();
70 XmlSchema xsd = GetXmlSchema (xtr);
72 RelaxngPattern rng = DtdXsd2Rng (xsd, ns);
73 if (compact)
74 rng.WriteCompact (Console.Out);
75 else {
76 XmlTextWriter w = new XmlTextWriter (Console.Out);
77 w.Formatting = Formatting.Indented;
78 rng.Write (w);
79 w.Close ();
81 return 0;
84 XmlSchema GetXmlSchema (XmlTextReader xtr)
86 // Hacky reflection part
87 object impl = xtr;
88 BF flag = BF.NonPublic | BF.Instance;
90 // In Mono NET_2_0 XmlTextReader is just a wrapper which
91 // does not contain DTD directly.
92 FieldInfo fi = typeof (XmlTextReader).GetField ("source", flag);
93 if (fi != null)
94 impl = fi.GetValue (xtr);
96 PropertyInfo pi = impl.GetType ().GetProperty ("DTD", flag);
97 object dtd = pi.GetValue (impl, null);
98 MethodInfo mi =
99 dtd.GetType ().GetMethod ("CreateXsdSchema", flag);
100 object o = mi.Invoke (dtd, null);
101 return (XmlSchema) o;
104 RelaxngGrammar g;
106 RelaxngGrammar DtdXsd2Rng (XmlSchema xsd, string ns)
108 g = new RelaxngGrammar ();
109 g.DefaultNamespace = ns;
110 RelaxngStart start = new RelaxngStart ();
111 g.Starts.Add (start);
112 RelaxngChoice choice = new RelaxngChoice ();
113 start.Pattern = choice;
115 // There are only elements.
116 foreach (XmlSchemaElement el in xsd.Items) {
117 RelaxngDefine def = DefineElement (el);
118 g.Defines.Add (def);
119 RelaxngRef dref = new RelaxngRef ();
120 dref.Name = def.Name;
121 choice.Patterns.Add (dref);
124 return g;
127 RelaxngDefine DefineElement (XmlSchemaElement el)
129 RelaxngDefine def = new RelaxngDefine ();
130 def.Patterns.Add (CreateElement (el));
131 def.Name = el.Name;
133 return def;
136 RelaxngPattern CreateElement (XmlSchemaElement xse)
138 if (xse.RefName != XmlQualifiedName.Empty) {
139 RelaxngRef r = new RelaxngRef ();
140 r.Name = xse.RefName.Name;
141 // namespace means nothing here.
142 return r;
145 RelaxngElement re = new RelaxngElement ();
146 RelaxngName name = new RelaxngName ();
147 name.LocalName = xse.Name;
148 re.NameClass = name;
150 XmlSchemaComplexType ct = xse.SchemaType as XmlSchemaComplexType;
152 foreach (XmlSchemaAttribute a in ct.Attributes)
153 re.Patterns.Add (CreateAttribute (a));
155 RelaxngPattern rpart;
156 if (ct.Particle == null)
157 rpart = new RelaxngEmpty ();
158 else
159 rpart = CreatePatternFromParticle (ct.Particle);
161 if (ct.IsMixed) {
162 if (rpart.PatternType != RelaxngPatternType.Empty) {
163 RelaxngMixed mixed = new RelaxngMixed ();
164 mixed.Patterns.Add (rpart);
165 rpart = mixed;
166 } else {
167 rpart = new RelaxngText ();
171 re.Patterns.Add (rpart);
173 return re;
176 RelaxngPattern CreateAttribute (XmlSchemaAttribute attr)
178 RelaxngAttribute ra = new RelaxngAttribute ();
179 RelaxngName name = new RelaxngName ();
180 name.LocalName = attr.Name;
181 ra.NameClass = name;
182 ra.Pattern = attr.SchemaType != null ?
183 CreatePatternFromType (attr.SchemaType) :
184 CreatePatternFromTypeName (attr.SchemaTypeName);
186 RelaxngPattern ret = ra;
188 if (attr.Use == XmlSchemaUse.Optional) {
189 RelaxngOptional opt = new RelaxngOptional ();
190 opt.Patterns.Add (ra);
191 ret = opt;
193 return ret;
196 RelaxngPattern CreatePatternFromParticle (XmlSchemaParticle xsdp)
198 RelaxngSingleContentPattern rngp = null;
199 if (xsdp.MinOccurs == 0 && xsdp.MaxOccursString == "unbounded")
200 rngp = new RelaxngZeroOrMore ();
201 else if (xsdp.MinOccurs == 1 && xsdp.MaxOccursString == "unbounded")
202 rngp = new RelaxngOneOrMore ();
203 else if (xsdp.MinOccurs == 0)
204 rngp = new RelaxngOptional ();
206 RelaxngPattern child = CreatePatternFromParticleCore (xsdp);
207 if (rngp == null)
208 return child;
209 rngp.Patterns.Add (child);
210 return rngp;
213 RelaxngPattern CreatePatternFromParticleCore (XmlSchemaParticle xsdp)
215 XmlSchemaGroupBase gb = xsdp as XmlSchemaGroupBase;
216 if (xsdp is XmlSchemaAny) {
217 RelaxngRef r = new RelaxngRef ();
218 r.Name = "anyType";
219 return r;
221 if (gb is XmlSchemaSequence) {
222 RelaxngGroup grp = new RelaxngGroup ();
223 foreach (XmlSchemaParticle xsdc in gb.Items)
224 grp.Patterns.Add (CreatePatternFromParticle (xsdc));
225 return grp;
227 if (gb is XmlSchemaChoice) {
228 RelaxngChoice rc = new RelaxngChoice ();
229 foreach (XmlSchemaParticle xsdc in gb.Items)
230 rc.Patterns.Add (CreatePatternFromParticle (xsdc));
231 return rc;
233 return CreateElement ((XmlSchemaElement) xsdp);
236 RelaxngPattern CreatePatternFromType (XmlSchemaType type)
238 XmlSchemaSimpleType st = type as XmlSchemaSimpleType;
239 if (st == null)
240 throw new NotSupportedException ("Complex types are not supported as an attribute type.");
241 XmlSchemaSimpleTypeRestriction r =
242 st.Content as XmlSchemaSimpleTypeRestriction;
243 if (r == null)
244 throw new NotSupportedException ("Only simple type restriction is supported as an attribute type.");
246 RelaxngChoice c = new RelaxngChoice ();
247 foreach (XmlSchemaFacet f in r.Facets) {
248 XmlSchemaEnumerationFacet en =
249 f as XmlSchemaEnumerationFacet;
250 if (en == null)
251 throw new NotSupportedException ("Only enumeration facet is supported.");
252 RelaxngValue v = new RelaxngValue ();
253 v.Type = r.BaseTypeName.Name;
254 v.DatatypeLibrary = RemapDatatypeLibrary (
255 r.BaseTypeName.Namespace);
256 v.Value = en.Value;
257 c.Patterns.Add (v);
259 return c;
262 RelaxngPattern CreatePatternFromTypeName (XmlQualifiedName name)
264 if (name == XmlQualifiedName.Empty)
265 return new RelaxngText ();
266 RelaxngData data = new RelaxngData ();
267 data.Type = name.Name;
268 data.DatatypeLibrary = RemapDatatypeLibrary (
269 name.Namespace);
270 return data;
273 string RemapDatatypeLibrary (string ns)
275 return ns == XmlSchema.Namespace ?
276 "http://www.w3.org/2001/XMLSchema-datatypes" :