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