2 // FieldTemplateFactory.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
6 // Marek Habersack <mhabersack@novell.com>
8 // Copyright (C) 2008-2009 Novell Inc. http://novell.com
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
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
;
39 using System
.Security
.Permissions
;
40 using System
.Security
.Principal
;
41 using System
.Web
.Caching
;
42 using System
.Web
.Compilation
;
43 using System
.Web
.Hosting
;
44 using System
.Web
.UI
.WebControls
;
46 namespace System
.Web
.DynamicData
48 [AspNetHostingPermission (SecurityAction
.LinkDemand
, Level
= AspNetHostingPermissionLevel
.Minimal
)]
49 [AspNetHostingPermission (SecurityAction
.InheritanceDemand
, Level
= AspNetHostingPermissionLevel
.Minimal
)]
50 public class FieldTemplateFactory
: IFieldTemplateFactory
52 const string DEFAULT_TEMPLATE_FOLDER_VIRTUAL_PATH
= "FieldTemplates/";
54 static readonly Dictionary
<Type
, Type
> typeFallbacks
= new Dictionary
<Type
, Type
> () {
55 {typeof (float), typeof (decimal)}
,
56 {typeof (double), typeof (decimal)}
,
57 {typeof (short), typeof (int)}
,
58 {typeof (long), typeof (int)}
,
59 {typeof (byte), typeof (int)}
,
60 {typeof (char), typeof (string)}
,
61 {typeof (int), typeof (string)}
,
62 {typeof (decimal), typeof (string)}
,
63 {typeof (Guid), typeof (string)}
,
64 {typeof (DateTime), typeof (string)}
,
65 {typeof (DateTimeOffset), typeof (string)}
,
66 {typeof (TimeSpan), typeof (string)}
69 string templateFolderVirtualPath
;
70 string userTemplateVirtualPath
;
72 public MetaModel Model { get; private set; }
74 public string TemplateFolderVirtualPath
{
76 if (templateFolderVirtualPath
== null) {
78 string virtualPath
= userTemplateVirtualPath
== null ? DEFAULT_TEMPLATE_FOLDER_VIRTUAL_PATH
: userTemplateVirtualPath
;
81 templateFolderVirtualPath
= VirtualPathUtility
.Combine (m
.DynamicDataFolderVirtualPath
, virtualPath
);
83 templateFolderVirtualPath
= virtualPath
;
85 templateFolderVirtualPath
= VirtualPathUtility
.AppendTrailingSlash (templateFolderVirtualPath
);
88 return templateFolderVirtualPath
;
92 userTemplateVirtualPath
= value;
93 templateFolderVirtualPath
= null;
97 public virtual string BuildVirtualPath (string templateName
, MetaColumn column
, DataBoundControlMode mode
)
99 // Tests show the 'column' parameter is not used here
101 if (String
.IsNullOrEmpty (templateName
))
102 throw new ArgumentNullException ("templateName");
104 string basePath
= TemplateFolderVirtualPath
;
109 case DataBoundControlMode
.ReadOnly
:
110 suffix
= String
.Empty
;
113 case DataBoundControlMode
.Edit
:
117 case DataBoundControlMode
.Insert
:
122 return basePath
+ templateName
+ suffix
+ ".ascx";
125 public virtual IFieldTemplate
CreateFieldTemplate (MetaColumn column
, DataBoundControlMode mode
, string uiHint
)
127 // NO checks are made on parameters in .NET, but well "handle" the NREX
128 // throws in the other methods
129 string virtualPath
= GetFieldTemplateVirtualPath (column
, mode
, uiHint
);
130 if (String
.IsNullOrEmpty (virtualPath
))
133 return BuildManager
.CreateInstanceFromVirtualPath (virtualPath
, typeof (IFieldTemplate
)) as IFieldTemplate
;
136 public virtual string GetFieldTemplateVirtualPath (MetaColumn column
, DataBoundControlMode mode
, string uiHint
)
138 // NO checks are made on parameters in .NET, but well "handle" the NREX
139 // throws in the other methods
140 DataBoundControlMode newMode
= PreprocessMode (column
, mode
);
142 // The algorithm is as follows:
144 // 1. If column has a DataTypeAttribute on it, get the data type
145 // - if it's Custom data type, uiHint is used unconditionally
146 // - if it's not a custom type, ignore uiHint and choose template based
149 // 2. If #1 is false and uiHint is not empty, use uiHint if the template
152 // 3. If #2 is false, look up type according to the following algorithm:
154 // 1. lookup column type's full name
155 // 2. if #1 fails, look up short type name
156 // 3. if #2 fails, map type to special type name (Int -> Integer, String
158 // 4. if #3 fails, try to find a fallback type
159 // 5. if #4 fails, check if it's a foreign key or child column
160 // 6. if #5 fails, return null
162 // From: http://msdn.microsoft.com/en-us/library/cc488523.aspx (augmented)
165 DataTypeAttribute attr
= column
.DataTypeAttribute
;
166 bool uiHintPresent
= !String
.IsNullOrEmpty (uiHint
);
167 string templatePath
= null;
168 int step
= uiHintPresent
? 0 : 1;
169 Type columnType
= column
.ColumnType
;
171 if (!uiHintPresent
&& attr
== null) {
172 if (column
is MetaChildrenColumn
)
173 templatePath
= GetExistingTemplateVirtualPath ("Children", column
, newMode
);
174 else if (column
is MetaForeignKeyColumn
)
175 templatePath
= GetExistingTemplateVirtualPath ("ForeignKey", column
, newMode
);
178 while (step
< 6 && templatePath
== null) {
181 templatePath
= GetExistingTemplateVirtualPath (uiHint
, column
, newMode
);
186 templatePath
= GetTemplateForDataType (attr
.DataType
, attr
.GetDataTypeName (), uiHint
, column
, newMode
);
190 templatePath
= GetExistingTemplateVirtualPath (columnType
.FullName
, column
, newMode
);
194 templatePath
= GetExistingTemplateVirtualPath (columnType
.Name
, column
, newMode
);
198 templatePath
= ColumnTypeToSpecialName (columnType
, column
, newMode
);
202 columnType
= GetFallbackType (columnType
, column
, newMode
);
203 if (columnType
== null)
206 step
= uiHintPresent
? 0 : 1;
216 Type
GetFallbackType (Type columnType
, MetaColumn column
, DataBoundControlMode mode
)
219 if (typeFallbacks
.TryGetValue (columnType
, out ret
))
225 string ColumnTypeToSpecialName (Type columnType
, MetaColumn column
, DataBoundControlMode mode
)
227 if (columnType
== typeof (int))
228 return GetExistingTemplateVirtualPath ("Integer", column
, mode
);
230 if (columnType
== typeof (string))
231 return GetExistingTemplateVirtualPath ("Text", column
, mode
);
236 string GetExistingTemplateVirtualPath (string baseName
, MetaColumn column
, DataBoundControlMode mode
)
238 string templatePath
= BuildVirtualPath (baseName
, column
, mode
);
239 if (String
.IsNullOrEmpty (templatePath
))
242 // TODO: cache positive hits (and watch for removal events on those)
243 string physicalPath
= HostingEnvironment
.MapPath (templatePath
);
244 if (File
.Exists (physicalPath
))
250 string GetTemplateForDataType (DataType dataType
, string customDataType
, string uiHint
, MetaColumn column
, DataBoundControlMode mode
)
253 case DataType
.Custom
:
254 return GetExistingTemplateVirtualPath (customDataType
, column
, mode
);
256 case DataType
.DateTime
:
257 return GetExistingTemplateVirtualPath ("DateTime", column
, mode
);
259 case DataType
.MultilineText
:
260 return GetExistingTemplateVirtualPath ("MultilineText", column
, mode
);
263 return GetExistingTemplateVirtualPath ("Text", column
, mode
);
267 public virtual void Initialize (MetaModel model
)
272 public virtual DataBoundControlMode
PreprocessMode (MetaColumn column
, DataBoundControlMode mode
)
274 // In good tradition of .NET's DynamicData, let's not check the
277 throw new NullReferenceException ();
279 if (column
.IsGenerated
)
280 return DataBoundControlMode
.ReadOnly
;
282 if (column
.IsPrimaryKey
) {
283 if (mode
== DataBoundControlMode
.Edit
)
284 return DataBoundControlMode
.ReadOnly
;