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