2004-04-02 Dick Porter <dick@ximian.com>
[mono.git] / mcs / class / corlib / System.Security / SecurityElement.cs
1 //
2 // System.Security.SecurityElement.cs
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Lawrence Pit (loz@cable.a2000.nl)
7 //   Sebastien Pouliot  <spouliot@videotron.ca>
8 //
9 // (C) Ximian, Inc. http://www.ximian.com
10
11 using System.Globalization;
12 using System.Collections;
13 using System.Text;
14
15 namespace System.Security 
16 {
17         [Serializable]
18         public sealed class SecurityElement 
19         {
20                 internal class SecurityAttribute {
21                         
22                         private string _name;
23                         private string _value;
24
25                         public SecurityAttribute (string name, string value) 
26                         {
27                                 if (!IsValidAttributeName (name))
28                                         throw new ArgumentException (Locale.GetText ("Invalid XML attribute name") + ": " + name);
29
30                                 if (!IsValidAttributeValue (value))
31                                         throw new ArgumentException (Locale.GetText ("Invalid XML attribute value") + ": " + value);
32
33                                 _name = name;
34                                 _value = value;
35                         }
36
37                         public string Name {
38                                 get { return _name; }
39                         }
40
41                         public string Value {
42                                 get { return _value; }
43                         }
44                 }
45
46                 string text;
47                 string tag;
48                 ArrayList attributes;
49                 ArrayList children;
50                 
51                 // these values are determined by a simple test program against the MS.Net implementation:
52                 //      for (int i = 0; i < 256; i++) {
53                 //              if (!SecurityElement.IsValidTag ("" + ((char) i))) {
54                 //                      System.Console.WriteLine ("TAG: " + i);
55                 //              }
56                 //      }               
57                 // note: this is actually an incorrect implementation of MS, as for example the &
58                 // character is not a valid character in tag names.
59                 private static char [] invalid_tag_chars = new char [] { ' ', '<', '>' };
60                 private static char [] invalid_text_chars = new char [] { '<', '>' };
61                 private static char [] invalid_attr_name_chars = new char [] { ' ', '<', '>' };
62                 private static char [] invalid_attr_value_chars = new char [] { '"', '<', '>' };
63                 private static char [] invalid_chars = new char [] { '<', '>', '"', '\'', '&' };
64                 
65                 public SecurityElement (string tag) : this (tag, null)
66                 {
67                 }
68                 
69                 public SecurityElement (string tag, string text)
70                 {
71                         this.Tag = tag;
72                         this.Text = text;
73                 }
74                 
75                 public Hashtable Attributes {
76                         get {
77                                 if (attributes == null) 
78                                         return null;
79                                         
80                                 Hashtable result = new Hashtable (attributes.Count);
81                                 foreach (SecurityAttribute sa in attributes) {
82                                         result.Add (sa.Name, sa.Value);
83                                 }
84                                 return result;
85                         }
86
87                         set {                           
88                                 if (value == null || value.Count == 0) {
89                                         attributes.Clear ();
90                                         return;
91                                 }
92                                 
93                                 if (attributes == null)
94                                         attributes = new ArrayList ();
95                                 else
96                                         attributes.Clear ();
97                                 IDictionaryEnumerator e = value.GetEnumerator ();
98                                 while (e.MoveNext ()) {
99                                         attributes.Add (new SecurityAttribute ((string) e.Key, (string) e.Value));
100                                 }
101                         }
102                 }
103
104                 public ArrayList Children {
105                         get {
106                                 return children;
107                         }
108
109                         set {
110                                 if (value != null) {
111                                         foreach (object o in value) {
112                                                 if (o == null)
113                                                         throw new ArgumentNullException ();
114                                                 // shouldn't we also throw an exception 
115                                                 // when o isn't an instance of SecurityElement?
116                                         }
117                                 }
118                                 children = value;
119                         }
120                 }
121
122                 public string Tag {
123                         get {
124                                 return tag;
125                         }
126                         set {
127                                 if (value == null)
128                                         throw new ArgumentNullException ();
129                                 if (!IsValidTag (value))
130                                         throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + value);
131                                 int colon = value.IndexOf (':');
132                                 tag = colon < 0 ? value : value.Substring (colon + 1);
133                         }
134                 }
135
136                 public string Text {
137                         get {
138                                 return text;
139                         }
140
141                         set {
142                                 if (!IsValidText (value))
143                                         throw new ArgumentException (Locale.GetText ("Invalid XML string") + ": " + text);                              
144                                 text = value;
145                         }
146                 }
147
148                 public void AddAttribute (string name, string value)
149                 {
150                         if (name == null)
151                                 throw new ArgumentNullException ("name");
152                         if (value == null)
153                                 throw new ArgumentNullException ("value");
154                         if (GetAttribute (name) != null)
155                                 throw new ArgumentException (Locale.GetText ("Duplicate attribute : " + name));
156
157                         if (attributes == null)
158                                 attributes = new ArrayList ();
159                         attributes.Add (new SecurityAttribute (name, value));
160                 }
161
162                 public void AddChild (SecurityElement child)
163                 {
164                         if (child == null)
165                                 throw new ArgumentNullException ("child");
166
167                         if (children == null)
168                                 children = new ArrayList ();
169
170                         children.Add (child);
171                 }
172
173                 public string Attribute (string name)
174                 {
175                         if (name == null)
176                                 throw new ArgumentNullException ("name");
177
178                         SecurityAttribute sa = GetAttribute (name);
179                         return ((sa == null) ? null : sa.Value);
180                 }
181
182                 public bool Equal (SecurityElement other)
183                 {
184                         if (other == null)
185                                 return false;
186                                 
187                         if (this == other)
188                                 return true;
189
190                         if (this.text != other.text)
191                                 return false;
192
193                         if (this.tag != other.tag)
194                                 return false;
195
196                         if (this.attributes == null && other.attributes != null && other.attributes.Count != 0)
197                                 return false;
198                                 
199                         if (other.attributes == null && this.attributes != null && this.attributes.Count != 0)
200                                 return false;
201
202                         if (this.attributes != null && other.attributes != null) {
203                                 if (this.attributes.Count != other.attributes.Count) 
204                                         return false;
205                                 foreach (SecurityAttribute sa1 in attributes) {
206                                         SecurityAttribute sa2 = other.GetAttribute (sa1.Name);
207                                         if ((sa2 == null) || (sa1.Value != sa2.Value))
208                                                 return false;
209                                 }
210                         }
211                         
212                         if (this.children == null && other.children != null && other.children.Count != 0)
213                                 return false;
214                                         
215                         if (other.children == null && this.children != null && this.children.Count != 0)
216                                 return false;
217                                 
218                         if (this.children != null && other.children != null) {
219                                 if (this.children.Count != other.children.Count)
220                                         return false;
221                                 for (int i = 0; i < this.children.Count; i++) 
222                                         if (!((SecurityElement) this.children [i]).Equal ((SecurityElement) other.children [i]))
223                                                 return false;
224                         }
225                         
226                         return true;
227                 }
228
229                 public static string Escape (string str)
230                 {
231                         StringBuilder sb;
232                         
233                         if (str.IndexOfAny (invalid_chars) == -1)
234                                 return str;
235
236                         sb = new StringBuilder ();
237                         int len = str.Length;
238                         
239                         for (int i = 0; i < len; i++) {
240                                 char c = str [i];
241
242                                 switch (c) {
243                                 case '<':  sb.Append ("&lt;"); break;
244                                 case '>':  sb.Append ("&gt;"); break;
245                                 case '"':  sb.Append ("&quot;"); break;
246                                 case '\'': sb.Append ("&apos;"); break;
247                                 case '&':  sb.Append ("&amp;"); break;
248                                 default:   sb.Append (c); break;
249                                 }
250                         }
251
252                         return sb.ToString ();
253                 }
254
255                 public static bool IsValidAttributeName (string name)
256                 {
257                         return name != null && name.IndexOfAny (invalid_attr_name_chars) == -1;
258                 }
259
260                 public static bool IsValidAttributeValue (string value)
261                 {
262                         return value != null && value.IndexOfAny (invalid_attr_value_chars) == -1;
263                 }
264
265                 public static bool IsValidTag (string value)
266                 {
267                         return value != null && value.IndexOfAny (invalid_tag_chars) == -1;
268                 }
269
270                 public static bool IsValidText (string value)
271                 {
272                         if (value == null)
273                                 return true;
274                         return value.IndexOfAny (invalid_text_chars) == -1;
275                 }
276
277                 public SecurityElement SearchForChildByTag (string tag) 
278                 {
279                         if (tag == null)
280                                 throw new ArgumentNullException ("tag");
281                                 
282                         if (this.children == null)
283                                 return null;
284                                 
285                         for (int i = 0; i < children.Count; i++) {
286                                 SecurityElement elem = (SecurityElement) children [i];
287                                 if (elem.tag == tag)
288                                         return elem;
289                         }
290                         return null;
291                 }                       
292
293                 public string SearchForTextOfTag (string tag) 
294                 {
295                         if (tag == null)
296                                 throw new ArgumentNullException ("tag");
297                                 
298                         if (this.tag == tag)
299                                 return this.text;
300                                 
301                         if (this.children == null)
302                                 return null;
303                         
304                         for (int i = 0; i < children.Count; i++) {
305                                 string result = ((SecurityElement) children [i]).SearchForTextOfTag (tag);
306                                 if (result != null) 
307                                         return result;
308                         }
309
310                         return null;                    
311                 }
312                 
313                 public override string ToString ()
314                 {
315                         StringBuilder s = new StringBuilder ();
316                         ToXml (ref s, 0);
317                         return s.ToString ();
318                 }
319                 
320                 private void ToXml (ref StringBuilder s, int level)
321                 {
322                         s.Append (' ', level * 3);
323                         s.Append ("<");
324                         s.Append (tag);
325                         
326                         if (attributes != null) {
327                                 for (int i=0; i < attributes.Count; i++) {
328                                         SecurityAttribute sa = (SecurityAttribute) attributes [i];
329                                         s.Append (" ");
330                                         // all other attributes must align with the first one
331                                         if (i != 0)
332                                                 s.Append (' ', (level * 3) + tag.Length + 1);
333                                         s.Append (sa.Name)
334                                          .Append ("=\"")
335                                          .Append (sa.Value)
336                                          .Append ("\"");
337                                         if (i != attributes.Count - 1)
338                                                 s.Append (Environment.NewLine);
339                                 }
340                         }
341                         
342                         if ((text == null || text == String.Empty) && 
343                             (children == null || children.Count == 0))
344                                 s.Append ("/>").Append (Environment.NewLine);
345                         else {
346                                 s.Append (">").Append (text);
347                                 if (children != null) {
348                                         s.Append (Environment.NewLine);
349                                         foreach (SecurityElement child in children) {
350                                                 child.ToXml (ref s, level + 1);
351                                         }
352                                         s.Append (' ', level * 3);
353                                 }
354                                 s.Append ("</")
355                                  .Append (tag)
356                                  .Append (">")
357                                  .Append (Environment.NewLine);
358                         }
359                 }
360
361                 internal SecurityAttribute GetAttribute (string name) 
362                 {
363                         if (attributes != null) {
364                                 foreach (SecurityAttribute sa in attributes) {
365                                         if (sa.Name == name)
366                                                 return sa;
367                                 }
368                         }
369                         return null;
370                 }
371         }
372 }