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 		object defaultValue;
66 		string description;
67 		string displayName;
68 		bool? readOnly;
69 		int? maxLength;
70 		string nullDisplayText;
71 		string requiredErrorMessage;
72 		string uiHint;
73 
74 		// Attributes
75 		AttributeCollection attributes;
76 
77 		DisplayFormatAttribute displayFormatAttr;
78 		DataTypeAttribute dataTypeAttr;
79 		ScaffoldColumnAttribute scaffoldAttr;
80 		RequiredAttribute requiredAttr;
81 
MetaColumn(MetaTable table, ColumnProvider provider)82 		internal MetaColumn (MetaTable table, ColumnProvider provider)
83 		{
84 			Table = table;
85 			Provider = provider;
86 			Model = table.Model;
87 			HtmlEncode = true;
88 
89 			Type columnType = ColumnType;
90 			TypeCode code = Type.GetTypeCode (columnType);
91 			TypeCode = code;
92 			switch (code) {
93 				case TypeCode.Single:
94 				case TypeCode.Double:
95 				case TypeCode.Decimal:
96 					IsFloatingPoint = true;
97 					break;
98 
99 				case TypeCode.Byte:
100 				case TypeCode.Int16:
101 				case TypeCode.Int32:
102 				case TypeCode.Int64:
103 					IsInteger = true;
104 					break;
105 
106 				case TypeCode.String:
107 					IsString = true;
108 					break;
109 
110 				case TypeCode.Object:
111 					// So far only byte[] seems to be treated as a binary type
112 					if (columnType.IsArray && columnType.GetArrayRank () == 1 && columnType.GetElementType () == typeof (byte))
113 						IsBinaryData = true;
114 					break;
115 
116 				default:
117 					TypeCode = TypeCode.Object;
118 					break;
119 			}
120 
121 			IsLongString = MaxLength > SHORT_STRING_MAX_LENGTH;
122 		}
123 
124 		public bool ApplyFormatInEditMode {
125 			get {
126 				if (applyFormatInEditMode == null)
127 					applyFormatInEditMode = CheckApplyFormatInEditMode ();
128 
129 				return (bool)applyFormatInEditMode;
130 			}
131 		}
132 
133 		public AttributeCollection Attributes {
134 			get {
135 				if (attributes == null)
136 					attributes = LoadAttributes ();
137 
138 				return attributes;
139 			}
140 
141 		}
142 
143 		public Type ColumnType {
144 			get { return Provider.ColumnType; }
145 		}
146 
147 		public bool ConvertEmptyStringToNull {
148 			get {
149 				if (convertEmptyStringToNull == null)
150 					convertEmptyStringToNull = CheckConvertEmptyStringToNull ();
151 
152 				return (bool)convertEmptyStringToNull;
153 			}
154 
155 		}
156 
157 		public string DataFormatString {
158 			get {
159 				if (dataFormatString == null)
160 					dataFormatString = CheckDataFormatString ();
161 
162 				return dataFormatString;
163 			}
164 		}
165 
166 		public DataTypeAttribute DataTypeAttribute {
167 			get {
168 				if (!dataTypeReflected && dataTypeAttr == null)
169 					dataTypeAttr = CheckDataTypeAttribute ();
170 
171 				return dataTypeAttr;
172 			}
173 
174 		}
175 
176 		public Object DefaultValue {
177 			get {
178 				if (!defaultValueReflected && defaultValue == null) {
179 					DefaultValueAttribute defaultValueAttr = CheckDefaultValueAttribute ();
180 					if (defaultValueAttr != null)
181 						defaultValue = defaultValueAttr.Value;
182 				}
183 
184 				return defaultValue;
185 			}
186 		}
187 
188 		public string Description {
189 			get {
190 				if (!descriptionReflected && description == null) {
191 					DescriptionAttribute descriptionAttr = CheckDescriptionAttribute ();
192 					if (descriptionAttr != null)
193 						description = descriptionAttr.Description;
194 				}
195 
196 				return description;
197 			}
198 		}
199 
200 		public string DisplayName {
201 			get {
202 				if (displayName == null)
203 					displayName = CheckDisplayName ();
204 
205 				return displayName;
206 			}
207 		}
208 
209 		public PropertyInfo EntityTypeProperty {
210 			get { return Provider.EntityTypeProperty; }
211 		}
212 
213 		public bool HtmlEncode { get; private set; }
214 
215 		public bool IsBinaryData { get; private set; }
216 
217 		public bool IsCustomProperty {
218 			get { return Provider.IsCustomProperty; }
219 		}
220 
221 		public bool IsFloatingPoint { get; private set; }
222 
223 		public bool IsForeignKeyComponent {
224 			get { return Provider.IsForeignKeyComponent; }
225 		}
226 
227 		public bool IsGenerated {
228 			get { return Provider.IsGenerated; }
229 		}
230 
231 		public bool IsInteger { get; private set; }
232 
233 		public bool IsLongString { get; private set; }
234 
235 		public bool IsPrimaryKey {
236 			get { return Provider.IsPrimaryKey; }
237 		}
238 
239 		public bool IsReadOnly {
240 			get {
241 				if (readOnly == null)
242 					readOnly = CheckReadOnlyAttribute ();
243 
244 				return (bool)readOnly;
245 			}
246 		}
247 
248 		// It appears that all columns are required unless Provider.Nullable is true for
249 		// them. We could skip checking for the RequiredAttribute for that reason, but that
250 		// way we wouldn't be forward-compatible.
251 		// What's more, it appears that a RequiredAttribute instance is always included in
252 		// Attributes, whether or not the corresponding field is decorataed with it.
253 		public bool IsRequired {
254 			get {
255 				if (!requiredReflected && requiredAttr == null)
256 					requiredAttr = CheckRequiredAttribute ();
257 
258 				return requiredAttr != null;
259 			}
260 		}
261 
262 		public bool IsString { get; private set; }
263 
264 		public int MaxLength {
265 			get {
266 				if (maxLength == null)
267 					maxLength = CheckMaxLength ();
268 
269 				return (int)maxLength;
270 			}
271 		}
272 
273 		public MetaModel Model { get; private set; }
274 
275 		public string Name {
276 			get { return Provider.Name; }
277 		}
278 
279 		public string NullDisplayText {
280 			get {
281 				if (nullDisplayText == null)
282 					nullDisplayText = CheckNullDisplayText ();
283 
284 				return nullDisplayText;
285 			}
286 		}
287 
288 		public ColumnProvider Provider { get; private set; }
289 
290 		public string RequiredErrorMessage {
291 			get {
292 				if (requiredErrorMessage == null) {
293 					RequiredAttribute attr = CheckRequiredAttribute ();
294 					if (attr == null)
295 						requiredErrorMessage = String.Empty;
296 					else
297 						requiredErrorMessage = attr.ErrorMessage;
298 				}
299 
300 				return requiredErrorMessage;
301 			}
302 		}
303 
304 		public bool Scaffold {
305 			get {
306 				if (scaffold != null)
307 					return (bool)scaffold;
308 				if (scaffoldReflected != null)
309 					return (bool)scaffoldReflected;
310 
311 				MetaModel.GetDataFieldAttribute <ScaffoldColumnAttribute> (Attributes, ref scaffoldAttr);
312 				if (scaffoldAttr != null) {
313 					scaffoldReflected = scaffoldAttr.Scaffold;
314 					return (bool)scaffoldReflected;
315 				}
316 
317 				string uiHint = UIHint;
318 				if (!String.IsNullOrEmpty (uiHint))
319 					scaffoldReflected = true;
320 				// LAMESPEC: IsForeignKeyComponent does NOT set Scaffold=false
321 				else if (IsGenerated || IsCustomProperty)
322 					scaffoldReflected = false;
323 				else if (Table.ScaffoldAllTables)
324 					scaffoldReflected = true;
325 				else
326 					scaffoldReflected = true;
327 
328 				return (bool)scaffoldReflected;
329 			}
330 
331 			set { scaffold = value; }
332 		}
333 
334 		public string SortExpression {
335 			get {
336 				ColumnProvider provider = Provider;
337 				if (provider.IsSortable)
338 					return Name;
339 
340 				return String.Empty;
341 			}
342 		}
343 
344 		public MetaTable Table { get; private set; }
345 
346 		public TypeCode TypeCode { get; private set; }
347 
348 		// LAMESPEC: if there's no attribute, null is returned
349 		public string UIHint {
350 			get {
351 				if (!uiHintReflected && uiHint == null)
352 					uiHint = CheckUIHintAttribute ();
353 
354 				return uiHint;
355 			}
356 		}
357 
CheckUIHintAttribute()358 		string CheckUIHintAttribute ()
359 		{
360 			if (uiHintReflected)
361 				return uiHint;
362 
363 			uiHintReflected = true;
364 			UIHintAttribute attr = null;
365 			MetaModel.GetDataFieldAttribute <UIHintAttribute> (Attributes, ref attr);
366 
367 			if (attr == null)
368 				return null;
369 
370 			return attr.UIHint;
371 		}
372 
CheckApplyFormatInEditMode()373 		bool CheckApplyFormatInEditMode ()
374 		{
375 			var displayFormat = GetDisplayFormat ();
376 			if (displayFormat == null)
377 				return false;
378 
379 			return displayFormat.ApplyFormatInEditMode;
380 		}
381 
CheckConvertEmptyStringToNull()382 		bool CheckConvertEmptyStringToNull ()
383 		{
384 			var displayFormat = GetDisplayFormat ();
385 			if (displayFormat == null)
386 				return true;
387 
388 			return displayFormat.ConvertEmptyStringToNull;
389 		}
390 
CheckDataFormatString()391 		string CheckDataFormatString ()
392 		{
393 			var displayFormat = GetDisplayFormat ();
394 			if (displayFormat == null)
395 				return String.Empty;
396 
397 			return displayFormat.DataFormatString;
398 		}
399 
CheckDataTypeAttribute()400 		DataTypeAttribute CheckDataTypeAttribute ()
401 		{
402 			if (dataTypeReflected)
403 				return dataTypeAttr;
404 
405 			dataTypeReflected = true;
406 			MetaModel.GetDataFieldAttribute <DataTypeAttribute> (Attributes, ref dataTypeAttr);
407 			if (dataTypeAttr == null && (ColumnType == typeof (string)))
408 				return new DataTypeAttribute (IsLongString ? DataType.MultilineText : DataType.Text);
409 
410 			return dataTypeAttr;
411 		}
412 
CheckDefaultValueAttribute()413 		DefaultValueAttribute CheckDefaultValueAttribute ()
414 		{
415 			defaultValueReflected = true;
416 			DefaultValueAttribute dummy = null;
417 			MetaModel.GetDataFieldAttribute <DefaultValueAttribute> (Attributes, ref dummy);
418 			if (dummy == null)
419 				return null;
420 
421 			return dummy;
422 		}
423 
CheckDescriptionAttribute()424 		DescriptionAttribute CheckDescriptionAttribute ()
425 		{
426 			descriptionReflected = true;
427 			DescriptionAttribute dummy = null;
428 			MetaModel.GetDataFieldAttribute <DescriptionAttribute> (Attributes, ref dummy);
429 			if (dummy == null)
430 				return null;
431 
432 			return dummy;
433 		}
434 
CheckDisplayName()435 		string CheckDisplayName ()
436 		{
437 			DisplayNameAttribute attr = null;
438 			MetaModel.GetDataFieldAttribute <DisplayNameAttribute> (Attributes, ref attr);
439 			if (attr != null)
440 				return attr.DisplayName;
441 
442 			return Name;
443 		}
444 
CheckRequiredAttribute()445 		RequiredAttribute CheckRequiredAttribute ()
446 		{
447 			if (requiredReflected)
448 				return requiredAttr;
449 
450 			requiredReflected = true;
451 			MetaModel.GetDataFieldAttribute <RequiredAttribute> (Attributes, ref requiredAttr);
452 
453 			return requiredAttr;
454 		}
455 
CheckReadOnlyAttribute()456 		bool CheckReadOnlyAttribute ()
457 		{
458 			ReadOnlyAttribute attr = null;
459 			MetaModel.GetDataFieldAttribute <ReadOnlyAttribute> (Attributes, ref attr);
460 
461 			// Apparently attr.IsReadOnly and/or comparisons to
462 			// ReadOnlyAttribute.{Yes,No} don't matter. The sole presence of the
463 			// attribute marks column as read-only
464 			return attr != null;
465 		}
466 
CheckMaxLength()467 		int CheckMaxLength ()
468 		{
469 			StringLengthAttribute attr = null;
470 			MetaModel.GetDataFieldAttribute <StringLengthAttribute> (Attributes, ref attr);
471 
472 			if (attr != null)
473 				return attr.MaximumLength;
474 
475 			return Provider.MaxLength;
476 		}
477 
CheckNullDisplayText()478 		string CheckNullDisplayText ()
479 		{
480 			DisplayFormatAttribute displayFormat = GetDisplayFormat ();
481 
482 			if (displayFormat == null)
483 				return String.Empty;
484 
485 			return displayFormat.NullDisplayText;
486 		}
487 
GetDisplayFormat()488 		DisplayFormatAttribute GetDisplayFormat ()
489 		{
490 			MetaModel.GetDataFieldAttribute <DisplayFormatAttribute> (Attributes, ref displayFormatAttr);
491 			if (displayFormatAttr == null) {
492 				var dta = DataTypeAttribute;
493 				displayFormatAttr = dta == null ? null : dta.DisplayFormat;
494 			}
495 
496 			return displayFormatAttr;
497 		}
498 
Init()499 		internal virtual void Init ()
500 		{
501 		}
502 
LoadAttributes()503 		AttributeCollection LoadAttributes ()
504 		{
505 			var props = MetaModel.GetTypeDescriptor (Table.EntityType).GetProperties ();
506 			AttributeCollection reflected;
507 
508 			int propsCount = props == null ? 0 : props.Count;
509 			if (propsCount == 0)
510 				reflected = AttributeCollection.Empty;
511 			else {
512 				var property = props.Find (Name, true);
513 				if (property == null)
514 					reflected = AttributeCollection.Empty;
515 				else
516 					reflected = property.Attributes;
517 			}
518 
519 			if (!Provider.Nullable && reflected.OfType <RequiredAttribute> ().Count () == 0)
520 				reflected = AttributeCollection.FromExisting (reflected, new Attribute[] { new RequiredAttribute () });
521 
522 			return reflected;
523 		}
524 
ToString()525  		public override string ToString ()
526 		{
527 			return Name;
528 		}
529 	}
530 }
531