1 // Copyright 2004-2007 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
.Framework
.Helpers
18 using System
.Collections
;
19 using Castle
.MonoRail
.Framework
.Internal
;
22 /// The SetOperation exposes an <see cref="IterateOnDataSource"/> that
23 /// extracts information from the attributes and creates a proper configured
26 /// It is shared by a handful of MonoRail operations related to sets.
29 public static class SetOperation
32 /// Combines a group of well thought rules to create
33 /// an <see cref="OperationState"/> instance.
37 /// The parameters read from the <paramref name="attributes"/> are
39 /// <list type="bullet">
41 /// <term>value</term>
42 /// <description>The property name used to extract the value</description>
46 /// <description>The property name used to extract the display text</description>
49 /// <term>textformat</term>
50 /// <description>A format rule to apply to the text</description>
53 /// <term>valueformat</term>
54 /// <description>A format rule to apply to the value</description>
57 /// <term>suffix</term>
58 /// <description>If the types on both sets are different,
59 /// the suffix specifies a different target property</description>
62 /// <term>sourceProperty</term>
64 /// If the types on both sets are different,
65 /// the sourceProperty identifies a different source property to extract the value from.
73 /// <param name="initialSelection">The initial selection.</param>
74 /// <param name="dataSource">The data source.</param>
75 /// <param name="attributes">The attributes.</param>
76 /// <returns></returns>
77 public static OperationState
IterateOnDataSource(object initialSelection
,
78 IEnumerable dataSource
, IDictionary attributes
)
80 // Extract necessary elements to know which "heuristic" to use
82 bool isInitialSelectionASet
= IsSet(initialSelection
);
84 Type initialSelectionType
= ExtractType(initialSelection
);
85 Type dataSourceType
= ExtractType(dataSource
);
87 String customSuffix
= CommonUtils
.ObtainEntryAndRemove(attributes
, "suffix");
88 String valueProperty
= CommonUtils
.ObtainEntryAndRemove(attributes
, "value");
89 String textProperty
= CommonUtils
.ObtainEntryAndRemove(attributes
, "text");
90 String textFormat
= CommonUtils
.ObtainEntryAndRemove(attributes
, "textformat");
91 String valueFormat
= CommonUtils
.ObtainEntryAndRemove(attributes
, "valueformat");
93 bool emptyValueCase
= CheckForEmpyTextValueCase(dataSourceType
);
95 if (dataSourceType
== null)
97 // If the dataSourceType could not be obtained
98 // then the datasource is empty or null
100 return NoIterationState
.Instance
;
102 else if (initialSelectionType
== null)
104 if (customSuffix
== null && valueProperty
!= null)
106 customSuffix
= valueProperty
;
109 return new ListDataSourceState(dataSourceType
, dataSource
,
110 valueProperty
, textProperty
, textFormat
, valueFormat
, customSuffix
);
112 else if (initialSelectionType
== dataSourceType
)
114 return new SameTypeOperationState(dataSourceType
,
115 initialSelection
, dataSource
,
116 emptyValueCase
, valueProperty
, textProperty
, textFormat
, valueFormat
, isInitialSelectionASet
);
118 else // types are different, most complex scenario
120 String sourceProperty
= CommonUtils
.ObtainEntryAndRemove(attributes
, "sourceProperty");
122 return new DifferentTypeOperationState(initialSelectionType
,
123 dataSourceType
, initialSelection
, dataSource
,
124 sourceProperty
, valueProperty
, textProperty
, textFormat
, valueFormat
,
125 isInitialSelectionASet
);
129 private static bool CheckForEmpyTextValueCase(Type datasourceType
)
131 if (typeof(Enum
).IsAssignableFrom(datasourceType
))
138 private static Type
ExtractType(object source
)
144 else if (source
is String
)
146 return typeof(String
);
148 else if (source
is IEnumerable
)
150 return ExtractType(source
as IEnumerable
);
154 return source
.GetType();
158 private static Type
ExtractType(IEnumerable source
)
165 IEnumerator enumerator
= source
.GetEnumerator();
167 if (enumerator
.MoveNext())
169 return ExtractType(enumerator
.Current
);
175 private static bool IsSet(object initialSelection
)
177 if (initialSelection
== null)
182 return initialSelection
.GetType() != typeof(String
) &&
183 initialSelection
is IEnumerable
;
188 /// Represents a set element
192 private readonly string value;
193 private readonly object item
;
194 private readonly string text
;
195 private readonly bool isSelected
;
198 /// Initializes a new instance of the <see cref="SetItem"/> class.
200 /// <param name="item">The item.</param>
201 /// <param name="value">The value.</param>
202 /// <param name="text">The text.</param>
203 /// <param name="isSelected">if set to <c>true</c> [is selected].</param>
204 public SetItem(object item
, String
value, String text
, bool isSelected
)
209 this.isSelected
= isSelected
;
215 /// <value>The item.</value>
224 /// <value>The value.</value>
227 get { return value; }
233 /// <value>The text.</value>
240 /// Gets a value indicating whether this instance is selected.
243 /// <c>true</c> if this instance is selected; otherwise, <c>false</c>.
245 public bool IsSelected
247 get { return isSelected; }
252 /// Base class for set iterators
254 public abstract class OperationState
: IEnumerable
, IEnumerator
259 protected readonly Type type
;
261 /// Value getter for value
263 protected readonly FormHelper
.ValueGetter valuePropInfo
;
265 /// Value getter for text
267 protected readonly FormHelper
.ValueGetter textPropInfo
;
270 /// Format rule for text
272 protected readonly String textFormat
;
274 /// Format rule for value
276 protected readonly String valueFormat
;
279 /// Source enumerator
281 protected IEnumerator enumerator
;
284 /// Initializes a new instance of the <see cref="OperationState"/> class.
286 /// <param name="type">The type.</param>
287 /// <param name="dataSource">The data source.</param>
288 /// <param name="emptyValueCase">if set to <c>true</c> [empty value case].</param>
289 /// <param name="valueProperty">The value property.</param>
290 /// <param name="textProperty">The text property.</param>
291 /// <param name="textFormat">The text format.</param>
292 /// <param name="valueFormat">The value format.</param>
293 protected OperationState(Type type
, IEnumerable dataSource
,
294 bool emptyValueCase
,String valueProperty
, String textProperty
, String textFormat
, String valueFormat
)
296 if (dataSource
!= null)
298 enumerator
= dataSource
.GetEnumerator();
302 this.textFormat
= textFormat
;
303 this.valueFormat
= valueFormat
;
305 if (valueProperty
!= null || emptyValueCase
)
307 valuePropInfo
= FormHelper
.ValueGetterAbstractFactory
.Create(type
, valueProperty
); // FormHelper.GetMethod(type, valueProperty);
310 if (textProperty
!= null)
312 textPropInfo
= FormHelper
.ValueGetterAbstractFactory
.Create(type
, textProperty
);
317 /// Gets the target suffix.
319 /// <value>The target suffix.</value>
320 public abstract String TargetSuffix { get; }
323 /// Formats the text.
325 /// <param name="value">The value to be formatted.</param>
326 /// <param name="format">The format to apply.</param>
327 protected static void FormatText(ref object value, string format
)
329 if (format
!= null && value != null)
331 IFormattable formattable
= value as IFormattable
;
333 if (formattable
!= null)
335 value = formattable
.ToString(format
, null);
341 /// Creates the item representation.
343 /// <param name="current">The current.</param>
344 /// <returns></returns>
345 protected abstract SetItem
CreateItemRepresentation(object current
);
347 #region IEnumerator implementation
350 /// Advances the enumerator to the next element of the collection.
353 /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
355 /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
356 public bool MoveNext()
358 if (enumerator
== null)
362 return enumerator
.MoveNext();
366 /// Sets the enumerator to its initial position, which is before the first element in the collection.
368 /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
371 if (enumerator
!= null)
378 /// Gets the current element in the collection.
381 /// <returns>The current element in the collection.</returns>
382 /// <exception cref="T:System.InvalidOperationException">The enumerator is positioned before the first element of the collection or after the last element. </exception>
383 public object Current
385 get { return CreateItemRepresentation(enumerator.Current); }
390 #region IEnumerable implementation
393 /// Returns an enumerator that iterates through a collection.
396 /// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
398 public IEnumerator
GetEnumerator()
407 /// Used for empty/null datasources
409 public class NoIterationState
: OperationState
412 /// Single instance for the iterator.
414 public static readonly NoIterationState Instance
= new NoIterationState();
416 private NoIterationState() : base(null, null, false, null, null, null, null)
421 /// Gets the target suffix.
423 /// <value>The target suffix.</value>
424 public override string TargetSuffix
430 /// Creates the item representation.
432 /// <param name="current">The current.</param>
433 /// <returns></returns>
434 protected override SetItem
CreateItemRepresentation(object current
)
436 throw new NotImplementedException();
443 public class ListDataSourceState
: OperationState
445 private readonly string customSuffix
;
448 /// Initializes a new instance of the <see cref="ListDataSourceState"/> class.
450 /// <param name="type">The type.</param>
451 /// <param name="dataSource">The data source.</param>
452 /// <param name="valueProperty">The value property.</param>
453 /// <param name="textProperty">The text property.</param>
454 /// <param name="textFormat">The text format.</param>
455 /// <param name="valueFormat">The value format.</param>
456 /// <param name="customSuffix">The custom suffix.</param>
457 public ListDataSourceState(Type type
, IEnumerable dataSource
, String valueProperty
,
458 String textProperty
, String textFormat
, String valueFormat
, String customSuffix
)
459 : base(type
, dataSource
, false, valueProperty
, textProperty
, textFormat
, valueFormat
)
461 this.customSuffix
= customSuffix
;
465 /// Gets the target suffix.
467 /// <value>The target suffix.</value>
468 public override string TargetSuffix
470 get { return customSuffix; }
474 /// Creates the item representation.
476 /// <param name="current">The current.</param>
477 /// <returns></returns>
478 protected override SetItem
CreateItemRepresentation(object current
)
480 object value = current
;
481 object text
= current
;
483 if (valuePropInfo
!= null)
485 value = valuePropInfo
.GetValue(current
);
488 if (textPropInfo
!= null)
490 text
= textPropInfo
.GetValue(current
);
493 FormatText(ref text
, textFormat
);
494 FormatText(ref value, valueFormat
);
496 return new SetItem(current
, value != null ? value.ToString() : String
.Empty
, text
!= null ? text
.ToString() : String
.Empty
, false);
501 /// Iterator for sets type same type
503 public class SameTypeOperationState
: OperationState
505 private readonly object initialSelection
;
506 private readonly bool isInitialSelectionASet
;
509 /// Initializes a new instance of the <see cref="SameTypeOperationState"/> class.
511 /// <param name="type">The type.</param>
512 /// <param name="initialSelection">The initial selection.</param>
513 /// <param name="dataSource">The data source.</param>
514 /// <param name="emptyValueCase">if set to <c>true</c> [empty value case].</param>
515 /// <param name="valueProperty">The value property.</param>
516 /// <param name="textProperty">The text property.</param>
517 /// <param name="textFormat">The text format.</param>
518 /// <param name="valueFormat">The value format.</param>
519 /// <param name="isInitialSelectionASet">if set to <c>true</c> [is initial selection A set].</param>
520 public SameTypeOperationState(Type type
, object initialSelection
, IEnumerable dataSource
,
521 bool emptyValueCase
,String valueProperty
, String textProperty
, String textFormat
, String valueFormat
, bool isInitialSelectionASet
)
522 : base(type
, dataSource
, emptyValueCase
, valueProperty
, textProperty
, textFormat
, valueFormat
)
524 this.initialSelection
= initialSelection
;
525 this.isInitialSelectionASet
= isInitialSelectionASet
;
529 /// Gets the target suffix.
531 /// <value>The target suffix.</value>
532 public override string TargetSuffix
534 get { return valuePropInfo == null ? "" : valuePropInfo.Name; }
538 /// Creates the item representation.
540 /// <param name="current">The current.</param>
541 /// <returns></returns>
542 protected override SetItem
CreateItemRepresentation(object current
)
544 object value = current
;
545 object text
= current
;
547 if (valuePropInfo
!= null)
549 value = valuePropInfo
.GetValue(current
);
552 if (textPropInfo
!= null)
554 text
= textPropInfo
.GetValue(current
);
557 FormatText(ref text
, textFormat
);
558 FormatText(ref value, valueFormat
);
560 bool isSelected
= FormHelper
.IsPresent(value, initialSelection
, valuePropInfo
, isInitialSelectionASet
);
562 return new SetItem(current
, value != null ? value.ToString() : String
.Empty
, text
!= null ? text
.ToString() : String
.Empty
, isSelected
);
567 /// Iterator for different types on the set
569 public class DifferentTypeOperationState
: OperationState
571 private readonly object initialSelection
;
572 private readonly bool isInitialSelectionASet
;
573 private readonly FormHelper
.ValueGetter sourcePropInfo
;
576 /// Initializes a new instance of the <see cref="DifferentTypeOperationState"/> class.
578 /// <param name="initialSelectionType">Initial type of the selection.</param>
579 /// <param name="dataSourceType">Type of the data source.</param>
580 /// <param name="initialSelection">The initial selection.</param>
581 /// <param name="dataSource">The data source.</param>
582 /// <param name="sourceProperty">The source property.</param>
583 /// <param name="valueProperty">The value property.</param>
584 /// <param name="textProperty">The text property.</param>
585 /// <param name="textFormat">The text format.</param>
586 /// <param name="valueFormat">The value format.</param>
587 /// <param name="isInitialSelectionASet">if set to <c>true</c> [is initial selection A set].</param>
588 public DifferentTypeOperationState(Type initialSelectionType
, Type dataSourceType
,
589 object initialSelection
, IEnumerable dataSource
,
590 String sourceProperty
, String valueProperty
,
591 String textProperty
, String textFormat
, String valueFormat
, bool isInitialSelectionASet
)
592 : base(dataSourceType
, dataSource
, false, valueProperty
, textProperty
, textFormat
, valueFormat
)
594 this.initialSelection
= initialSelection
;
595 this.isInitialSelectionASet
= isInitialSelectionASet
;
597 if (sourceProperty
!= null)
599 sourcePropInfo
= FormHelper
.ValueGetterAbstractFactory
.Create(initialSelectionType
, sourceProperty
); // FormHelper.GetMethod(initialSelectionType, sourceProperty);
601 else if (valueProperty
!= null)
603 sourcePropInfo
= FormHelper
.ValueGetterAbstractFactory
.Create(initialSelectionType
, valueProperty
); // FormHelper.GetMethod(initialSelectionType, valueProperty);
608 /// Gets the target suffix.
610 /// <value>The target suffix.</value>
611 public override string TargetSuffix
613 get { return sourcePropInfo == null ? "" : sourcePropInfo.Name; }
617 /// Creates the item representation.
619 /// <param name="current">The current.</param>
620 /// <returns></returns>
621 protected override SetItem
CreateItemRepresentation(object current
)
623 object value = current
;
624 object text
= current
;
626 if (valuePropInfo
!= null)
628 value = valuePropInfo
.GetValue(current
);
631 if (textPropInfo
!= null)
633 text
= textPropInfo
.GetValue(current
);
636 FormatText(ref text
, textFormat
);
637 FormatText(ref value, valueFormat
);
639 bool isSelected
= FormHelper
.IsPresent(value, initialSelection
, sourcePropInfo
, isInitialSelectionASet
);
641 return new SetItem(current
, value != null ? value.ToString() : String
.Empty
, text
!= null ? text
.ToString() : String
.Empty
, isSelected
);