2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.Web.DynamicData / System.Web.DynamicData / MetaColumn.cs
1 //
2 // MetaColumn.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //      Marek Habersack <mhabersack@novell.com>
7 //
8 // Copyright (C) 2008-2009 Novell Inc. http://novell.com
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 using System;
32 using System.Collections;
33 using System.Collections.Generic;
34 using System.Collections.Specialized;
35 using System.ComponentModel;
36 using System.ComponentModel.DataAnnotations;
37 using System.Globalization;
38 using System.Linq;
39 using System.Reflection;
40 using System.Security.Permissions;
41 using System.Security.Principal;
42 using System.Web.Caching;
43 using System.Web.DynamicData.ModelProviders;
44
45 namespace System.Web.DynamicData
46 {
47         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
48         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
49         public class MetaColumn : IFieldFormattingOptions
50         {
51                 // (Int32.MaxValue / 2) - 5
52                 const int SHORT_STRING_MAX_LENGTH = 0x3ffffffa;
53
54                 bool? scaffold;
55                 bool? scaffoldReflected;
56                 bool? applyFormatInEditMode;
57                 bool? convertEmptyStringToNull;
58                 bool dataTypeReflected;
59                 bool defaultValueReflected;
60                 bool descriptionReflected;
61                 bool requiredReflected;
62                 bool uiHintReflected;
63                 
64                 string dataFormatString;
65                 PropertyDescriptor property;
66                 object defaultValue;
67                 string description;
68                 string displayName;
69                 bool? readOnly;
70                 int? maxLength;
71                 string nullDisplayText;
72                 string requiredErrorMessage;
73                 string uiHint;
74                 
75                 // Attributes
76                 AttributeCollection attributes;
77                 
78                 DisplayFormatAttribute displayFormatAttr;
79                 DataTypeAttribute dataTypeAttr;
80                 ScaffoldColumnAttribute scaffoldAttr;
81                 RequiredAttribute requiredAttr;
82                 
83                 internal MetaColumn (MetaTable table, ColumnProvider provider)
84                 {
85                         Table = table;
86                         Provider = provider;
87                         Model = table.Model;
88                         HtmlEncode = true;
89
90                         Type columnType = ColumnType;
91                         TypeCode code = Type.GetTypeCode (columnType);
92                         TypeCode = code;
93                         switch (code) {
94                                 case TypeCode.Single:
95                                 case TypeCode.Double:
96                                 case TypeCode.Decimal:
97                                         IsFloatingPoint = true;
98                                         break;
99
100                                 case TypeCode.Byte:
101                                 case TypeCode.Int16:
102                                 case TypeCode.Int32:
103                                 case TypeCode.Int64:
104                                         IsInteger = true;
105                                         break;
106
107                                 case TypeCode.String:
108                                         IsString = true;
109                                         break;
110
111                                 case TypeCode.Object:
112                                         // So far only byte[] seems to be treated as a binary type
113                                         if (columnType.IsArray && columnType.GetArrayRank () == 1 && columnType.GetElementType () == typeof (byte))
114                                                 IsBinaryData = true;
115                                         break;
116
117                                 default:
118                                         TypeCode = TypeCode.Object;
119                                         break;
120                         }
121
122                         IsLongString = MaxLength > SHORT_STRING_MAX_LENGTH;
123                 }
124
125                 public bool ApplyFormatInEditMode {
126                         get {
127                                 if (applyFormatInEditMode == null)
128                                         applyFormatInEditMode = CheckApplyFormatInEditMode ();
129
130                                 return (bool)applyFormatInEditMode;
131                         }
132                 }
133
134                 public AttributeCollection Attributes {
135                         get {
136                                 if (attributes == null)
137                                         attributes = LoadAttributes ();
138                                 
139                                 return attributes;
140                         }
141                         
142                 }
143
144                 public Type ColumnType {
145                         get { return Provider.ColumnType; }
146                 }
147
148                 public bool ConvertEmptyStringToNull {
149                         get {
150                                 if (convertEmptyStringToNull == null)
151                                         convertEmptyStringToNull = CheckConvertEmptyStringToNull ();
152
153                                 return (bool)convertEmptyStringToNull;
154                         }
155                         
156                 }
157
158                 public string DataFormatString {
159                         get {
160                                 if (dataFormatString == null)
161                                         dataFormatString = CheckDataFormatString ();
162
163                                 return dataFormatString;
164                         }
165                 }
166
167                 public DataTypeAttribute DataTypeAttribute {
168                         get {
169                                 if (!dataTypeReflected && dataTypeAttr == null)
170                                         dataTypeAttr = CheckDataTypeAttribute ();
171                                         
172                                 return dataTypeAttr;
173                         }
174                         
175                 }
176
177                 public Object DefaultValue {
178                         get {
179                                 if (!defaultValueReflected && defaultValue == null) {
180                                         DefaultValueAttribute defaultValueAttr = CheckDefaultValueAttribute ();
181                                         if (defaultValueAttr != null)
182                                                 defaultValue = defaultValueAttr.Value;
183                                 }
184                                 
185                                 return defaultValue;
186                         }
187                 }
188
189                 public string Description {
190                         get {
191                                 if (!descriptionReflected && description == null) {
192                                         DescriptionAttribute descriptionAttr = CheckDescriptionAttribute ();
193                                         if (descriptionAttr != null)
194                                                 description = descriptionAttr.Description;
195                                 }
196
197                                 return description;
198                         }
199                 }
200
201                 public string DisplayName {
202                         get {
203                                 if (displayName == null)
204                                         displayName = CheckDisplayName ();
205
206                                 return displayName;
207                         }
208                 }
209
210                 public PropertyInfo EntityTypeProperty {
211                         get { return Provider.EntityTypeProperty; }
212                 }
213
214                 public bool HtmlEncode { get; private set; }
215
216                 public bool IsBinaryData { get; private set; }
217
218                 public bool IsCustomProperty {
219                         get { return Provider.IsCustomProperty; }
220                 }
221
222                 public bool IsFloatingPoint { get; private set; }
223
224                 public bool IsForeignKeyComponent {
225                         get { return Provider.IsForeignKeyComponent; }
226                 }
227
228                 public bool IsGenerated {
229                         get { return Provider.IsGenerated; }
230                 }
231
232                 public bool IsInteger { get; private set; }
233
234                 public bool IsLongString { get; private set; }
235
236                 public bool IsPrimaryKey {
237                         get { return Provider.IsPrimaryKey; }
238                 }
239
240                 public bool IsReadOnly {
241                         get {
242                                 if (readOnly == null)
243                                         readOnly = CheckReadOnlyAttribute ();
244
245                                 return (bool)readOnly;
246                         }
247                 }
248
249                 // It appears that all columns are required unless Provider.Nullable is true for
250                 // them. We could skip checking for the RequiredAttribute for that reason, but that
251                 // way we wouldn't be forward-compatible.
252                 // What's more, it appears that a RequiredAttribute instance is always included in
253                 // Attributes, whether or not the corresponding field is decorataed with it.
254                 public bool IsRequired {
255                         get {
256                                 if (!requiredReflected && requiredAttr == null)
257                                         requiredAttr = CheckRequiredAttribute ();
258
259                                 return requiredAttr != null;
260                         }
261                 }
262
263                 public bool IsString { get; private set; }
264
265                 public int MaxLength {
266                         get {
267                                 if (maxLength == null)
268                                         maxLength = CheckMaxLength ();
269
270                                 return (int)maxLength;
271                         }
272                 }
273
274                 public MetaModel Model { get; private set; }
275
276                 public string Name {
277                         get { return Provider.Name; }
278                 }
279
280                 public string NullDisplayText {
281                         get {
282                                 if (nullDisplayText == null)
283                                         nullDisplayText = CheckNullDisplayText ();
284
285                                 return nullDisplayText;
286                         }
287                 }
288
289                 public ColumnProvider Provider { get; private set; }
290
291                 public string RequiredErrorMessage {
292                         get {
293                                 if (requiredErrorMessage == null) {
294                                         RequiredAttribute attr = CheckRequiredAttribute ();
295                                         if (attr == null)
296                                                 requiredErrorMessage = String.Empty;
297                                         else
298                                                 requiredErrorMessage = attr.ErrorMessage;
299                                 }
300
301                                 return requiredErrorMessage;
302                         }
303                 }
304
305                 public bool Scaffold {
306                         get {
307                                 if (scaffold != null)
308                                         return (bool)scaffold;
309                                 if (scaffoldReflected != null)
310                                         return (bool)scaffoldReflected;                         
311
312                                 MetaModel.GetDataFieldAttribute <ScaffoldColumnAttribute> (Attributes, ref scaffoldAttr);
313                                 if (scaffoldAttr != null) {
314                                         scaffoldReflected = scaffoldAttr.Scaffold;
315                                         return (bool)scaffoldReflected;
316                                 }
317
318                                 string uiHint = UIHint;
319                                 if (!String.IsNullOrEmpty (uiHint))
320                                         scaffoldReflected = true;
321                                 // LAMESPEC: IsForeignKeyComponent does NOT set Scaffold=false
322                                 else if (IsGenerated || IsCustomProperty)
323                                         scaffoldReflected = false;
324                                 else if (Table.ScaffoldAllTables)
325                                         scaffoldReflected = true;
326                                 else
327                                         scaffoldReflected = true;
328
329                                 return (bool)scaffoldReflected;
330                         }
331                         
332                         set { scaffold = value; }
333                 }
334
335                 public string SortExpression {
336                         get {
337                                 ColumnProvider provider = Provider;
338                                 if (provider.IsSortable)
339                                         return Name;
340
341                                 return String.Empty;
342                         }
343                 }
344
345                 public MetaTable Table { get; private set; }
346
347                 public TypeCode TypeCode { get; private set; }
348
349                 // LAMESPEC: if there's no attribute, null is returned
350                 public string UIHint {
351                         get {
352                                 if (!uiHintReflected && uiHint == null)
353                                         uiHint = CheckUIHintAttribute ();
354                                 
355                                 return uiHint;
356                         }
357                 }
358
359                 string CheckUIHintAttribute ()
360                 {
361                         if (uiHintReflected)
362                                 return uiHint;
363
364                         uiHintReflected = true;
365                         UIHintAttribute attr = null;
366                         MetaModel.GetDataFieldAttribute <UIHintAttribute> (Attributes, ref attr);
367
368                         if (attr == null)
369                                 return null;
370
371                         return attr.UIHint;
372                 }
373                 
374                 bool CheckApplyFormatInEditMode ()
375                 {
376                         var displayFormat = GetDisplayFormat ();
377                         if (displayFormat == null)
378                                 return false;
379
380                         return displayFormat.ApplyFormatInEditMode;
381                 }
382
383                 bool CheckConvertEmptyStringToNull ()
384                 {
385                         var displayFormat = GetDisplayFormat ();
386                         if (displayFormat == null)
387                                 return true;
388
389                         return displayFormat.ConvertEmptyStringToNull;
390                 }
391
392                 string CheckDataFormatString ()
393                 {
394                         var displayFormat = GetDisplayFormat ();
395                         if (displayFormat == null)
396                                 return String.Empty;
397
398                         return displayFormat.DataFormatString;
399                 }
400
401                 DataTypeAttribute CheckDataTypeAttribute ()
402                 {
403                         if (dataTypeReflected)
404                                 return dataTypeAttr;
405
406                         dataTypeReflected = true;
407                         MetaModel.GetDataFieldAttribute <DataTypeAttribute> (Attributes, ref dataTypeAttr);
408                         if (dataTypeAttr == null && (ColumnType == typeof (string)))
409                                 return new DataTypeAttribute (DataType.Text);
410
411                         return dataTypeAttr;
412                 }
413
414                 DefaultValueAttribute CheckDefaultValueAttribute ()
415                 {
416                         defaultValueReflected = true;
417                         DefaultValueAttribute dummy = null;
418                         MetaModel.GetDataFieldAttribute <DefaultValueAttribute> (Attributes, ref dummy);
419                         if (dummy == null)
420                                 return null;
421
422                         return dummy;
423                 }
424
425                 DescriptionAttribute CheckDescriptionAttribute ()
426                 {
427                         descriptionReflected = true;
428                         DescriptionAttribute dummy = null;
429                         MetaModel.GetDataFieldAttribute <DescriptionAttribute> (Attributes, ref dummy);
430                         if (dummy == null)
431                                 return null;
432                         
433                         return dummy;
434                 }
435
436                 string CheckDisplayName ()
437                 {
438                         DisplayNameAttribute attr = null;
439                         MetaModel.GetDataFieldAttribute <DisplayNameAttribute> (Attributes, ref attr);
440                         if (attr != null)
441                                 return attr.DisplayName;
442
443                         return Name;
444                 }
445
446                 RequiredAttribute CheckRequiredAttribute ()
447                 {
448                         if (requiredReflected)
449                                 return requiredAttr;
450
451                         requiredReflected = true;
452                         MetaModel.GetDataFieldAttribute <RequiredAttribute> (Attributes, ref requiredAttr);
453
454                         return requiredAttr;
455                 }
456
457                 bool CheckReadOnlyAttribute ()
458                 {
459                         ReadOnlyAttribute attr = null;
460                         MetaModel.GetDataFieldAttribute <ReadOnlyAttribute> (Attributes, ref attr);
461
462                         // Apparently attr.IsReadOnly and/or comparisons to
463                         // ReadOnlyAttribute.{Yes,No} don't matter. The sole presence of the
464                         // attribute marks column as read-only
465                         return attr != null;
466                 }
467
468                 int CheckMaxLength ()
469                 {
470                         StringLengthAttribute attr = null;
471                         MetaModel.GetDataFieldAttribute <StringLengthAttribute> (Attributes, ref attr);
472
473                         if (attr != null)
474                                 return attr.MaximumLength;
475                         
476                         return Provider.MaxLength;
477                 }
478
479                 string CheckNullDisplayText ()
480                 {
481                         DisplayFormatAttribute displayFormat = GetDisplayFormat ();
482
483                         if (displayFormat == null)
484                                 return String.Empty;
485
486                         return displayFormat.NullDisplayText;
487                 }
488
489                 DisplayFormatAttribute GetDisplayFormat ()
490                 {
491                         MetaModel.GetDataFieldAttribute <DisplayFormatAttribute> (Attributes, ref displayFormatAttr);
492                         if (displayFormatAttr == null) {
493                                 var dta = DataTypeAttribute;
494                                 displayFormatAttr = dta == null ? null : dta.DisplayFormat;
495                         }
496                         
497                         return displayFormatAttr;
498                 }
499                 
500                 AttributeCollection LoadAttributes ()
501                 {
502                         var props = MetaModel.GetTypeDescriptor (Table.EntityType).GetProperties ();
503                         AttributeCollection reflected;
504
505                         int propsCount = props == null ? 0 : props.Count;
506                         if (propsCount == 0)
507                                 reflected = AttributeCollection.Empty;
508                         else {
509                                 var property = props.Find (Name, true);
510                                 if (property == null)
511                                         reflected = AttributeCollection.Empty;
512                                 else
513                                         reflected = property.Attributes;
514                         }
515
516                         if (!Provider.Nullable && reflected.OfType <RequiredAttribute> ().Count () == 0)
517                                 reflected = AttributeCollection.FromExisting (reflected, new Attribute[] { new RequiredAttribute () });
518                         
519                         return reflected;
520                 }
521                 
522                 public override string ToString ()
523                 {
524                         return Name;
525                 }
526         }
527 }