|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
//
// Description: Converters for IList<double>, IList<ushort>, IList<Point>
// IList<bool> and IList<char>.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Text;
using System.Windows;
using MS.Internal;
using SR=MS.Internal.PresentationCore.SR;
namespace System.Windows.Media.Converters
{
//
// The following are IList converters for IList<double>, IList<ushort>, IList<bool>, IList<Point>, IList<char>.
// More code sharing might be achieved by creating a generic TypeConverter. But XAML parser doesn't
// support generic TypeConverter now.
//
/// <summary>
/// The base converter for IList of T to string conversion in XAML serialization
/// </summary>
public abstract class BaseIListConverter : TypeConverter
{
/// <Summary>
/// Indicates if a type can be converted from (returns true for string).
/// </Summary>
public override bool CanConvertFrom(ITypeDescriptorContext td, Type t)
{
return t == typeof(string); // can only convert from string
}
/// <Summary>
/// Indicates if a type can be converted to (returns true for string).
/// </Summary>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string); // can only convert to string
}
/// <Summary>
/// Converts from a string.
/// </Summary>
public override object ConvertFrom(ITypeDescriptorContext td, CultureInfo ci, object value)
{
if (null == value)
{
throw GetConvertFromException(value);
}
string s = value as string;
if (null == s)
{
throw new ArgumentException(SR.Format(SR.General_BadType, "ConvertFrom"), "value");
}
return ConvertFromCore(td, ci, s);
}
/// <Summary>
/// Converts to a string
/// </Summary>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType != null)
return ConvertToCore(context, culture, value, destinationType);
// Pass unhandled cases to base class (which will throw exceptions for null value or destinationType.)
return base.ConvertTo(context, culture, value, destinationType);
}
/// <summary>
/// To be implemented by subclasses to convert to various types from string.
/// </summary>
internal abstract object ConvertFromCore(ITypeDescriptorContext td, CultureInfo ci, string value);
/// <summary>
/// To be implemented by subclasses to convert string to various types.
/// </summary>
internal abstract object ConvertToCore(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType);
internal TokenizerHelper _tokenizer;
internal const char DelimiterChar = ' ';
}
/// <summary>
/// TypeConverter to convert IList of double to and from string.
/// The converted string is a sequence of delimited double number.
/// </summary>
public sealed class DoubleIListConverter : BaseIListConverter
{
internal sealed override object ConvertFromCore(ITypeDescriptorContext td, CultureInfo ci, string value)
{
_tokenizer = new TokenizerHelper(value, '\0' /* quote char */, DelimiterChar);
// Estimate the output list's capacity from length of the input string.
List<double> list = new List<double>(Math.Min(256, value.Length / EstimatedCharCountPerItem + 1));
while (_tokenizer.NextToken())
{
list.Add(Convert.ToDouble(_tokenizer.GetCurrentToken(), ci));
}
return list;
}
internal override object ConvertToCore(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
IList<double> list = value as IList<double>;
if (list == null)
{
throw GetConvertToException(value, destinationType);
}
// Estimate the output string's length from the element count of the List.
StringBuilder builder = new StringBuilder(EstimatedCharCountPerItem * list.Count);
for (int i = 0; i < list.Count; i++)
{
if (i > 0)
builder.Append(DelimiterChar);
builder.Append(list[i].ToString(culture));
}
return builder.ToString();
}
// A double is estimated to take 5 characters on average, plus 1 delimiter, e.g. "5.328 ".
private const int EstimatedCharCountPerItem = 6;
}
/// <summary>
/// TypeConverter to convert IList of ushort to and from string.
/// The converted string is a sequence of delimited ushort number.
/// </summary>
public sealed class UShortIListConverter : BaseIListConverter
{
internal override object ConvertFromCore(ITypeDescriptorContext td, CultureInfo ci, string value)
{
_tokenizer = new TokenizerHelper(value, '\0' /* quote char */, DelimiterChar);
List<ushort> list = new List<ushort>(Math.Min(256, value.Length / EstimatedCharCountPerItem + 1));
while (_tokenizer.NextToken())
{
list.Add(Convert.ToUInt16(_tokenizer.GetCurrentToken(), ci));
}
return list;
}
internal override object ConvertToCore(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
IList<ushort> list = value as IList<ushort>;
if (list == null)
{
throw GetConvertToException(value, destinationType);
}
StringBuilder builder = new StringBuilder(EstimatedCharCountPerItem * list.Count);
for (int i = 0; i < list.Count; i++)
{
if (i > 0)
builder.Append(DelimiterChar);
builder.Append(list[i].ToString(culture));
}
return builder.ToString();
}
// A ushort is estimated to take 2 characters on average, plus 1 delimiter.
private const int EstimatedCharCountPerItem = 3;
}
/// <summary>
/// TypeConverter to convert IList of bool to and from string.
/// The converted string is a sequence of delimited 0s and 1s. "0" for false and "1" for true.
/// </summary>
public sealed class BoolIListConverter : BaseIListConverter
{
internal override object ConvertFromCore(ITypeDescriptorContext td, CultureInfo ci, string value)
{
_tokenizer = new TokenizerHelper(value, '\0' /* quote char */, DelimiterChar);
List<bool> list = new List<bool>(Math.Min(256, value.Length / EstimatedCharCountPerItem + 1));
while (_tokenizer.NextToken())
{
list.Add(Convert.ToInt32(_tokenizer.GetCurrentToken(), ci) != 0);
}
return list;
}
internal override object ConvertToCore(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
IList<bool> list = value as IList<bool>;
if (list == null)
{
throw GetConvertToException(value, destinationType);
}
StringBuilder builder = new StringBuilder(EstimatedCharCountPerItem * list.Count);
for (int i = 0; i < list.Count; i++)
{
if (i > 0)
builder.Append(DelimiterChar);
builder.Append((list[i] ? 1 : 0));
}
return builder.ToString();
}
// A bool takes 1 character plus 1 delimiter.
private const int EstimatedCharCountPerItem = 2;
}
/// <summary>
/// TypeConverter to convert IList of Point to and from string.
/// The converted string is a sequence of delimited Point values. Point values are converted by PointConverter.
/// </summary>
public sealed class PointIListConverter : BaseIListConverter
{
internal override object ConvertFromCore(ITypeDescriptorContext td, CultureInfo ci, string value)
{
_tokenizer = new TokenizerHelper(value, '\0' /* quote char */, DelimiterChar);
List<Point> list = new List<Point>(Math.Min(256, value.Length / EstimatedCharCountPerItem + 1));
while (_tokenizer.NextToken())
{
list.Add((Point) converter.ConvertFrom(td, ci, _tokenizer.GetCurrentToken()));
}
return list;
}
internal override object ConvertToCore(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
IList<Point> list = value as IList<Point>;
if (list == null)
{
throw GetConvertToException(value, destinationType);
}
StringBuilder builder = new StringBuilder(EstimatedCharCountPerItem * list.Count);
for (int i = 0; i < list.Count; i++)
{
if (i > 0)
builder.Append(DelimiterChar);
builder.Append((string) converter.ConvertTo(context, culture, list[i], typeof(string)));
}
return builder.ToString();
}
private PointConverter converter = new PointConverter();
// A point takes 2 double and 2 delimiters. Estimated to be 12 characters.
private const int EstimatedCharCountPerItem = 12;
}
/// <summary>
/// TypeConverter to convert IList of Char to and from string.
/// The converted string is a concatenation of all the chars in the IList. There is no delimiter.
/// </summary>
public sealed class CharIListConverter : BaseIListConverter
{
internal override object ConvertFromCore(ITypeDescriptorContext td, CultureInfo ci, string value)
{
return value.ToCharArray();
}
internal override object ConvertToCore(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
IList<char> list = value as IList<char>;
if (list == null)
{
throw GetConvertToException(value, destinationType);
}
// An intermediate allocation is needed to construct the string as string doesn't take IList<char>
// in its constructor.
char[] chars = new char[list.Count];
list.CopyTo(chars, 0);
return new string(chars);
}
}
}
|