Merge pull request #961 from ermshiperete/bug-xamarin-18118
[mono.git] / mcs / class / System.XML / Mono.Xml.Xsl.Operations / XslVariable.cs
1 //
2 // XslVariable.cs
3 //
4 // Authors:
5 //      Ben Maurer (bmaurer@users.sourceforge.net)
6 //      Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 //      
8 // (C) 2003 Ben Maurer
9 // (C) 2003 Atsushi Enomoto
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Collections;
35 using System.Xml;
36 using System.Xml.XPath;
37 using System.Xml.Xsl;
38 using Mono.Xml.XPath;
39
40 using QName = System.Xml.XmlQualifiedName;
41
42 namespace Mono.Xml.Xsl.Operations {
43         
44         internal class XslVariableInformation
45         {
46                 QName name;
47                 XPathExpression select;
48                 XslOperation content;
49                 
50                 public XslVariableInformation (Compiler c)
51                 {
52
53                         c.CheckExtraAttributes (c.Input.LocalName, "name", "select");
54
55                         c.AssertAttribute ("name");
56
57                         name = c.ParseQNameAttribute ("name");
58                         try {
59                                 XmlConvert.VerifyName (name.Name);
60                         } catch (XmlException ex) {
61                                 throw new XsltCompileException ("Variable name is not qualified name", ex, c.Input);
62                         }
63
64                         string sel = c.GetAttribute ("select");
65                         if (sel != null && sel != "" ) {
66                                 select = c.CompileExpression (c.GetAttribute ("select"));
67                                 // TODO assert empty
68                         } else  if (c.Input.MoveToFirstChild ()) {
69                                 content = c.CompileTemplateContent ();
70                                 c.Input.MoveToParent ();
71                         }
72                 }
73                 
74                 public object Evaluate (XslTransformProcessor p)
75                 {
76                         if (select != null) {
77                                 object o = p.Evaluate (select);
78                                 // To resolve variable references correctly, we
79                                 // have to collect all the target nodes here.
80                                 // (otherwise, variables might be resolved with
81                                 // different level of variable stack in
82                                 // XslTransformProcessor).
83                                 if (o is XPathNodeIterator) {
84                                         ArrayList al = new ArrayList ();
85                                         XPathNodeIterator iter = (XPathNodeIterator) o;
86                                         while (iter.MoveNext ())
87                                                 al.Add (iter.Current.Clone ());
88                                         o = new ListIterator (al, p.XPathContext);
89                                 }
90                                 return o;
91                         } else if (content != null) {
92                                 DTMXPathDocumentWriter2 w = new DTMXPathDocumentWriter2 (p.Root.NameTable, 200);
93                                 Outputter outputter = new GenericOutputter(w, p.Outputs, null, true);
94                                 p.PushOutput (outputter);
95                                 if (p.CurrentNodeset.CurrentPosition == 0)
96                                         p.NodesetMoveNext ();
97                                 content.Evaluate (p);
98                                 p.PopOutput ();
99                                 return w.CreateDocument ().CreateNavigator ();
100                         } else {
101                                 return "";
102                         }
103                 }
104                 
105                 public QName Name { get { return name; }}
106
107                 internal XPathExpression Select {
108                         get { return select; }
109                 }
110
111                 internal XslOperation Content {
112                         get { return content; }
113                 }
114         }
115         
116         internal abstract class XslGeneralVariable : XslCompiledElement, IXsltContextVariable {
117                 protected XslVariableInformation var;   
118                 
119                 public XslGeneralVariable (Compiler c) : base (c) {}
120                 
121                 protected override void Compile (Compiler c)
122                 {
123                         if (c.Debugger != null)
124                                 c.Debugger.DebugCompile (this.DebugInput);
125
126                         this.var = new XslVariableInformation (c);
127                 }
128                 
129                 public override abstract void Evaluate (XslTransformProcessor p);
130                 protected abstract object GetValue (XslTransformProcessor p);
131                 
132                 
133                 public object Evaluate (XsltContext xsltContext)
134                 {       
135                         object value = GetValue (((XsltCompiledContext)xsltContext).Processor);
136                         
137                         if (value is XPathNodeIterator)
138                                 return new WrapperIterator (((XPathNodeIterator)value).Clone (), xsltContext);
139                         
140                         return value;
141                 }
142
143                 public QName Name {get {return  var.Name;}}
144                 public XPathResultType VariableType { get {return XPathResultType.Any;}}
145                 public abstract bool IsLocal { get; }
146                 public abstract bool IsParam { get; }
147         }
148         
149         internal class XslGlobalVariable : XslGeneralVariable {
150                 public XslGlobalVariable (Compiler c) : base (c) {}
151                 static object busyObject = new Object ();
152                 
153                         
154                 public override void Evaluate (XslTransformProcessor p)
155                 {
156                         if (p.Debugger != null)
157                                 p.Debugger.DebugExecute (p, this.DebugInput);
158
159                         Hashtable varInfo = p.globalVariableTable;
160                         
161                         if (varInfo.Contains (this)) {
162                                 if (varInfo [this] == busyObject)
163                                         throw new XsltException ("Circular dependency was detected", null, p.CurrentNode);
164                                 return;
165                         }
166                         
167                         varInfo [this] = busyObject;
168                         varInfo [this] = var.Evaluate (p);
169                         
170                 }
171                 
172                 protected override object GetValue (XslTransformProcessor p)
173                 {
174                         p.PushNodeset (new SelfIterator (p.Root, p.XPathContext));
175                         p.NodesetMoveNext ();
176                         Evaluate (p);
177                         p.PopNodeset ();
178                         return p.globalVariableTable [this];
179                 }
180                         
181                 public override bool IsLocal { get { return false; }}
182                 public override bool IsParam { get { return false; }}
183         }
184         
185         internal class XslGlobalParam : XslGlobalVariable {
186                 
187                 public XslGlobalParam (Compiler c) : base (c) {}
188                         
189                 public void Override (XslTransformProcessor p, object paramVal)
190                 {
191                         Debug.Assert (!p.globalVariableTable.Contains (this), "Shouldn't have been evaluated by this point");
192                         
193                         p.globalVariableTable [this] = paramVal;
194                 }
195                 
196                 public override bool IsParam { get { return true; }}
197         }
198         
199         internal class XslLocalVariable : XslGeneralVariable {
200                 protected int slot;
201                                 
202                 public XslLocalVariable (Compiler c) : base (c)
203                 {
204                         slot = c.AddVariable (this);
205                 }
206                 
207                 public override void Evaluate (XslTransformProcessor p)
208                 {       
209                         if (p.Debugger != null)
210                                 p.Debugger.DebugExecute (p, this.DebugInput);
211
212                         p.SetStackItem (slot, var.Evaluate (p));
213                 }
214                 
215                 protected override object GetValue (XslTransformProcessor p)
216                 {
217                         return p.GetStackItem (slot);
218                 }
219                 
220                 public bool IsEvaluated (XslTransformProcessor p)
221                 {
222                         return p.GetStackItem (slot) != null;
223                 }
224                 
225                 public override bool IsLocal { get { return true; }}
226                 public override bool IsParam { get { return false; }}
227         }
228         
229         internal class XslLocalParam : XslLocalVariable {
230                 
231                 public XslLocalParam (Compiler c) : base (c) {}
232                 
233                 public override void Evaluate (XslTransformProcessor p)
234                 {
235                         if (p.Debugger != null)
236                                 p.Debugger.DebugExecute (p, this.DebugInput);
237
238                         if (p.GetStackItem (slot) != null)
239                                 return; // evaluated already
240
241                         if (p.Arguments != null &&
242                                 var.Select == null &&
243                                 var.Content == null) {
244                                 object val = p.Arguments.GetParam (Name.Name,
245                                         Name.Namespace);
246                                 if (val != null) {
247                                         Override (p, val);
248                                         return;
249                                 }
250                         }
251
252                         base.Evaluate (p);
253                 }
254                 
255                 public void Override (XslTransformProcessor p, object paramVal)
256                 {
257                         p.SetStackItem (slot, paramVal);
258                 }
259                 
260                 public override bool IsParam { get { return true; }}
261         }
262         
263         internal class XPathVariableBinding : Expression {
264                 XslGeneralVariable v;
265                 public XPathVariableBinding (XslGeneralVariable v)
266                 {
267                         this.v = v;
268                 }
269                 public override String ToString () { return "$" + v.Name.ToString (); }
270                 public override XPathResultType ReturnType { get { return XPathResultType.Any; }}
271                 public override XPathResultType GetReturnType (BaseIterator iter)
272                 {
273                         return XPathResultType.Any;
274                 }
275                 
276                 public override object Evaluate (BaseIterator iter)
277                 {
278                         return v.Evaluate (iter.NamespaceManager as XsltContext);
279                 }
280         }
281 }