2006-09-12 Lluis Sanchez Gual <lluis@novell.com>
[mono.git] / mcs / class / System.XML / System.Xml.Schema / XmlSchemaXPath.cs
1 // Author: Dwivedi, Ajay kumar\r
2 //            Adwiv@Yahoo.com\r
3
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 // 
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 // 
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 using System;\r
25 using System.Collections;\r
26 using System.Xml.Serialization;\r
27 using System.ComponentModel;\r
28 using System.Xml;\r
29 using Mono.Xml;\r
30 using Mono.Xml.Schema;\r
31 \r
32 namespace System.Xml.Schema\r
33 {\r
34         /// <summary>\r
35         /// Summary description for XmlSchemaXPath.\r
36         /// </summary>\r
37         public class XmlSchemaXPath : XmlSchemaAnnotated\r
38         {\r
39                 private string xpath;\r
40                 XmlNamespaceManager nsmgr;\r
41                 internal bool isSelector;\r
42                 XsdIdentityPath [] compiledExpression;\r
43 \r
44                 XsdIdentityPath currentPath;\r
45 \r
46                 public XmlSchemaXPath()\r
47                 {\r
48                 }\r
49                 [DefaultValue("")]\r
50                 [System.Xml.Serialization.XmlAttribute("xpath")]\r
51                 public string XPath \r
52                 {\r
53                         get{ return  xpath; } \r
54                         set{ xpath = value; }\r
55                 }\r
56 \r
57                 internal override int Compile(ValidationEventHandler h, XmlSchema schema)\r
58                 {\r
59                         // If this is already compiled this time, simply skip.\r
60                         if (CompilationId == schema.CompilationId)\r
61                                 return 0;\r
62 \r
63                         if (nsmgr == null) {\r
64                                 nsmgr = new XmlNamespaceManager (new NameTable ());\r
65                                 if (Namespaces != null)\r
66                                         foreach (XmlQualifiedName qname in Namespaces.ToArray ())\r
67                                                 nsmgr.AddNamespace (qname.Name, qname.Namespace);\r
68                         }\r
69 \r
70                         currentPath = new XsdIdentityPath ();\r
71                         ParseExpression (xpath, h, schema);\r
72 \r
73                         XmlSchemaUtil.CompileID(Id, this, schema.IDCollection, h);\r
74                         this.CompilationId = schema.CompilationId;\r
75                         return errorCount;\r
76                 }\r
77 \r
78                 internal XsdIdentityPath [] CompiledExpression {\r
79                         get { return compiledExpression; }\r
80                 }\r
81 \r
82                 private void ParseExpression (string xpath, ValidationEventHandler h, XmlSchema schema)\r
83                 {\r
84                         ArrayList paths = new ArrayList ();\r
85                         ParsePath (xpath, 0, paths, h, schema);\r
86                         this.compiledExpression = (XsdIdentityPath []) paths.ToArray (typeof (XsdIdentityPath));\r
87                 }\r
88 \r
89                 private void ParsePath (string xpath, int pos, ArrayList paths,\r
90                         ValidationEventHandler h, XmlSchema schema)\r
91                 {\r
92                         pos = SkipWhitespace (xpath, pos);\r
93                         if (xpath.Length >= pos + 3 && xpath [pos] == '.') {\r
94                                 int tmp = pos;\r
95                                 pos++;\r
96                                 pos = SkipWhitespace (xpath, pos);\r
97                                 if (xpath.Length > pos + 2 && xpath.IndexOf ("//", pos, 2) == pos) {\r
98                                         currentPath.Descendants = true;\r
99                                         pos += 2;\r
100                                 }\r
101                                 else\r
102                                         pos = tmp;      // revert\r
103                         }\r
104                         ArrayList al = new ArrayList ();\r
105                         ParseStep (xpath, pos, al, paths, h, schema);\r
106                 }\r
107 \r
108                 private void ParseStep (string xpath, int pos, ArrayList steps,\r
109                         ArrayList paths, ValidationEventHandler h, XmlSchema schema)\r
110                 {\r
111                         pos = SkipWhitespace (xpath, pos);\r
112                         if (xpath.Length == pos) {\r
113                                 error (h, "Empty xpath expression is specified");\r
114                                 return;\r
115                         }\r
116 \r
117                         XsdIdentityStep step = new XsdIdentityStep ();\r
118                         switch (xpath [pos]) {\r
119                         case '@':\r
120                                 if (isSelector) {\r
121                                         error (h, "Selector cannot include attribute axes.");\r
122                                         currentPath = null;\r
123                                         return;\r
124                                 }\r
125                                 pos++;\r
126                                 step.IsAttribute = true;\r
127                                 pos = SkipWhitespace (xpath, pos);\r
128                                 if (xpath.Length > pos && xpath [pos] == '*') {\r
129                                         pos++;\r
130                                         step.IsAnyName = true;\r
131                                         break;\r
132                                 }\r
133                                 goto default;\r
134                         case '.':\r
135                                 pos++;  // do nothing ;-)\r
136                                 step.IsCurrent = true;\r
137                                 break;\r
138                         case '*':\r
139                                 pos++;\r
140                                 step.IsAnyName = true;\r
141                                 break;\r
142                         case 'c':\r
143                                 if (xpath.Length > pos + 5 && xpath.IndexOf ("child", pos, 5) == pos) {\r
144                                         int tmp = pos;\r
145                                         pos += 5;\r
146                                         pos = SkipWhitespace (xpath, pos);\r
147                                         if (xpath.Length > pos && xpath [pos] == ':' && xpath [pos+1] == ':') {\r
148                                                 pos += 2;\r
149                                                 if (xpath.Length > pos && xpath [pos] == '*') {\r
150                                                         pos++;\r
151                                                         step.IsAnyName = true;\r
152                                                         break;\r
153                                                 }\r
154                                                 pos = SkipWhitespace (xpath, pos);\r
155                                         }\r
156                                         else\r
157                                                 pos = tmp;\r
158                                 }\r
159                                 goto default;\r
160                         case 'a':\r
161                                 if (xpath.Length > pos + 9 && xpath.IndexOf ("attribute", pos, 9) == pos) {\r
162                                         int tmp = pos;\r
163                                         pos += 9;\r
164                                         pos = SkipWhitespace (xpath, pos);\r
165                                         if (xpath.Length > pos && xpath [pos] == ':' && xpath [pos+1] == ':') {\r
166                                                 if (isSelector) {\r
167                                                         error (h, "Selector cannot include attribute axes.");\r
168                                                         currentPath = null;\r
169                                                         return;\r
170                                                 }\r
171                                                 pos += 2;\r
172                                                 step.IsAttribute = true;\r
173                                                 if (xpath.Length > pos && xpath [pos] == '*') {\r
174                                                         pos++;\r
175                                                         step.IsAnyName = true;\r
176                                                         break;\r
177                                                 }\r
178                                                 pos = SkipWhitespace (xpath, pos);\r
179                                         }\r
180                                         else\r
181                                                 pos = tmp;\r
182                                 }\r
183                                 goto default;\r
184                         default:\r
185                                 int nameStart = pos;\r
186                                 while (xpath.Length > pos) {\r
187                                         if (!XmlChar.IsNCNameChar (xpath [pos]))\r
188                                                 break;\r
189                                         else\r
190                                                 pos++;\r
191                                 }\r
192                                 if (pos == nameStart) {\r
193                                         error (h, "Invalid path format for a field.");\r
194                                         this.currentPath = null;\r
195                                         return;\r
196                                 }\r
197                                 if (xpath.Length == pos || xpath [pos] != ':')\r
198                                         step.Name = xpath.Substring (nameStart, pos - nameStart);\r
199                                 else {\r
200                                         string prefix = xpath.Substring (nameStart, pos - nameStart);\r
201                                         pos++;\r
202                                         if (xpath.Length > pos && xpath [pos] == '*') {\r
203                                                 string ns = nsmgr.LookupNamespace (prefix, false);\r
204                                                 if (ns == null) {\r
205                                                         error (h, "Specified prefix '" + prefix + "' is not declared.");\r
206                                                         this.currentPath = null;\r
207                                                         return;\r
208                                                 }\r
209                                                 step.NsName = ns;\r
210                                                 pos++;\r
211                                         } else {\r
212                                                 int localNameStart = pos;\r
213                                                 while (xpath.Length > pos) {\r
214                                                         if (!XmlChar.IsNCNameChar (xpath [pos]))\r
215                                                                 break;\r
216                                                         else\r
217                                                                 pos++;\r
218                                                 }\r
219                                                 step.Name = xpath.Substring (localNameStart, pos - localNameStart);\r
220                                                 string ns = nsmgr.LookupNamespace (prefix, false);\r
221                                                 if (ns == null) {\r
222                                                         error (h, "Specified prefix '" + prefix + "' is not declared.");\r
223                                                         this.currentPath = null;\r
224                                                         return;\r
225                                                 }\r
226                                                 step.Namespace = ns;\r
227                                         }\r
228                                 }\r
229                                 break;\r
230                         }\r
231                         if (!step.IsCurrent)    // Current step is meaningless, other than its representation.\r
232                                 steps.Add (step);\r
233                         pos = SkipWhitespace (xpath, pos);\r
234                         if (xpath.Length == pos) {\r
235                                 currentPath.OrderedSteps = (XsdIdentityStep []) steps.ToArray (typeof (XsdIdentityStep));\r
236                                 paths.Add (currentPath);\r
237                                 return;\r
238                         }\r
239                         else if (xpath [pos] == '/') {\r
240                                 pos++;\r
241                                 if (step.IsAttribute) {\r
242                                         error (h, "Unexpected xpath token after Attribute NameTest.");\r
243                                         this.currentPath = null;\r
244                                         return;\r
245                                 }\r
246                                 this.ParseStep (xpath, pos, steps, paths, h, schema);\r
247                                 if (currentPath == null) // For ValidationEventHandler\r
248                                         return;\r
249                                 currentPath.OrderedSteps = (XsdIdentityStep []) steps.ToArray (typeof (XsdIdentityStep));\r
250                         } else if (xpath [pos] == '|') {\r
251                                 pos++;\r
252                                 currentPath.OrderedSteps = (XsdIdentityStep []) steps.ToArray (typeof (XsdIdentityStep));\r
253                                 paths.Add (this.currentPath);\r
254                                 this.currentPath = new XsdIdentityPath ();\r
255                                 this.ParsePath (xpath, pos, paths, h, schema);\r
256                         } else {\r
257                                 error (h, "Unexpected xpath token after NameTest.");\r
258                                 this.currentPath = null;\r
259                                 return;\r
260                         }\r
261                 }\r
262 \r
263                 private int SkipWhitespace (string xpath, int pos)\r
264                 {\r
265                         bool loop = true;\r
266                         while (loop && xpath.Length > pos) {\r
267                                 switch (xpath [pos]) {\r
268                                 case ' ':\r
269                                 case '\t':\r
270                                 case '\r':\r
271                                 case '\n':\r
272                                         pos++;\r
273                                         continue;\r
274                                 default:\r
275                                         loop = false;\r
276                                         break;\r
277                                 }\r
278                         }\r
279                         return pos;\r
280                 }\r
281 \r
282                 //<selector \r
283                 //  id = ID \r
284                 //  xpath = a subset of XPath expression, see below \r
285                 //  {any attributes with non-schema namespace . . .}>\r
286                 //  Content: (annotation?)\r
287                 //</selector>\r
288                 internal static XmlSchemaXPath Read(XmlSchemaReader reader, ValidationEventHandler h,string name)\r
289                 {\r
290                         XmlSchemaXPath path = new XmlSchemaXPath();\r
291                         reader.MoveToElement();\r
292 \r
293                         if(reader.NamespaceURI != XmlSchema.Namespace || reader.LocalName != name)\r
294                         {\r
295                                 error(h,"Should not happen :1: XmlSchemaComplexContentRestriction.Read, name="+reader.Name,null);\r
296                                 reader.Skip();\r
297                                 return null;\r
298                         }\r
299 \r
300                         path.LineNumber = reader.LineNumber;\r
301                         path.LinePosition = reader.LinePosition;\r
302                         path.SourceUri = reader.BaseURI;\r
303 \r
304                         XmlNamespaceManager currentMgr = XmlSchemaUtil.GetParserContext (reader.Reader).NamespaceManager;\r
305                         if (currentMgr != null) {\r
306                                 path.nsmgr = new XmlNamespaceManager (reader.NameTable);\r
307                                 IEnumerator e = currentMgr.GetEnumerator ();\r
308                                 while (e.MoveNext ()) {\r
309                                         string prefix = e.Current as string;\r
310                                         switch (prefix) {\r
311                                         case "xml":\r
312                                         case "xmlns":\r
313                                                 continue;\r
314                                         default:\r
315                                                 path.nsmgr.AddNamespace (prefix, currentMgr.LookupNamespace (prefix, false));\r
316                                                 break;\r
317                                         }\r
318                                 }\r
319                         }\r
320 \r
321                         while(reader.MoveToNextAttribute())\r
322                         {\r
323                                 if(reader.Name == "id")\r
324                                 {\r
325                                         path.Id = reader.Value;\r
326                                 }\r
327                                 else if(reader.Name == "xpath")\r
328                                 {\r
329                                         path.xpath = reader.Value;\r
330                                 }\r
331                                 else if((reader.NamespaceURI == "" && reader.Name != "xmlns") || reader.NamespaceURI == XmlSchema.Namespace)\r
332                                 {\r
333                                         error(h,reader.Name + " is not a valid attribute for "+name,null);\r
334                                 }\r
335                                 else\r
336                                 {\r
337                                         XmlSchemaUtil.ReadUnhandledAttribute(reader,path);\r
338                                 }\r
339                         }\r
340 \r
341                         reader.MoveToElement(); \r
342                         if(reader.IsEmptyElement)\r
343                                 return path;\r
344 \r
345                         //  Content: (annotation?)\r
346                         int level = 1;\r
347                         while(reader.ReadNextElement())\r
348                         {\r
349                                 if(reader.NodeType == XmlNodeType.EndElement)\r
350                                 {\r
351                                         if(reader.LocalName != name)\r
352                                                 error(h,"Should not happen :2: XmlSchemaXPath.Read, name="+reader.Name,null);\r
353                                         break;\r
354                                 }\r
355                                 if(level <= 1 && reader.LocalName == "annotation")\r
356                                 {\r
357                                         level = 2;      //Only one annotation\r
358                                         XmlSchemaAnnotation annotation = XmlSchemaAnnotation.Read(reader,h);\r
359                                         if(annotation != null)\r
360                                                 path.Annotation = annotation;\r
361                                         continue;\r
362                                 }\r
363                                 reader.RaiseInvalidElementError();\r
364                         }\r
365                         return path;\r
366                 }\r
367 \r
368         }\r
369 }