[runtime] Switch getenv to use heap memory
[mono.git] / mcs / class / System.Web / System.Web.UI.HtmlControls / HtmlSelect.cs
1 //
2 // System.Web.UI.HtmlControls.HtmlSelect.cs
3 //
4 // Author:
5 //      Dick Porter  <dick@ximian.com>
6 //
7 // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System.Web.UI.WebControls;
30 using System.Web.Util;
31 using System.ComponentModel;
32 using System.Collections;
33 using System.Collections.Specialized;
34 using System.Globalization;
35 using System.Security.Permissions;
36
37 namespace System.Web.UI.HtmlControls 
38 {
39         // CAS
40         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
41         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
42         // attributes
43         [DefaultEvent ("ServerChange")]
44         [ValidationProperty ("Value")]
45         [ControlBuilder (typeof (HtmlSelectBuilder))]
46         [SupportsEventValidation]
47         public class HtmlSelect : HtmlContainerControl, IPostBackDataHandler, IParserAccessor
48         {
49                 static readonly object EventServerChange = new object ();
50
51                 DataSourceView _boundDataSourceView;
52                 bool requiresDataBinding;
53                 bool _initialized;
54                 object datasource;
55                 ListItemCollection items;
56
57                 public HtmlSelect () : base ("select")
58                 {
59                 }
60
61                 [DefaultValue ("")]
62                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
63                 [WebSysDescription("")]
64                 [WebCategory("Data")]
65                 public virtual string DataMember {
66                         get {
67                                 string member = Attributes["datamember"];
68
69                                 if (member == null) {
70                                         return (String.Empty);
71                                 }
72
73                                 return (member);
74                         }
75                         set {
76                                 if (value == null) {
77                                         Attributes.Remove ("datamember");
78                                 } else {
79                                         Attributes["datamember"] = value;
80                                 }
81                         }
82                 }
83                 
84                 [DefaultValue (null)]
85                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
86                 [WebSysDescription("")]
87                 [WebCategory("Data")]
88                 public virtual object DataSource {
89                         get {
90                                 return (datasource);
91                         }
92                         set {
93                                 if ((value != null) &&
94                                     !(value is IEnumerable) &&
95                                     !(value is IListSource)) {
96                                         throw new ArgumentException ();
97                                 }
98
99                                 datasource = value;
100                         }
101                 }
102
103                 [DefaultValue ("")]
104                 public virtual string DataSourceID {
105                         get {
106                                 return ViewState.GetString ("DataSourceID", "");
107                         }
108                         set {
109                                 if (DataSourceID == value)
110                                         return;
111                                 ViewState ["DataSourceID"] = value;
112                                 if (_boundDataSourceView != null)
113                                         _boundDataSourceView.DataSourceViewChanged -= OnDataSourceViewChanged;
114                                 _boundDataSourceView = null;
115                                 OnDataPropertyChanged ();
116                         }
117                 }
118
119                 [DefaultValue ("")]
120                 [WebSysDescription("")]
121                 [WebCategory("Data")]
122                 public virtual string DataTextField {
123                         get {
124                                 string text = Attributes["datatextfield"];
125
126                                 if (text == null) {
127                                         return (String.Empty);
128                                 }
129
130                                 return (text);
131                         }
132                         set {
133                                 if (value == null) {
134                                         Attributes.Remove ("datatextfield");
135                                 } else {
136                                         Attributes["datatextfield"] = value;
137                                 }
138                         }
139                 }
140
141                 [DefaultValue ("")]
142                 [WebSysDescription("")]
143                 [WebCategory("Data")]
144                 public virtual string DataValueField {
145                         get {
146                                 string value = Attributes["datavaluefield"];
147
148                                 if (value == null) {
149                                         return (String.Empty);
150                                 }
151
152                                 return (value);
153                         }
154                         set {
155                                 if (value == null) {
156                                         Attributes.Remove ("datavaluefield");
157                                 } else {
158                                         Attributes["datavaluefield"] = value;
159                                 }
160                         }
161                 }
162
163                 public override string InnerHtml {
164                         get {
165                                 throw new NotSupportedException ();
166                         }
167                         set {
168                                 throw new NotSupportedException ();
169                         }
170                 }
171
172                 public override string InnerText {
173                         get {
174                                 throw new NotSupportedException ();
175                         }
176                         set {
177                                 throw new NotSupportedException ();
178                         }
179                 }
180
181                 protected bool IsBoundUsingDataSourceID {
182                         get {
183                                 return (DataSourceID.Length != 0);
184                         }
185                 }
186                 
187                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
188                 [Browsable (false)]
189                 public ListItemCollection Items {
190                         get {
191                                 if (items == null) {
192                                         items = new ListItemCollection ();
193                                         if (IsTrackingViewState)
194                                                 ((IStateManager) items).TrackViewState ();
195                                 }
196
197                                 return (items);
198                         }
199                 }
200
201                 [DefaultValue ("")]
202                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
203                 [WebSysDescription("")]
204                 [WebCategory("Behavior")]
205                 public bool Multiple {
206                         get {
207                                 string multi = Attributes["multiple"];
208
209                                 if (multi == null) {
210                                         return (false);
211                                 }
212
213                                 return (true);
214                         }
215                         set {
216                                 if (value == false) {
217                                         Attributes.Remove ("multiple");
218                                 } else {
219                                         Attributes["multiple"] = "multiple";
220                                 }
221                         }
222                 }
223                 
224                 [DefaultValue ("")]
225                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
226                 [WebSysDescription("")]
227                 [WebCategory("Behavior")]
228                 public string Name {
229                         get {
230                                 return (UniqueID);
231                         }
232                         set {
233                                 /* Do nothing */
234                         }
235                 }
236
237                 protected bool RequiresDataBinding {
238                         get { return requiresDataBinding; }
239                         set { requiresDataBinding = value; }
240                 }
241
242                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
243                 [Browsable (false)]
244                 public virtual int SelectedIndex {
245                         get {
246                                 /* Make sure Items has been initialised */
247                                 ListItemCollection listitems = Items;
248
249                                 for (int i = 0; i < listitems.Count; i++) {
250                                         if (listitems[i].Selected) {
251                                                 return (i);
252                                         }
253                                 }
254
255                                 /* There is always a selected item in
256                                  * non-multiple mode, if the size is
257                                  * <= 1
258                                  */
259                                 if (!Multiple && Size <= 1) {
260                                         /* Select the first item */
261                                         if (listitems.Count > 0) {
262                                                 /* And make it stick
263                                                  * if there is
264                                                  * anything in the
265                                                  * list
266                                                  */
267                                                 listitems[0].Selected = true;
268                                         }
269                                         
270                                         return (0);
271                                 }
272                                 
273                                 return (-1);
274                         }
275                         set {
276                                 ClearSelection ();
277
278                                 if (value == -1 || items == null) {
279                                         return;
280                                 }
281
282                                 if (value < 0 || value >= items.Count) {
283                                         throw new ArgumentOutOfRangeException ("value");
284                                 }
285
286                                 items[value].Selected = true;
287                         }
288                 }
289
290                 /* "internal infrastructure" according to the docs,
291                  * but has some documentation in 2.0
292                  */
293                 protected virtual int[] SelectedIndices {
294                         get {
295                                 ArrayList selected = new ArrayList ();
296
297                                 int count = Items.Count;
298
299                                 for (int i = 0; i < count; i++) {
300                                         if (Items [i].Selected) {
301                                                 selected.Add (i);
302                                         }
303                                 }
304                                 
305                                 return ((int[])selected.ToArray (typeof (int)));
306                         }
307                 }
308                 
309
310                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
311                 public int Size {
312                         get {
313                                 string size = Attributes["size"];
314
315                                 if (size == null) {
316                                         return (-1);
317                                 }
318
319                                 return (Int32.Parse (size, Helpers.InvariantCulture));
320                         }
321                         set {
322                                 if (value == -1) {
323                                         Attributes.Remove ("size");
324                                 } else {
325                                         Attributes["size"] = value.ToString ();
326                                 }
327                         }
328                 }
329
330                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
331                 public string Value {
332                         get {
333                                 int sel = SelectedIndex;
334
335                                 if (sel >= 0 && sel < Items.Count) {
336                                         return (Items[sel].Value);
337                                 }
338                                 
339                                 return (String.Empty);
340                         }
341                         set {
342                                 int sel = Items.IndexOf (value);
343
344                                 if (sel >= 0) {
345                                         SelectedIndex = sel;
346                                 }
347                         }
348                 }
349
350                 [WebSysDescription("")]
351                 [WebCategory("Action")]
352                 public event EventHandler ServerChange {
353                         add {
354                                 Events.AddHandler (EventServerChange, value);
355                         }
356                         remove {
357                                 Events.RemoveHandler (EventServerChange, value);
358                         }
359                 }
360
361                 protected override void AddParsedSubObject (object obj)
362                 {
363                         if (!(obj is ListItem)) {
364                                 throw new HttpException ("HtmlSelect can only contain ListItem");
365                         }
366
367                         Items.Add ((ListItem)obj);
368                         
369                         base.AddParsedSubObject (obj);
370                 }
371
372                 /* "internal infrastructure" according to the docs,
373                  * but has some documentation in 2.0
374                  */
375                 protected virtual void ClearSelection ()
376                 {
377                         if (items == null) {
378                                 return;
379                         }
380
381                         int count = items.Count;
382                         for (int i = 0; i < count; i++) {
383                                 items[i].Selected = false;
384                         }
385                 }
386                 
387                 protected override ControlCollection CreateControlCollection ()
388                 {
389                         return (base.CreateControlCollection ());
390                 }
391
392                 protected void EnsureDataBound ()
393                 {
394                         if (IsBoundUsingDataSourceID && RequiresDataBinding)
395                                 DataBind ();
396                 }
397
398                 protected virtual IEnumerable GetData ()
399                 {
400                         if (DataSource != null && IsBoundUsingDataSourceID)
401                                 throw new HttpException ("Control bound using both DataSourceID and DataSource properties.");
402
403                         if (DataSource != null)
404                                 return DataSourceResolver.ResolveDataSource (DataSource, DataMember);
405
406                         if (!IsBoundUsingDataSourceID)
407                                 return null;
408
409                         IEnumerable result = null;
410
411                         DataSourceView boundDataSourceView = ConnectToDataSource ();
412                         boundDataSourceView.Select (DataSourceSelectArguments.Empty, delegate (IEnumerable data) { result = data; });
413
414                         return result;
415                 }
416
417                 protected override void LoadViewState (object savedState)
418                 {
419                         object first = null;
420                         object second = null;
421
422                         Pair pair = savedState as Pair;
423                         if (pair != null) {
424                                 first = pair.First;
425                                 second = pair.Second;
426                         }
427
428                         base.LoadViewState (first);
429
430                         if (second != null) {
431                                 IStateManager manager = Items as IStateManager;
432                                 manager.LoadViewState (second);
433                         }
434                 }
435
436                 protected override void OnDataBinding (EventArgs e)
437                 {
438                         base.OnDataBinding (e);
439
440                         /* Make sure Items has been initialised */
441                         ListItemCollection listitems = Items;
442
443                         listitems.Clear ();
444                         
445                         IEnumerable list = GetData ();
446                         if (list == null)
447                                 return;
448                         
449                         foreach (object container in list) {
450                                 string text = null;
451                                 string value = null;
452
453                                 if (DataTextField == String.Empty &&
454                                     DataValueField == String.Empty) {
455                                         text = container.ToString ();
456                                         value = text;
457                                 } else {
458                                         if (DataTextField != String.Empty) {
459                                                 text = DataBinder.Eval (container, DataTextField).ToString ();
460                                         }
461
462                                         if (DataValueField != String.Empty) {
463                                                 value = DataBinder.Eval (container, DataValueField).ToString ();
464                                         } else {
465                                                 value = text;
466                                         }
467
468                                         if (text == null &&
469                                             value != null) {
470                                                 text = value;
471                                         }
472                                 }
473
474                                 if (text == null) {
475                                         text = String.Empty;
476                                 }
477                                 if (value == null) {
478                                         value = String.Empty;
479                                 }
480
481                                 ListItem item = new ListItem (text, value);
482                                 listitems.Add (item);
483                         }
484                         RequiresDataBinding = false;
485                         IsDataBound = true;
486                 }
487
488                 protected virtual void OnDataPropertyChanged ()
489                 {
490                         if (_initialized)
491                         RequiresDataBinding = true;
492                 }
493
494                 protected virtual void OnDataSourceViewChanged (object sender,
495                                                                 EventArgs e)
496                 {
497                         RequiresDataBinding = true;
498                 }
499
500                 protected internal override void OnInit (EventArgs e)
501                 {
502                         base.OnInit (e);
503
504                         Page.PreLoad += new EventHandler (OnPagePreLoad);
505                 }
506
507                 protected virtual void OnPagePreLoad (object sender, EventArgs e)
508                 {
509                         Initialize ();
510                 }
511
512                 protected internal override void OnLoad (EventArgs e)
513                 {
514                         if (!_initialized)
515                                 Initialize ();
516
517                         base.OnLoad (e);
518                 }
519
520                 void Initialize ()
521                 {
522                         _initialized = true;
523
524                         if (!IsDataBound)
525                                 RequiresDataBinding = true;
526
527                         if (IsBoundUsingDataSourceID)
528                                 ConnectToDataSource ();
529                 }
530
531                 bool IsDataBound{
532                         get {
533                                 return ViewState.GetBool ("_DataBound", false);
534                         }
535                         set {
536                                 ViewState ["_DataBound"] = value;
537                         }
538                 }
539
540                 DataSourceView ConnectToDataSource ()
541                 {
542                         if (_boundDataSourceView != null)
543                                 return _boundDataSourceView;
544
545                         /* verify that the data source exists and is an IDataSource */
546                         object ctrl = null;
547                         Page page = Page;
548                         if (page != null)
549                                 ctrl = page.FindControl (DataSourceID);
550
551                         if (ctrl == null || !(ctrl is IDataSource)) {
552                                 string format;
553
554                                 if (ctrl == null)
555                                         format = "DataSourceID of '{0}' must be the ID of a control of type IDataSource.  A control with ID '{1}' could not be found.";
556                                 else
557                                         format = "DataSourceID of '{0}' must be the ID of a control of type IDataSource.  '{1}' is not an IDataSource.";
558
559                                 throw new HttpException (String.Format (format, ID, DataSourceID));
560                         }
561
562                         _boundDataSourceView = ((IDataSource)ctrl).GetView (String.Empty);
563                         _boundDataSourceView.DataSourceViewChanged += OnDataSourceViewChanged;
564                         return _boundDataSourceView;
565                 }
566
567                 protected internal override void OnPreRender (EventArgs e)
568                 {
569                         EnsureDataBound ();
570                         base.OnPreRender (e);
571
572                         Page page = Page;
573                         if (page != null && !Disabled) {
574                                 page.RegisterRequiresPostBack (this);
575                                 page.RegisterEnabledControl (this);
576                         }
577                 }
578
579                 protected virtual void OnServerChange (EventArgs e)
580                 {
581                         EventHandler handler = (EventHandler)Events[EventServerChange];
582
583                         if (handler != null) {
584                                 handler (this, e);
585                         }
586                 }
587                 
588                 protected override void RenderAttributes (HtmlTextWriter w)
589                 {
590                         Page page = Page;
591                         if (page != null)
592                                 page.ClientScript.RegisterForEventValidation (UniqueID);
593
594                         /* If there is no "name" attribute,
595                          * LoadPostData doesn't work...
596                          */
597                         w.WriteAttribute ("name", Name);
598                         Attributes.Remove ("name");
599                         
600                         /* Don't render the databinding attributes */
601                         Attributes.Remove ("datamember");
602                         Attributes.Remove ("datatextfield");
603                         Attributes.Remove ("datavaluefield");
604                         
605                         base.RenderAttributes (w);
606                 }
607                 
608                 protected internal override void RenderChildren (HtmlTextWriter w)
609                 {
610                         base.RenderChildren (w);
611
612                         if (items == null)
613                                 return;
614                         
615                         w.WriteLine ();
616
617                         bool done_sel = false;
618                         
619                         int count = items.Count;
620                         for (int i = 0; i < count; i++) {
621                                 ListItem item = items[i];
622                                 w.Indent++;
623                                 
624                                 /* Write the <option> elements this
625                                  * way so that the output HTML matches
626                                  * the ms version (can't make
627                                  * HtmlTextWriterTag.Option an inline
628                                  * element, cos that breaks other
629                                  * stuff.)
630                                  */
631                                 w.WriteBeginTag ("option");
632                                 if (item.Selected && !done_sel) {
633
634                                         w.WriteAttribute ("selected", "selected");
635
636                                         if (!Multiple) {
637                                                 done_sel = true;
638                                         }
639                                 }
640                                 
641                                 w.WriteAttribute ("value", item.Value, true);
642                                 if (item.HasAttributes) {
643                                         AttributeCollection attrs = item.Attributes;
644                                         foreach (string key in attrs.Keys)
645                                                 w.WriteAttribute (key, HttpUtility.HtmlAttributeEncode (attrs [key]));
646                                 }
647                                 w.Write (HtmlTextWriter.TagRightChar);
648                                 
649                                 w.Write (HttpUtility.HtmlEncode(item.Text));
650                                 w.WriteEndTag ("option");
651                                 w.WriteLine ();
652
653                                 w.Indent--;
654                         }
655                 }
656
657                 protected override object SaveViewState ()
658                 {
659                         object first = null;
660                         object second = null;
661
662                         first = base.SaveViewState ();
663
664                         IStateManager manager = items as IStateManager;
665                         if (manager != null) {
666                                 second = manager.SaveViewState ();
667                         }
668
669                         if (first == null && second == null)
670                                 return (null);
671
672                         return new Pair (first, second);
673                 }
674
675                 /* "internal infrastructure" according to the docs,
676                  * but has some documentation in 2.0
677                  */
678                 protected virtual void Select (int[] selectedIndices)
679                 {
680                         if (items == null) {
681                                 return;
682                         }
683
684                         ClearSelection ();
685                         
686                         int count = items.Count;
687                         foreach (int i in selectedIndices) {
688                                 if (i >= 0 && i < count) {
689                                         items[i].Selected = true;
690                                 }
691                         }
692                 }
693
694                 protected override void TrackViewState ()
695                 {
696                         base.TrackViewState ();
697
698                         IStateManager manager = items as IStateManager;
699                         if (manager != null) {
700                                 manager.TrackViewState ();
701                         }
702                 }
703
704                 protected virtual void RaisePostDataChangedEvent ()
705                 {
706                         OnServerChange (EventArgs.Empty);
707                 }
708
709                 protected virtual bool LoadPostData (string postDataKey, NameValueCollection postCollection)
710                 {
711                         /* postCollection contains the values that are
712                          * selected
713                          */
714
715                         string[] values = postCollection.GetValues (postDataKey);
716                         bool changed = false;
717
718                         if (values != null) {
719                                 if (Multiple) {
720                                         /* We have a set of
721                                          * selections.  We can't just
722                                          * set the new list, because
723                                          * we need to know if the set
724                                          * has changed from last time
725                                          */
726                                         int value_len = values.Length;
727                                         int[] old_sel = SelectedIndices;
728                                         int[] new_sel = new int[value_len];
729                                         int old_sel_len = old_sel.Length;
730                                         
731                                         for (int i = 0; i < value_len; i++) {
732                                                 new_sel[i] = Items.IndexOf (values[i]);
733                                                 if (old_sel_len != value_len ||
734                                                     old_sel[i] != new_sel[i]) {
735                                                         changed = true;
736                                                 }
737                                         }
738
739                                         if (changed) {
740                                                 Select (new_sel);
741                                         }
742                                 } else {
743                                         /* Just take the first one */
744                                         int sel = Items.IndexOf (values[0]);
745
746                                         if (sel != SelectedIndex) {
747                                                 SelectedIndex = sel;
748                                                 changed = true;
749                                         }
750                                 }
751                         }
752
753                         if (changed)
754                                 ValidateEvent (postDataKey, String.Empty);
755                         return (changed);
756                 }
757
758                 bool IPostBackDataHandler.LoadPostData (string postDataKey, NameValueCollection postCollection)
759                 {
760                         return LoadPostData (postDataKey, postCollection);
761                 }
762
763                 void IPostBackDataHandler.RaisePostDataChangedEvent ()
764                 {
765                         RaisePostDataChangedEvent ();
766                 }
767         }
768 }