1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle
.MonoRail
.ActiveRecordSupport
.Scaffold
.Helpers
18 using System
.Collections
;
19 using System
.Collections
.Specialized
;
21 using System
.Reflection
;
22 using Castle
.ActiveRecord
;
23 using Castle
.ActiveRecord
.Framework
.Internal
;
24 using Castle
.MonoRail
.Framework
.Helpers
;
26 public class ARFormHelper
: FormHelper
28 private StringBuilder stringBuilder
= new StringBuilder(1024);
30 private IDictionary model2nestedInstance
= new Hashtable();
32 private static readonly int[] Months
= { 1,2,3,4,5,6,7,8,9,10,11,12 }
;
33 private static readonly int[] Days
= { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
34 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
35 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
36 private static readonly int[] Years
;
37 private TextHelper textHelper
= new TextHelper();
41 int lastYear
= DateTime
.Now
.Year
;
43 Years
= new int[lastYear
- 1950 + 50];
45 for(int year
= 1950; year
< lastYear
+ 50; year
++)
47 Years
[year
- 1950] = year
;
51 public TextHelper TextHelper
53 get { return textHelper; }
56 public ICollection
GetModelHierarchy(ActiveRecordModel model
, object instance
)
58 ArrayList list
= new ArrayList();
60 ActiveRecordModel hierarchy
= model
;
62 while(hierarchy
!= null)
66 hierarchy
= ActiveRecordModel
.GetModel(hierarchy
.Type
.BaseType
);
71 while(hierarchy
!= null)
73 foreach(NestedModel nested
in hierarchy
.Components
)
75 object nestedInstance
= nested
.Property
.GetValue(instance
, null);
77 if (nestedInstance
== null)
79 nestedInstance
= CreationUtil
.Create(nested
.Property
.PropertyType
);
82 if (nestedInstance
!= null)
84 model2nestedInstance
[nested
.Model
] = nestedInstance
;
87 list
.Add(nested
.Model
);
90 hierarchy
= ActiveRecordModel
.GetModel(hierarchy
.Type
.BaseType
);
96 #region CanHandle methods
98 public bool CanHandle(FieldModel field
)
100 return CanHandleType(field
.Field
.FieldType
);
103 public bool CanHandle(PropertyModel propModel
)
105 return CanHandleType(propModel
.Property
.PropertyType
);
108 public bool CanHandle(PropertyInfo propInfo
)
110 return CanHandleType(propInfo
.PropertyType
);
113 public bool CanHandle(BelongsToModel model
)
115 return CheckModelAndKeyAreAccessible(model
.BelongsToAtt
.Type
);
118 public bool CanHandle(HasManyModel model
)
120 if (!model
.HasManyAtt
.Inverse
)
122 return CheckModelAndKeyAreAccessible(model
.HasManyAtt
.MapType
);
127 public bool CanHandle(HasAndBelongsToManyModel model
)
129 if (!model
.HasManyAtt
.Inverse
)
131 return CheckModelAndKeyAreAccessible(model
.HasManyAtt
.MapType
);
136 private bool CheckModelAndKeyAreAccessible(Type type
)
138 ActiveRecordModel otherModel
= ActiveRecordModel
.GetModel(type
);
140 PrimaryKeyModel keyModel
= ObtainPKProperty(otherModel
);
142 if (otherModel
== null || keyModel
== null)
150 private PrimaryKeyModel
ObtainPKProperty(ActiveRecordModel model
)
152 if (model
== null) return null;
154 ActiveRecordModel curModel
= model
;
156 while(curModel
!= null)
158 PrimaryKeyModel keyModel
= curModel
.PrimaryKey
;
160 if (keyModel
!= null)
165 curModel
= curModel
.Parent
;
171 private bool CanHandleType(Type type
)
173 return (type
.IsPrimitive
||
174 type
== typeof(String
) ||
175 type
== typeof(Decimal
) ||
176 type
== typeof(Single
) ||
177 type
== typeof(Double
) ||
178 type
== typeof(Byte
) ||
179 type
== typeof(SByte
) ||
180 type
== typeof(bool) ||
181 type
== typeof(Enum
) ||
182 type
== typeof(DateTime
));
187 #region CreateControl methods
189 public String
CreateControl(ActiveRecordModel model
, String prefix
,
190 FieldModel fieldModel
, object instance
)
192 stringBuilder
.Length
= 0;
194 FieldInfo fieldInfo
= fieldModel
.Field
;
196 String propName
= CreatePropName(model
, prefix
, fieldInfo
.Name
);
198 if (fieldInfo
.FieldType
== typeof(DateTime
))
200 stringBuilder
.Append(LabelFor(propName
+ "day", TextHelper
.PascalCaseToWord(fieldInfo
.Name
) + ": "));
204 stringBuilder
.Append(LabelFor(propName
, TextHelper
.PascalCaseToWord(fieldInfo
.Name
) + ": "));
207 FieldAttribute propAtt
= fieldModel
.FieldAtt
;
209 RenderAppropriateControl(model
, fieldInfo
.FieldType
, propName
, null, null,
210 propAtt
.Unique
, propAtt
.NotNull
, propAtt
.ColumnType
, propAtt
.Length
);
212 return stringBuilder
.ToString();
215 public String
CreateControl(ActiveRecordModel model
, String prefix
,
216 PropertyModel propertyModel
, object instance
)
218 stringBuilder
.Length
= 0;
220 PropertyInfo prop
= propertyModel
.Property
;
222 // Skip non standard properties
223 if (!prop
.CanWrite
|| !prop
.CanRead
) return String
.Empty
;
226 if (prop
.GetIndexParameters().Length
!= 0) return String
.Empty
;
228 String propName
= CreatePropName(model
, prefix
, prop
.Name
);
230 if (prop
.PropertyType
== typeof(DateTime
))
232 stringBuilder
.Append(LabelFor(propName
+ "day", TextHelper
.PascalCaseToWord(prop
.Name
) + ": "));
236 stringBuilder
.Append(LabelFor(propName
, TextHelper
.PascalCaseToWord(prop
.Name
) + ": "));
239 PropertyAttribute propAtt
= propertyModel
.PropertyAtt
;
241 RenderAppropriateControl(model
, prop
.PropertyType
, propName
, prop
, null,
242 propAtt
.Unique
, propAtt
.NotNull
, propAtt
.ColumnType
, propAtt
.Length
);
244 return stringBuilder
.ToString();
247 public String
CreateControl(ActiveRecordModel model
, String prefix
,
248 PropertyInfo prop
, object instance
)
250 stringBuilder
.Length
= 0;
252 // Skip non standard properties
253 if (!prop
.CanWrite
|| !prop
.CanRead
) return String
.Empty
;
256 if (prop
.GetIndexParameters().Length
!= 0) return String
.Empty
;
258 String propName
= CreatePropName(model
, prefix
, prop
.Name
);
260 if (prop
.PropertyType
== typeof(DateTime
))
262 stringBuilder
.Append(LabelFor(propName
+ "day", TextHelper
.PascalCaseToWord(prop
.Name
) + ": "));
266 stringBuilder
.Append(LabelFor(propName
, TextHelper
.PascalCaseToWord(prop
.Name
) + ": "));
269 RenderAppropriateControl(model
, prop
.PropertyType
,
270 propName
, prop
, null, false, false, null, 0);
272 return stringBuilder
.ToString();
275 public String
CreateControl(ActiveRecordModel model
, String prefix
,
276 BelongsToModel belongsToModel
, object instance
)
278 stringBuilder
.Length
= 0;
280 PropertyInfo prop
= belongsToModel
.Property
;
282 prefix
+= "." + prop
.Name
;
284 ActiveRecordModel otherModel
= ActiveRecordModel
.GetModel(belongsToModel
.BelongsToAtt
.Type
);
286 PrimaryKeyModel keyModel
= ObtainPKProperty(otherModel
);
288 if (otherModel
== null || keyModel
== null)
290 return "Model not found or PK not found";
293 object[] items
= CommonOperationUtils
.FindAll(otherModel
.Type
);
295 String propName
= CreatePropName(model
, prefix
, keyModel
.Property
.Name
);
297 stringBuilder
.Append(LabelFor(propName
, TextHelper
.PascalCaseToWord(prop
.Name
) + ": "));
299 IDictionary attrs
= new HybridDictionary(true);
301 attrs
["value"] = keyModel
.Property
.Name
;
303 if (!belongsToModel
.BelongsToAtt
.NotNull
)
305 attrs
.Add("firstOption", "Empty");
306 attrs
.Add("firstOptionValue", "");
309 stringBuilder
.Append(Select(propName
, items
, attrs
));
311 return stringBuilder
.ToString();
314 public String
CreateControl(ActiveRecordModel model
, String prefix
,
315 HasManyModel hasManyModel
, object instance
)
317 stringBuilder
.Length
= 0;
319 PropertyInfo prop
= hasManyModel
.Property
;
321 prefix
+= "." + prop
.Name
;
323 ActiveRecordModel otherModel
= ActiveRecordModel
.GetModel(hasManyModel
.HasManyAtt
.MapType
);
325 PrimaryKeyModel keyModel
= ObtainPKProperty(otherModel
);
327 if (otherModel
== null || keyModel
== null)
329 return "Model not found or PK not found";
332 object[] source
= CommonOperationUtils
.FindAll(otherModel
.Type
);
334 stringBuilder
.Append(prop
.Name
+ ": ");
335 stringBuilder
.Append("<br/>\r\n");
337 IDictionary attrs
= new HybridDictionary(true);
339 attrs
["value"] = keyModel
.Property
.Name
;
341 FormHelper
.CheckboxList list
= CreateCheckboxList(prefix
, source
, attrs
);
343 foreach(object item
in list
)
345 stringBuilder
.Append(list
.Item());
347 stringBuilder
.Append(item
.ToString());
349 stringBuilder
.Append("<br/>\r\n");
352 return stringBuilder
.ToString();
355 public String
CreateControl(ActiveRecordModel model
, String prefix
,
356 HasAndBelongsToManyModel hasAndBelongsModel
, object instance
)
358 stringBuilder
.Length
= 0;
360 PropertyInfo prop
= hasAndBelongsModel
.Property
;
362 prefix
+= "." + prop
.Name
;
364 ActiveRecordModel otherModel
= ActiveRecordModel
.GetModel(hasAndBelongsModel
.HasManyAtt
.MapType
);
366 PrimaryKeyModel keyModel
= ObtainPKProperty(otherModel
);
368 if (otherModel
== null || keyModel
== null)
370 return "Model not found or PK not found";
373 object[] source
= CommonOperationUtils
.FindAll(otherModel
.Type
);
375 stringBuilder
.Append(prop
.Name
+ ": ");
376 stringBuilder
.Append("<br/>\r\n");
378 IDictionary attrs
= new HybridDictionary(true);
380 attrs
["value"] = keyModel
.Property
.Name
;
382 FormHelper
.CheckboxList list
= CreateCheckboxList(prefix
, source
, attrs
);
384 foreach(object item
in list
)
386 stringBuilder
.Append(list
.Item());
388 stringBuilder
.Append(item
.ToString());
390 stringBuilder
.Append("<br/>\r\n");
393 return stringBuilder
.ToString();
398 private void RenderAppropriateControl(ActiveRecordModel model
,
399 Type propType
, string propName
, PropertyInfo property
,
400 object value, bool unique
, bool notNull
, String columnType
, int length
)
402 IDictionary htmlAttributes
= new Hashtable();
404 if (propType
== typeof(String
))
406 if (String
.Compare("stringclob", columnType
, true) == 0)
408 stringBuilder
.AppendFormat(TextArea(propName
));
414 htmlAttributes
["maxlength"] = length
.ToString();
417 stringBuilder
.AppendFormat(TextField(propName
, htmlAttributes
));
420 else if (propType
== typeof(Int16
) || propType
== typeof(Int32
) || propType
== typeof(Int64
))
422 stringBuilder
.AppendFormat(NumberField(propName
, htmlAttributes
));
424 else if (propType
== typeof(Single
) || propType
== typeof(Double
) || propType
== typeof(Decimal
))
426 stringBuilder
.AppendFormat(NumberField(propName
, htmlAttributes
));
428 else if (propType
== typeof(DateTime
))
430 stringBuilder
.AppendFormat(Select(propName
+ "month", Months
, htmlAttributes
));
431 stringBuilder
.AppendFormat(Select(propName
+ "day", Days
, htmlAttributes
));
432 stringBuilder
.AppendFormat(Select(propName
+ "year", Years
, htmlAttributes
));
434 else if (propType
== typeof(bool))
436 stringBuilder
.Append(CheckboxField(propName
));
438 else if (propType
== typeof(Enum
))
440 // TODO: Support flags as well
442 String
[] names
= System
.Enum
.GetNames(propType
);
444 IList options
= new ArrayList();
446 foreach(String name
in names
)
448 options
.Add(String
.Format("{0} {1}\r\n",
449 RadioField(propName
, name
), LabelFor(name
, TextHelper
.PascalCaseToWord(name
))));
454 private static string CreatePropName(ActiveRecordModel model
, String prefix
, String name
)
458 if (model
.IsNestedType
)
460 propName
= String
.Format("{0}.{1}.{2}", prefix
, model
.ParentNested
.Property
.Name
, name
);
464 propName
= String
.Format("{0}.{1}", prefix
, name
);