Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mcs / class / Commons.Xml.Relaxng / Commons.Xml.Relaxng.Rnc / RncWriter.cs
1 //\r
2 // RELAX NG Compact Syntax writer\r
3 //\r
4 // Author:\r
5 //      Atsushi Enomoto <atsushi@ximian.com>\r
6 //\r
7 // (C)2005 Novell Inc.\r
8 //\r
9 \r
10 //\r
11 // Permission is hereby granted, free of charge, to any person obtaining\r
12 // a copy of this software and associated documentation files (the\r
13 // "Software"), to deal in the Software without restriction, including\r
14 // without limitation the rights to use, copy, modify, merge, publish,\r
15 // distribute, sublicense, and/or sell copies of the Software, and to\r
16 // permit persons to whom the Software is furnished to do so, subject to\r
17 // the following conditions:\r
18 // \r
19 // The above copyright notice and this permission notice shall be\r
20 // included in all copies or substantial portions of the Software.\r
21 // \r
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
29 //\r
30 \r
31 using System;\r
32 using System.Collections;\r
33 using System.IO;\r
34 using System.Xml;\r
35 using Commons.Xml.Relaxng;\r
36 \r
37 using NSResolver = System.Xml.IXmlNamespaceResolver;\r
38 \r
39 namespace Commons.Xml.Relaxng.Rnc\r
40 {\r
41         internal class RncWriter\r
42         {\r
43                 static readonly XmlNamespaceManager defaultNamespaceManager;\r
44 \r
45                 static RncWriter ()\r
46                 {\r
47                         XmlNamespaceManager n = new XmlNamespaceManager (\r
48                                 new NameTable ());\r
49                         n.AddNamespace ("xs", "http://www.w3.org/2001/XMLSchema-datatypes");\r
50                         defaultNamespaceManager = n;\r
51                 }\r
52 \r
53                 TextWriter w;\r
54                 NSResolver nsmgr;\r
55                 NSResolver datansmgr;\r
56 \r
57                 public RncWriter (TextWriter writer)\r
58                         : this (writer, null, defaultNamespaceManager)\r
59                 {\r
60                 }\r
61 \r
62                 public RncWriter (TextWriter writer, NSResolver nsmgr)\r
63                         : this (writer, nsmgr, defaultNamespaceManager)\r
64                 {\r
65                 }\r
66 \r
67                 public RncWriter (TextWriter writer, NSResolver structureNamespaces, NSResolver dataNamespaces)\r
68                 {\r
69                         this.w = writer;\r
70                         this.nsmgr = structureNamespaces;\r
71                         this.datansmgr = dataNamespaces;\r
72                         XmlNameTable nt = GetNameTable (nsmgr, datansmgr);\r
73                         if (nsmgr == null)\r
74                                 nsmgr = new XmlNamespaceManager (nt);\r
75                         if (datansmgr == null)\r
76                                 datansmgr = new XmlNamespaceManager (nt);\r
77                 }\r
78 \r
79                 #region Utility methods\r
80 \r
81                 private XmlNameTable GetNameTable (NSResolver nss1, NSResolver nss2)\r
82                 {\r
83                         XmlNameTable nt = null;\r
84                         if (nss1 is XmlNamespaceManager)\r
85                                 nt = ((XmlNamespaceManager) nss1).NameTable;\r
86                         if (nss2 is XmlNamespaceManager)\r
87                                 nt = ((XmlNamespaceManager) nss2).NameTable;\r
88                         if (nt == null)\r
89                                 nt = new NameTable ();\r
90                         return nt;\r
91                 }\r
92 \r
93                 private bool IsKeyword (string name)\r
94                 {\r
95                         switch (name) {\r
96                         case "attribute":\r
97                         case "default":\r
98                         case "datatypes":\r
99                         case "div":\r
100                         case "element":\r
101                         case "empty":\r
102                         case "external":\r
103                         case "grammar":\r
104                         case "include":\r
105                         case "inherit":\r
106                         case "list":\r
107                         case "mixed":\r
108                         case "namespace":\r
109                         case "notAllowed":\r
110                         case "parent":\r
111                         case "start":\r
112                         case "string":\r
113                         case "text":\r
114                         case "token":\r
115                                 return true;\r
116                         default:\r
117                                 return false;\r
118                         }\r
119                 }\r
120 \r
121                 private void WriteNames (RelaxngNameClassList l, bool wrap)\r
122                 {\r
123                         switch (l.Count) {\r
124                         case 0:\r
125                                 throw new RelaxngException ("name choice must contain at least one name class.");\r
126                         case 1:\r
127                                 l [0].WriteRnc (this);\r
128                                 break;\r
129                         default:\r
130                                 if (wrap)\r
131                                         w.Write ('(');\r
132                                 l [0].WriteRnc (this);\r
133                                 for (int i = 1; i < l.Count; i++) {\r
134                                         w.Write ('|');\r
135                                         l [i].WriteRnc (this);\r
136                                 }\r
137                                 if (wrap)\r
138                                         w.Write (')');\r
139                                 break;\r
140                         }\r
141                 }\r
142 \r
143                 private void WritePatterns (RelaxngPatternList l, bool parens)\r
144                 {\r
145                         WritePatterns (l, ',', parens);\r
146                 }\r
147 \r
148                 private void WritePatterns (RelaxngPatternList l,\r
149                         char sep, bool parens)\r
150                 {\r
151                         switch (l.Count) {\r
152                         case 0:\r
153                                 w.Write ("empty");\r
154                                 break;\r
155                         case 1:\r
156                                 parens = (l [0] is RelaxngBinaryContentPattern \r
157                                         || l [0] is RelaxngData && ((RelaxngData) l [0]).Except != null);\r
158                                 if (parens)\r
159                                         w.Write ('(');\r
160                                 l [0].WriteRnc (this);\r
161                                 if (parens)\r
162                                         w.Write (')');\r
163                                 break;\r
164                         default:\r
165                                 if (parens)\r
166                                         w.Write ('(');\r
167                                 l [0].WriteRnc (this);\r
168                                 for (int i = 1; i < l.Count; i++) {\r
169                                         if (sep != ',')\r
170                                                 w.Write (' ');\r
171                                         w.Write (sep);\r
172                                         w.Write (' ');\r
173                                         l [i].WriteRnc (this);\r
174                                 }\r
175                                 if (parens)\r
176                                         w.Write (')');\r
177                                 break;\r
178                         }\r
179                 }\r
180 \r
181                 private void WriteGrammarIncludeContents (\r
182                         RelaxngGrammarContentList starts,\r
183                         RelaxngGrammarContentList defines,\r
184                         RelaxngGrammarContentList divs,\r
185                         RelaxngGrammarContentList includes)\r
186                 {\r
187                         if (includes != null) {\r
188                                 foreach (RelaxngInclude inc in includes)\r
189                                         inc.WriteRnc (this);\r
190                                 w.WriteLine ();\r
191                         }\r
192                         if (divs != null) {\r
193                                 foreach (RelaxngDiv div in divs)\r
194                                         div.WriteRnc (this);\r
195                                 w.WriteLine ();\r
196                         }\r
197                         if (starts != null) {\r
198                                 foreach (RelaxngStart s in starts)\r
199                                         s.WriteRnc (this);\r
200                                 w.WriteLine ();\r
201                         }\r
202                         if (defines != null)\r
203                                 foreach (RelaxngDefine def in defines)\r
204                                         def.WriteRnc (this);\r
205                 }\r
206 \r
207                 private void WriteQName (string name, string ns)\r
208                 {\r
209                         WriteQName (name, ns, nsmgr);\r
210                 }\r
211 \r
212                 private void WriteQName (string name, string ns, NSResolver nss)\r
213                 {\r
214                         string prefix = String.Empty;\r
215                         if (ns != null && ns != String.Empty) {\r
216                                 prefix = nss.LookupPrefix (ns);\r
217                         }\r
218                         if (prefix == null)\r
219                                 throw new RelaxngException (String.Format ("Namespace '{0}' is not mapped to a prefix in argument XmlNamespaceManager.", ns));\r
220                         if (prefix != String.Empty) {\r
221                                 w.Write (prefix);\r
222                                 w.Write (':');\r
223                         }\r
224                         if (IsKeyword (name))\r
225                                 w.Write ('\\');\r
226                         w.Write (name);\r
227                 }\r
228 \r
229                 private void WriteLiteral (string value)\r
230                 {\r
231                         w.Write ('"');\r
232                         for (int i = 0; i < value.Length; i++) {\r
233                                 switch (value [i]) {\r
234                                 case '"':\r
235                                         w.Write ("\\x{22}");\r
236                                         break;\r
237                                 case '\r':\r
238                                         w.Write ("\\x{13}");\r
239                                         break;\r
240                                 case '\n':\r
241                                         w.Write ("\\x{10}");\r
242                                         break;\r
243                                 case '\t': // It is not required, but would be better.\r
244                                         w.Write ("\\x{9}");\r
245                                         break;\r
246                                 default:\r
247                                         w.Write (value [i]);\r
248                                         break;\r
249                                 }\r
250                         }\r
251                         w.Write ('"');\r
252                 }\r
253 \r
254                 #endregion\r
255 \r
256                 public void WriteNamespaces (string defaultNamespace)\r
257                 {\r
258                         WriteNamespaces (defaultNamespace, nsmgr, false);\r
259                         WriteNamespaces (null, datansmgr, true);\r
260                 }\r
261 \r
262                 public void WriteNamespaces (string defaultNamespace, NSResolver nsmgr, bool isData)\r
263                 {\r
264                         if (defaultNamespace == null)\r
265                                 defaultNamespace = String.Empty;\r
266 \r
267                         if (defaultNamespace.Length > 0)\r
268                                 w.WriteLine ("default namespace = {0}",\r
269                                         defaultNamespace);\r
270 \r
271                         if (nsmgr != null) {\r
272                                 foreach (string s in nsmgr.GetNamespacesInScope (\r
273                                         XmlNamespaceScope.All).Keys) {\r
274                                         switch (s) {\r
275                                         case "xml":\r
276                                         case "xmlns":\r
277                                                 continue;\r
278                                         case "":\r
279                                                 if (defaultNamespace.Length > 0)\r
280                                                         w.WriteLine ("default namespace = '{0}'",\r
281                                                                 nsmgr.LookupNamespace (s).Replace ('\'', '\"'));\r
282                                                 break;\r
283                                         default:\r
284                                                 w.WriteLine ("{2} {0} = '{1}'",\r
285                                                         s,\r
286                                                         nsmgr.LookupNamespace (s).Replace ('\'', '\"'),\r
287                                                         isData ? "datatypes" : "namespace");\r
288                                                 break;\r
289                                         }\r
290                                 }\r
291                         }\r
292                         w.WriteLine ();\r
293                 }\r
294 \r
295                 #region Elements\r
296                 // Note that it might not be used directly when a grammar\r
297                 // contains more than one "start" (compact syntax does not\r
298                 // support "combine" attribute).\r
299                 public void WriteStart (RelaxngStart start)\r
300                 {\r
301                         w.Write ("start");\r
302                         if (start.Combine == null)\r
303                                 w.Write (" = ");\r
304                         else\r
305                                 w.Write (start.Combine.Trim () == "interleave" ?\r
306                                         " &= " : " |= ");\r
307                         start.Pattern.WriteRnc (this);\r
308                         w.WriteLine ();\r
309                 }\r
310 \r
311                 // Note that it might not be used directly when a grammar\r
312                 // contains more than one "define" for an identical name\r
313                 // (compact syntax does not support "combine" attribute).\r
314                 public void WriteDefine (RelaxngDefine define)\r
315                 {\r
316                         if (IsKeyword (define.Name))\r
317                                 w.Write ('\\');\r
318                         w.Write (define.Name);\r
319                         if (define.Combine == null)\r
320                                 w.Write (" = ");\r
321                         else\r
322                                 w.Write (define.Combine.Trim () == "interleave" ?\r
323                                         " &= " : " |= ");\r
324                         if (define.Patterns.Count == 0)\r
325                                 w.Write ("empty");\r
326                         else {\r
327                                 define.Patterns [0].WriteRnc (this);\r
328                                 for (int i = 1; i < define.Patterns.Count; i++) {\r
329                                         w.Write (",");\r
330                                         define.Patterns [i].WriteRnc (this);\r
331                                 }\r
332                         }\r
333                         w.WriteLine ();\r
334                         w.WriteLine ();\r
335                 }\r
336 \r
337                 public void WriteInclude (RelaxngInclude include)\r
338                 {\r
339                         w.Write ("include ");\r
340                         w.Write (include.Href);\r
341 \r
342                         // FIXME: optInherit?\r
343 \r
344                         if (include.Starts.Count > 0 ||\r
345                                 include.Defines.Count > 0 ||\r
346                                 include.Divs.Count > 0) {\r
347                                 w.Write ('(');\r
348                                 WriteGrammarIncludeContents (include.Starts,\r
349                                         include.Defines, include.Divs, null);\r
350                                 w.Write (')');\r
351                         }\r
352                         w.WriteLine ();\r
353                 }\r
354 \r
355                 public void WriteDiv (RelaxngDiv div)\r
356                 {\r
357                         w.Write ("div { ");\r
358                         WriteGrammarIncludeContents (div.Starts,\r
359                                 div.Defines, div.Divs, div.Includes);\r
360                         w.WriteLine ('}');\r
361                 }\r
362 \r
363                 public void WriteNotAllowed (RelaxngNotAllowed na)\r
364                 {\r
365                         w.Write ("notAllowed ");\r
366                 }\r
367 \r
368                 public void WriteEmpty (RelaxngEmpty empty)\r
369                 {\r
370                         w.Write ("empty ");\r
371                 }\r
372 \r
373                 public void WriteText (RelaxngText text)\r
374                 {\r
375                         w.Write ("text ");\r
376                 }\r
377 \r
378                 public void WriteData (RelaxngData data)\r
379                 {\r
380                         WriteQName (data.Type, data.DatatypeLibrary, datansmgr);\r
381                         if (data.ParamList.Count > 0) {\r
382                                 w.Write (" { ");\r
383                                 foreach (RelaxngParam p in data.ParamList)\r
384                                         p.WriteRnc (this);\r
385                                 w.Write (" }");\r
386                         }\r
387                         if (data.Except != null)\r
388                                 data.Except.WriteRnc (this);\r
389                 }\r
390 \r
391                 public void WriteValue (RelaxngValue v)\r
392                 {\r
393                         WriteQName (v.Type, v.DatatypeLibrary, datansmgr);\r
394                         w.Write (' ');\r
395                         WriteLiteral (v.Value);\r
396                 }\r
397 \r
398                 public void WriteList (RelaxngList p)\r
399                 {\r
400                         w.Write ("list {");\r
401                         WritePatterns (p.Patterns, false);\r
402                         w.Write ("}");\r
403                 }\r
404 \r
405                 public void WriteMixed (RelaxngMixed p)\r
406                 {\r
407                         w.Write ("mixed {");\r
408                         WritePatterns (p.Patterns, false);\r
409                         w.Write ("}");\r
410                 }\r
411 \r
412                 public void WriteElement (RelaxngElement element)\r
413                 {\r
414                         w.Write ("element ");\r
415                         element.NameClass.WriteRnc (this);\r
416                         w.Write (" {");\r
417                         WritePatterns (element.Patterns, false);\r
418                         w.Write ("}");\r
419                 }\r
420 \r
421                 public void WriteAttribute (RelaxngAttribute attribute)\r
422                 {\r
423                         w.Write ("attribute ");\r
424                         attribute.NameClass.WriteRnc (this);\r
425                         w.Write (" {");\r
426                         if (attribute.Pattern == null)\r
427                                 w.Write ("empty");\r
428                         else\r
429                                 attribute.Pattern.WriteRnc (this);\r
430                         w.Write (" }");\r
431                 }\r
432 \r
433                 public void WriteRef (RelaxngRef r)\r
434                 {\r
435                         if (IsKeyword (r.Name))\r
436                                 w.Write ('\\');\r
437                         w.Write (r.Name);\r
438                 }\r
439 \r
440                 public void WriteParentRef (RelaxngParentRef r)\r
441                 {\r
442                         w.Write ("parent ");\r
443                         if (IsKeyword (r.Name))\r
444                                 w.Write ('\\');\r
445                         w.Write (r.Name);\r
446                         w.Write (' ');\r
447                 }\r
448 \r
449                 public void WriteExternalRef (RelaxngExternalRef r)\r
450                 {\r
451                         w.Write ("external ");\r
452                         w.Write (r.Href);\r
453                         // FIXME: optInherit?\r
454                         w.Write (' ');\r
455                 }\r
456 \r
457                 public void WriteOneOrMore (RelaxngOneOrMore p)\r
458                 {\r
459                         WritePatterns (p.Patterns, true);\r
460                         w.Write ('+');\r
461                 }\r
462 \r
463                 public void WriteZeroOrMore (RelaxngZeroOrMore p)\r
464                 {\r
465                         WritePatterns (p.Patterns, true);\r
466                         w.Write ('*');\r
467                 }\r
468 \r
469                 public void WriteOptional (RelaxngOptional p)\r
470                 {\r
471                         WritePatterns (p.Patterns, true);\r
472                         w.Write ('?');\r
473                 }\r
474 \r
475                 public void WriteChoice (RelaxngChoice p)\r
476                 {\r
477                         WritePatterns (p.Patterns, '|', false);\r
478                 }\r
479 \r
480                 public void WriteGroup (RelaxngGroup p)\r
481                 {\r
482                         WritePatterns (p.Patterns, ',', false);\r
483                 }\r
484 \r
485                 public void WriteInterleave (RelaxngInterleave p)\r
486                 {\r
487                         WritePatterns (p.Patterns, '&', false);\r
488                 }\r
489 \r
490                 public void WriteParam (RelaxngParam p)\r
491                 {\r
492                         if (IsKeyword (p.Name))\r
493                                 w.Write ('\\');\r
494                         w.Write (p.Name);\r
495                         w.Write (" = ");\r
496                         WriteLiteral (p.Value);\r
497                 }\r
498 \r
499                 public void WriteDataExcept (RelaxngExcept e)\r
500                 {\r
501                         w.Write (" - ");\r
502                         WritePatterns (e.Patterns, true);\r
503                 }\r
504 \r
505                 public void WriteGrammar (RelaxngGrammar g)\r
506                 {\r
507                         w.WriteLine ("grammar {");\r
508                         WriteGrammarIncludeContents (g.Starts,\r
509                                 g.Defines, g.Divs, g.Includes);\r
510                         w.WriteLine ('}');\r
511                 }\r
512 \r
513                 public void WriteAnyName (RelaxngAnyName n)\r
514                 {\r
515                         w.Write ('*');\r
516                         if (n.Except != null)\r
517                                 n.Except.WriteRnc (this);\r
518                 }\r
519 \r
520                 public void WriteNsName (RelaxngNsName n)\r
521                 {\r
522                         WriteQName ("*", n.Namespace);\r
523                         if (n.Except != null)\r
524                                 n.Except.WriteRnc (this);\r
525                 }\r
526 \r
527                 public void WriteName (RelaxngName n)\r
528                 {\r
529                         WriteQName (n.LocalName, n.Namespace);\r
530                 }\r
531 \r
532                 public void WriteNameChoice (RelaxngNameChoice c)\r
533                 {\r
534                         WriteNames (c.Children, false);\r
535                 }\r
536 \r
537                 public void WriteNameExcept (RelaxngExceptNameClass e)\r
538                 {\r
539                         w.Write (" - ");\r
540                         WriteNames (e.Names, true);\r
541                 }\r
542                 #endregion\r
543         }\r
544 }\r