Update mcs/class/Commons.Xml.Relaxng/Commons.Xml.Relaxng/RelaxngPattern.cs
[mono.git] / mcs / class / Commons.Xml.Relaxng / Commons.Xml.Relaxng / XsdDatatypeProvider.cs
1 //
2 // XsdDatatypeProvider.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (c) 2004 Novell Inc.
8 // All rights reserved
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 using System;
32 using System.Collections;
33 using System.Text;
34 using System.Xml;
35 using System.Xml.Schema;
36
37 using XSchema = System.Xml.Schema.XmlSchema;
38
39 namespace Commons.Xml.Relaxng.XmlSchema
40 {
41         public class XsdDatatypeProvider : RelaxngDatatypeProvider
42         {
43                 static XsdDatatypeProvider instance = new XsdDatatypeProvider ();
44                 static Hashtable table;
45                 static XsdQNameWrapper qnameType = new XsdQNameWrapper ();
46
47                 private XsdDatatypeProvider ()
48                 {
49                         if (table != null)
50                                 return;
51
52                         table = new Hashtable ();
53
54                         // TODO: fill all type names.
55                         string [] names = new string [] {
56                                 "anySimpleType",
57                                 "string",
58                                 "normalizedString",
59                                 "token",
60                                 "language",
61                                 "NMTOKEN", "NMTOKENS",
62                                 "Name", "NCName",
63                                 "ID", "IDREF", "IDREFS",
64                                 "ENTITY", "ENTITIES", //"NOTATION",
65                                 "decimal", 
66                                 "integer", "long", "int", "short", "byte",
67                                 "nonPositiveInteger", "negativeInteger",
68                                 "nonNegativeInteger", "positiveInteger",
69                                 "unsignedLong", "unsignedInt", 
70                                 "unsignedShort", "unsignedByte",
71                                 "double", "float", 
72                                 "base64Binary", "hexBinary",
73                                 "boolean",
74                                 "anyURI",
75                                 "duration", "dateTime", "date", "time",
76 //                              "QName",
77                                 "gYearMonth", "gMonthDay",
78                                 "gYear", "gMonth", "gDay",
79                         };
80
81                         StringBuilder sb = new StringBuilder ();
82                         sb.Append ("<xs:schema xmlns:xs='" + XSchema.Namespace + "'>");
83                         foreach (string name in names)
84                                 sb.Append ("<xs:element name='" + name + "' type='xs:" + name + "'/>");
85                         sb.Append ("</xs:schema>");
86                         XSchema schema = XSchema.Read (new XmlTextReader (sb.ToString (), XmlNodeType.Document, null), null);
87                         schema.Compile (null);
88                         foreach (XmlSchemaElement el in schema.Elements.Values)
89                                 table.Add (el.Name, new XsdPrimitiveType (el.Name, el.ElementType as XmlSchemaDatatype));
90                 }
91
92                 public static XsdDatatypeProvider Instance {
93                         get { return instance; }
94                 }
95
96                 public override RelaxngDatatype GetDatatype (string name, string ns, RelaxngParamList parameters)
97                 {
98                         RelaxngDatatype dt = GetPrimitiveType (name, ns);
99                         if (dt == null)
100                                 return null;
101                         else if (parameters == null || parameters.Count == 0)
102                                 return dt;
103                         else
104                                 return new XsdSimpleRestrictionType (dt, parameters);
105                 }
106
107                 private RelaxngDatatype GetPrimitiveType (string name, string ns)
108                 {
109                         switch (ns) {
110                         case System.Xml.Schema.XmlSchema.Namespace:
111                         case "http://www.w3.org/2001/XMLSchema-datatypes":
112                                 break;
113                         default:
114                                 return null;
115                         }
116                         if (name == "QName")
117                                 return qnameType;
118                         return table [name] as RelaxngDatatype;
119                 }
120
121         }
122
123         public class XsdSimpleRestrictionType : RelaxngDatatype
124         {
125                 XmlSchemaSimpleType type;
126                 XSchema schema;
127
128                 public XsdSimpleRestrictionType (RelaxngDatatype primitive, RelaxngParamList parameters)
129                 {
130                         type = new XmlSchemaSimpleType ();
131                         XmlSchemaSimpleTypeRestriction r =
132                                 new XmlSchemaSimpleTypeRestriction ();
133                         type.Content = r;
134                         string ns = primitive.NamespaceURI;
135                         // Remap XML Schema datatypes namespace -> XML Schema namespace.
136                         if (ns == "http://www.w3.org/2001/XMLSchema-datatypes")
137                                 ns = XSchema.Namespace;
138                         r.BaseTypeName = new XmlQualifiedName (primitive.Name, ns);
139                         foreach (RelaxngParam p in parameters) {
140                                 XmlSchemaFacet f = null;
141                                 string value = p.Value;
142                                 switch (p.Name) {
143                                 case "maxExclusive":
144                                         f = new XmlSchemaMaxExclusiveFacet ();
145                                         break;
146                                 case "maxInclusive":
147                                         f = new XmlSchemaMaxInclusiveFacet ();
148                                         break;
149                                 case "minExclusive":
150                                         f = new XmlSchemaMinExclusiveFacet ();
151                                         break;
152                                 case "minInclusive":
153                                         f = new XmlSchemaMinInclusiveFacet ();
154                                         break;
155                                 case "pattern":
156                                         f = new XmlSchemaPatternFacet ();
157                                         // .NET/Mono Regex has a bug that it does not support "IsLatin-1Supplement"
158                                         // (it somehow breaks at '-').
159                                         value = value.Replace ("\\p{IsLatin-1Supplement}", "[\\x80-\\xFF]");
160                                         break;
161                                 case "whiteSpace":
162                                         f = new XmlSchemaWhiteSpaceFacet ();
163                                         break;
164                                 case "length":
165                                         f = new XmlSchemaLengthFacet ();
166                                         break;
167                                 case "maxLength":
168                                         f = new XmlSchemaMaxLengthFacet ();
169                                         break;
170                                 case "minLength":
171                                         f = new XmlSchemaMinLengthFacet ();
172                                         break;
173                                 case "fractionDigits":
174                                         f = new XmlSchemaFractionDigitsFacet ();
175                                         break;
176                                 case "totalDigits":
177                                         f = new XmlSchemaTotalDigitsFacet ();
178                                         break;
179                                 default:
180                                         throw new RelaxngException (String.Format ("XML Schema facet {0} is not recognized or not supported.", p.Name));
181                                 }
182                                 f.Value = value;
183                                 r.Facets.Add (f);
184                         }
185
186                         // Now we create XmlSchema to handle simple-type
187                         // based validation (since there is no other way, 
188                         // because of sucky XmlSchemaSimpleType design).
189                         schema = new XSchema ();
190                         XmlSchemaElement el = new XmlSchemaElement ();
191                         el.Name = "root";
192                         el.SchemaType = type;
193                         schema.Items.Add (el);
194                         schema.Compile (null);
195                 }
196
197                 public override string Name {
198                         get { return type.QualifiedName.Name; }
199                 }
200
201                 public override string NamespaceURI {
202                         get { return type.QualifiedName.Namespace; }
203                 }
204
205                 internal override bool IsContextDependent {
206                         get { return type.Datatype != null && type.Datatype.TokenizedType == XmlTokenizedType.QName; }
207                 }
208
209                 public override object Parse (string value, XmlReader reader)
210                 {
211                         // Now we create XmlValidatingReader to handle
212                         // simple-type based validation (since there is no
213                         // other way, because of sucky XmlSchemaSimpleType
214                         // design).
215                         XmlValidatingReader v = new XmlValidatingReader (
216                                 new XmlTextReader (
217                                         String.Concat ("<root>", value, "</root>"),
218                                         XmlNodeType.Document,
219                                         null));
220                         v.Schemas.Add (schema);
221                         v.Read (); // <root>
222                         try {
223                                 return v.ReadTypedValue ();
224                         } finally {
225                                 v.Read (); // </root>
226                         }
227                 }
228         }
229
230         public class XsdPrimitiveType : RelaxngDatatype
231         {
232                 XmlSchemaDatatype dt;
233                 string name;
234
235                 public XsdPrimitiveType (string name, XmlSchemaDatatype xstype)
236                 {
237                         this.name = name;
238                         dt = xstype;
239                 }
240
241                 internal override bool IsContextDependent {
242                         get { return dt.TokenizedType == XmlTokenizedType.QName; }
243                 }
244
245                 public override string Name {
246                         get { return name; }
247                 }
248
249                 public override string NamespaceURI {
250                         get { return "http://www.w3.org/2001/XMLSchema-datatypes"; }
251                 }
252
253                 public override object Parse (string text, XmlReader reader) 
254                 {
255                         return dt.ParseValue (text,
256                                 reader != null ? reader.NameTable : null,
257                                 null);
258                 }
259         }
260
261         // since QName resolution will fail, it must be implemented differently.
262         public class XsdQNameWrapper : RelaxngDatatype
263         {
264                 public XsdQNameWrapper ()
265                 {
266                 }
267
268                 public override string Name {
269                         get { return "QName"; }
270                 }
271
272                 public override string NamespaceURI {
273                         get { return "http://www.w3.org/2001/XMLSchema-datatypes"; }
274                 }
275
276                 internal override bool IsContextDependent {
277                         get { return true; }
278                 }
279
280                 public override object Parse (string s, XmlReader reader) 
281                 {
282                         int colonAt = s.IndexOf (':');
283                         string localName = colonAt < 0 ? s : s.Substring (colonAt + 1);
284 //                      string localName = nameTable.Add (colonAt < 0 ? s : s.Substring (colonAt + 1));
285                         return new XmlQualifiedName (localName, reader.LookupNamespace (
286                                 colonAt < 0 ? "" : s.Substring (0, colonAt - 1)));
287                 }
288
289         }
290 }