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
.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
.GetType().IsArray
)
150 return source
.GetType().GetElementType();
152 else if (source
is IEnumerable
)
154 return ExtractType(source
as IEnumerable
);
158 return source
.GetType();
162 private static Type
ExtractType(IEnumerable source
)
169 IEnumerator enumerator
= source
.GetEnumerator();
171 if (enumerator
.MoveNext())
173 return ExtractType(enumerator
.Current
);
179 private static bool IsSet(object initialSelection
)
181 if (initialSelection
== null)
186 return initialSelection
.GetType() != typeof(String
) &&
187 initialSelection
is IEnumerable
;
192 /// Represents a set element
196 private readonly string value;
197 private readonly object item
;
198 private readonly string text
;
199 private readonly bool isSelected
;
202 /// Initializes a new instance of the <see cref="SetItem"/> class.
204 /// <param name="item">The item.</param>
205 /// <param name="value">The value.</param>
206 /// <param name="text">The text.</param>
207 /// <param name="isSelected">if set to <c>true</c> [is selected].</param>
208 public SetItem(object item
, String
value, String text
, bool isSelected
)
213 this.isSelected
= isSelected
;
219 /// <value>The item.</value>
228 /// <value>The value.</value>
231 get { return value; }
237 /// <value>The text.</value>
244 /// Gets a value indicating whether this instance is selected.
247 /// <c>true</c> if this instance is selected; otherwise, <c>false</c>.
249 public bool IsSelected
251 get { return isSelected; }
256 /// Base class for set iterators
258 public abstract class OperationState
: IEnumerable
, IEnumerator
263 protected readonly Type type
;
265 /// Value getter for value
267 protected readonly FormHelper
.ValueGetter valuePropInfo
;
269 /// Value getter for text
271 protected readonly FormHelper
.ValueGetter textPropInfo
;
274 /// Format rule for text
276 protected readonly String textFormat
;
278 /// Format rule for value
280 protected readonly String valueFormat
;
283 /// Source enumerator
285 protected IEnumerator enumerator
;
288 /// Initializes a new instance of the <see cref="OperationState"/> class.
290 /// <param name="type">The type.</param>
291 /// <param name="dataSource">The data source.</param>
292 /// <param name="emptyValueCase">if set to <c>true</c> [empty value case].</param>
293 /// <param name="valueProperty">The value property.</param>
294 /// <param name="textProperty">The text property.</param>
295 /// <param name="textFormat">The text format.</param>
296 /// <param name="valueFormat">The value format.</param>
297 protected OperationState(Type type
, IEnumerable dataSource
,
298 bool emptyValueCase
,String valueProperty
, String textProperty
, String textFormat
, String valueFormat
)
300 if (dataSource
!= null)
302 enumerator
= dataSource
.GetEnumerator();
306 this.textFormat
= textFormat
;
307 this.valueFormat
= valueFormat
;
309 if (valueProperty
!= null || emptyValueCase
)
311 valuePropInfo
= FormHelper
.ValueGetterAbstractFactory
.Create(type
, valueProperty
);
314 if (textProperty
!= null)
316 textPropInfo
= FormHelper
.ValueGetterAbstractFactory
.Create(type
, textProperty
);
321 /// Gets the target suffix.
323 /// <value>The target suffix.</value>
324 public abstract String TargetSuffix { get; }
327 /// Formats the text.
329 /// <param name="value">The value to be formatted.</param>
330 /// <param name="format">The format to apply.</param>
331 protected static void FormatText(ref object value, string format
)
333 if (format
!= null && value != null)
335 IFormattable formattable
= value as IFormattable
;
337 if (formattable
!= null)
339 value = formattable
.ToString(format
, null);
345 /// Creates the item representation.
347 /// <param name="current">The current.</param>
348 /// <returns></returns>
349 protected abstract SetItem
CreateItemRepresentation(object current
);
351 #region IEnumerator implementation
354 /// Advances the enumerator to the next element of the collection.
357 /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
359 /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
360 public bool MoveNext()
362 if (enumerator
== null)
366 return enumerator
.MoveNext();
370 /// Sets the enumerator to its initial position, which is before the first element in the collection.
372 /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
375 if (enumerator
!= null)
382 /// Gets the current element in the collection.
385 /// <returns>The current element in the collection.</returns>
386 /// <exception cref="T:System.InvalidOperationException">The enumerator is positioned before the first element of the collection or after the last element. </exception>
387 public object Current
389 get { return CreateItemRepresentation(enumerator.Current); }
394 #region IEnumerable implementation
397 /// Returns an enumerator that iterates through a collection.
400 /// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
402 public IEnumerator
GetEnumerator()
411 /// Used for empty/null datasources
413 public class NoIterationState
: OperationState
416 /// Single instance for the iterator.
418 public static readonly NoIterationState Instance
= new NoIterationState();
420 private NoIterationState() : base(null, null, false, null, null, null, null)
425 /// Gets the target suffix.
427 /// <value>The target suffix.</value>
428 public override string TargetSuffix
434 /// Creates the item representation.
436 /// <param name="current">The current.</param>
437 /// <returns></returns>
438 protected override SetItem
CreateItemRepresentation(object current
)
440 throw new NotImplementedException();
447 public class ListDataSourceState
: OperationState
449 private readonly string customSuffix
;
452 /// Initializes a new instance of the <see cref="ListDataSourceState"/> class.
454 /// <param name="type">The type.</param>
455 /// <param name="dataSource">The data source.</param>
456 /// <param name="valueProperty">The value property.</param>
457 /// <param name="textProperty">The text property.</param>
458 /// <param name="textFormat">The text format.</param>
459 /// <param name="valueFormat">The value format.</param>
460 /// <param name="customSuffix">The custom suffix.</param>
461 public ListDataSourceState(Type type
, IEnumerable dataSource
, String valueProperty
,
462 String textProperty
, String textFormat
, String valueFormat
, String customSuffix
)
463 : base(type
, dataSource
, false, valueProperty
, textProperty
, textFormat
, valueFormat
)
465 this.customSuffix
= customSuffix
;
469 /// Gets the target suffix.
471 /// <value>The target suffix.</value>
472 public override string TargetSuffix
474 get { return customSuffix; }
478 /// Creates the item representation.
480 /// <param name="current">The current.</param>
481 /// <returns></returns>
482 protected override SetItem
CreateItemRepresentation(object current
)
484 object value = current
;
485 object text
= current
;
487 if (valuePropInfo
!= null)
489 value = valuePropInfo
.GetValue(current
);
492 if (textPropInfo
!= null)
494 text
= textPropInfo
.GetValue(current
);
497 FormatText(ref text
, textFormat
);
498 FormatText(ref value, valueFormat
);
500 return new SetItem(current
, value != null ? value.ToString() : String
.Empty
, text
!= null ? text
.ToString() : String
.Empty
, false);
505 /// Iterator for sets type same type
507 public class SameTypeOperationState
: OperationState
509 private readonly object initialSelection
;
510 private readonly bool isInitialSelectionASet
;
513 /// Initializes a new instance of the <see cref="SameTypeOperationState"/> class.
515 /// <param name="type">The type.</param>
516 /// <param name="initialSelection">The initial selection.</param>
517 /// <param name="dataSource">The data source.</param>
518 /// <param name="emptyValueCase">if set to <c>true</c> [empty value case].</param>
519 /// <param name="valueProperty">The value property.</param>
520 /// <param name="textProperty">The text property.</param>
521 /// <param name="textFormat">The text format.</param>
522 /// <param name="valueFormat">The value format.</param>
523 /// <param name="isInitialSelectionASet">if set to <c>true</c> [is initial selection A set].</param>
524 public SameTypeOperationState(Type type
, object initialSelection
, IEnumerable dataSource
,
525 bool emptyValueCase
,String valueProperty
, String textProperty
, String textFormat
, String valueFormat
, bool isInitialSelectionASet
)
526 : base(type
, dataSource
, emptyValueCase
, valueProperty
, textProperty
, textFormat
, valueFormat
)
528 this.initialSelection
= initialSelection
;
529 this.isInitialSelectionASet
= isInitialSelectionASet
;
533 /// Gets the target suffix.
535 /// <value>The target suffix.</value>
536 public override string TargetSuffix
538 get { return valuePropInfo == null ? "" : valuePropInfo.Name; }
542 /// Creates the item representation.
544 /// <param name="current">The current.</param>
545 /// <returns></returns>
546 protected override SetItem
CreateItemRepresentation(object current
)
548 object value = current
;
549 object text
= current
;
551 if (valuePropInfo
!= null)
553 value = valuePropInfo
.GetValue(current
);
556 if (textPropInfo
!= null)
558 text
= textPropInfo
.GetValue(current
);
561 FormatText(ref text
, textFormat
);
562 FormatText(ref value, valueFormat
);
564 bool isSelected
= FormHelper
.IsPresent(value, initialSelection
, valuePropInfo
, isInitialSelectionASet
);
566 return new SetItem(current
, value != null ? value.ToString() : String
.Empty
, text
!= null ? text
.ToString() : String
.Empty
, isSelected
);
571 /// Iterator for different types on the set
573 public class DifferentTypeOperationState
: OperationState
575 private readonly object initialSelection
;
576 private readonly bool isInitialSelectionASet
;
577 private readonly FormHelper
.ValueGetter sourcePropInfo
;
580 /// Initializes a new instance of the <see cref="DifferentTypeOperationState"/> class.
582 /// <param name="initialSelectionType">Initial type of the selection.</param>
583 /// <param name="dataSourceType">Type of the data source.</param>
584 /// <param name="initialSelection">The initial selection.</param>
585 /// <param name="dataSource">The data source.</param>
586 /// <param name="sourceProperty">The source property.</param>
587 /// <param name="valueProperty">The value property.</param>
588 /// <param name="textProperty">The text property.</param>
589 /// <param name="textFormat">The text format.</param>
590 /// <param name="valueFormat">The value format.</param>
591 /// <param name="isInitialSelectionASet">if set to <c>true</c> [is initial selection A set].</param>
592 public DifferentTypeOperationState(Type initialSelectionType
, Type dataSourceType
,
593 object initialSelection
, IEnumerable dataSource
,
594 String sourceProperty
, String valueProperty
,
595 String textProperty
, String textFormat
, String valueFormat
, bool isInitialSelectionASet
)
596 : base(dataSourceType
, dataSource
, false, valueProperty
, textProperty
, textFormat
, valueFormat
)
598 this.initialSelection
= initialSelection
;
599 this.isInitialSelectionASet
= isInitialSelectionASet
;
601 if (sourceProperty
!= null)
603 sourcePropInfo
= FormHelper
.ValueGetterAbstractFactory
.Create(initialSelectionType
, sourceProperty
); // FormHelper.GetMethod(initialSelectionType, sourceProperty);
605 else if (valueProperty
!= null)
607 sourcePropInfo
= FormHelper
.ValueGetterAbstractFactory
.Create(initialSelectionType
, valueProperty
); // FormHelper.GetMethod(initialSelectionType, valueProperty);
612 /// Gets the target suffix.
614 /// <value>The target suffix.</value>
615 public override string TargetSuffix
617 get { return sourcePropInfo == null ? "" : sourcePropInfo.Name; }
621 /// Creates the item representation.
623 /// <param name="current">The current.</param>
624 /// <returns></returns>
625 protected override SetItem
CreateItemRepresentation(object current
)
627 object value = current
;
628 object text
= current
;
630 if (valuePropInfo
!= null)
632 value = valuePropInfo
.GetValue(current
);
635 if (textPropInfo
!= null)
637 text
= textPropInfo
.GetValue(current
);
640 FormatText(ref text
, textFormat
);
641 FormatText(ref value, valueFormat
);
643 bool isSelected
= FormHelper
.IsPresent(value, initialSelection
, sourcePropInfo
, isInitialSelectionASet
);
645 return new SetItem(current
, value != null ? value.ToString() : String
.Empty
, text
!= null ? text
.ToString() : String
.Empty
, isSelected
);