* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / tools / dtd2rng / dtd2rng.cs
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;
7
8 using BF = System.Reflection.BindingFlags;
9
10 namespace Mono.XmlTools
11 {
12         public class Dtd2Rng
13         {
14                 public static int Main (string [] args)
15                 {
16                         if (args.Length == 0) {
17                                 Usage ();
18                                 return 1;
19                         }
20
21                         return new Dtd2Rng ().Process (args);
22                 }
23
24                 static void Usage ()
25                 {
26                         Console.Error.WriteLine (@"
27 Usage dtd2rng [options] dtdfile [ns]
28
29 options:
30         --help : show this message.
31         --compact, -c : output compact syntax.
32 ");
33                 }
34
35                 public int Process (string [] args)
36                 {
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;
44                                 }
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;
53                                 }
54                                 else
55                                         ns = arg;
56                         }
57
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);
65                         }
66                         xtr.Read ();
67                         if (xtr.NodeType == XmlNodeType.XmlDeclaration)
68                                 xtr.Read ();
69
70                         XmlSchema xsd = GetXmlSchema (xtr);
71
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 ();
80                         }
81                         return 0;
82                 }
83
84                 XmlSchema GetXmlSchema (XmlTextReader xtr)
85                 {
86                         // Hacky reflection part
87                         object impl = xtr;
88                         BF flag = BF.NonPublic | BF.Instance;
89
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);
95
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;
102                 }
103
104                 RelaxngGrammar g;
105
106                 RelaxngGrammar DtdXsd2Rng (XmlSchema xsd, string ns)
107                 {
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;
114
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);
122                         }
123
124                         return g;
125                 }
126
127                 RelaxngDefine DefineElement (XmlSchemaElement el)
128                 {
129                         RelaxngDefine def = new RelaxngDefine ();
130                         def.Patterns.Add (CreateElement (el));
131                         def.Name = el.Name;
132
133                         return def;
134                 }
135
136                 RelaxngPattern CreateElement (XmlSchemaElement xse)
137                 {
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;
143                         }
144
145                         RelaxngElement re = new RelaxngElement ();
146                         RelaxngName name = new RelaxngName ();
147                         name.LocalName = xse.Name;
148                         re.NameClass = name;
149
150                         XmlSchemaComplexType ct = xse.SchemaType as XmlSchemaComplexType;
151
152                         foreach (XmlSchemaAttribute a in ct.Attributes)
153                                 re.Patterns.Add (CreateAttribute (a));
154
155                         RelaxngPattern rpart;
156                         if (ct.Particle == null)
157                                 rpart = new RelaxngEmpty ();
158                         else
159                                 rpart = CreatePatternFromParticle (ct.Particle);
160
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 ();
168                                 }
169                         }
170
171                         re.Patterns.Add (rpart);
172
173                         return re;
174                 }
175
176                 RelaxngPattern CreateAttribute (XmlSchemaAttribute attr)
177                 {
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);
185
186                         RelaxngPattern ret = ra;
187
188                         if (attr.Use == XmlSchemaUse.Optional) {
189                                 RelaxngOptional opt = new RelaxngOptional ();
190                                 opt.Patterns.Add (ra);
191                                 ret = opt;
192                         }
193                         return ret;
194                 }
195
196                 RelaxngPattern CreatePatternFromParticle (XmlSchemaParticle xsdp)
197                 {
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 ();
205
206                         RelaxngPattern child = CreatePatternFromParticleCore (xsdp);
207                         if (rngp == null)
208                                 return child;
209                         rngp.Patterns.Add (child);
210                         return rngp;
211                 }
212
213                 RelaxngPattern CreatePatternFromParticleCore (XmlSchemaParticle xsdp)
214                 {
215                         XmlSchemaGroupBase gb = xsdp as XmlSchemaGroupBase;
216                         if (xsdp is XmlSchemaAny) {
217                                 RelaxngRef r = new RelaxngRef ();
218                                 r.Name = "anyType";
219                                 return r;
220                         }
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;
226                         }
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;
232                         }
233                         return CreateElement ((XmlSchemaElement) xsdp);
234                 }
235
236                 RelaxngPattern CreatePatternFromType (XmlSchemaType type)
237                 {
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.");
245
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);
258                         }
259                         return c;
260                 }
261
262                 RelaxngPattern CreatePatternFromTypeName (XmlQualifiedName name)
263                 {
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;
271                 }
272
273                 string RemapDatatypeLibrary (string ns)
274                 {
275                         return ns == XmlSchema.Namespace ?
276                                 "http://www.w3.org/2001/XMLSchema-datatypes" :
277                                 ns;
278                 }
279         }
280 }