Minor changes to improve testability of helpers
[castle.git] / MonoRail / Castle.MonoRail.ActiveRecordScaffold / Helpers / ARFormHelper.cs
blob517fa0de4bf86e45f0516300627d42c660e83e4c
1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
2 //
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
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
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.ActiveRecordScaffold.Helpers
17 using System;
18 using System.Collections;
19 using System.Collections.Specialized;
20 using System.Text;
21 using System.Reflection;
22 using Castle.ActiveRecord;
23 using Castle.ActiveRecord.Framework.Internal;
24 using Castle.MonoRail.Framework;
25 using Castle.MonoRail.Framework.Helpers;
27 public class ARFormHelper : FormHelper
29 private StringBuilder stringBuilder = new StringBuilder(1024);
31 private IDictionary model2nestedInstance = new Hashtable();
33 private static readonly int[] Months = { 1,2,3,4,5,6,7,8,9,10,11,12 };
34 private static readonly int[] Days = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
35 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
36 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
37 private static readonly int[] Years;
39 static ARFormHelper()
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 ICollection GetModelHierarchy(ActiveRecordModel model, object instance)
53 ArrayList list = new ArrayList();
55 ActiveRecordModel hierarchy = model;
57 while(hierarchy != null)
59 list.Add(hierarchy);
61 hierarchy = ActiveRecordModel.GetModel(hierarchy.Type.BaseType);
64 hierarchy = model;
66 while(hierarchy != null)
68 foreach(NestedModel nested in hierarchy.Components)
70 object nestedInstance = nested.Property.GetValue(instance, null);
72 if (nestedInstance == null)
74 nestedInstance = CreationUtil.Create(nested.Property.PropertyType);
77 if (nestedInstance != null)
79 model2nestedInstance[nested.Model] = nestedInstance;
82 list.Add(nested.Model);
85 hierarchy = ActiveRecordModel.GetModel(hierarchy.Type.BaseType);
88 return list;
91 #region CanHandle methods
93 public bool CanHandle(FieldModel field)
95 return CanHandleType(field.Field.FieldType);
98 public bool CanHandle(PropertyModel propModel)
100 return CanHandleType(propModel.Property.PropertyType);
103 public bool CanHandle(PropertyInfo propInfo)
105 return CanHandleType(propInfo.PropertyType);
108 public bool CanHandle(BelongsToModel model)
110 return CheckModelAndKeyAreAccessible(model.BelongsToAtt.Type);
113 public bool CanHandle(HasManyModel model)
115 if (!model.HasManyAtt.Inverse)
117 return CheckModelAndKeyAreAccessible(model.HasManyAtt.MapType);
119 return false;
122 public bool CanHandle(HasAndBelongsToManyModel model)
124 if (!model.HasManyAtt.Inverse)
126 return CheckModelAndKeyAreAccessible(model.HasManyAtt.MapType);
128 return false;
131 private bool CheckModelAndKeyAreAccessible(Type type)
133 ActiveRecordModel otherModel = ActiveRecordModel.GetModel(type);
135 PrimaryKeyModel keyModel = ObtainPKProperty(otherModel);
137 if (otherModel == null || keyModel == null)
139 return false;
142 return true;
145 private PrimaryKeyModel ObtainPKProperty(ActiveRecordModel model)
147 if (model == null) return null;
149 ActiveRecordModel curModel = model;
151 while(curModel != null)
153 PrimaryKeyModel keyModel = curModel.PrimaryKey;
155 if (keyModel != null)
157 return keyModel;
160 curModel = curModel.Parent;
163 return null;
166 private bool CanHandleType(Type type)
168 return (type.IsPrimitive ||
169 type == typeof(String) ||
170 type == typeof(Decimal) ||
171 type == typeof(Single) ||
172 type == typeof(Double) ||
173 type == typeof(Byte) ||
174 type == typeof(SByte) ||
175 type == typeof(bool) ||
176 type == typeof(Enum) ||
177 type == typeof(DateTime));
180 #endregion
182 #region CreateControl methods
184 public String CreateControl(ActiveRecordModel model, String prefix,
185 FieldModel fieldModel, object instance)
187 stringBuilder.Length = 0;
189 FieldInfo fieldInfo = fieldModel.Field;
191 String propName = CreatePropName(model, prefix, fieldInfo.Name);
193 if (fieldInfo.FieldType == typeof(DateTime))
195 stringBuilder.Append(LabelFor(propName + "day", fieldInfo.Name + ": &nbsp;"));
197 else
199 stringBuilder.Append(LabelFor(propName, fieldInfo.Name + ": &nbsp;"));
202 FieldAttribute propAtt = fieldModel.FieldAtt;
204 RenderAppropriateControl(model, fieldInfo.FieldType, propName, null, null,
205 propAtt.Unique, propAtt.NotNull, propAtt.ColumnType, propAtt.Length);
207 return stringBuilder.ToString();
210 public String CreateControl(ActiveRecordModel model, String prefix,
211 PropertyModel propertyModel, object instance)
213 stringBuilder.Length = 0;
215 PropertyInfo prop = propertyModel.Property;
217 // Skip non standard properties
218 if (!prop.CanWrite || !prop.CanRead) return String.Empty;
220 // Skip indexers
221 if (prop.GetIndexParameters().Length != 0) return String.Empty;
223 String propName = CreatePropName(model, prefix, prop.Name);
225 if (prop.PropertyType == typeof(DateTime))
227 stringBuilder.Append(LabelFor(propName + "day", prop.Name + ": &nbsp;"));
229 else
231 stringBuilder.Append(LabelFor(propName, prop.Name + ": &nbsp;"));
234 PropertyAttribute propAtt = propertyModel.PropertyAtt;
236 RenderAppropriateControl(model, prop.PropertyType, propName, prop, null,
237 propAtt.Unique, propAtt.NotNull, propAtt.ColumnType, propAtt.Length);
239 return stringBuilder.ToString();
242 public String CreateControl(ActiveRecordModel model, String prefix,
243 PropertyInfo prop, object instance)
245 stringBuilder.Length = 0;
247 // Skip non standard properties
248 if (!prop.CanWrite || !prop.CanRead) return String.Empty;
250 // Skip indexers
251 if (prop.GetIndexParameters().Length != 0) return String.Empty;
253 String propName = CreatePropName(model, prefix, prop.Name);
255 if (prop.PropertyType == typeof(DateTime))
257 stringBuilder.Append(LabelFor(propName + "day", prop.Name + ": &nbsp;"));
259 else
261 stringBuilder.Append(LabelFor(propName, prop.Name + ": &nbsp;"));
264 RenderAppropriateControl(model, prop.PropertyType,
265 propName, prop, null, false, false, null, 0);
267 return stringBuilder.ToString();
270 public String CreateControl(ActiveRecordModel model, String prefix,
271 BelongsToModel belongsToModel, object instance)
273 stringBuilder.Length = 0;
275 PropertyInfo prop = belongsToModel.Property;
277 prefix += "." + prop.Name;
279 ActiveRecordModel otherModel = ActiveRecordModel.GetModel(belongsToModel.BelongsToAtt.Type);
281 PrimaryKeyModel keyModel = ObtainPKProperty(otherModel);
283 if (otherModel == null || keyModel == null)
285 return "Model not found or PK not found";
288 object[] items = CommonOperationUtils.FindAll(otherModel.Type);
290 String propName = CreatePropName(model, prefix, keyModel.Property.Name);
292 stringBuilder.Append(LabelFor(propName, prop.Name + ": &nbsp;"));
294 IDictionary attrs = new HybridDictionary(true);
296 attrs["value"] = keyModel.Property.Name;
298 if (!belongsToModel.BelongsToAtt.NotNull)
300 attrs.Add("firstOption", "Empty");
301 attrs.Add("firstOptionValue", "");
304 stringBuilder.Append(Select(propName, items, attrs));
306 return stringBuilder.ToString();
309 public String CreateControl(ActiveRecordModel model, String prefix,
310 HasManyModel hasManyModel, object instance)
312 stringBuilder.Length = 0;
314 PropertyInfo prop = hasManyModel.Property;
316 prefix += "." + prop.Name;
318 ActiveRecordModel otherModel = ActiveRecordModel.GetModel(hasManyModel.HasManyAtt.MapType);
320 PrimaryKeyModel keyModel = ObtainPKProperty(otherModel);
322 if (otherModel == null || keyModel == null)
324 return "Model not found or PK not found";
327 object[] source = CommonOperationUtils.FindAll(otherModel.Type);
329 stringBuilder.Append(prop.Name + ": &nbsp;");
330 stringBuilder.Append("<br/>\r\n");
332 IDictionary attrs = new HybridDictionary(true);
334 attrs["value"] = keyModel.Property.Name;
336 FormHelper.CheckboxList list = CreateCheckboxList(prefix, source, attrs);
338 foreach(object item in list)
340 stringBuilder.Append(list.Item());
342 stringBuilder.Append(item.ToString());
344 stringBuilder.Append("<br/>\r\n");
347 return stringBuilder.ToString();
350 public String CreateControl(ActiveRecordModel model, String prefix,
351 HasAndBelongsToManyModel hasAndBelongsModel, object instance)
353 stringBuilder.Length = 0;
355 PropertyInfo prop = hasAndBelongsModel.Property;
357 prefix += "." + prop.Name;
359 ActiveRecordModel otherModel = ActiveRecordModel.GetModel(hasAndBelongsModel.HasManyAtt.MapType);
361 PrimaryKeyModel keyModel = ObtainPKProperty(otherModel);
363 if (otherModel == null || keyModel == null)
365 return "Model not found or PK not found";
368 object[] source = CommonOperationUtils.FindAll(otherModel.Type);
370 stringBuilder.Append(prop.Name + ": &nbsp;");
371 stringBuilder.Append("<br/>\r\n");
373 IDictionary attrs = new HybridDictionary(true);
375 attrs["value"] = keyModel.Property.Name;
377 FormHelper.CheckboxList list = CreateCheckboxList(prefix, source, attrs);
379 foreach(object item in list)
381 stringBuilder.Append(list.Item());
383 stringBuilder.Append(item.ToString());
385 stringBuilder.Append("<br/>\r\n");
388 return stringBuilder.ToString();
391 #endregion
393 private void RenderAppropriateControl(ActiveRecordModel model,
394 Type propType, string propName, PropertyInfo property,
395 object value, bool unique, bool notNull, String columnType, int length)
397 IDictionary htmlAttributes = new Hashtable();
399 if (propType == typeof(String))
401 if (String.Compare("stringclob", columnType, true) == 0)
403 stringBuilder.AppendFormat(TextArea(propName));
405 else
407 if (length > 0)
409 htmlAttributes["maxlength"] = length.ToString();
412 stringBuilder.AppendFormat(TextField(propName, htmlAttributes));
415 else if (propType == typeof(Int16) || propType == typeof(Int32) || propType == typeof(Int64))
417 stringBuilder.AppendFormat(NumberField(propName, htmlAttributes));
419 else if (propType == typeof(Single) || propType == typeof(Double))
421 stringBuilder.AppendFormat(NumberField(propName, htmlAttributes));
423 else if (propType == typeof(DateTime))
425 stringBuilder.AppendFormat(Select(propName + "month", Months, htmlAttributes));
426 stringBuilder.AppendFormat(Select(propName + "day", Days, htmlAttributes));
427 stringBuilder.AppendFormat(Select(propName + "year", Years, htmlAttributes));
429 else if (propType == typeof(bool))
431 stringBuilder.Append(CheckboxField(propName));
433 else if (propType == typeof(Enum))
435 // TODO: Support flags as well
437 String[] names = System.Enum.GetNames(propType);
439 IList options = new ArrayList();
441 foreach(String name in names)
443 options.Add(String.Format("{0} {1}\r\n",
444 RadioField(propName, name), LabelFor(name, name)));
449 private static string CreatePropName(ActiveRecordModel model, String prefix, String name)
451 string propName;
453 if (model.IsNestedType)
455 propName = String.Format("{0}.{1}.{2}", prefix, model.Type.Name, name);
457 else
459 propName = String.Format("{0}.{1}", prefix, name);
462 return propName;