2004-11-17 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / Mono.Xml.Xsl.Operations / XslAttribute.cs
index a3d01f8393d463ca409bdaab65a5e1c2b428fee2..09b2e6562e39551cca0748e58ea0b5af5986ca57 100644 (file)
@@ -9,6 +9,27 @@
 // (C) 2003 Atsushi Enomoto
 //
 
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
 using System.Collections;
 using System.Xml;
@@ -19,54 +40,122 @@ using System.IO;
 using QName = System.Xml.XmlQualifiedName;
 
 namespace Mono.Xml.Xsl.Operations {
-       public class XslAttribute : XslCompiledElement {
+       internal class XslAttribute : XslCompiledElement {
                XslAvt name, ns;
-               string calcName, calcNs;
+               string calcName, calcNs, calcPrefix;
+               XmlNamespaceManager nsm;
                
                XslOperation value;
+               XPathNavigator nav;
+
                public XslAttribute (Compiler c) : base (c) {}
                
                protected override void Compile (Compiler c)
                {
+                       nav = c.Input.Clone ();
+                       
                        name = c.ParseAvtAttribute ("name");
+                       if (name == null)
+                               throw new XsltCompileException ("attribute \"name\" is required on XSLT attribute element.", null, c.Input);
                        ns = c.ParseAvtAttribute ("namespace");
-                       
-                       
+
                        calcName = XslAvt.AttemptPreCalc (ref name);
-                       
-                       if (calcName != null && ns == null) {
-                               QName q = XslNameUtil.FromString (calcName, c.Input);
-                               calcName = q.Name;
-                               calcNs = q.Namespace;   
-                       } else if (ns != null)
+                       calcPrefix = String.Empty;
+
+                       if (calcName != null) {
+                               int colonAt = calcName.IndexOf (':');
+                               calcPrefix = colonAt < 0 ? String.Empty : calcName.Substring (0, colonAt);
+                               calcName = colonAt < 0 ? calcName : calcName.Substring (colonAt + 1, calcName.Length - colonAt - 1);
+
+                               try {
+                                       XmlConvert.VerifyNCName (calcName);
+                                       if (calcPrefix != String.Empty)
+                                               XmlConvert.VerifyNCName (calcPrefix);
+                               } catch (XmlException ex) {
+                                       throw new XsltCompileException ("Invalid attribute name.", ex, c.Input);
+                               }
+                       }
+                       if (calcPrefix != String.Empty && ns == null)
+                               calcNs = nav.GetNamespace (calcPrefix);
+                       else if (ns != null)
                                calcNs = XslAvt.AttemptPreCalc (ref ns);
+
+                       if (calcNs ==null && calcPrefix != String.Empty) {
+                               string test = c.CurrentStylesheet.PrefixInEffect (calcPrefix, null);
+                               if (test != null) {
+                                       string alias = c.CurrentStylesheet.NamespaceAliases [calcPrefix] as string;
+                                       if (alias != null)
+                                               calcNs = c.Input.GetNamespace (alias);
+                                       else
+                                               calcNs = c.Input.NamespaceURI;
+                               }
+                       }
+
+                       if (ns == null && calcNs == null)
+                               nsm = c.GetNsm ();
                                
                        if (c.Input.MoveToFirstChild ()) {
-                               value = c.CompileTemplateContent ();
+                               value = c.CompileTemplateContent (XPathNodeType.Attribute);
                                c.Input.MoveToParent ();
                        }
+
                }
 
                public override void Evaluate (XslTransformProcessor p)
                {
-                       string nm, nmsp;
+                       string nm, nmsp, prefix;
                        
                        nm = calcName != null ? calcName : name.Evaluate (p);
-                       nmsp = calcNs != null ? calcNs : ns != null ? ns.Evaluate (p) : null;
-                       
-                       if (nmsp == null)
-                               throw new NotImplementedException ();
+                       nmsp = calcNs != null ? calcNs : ns != null ? ns.Evaluate (p) : String.Empty;
+                       prefix = calcPrefix != null ? calcPrefix : String.Empty;
+
+                       if (nm == "xmlns")
+                               // It is an error. We must recover by not emmiting any attributes 
+                               // (unless we throw an exception).
+                               return;
+
+                       int colonAt = nm.IndexOf (':');
+                       // local attribute
+                       if (colonAt > 0) {
+                               prefix = nm.Substring (0, colonAt);
+                               nm = nm.Substring (colonAt + 1, nm.Length - colonAt - 1);
+
+                               // global attribute
+                               if (nmsp == null) {
+                                       QName q = XslNameUtil.FromString (nm, nsm);
+                                       nm = q.Name;
+                                       nmsp = q.Namespace;
+                               } else
+                                       nm = XslNameUtil.LocalNameOf (nm);
+                       }
+
+                       if (nmsp != String.Empty && prefix == String.Empty) {
+                               if (nav.MoveToFirstNamespace (XPathNamespaceScope.ExcludeXml)) {
+                                       do {
+                                               if (nav.Value == nmsp) {
+                                                       prefix = nav.Name;
+                                                       break;
+                                               }
+                                       } while (nav.MoveToNextNamespace (XPathNamespaceScope.ExcludeXml));
+                                       nav.MoveToParent ();
+                               }
+                       }
+
+                       if (prefix == "xmlns")
+                               prefix = String.Empty;  // Should not be allowed.
+
+                       XmlConvert.VerifyName (nm);
 
                        if (value == null)
-                           p.Out.WriteAttributeString("", XslNameUtil.LocalNameOf(nm), nmsp, "");
+                               p.Out.WriteAttributeString(prefix, nm, nmsp, "");
                        else {
-                           StringWriter sw = new StringWriter();
-                           Outputter outputter = new TextOutputter(sw, true);
-                           p.PushOutput(outputter);
-                           value.Evaluate (p);                     
-                           p.PopOutput();
-                           outputter.Done();                           
-                p.Out.WriteAttributeString("", XslNameUtil.LocalNameOf(nm), nmsp, sw.ToString());                                                                      
+                               StringWriter sw = new StringWriter ();
+                               Outputter outputter = new TextOutputter (sw, true);
+                               p.PushOutput (outputter);
+                               value.Evaluate (p);                         
+                               p.PopOutput ();
+                               outputter.Done ();                              
+                               p.Out.WriteAttributeString (prefix, nm, nmsp, sw.ToString ());                                                                  
                        }                                               
                }
        }