|
//------------------------------------------------------------------------------
// <copyright file="DeviceSpecificChoice.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Web;
using System.Web.UI;
using System.Web.Mobile;
using System.Security.Permissions;
namespace System.Web.UI.MobileControls
{
/*
* DeviceSpecificChoice object.
*
* Copyright (c) 2000 Microsoft Corporation
*/
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoice"]/*' />
[
ControlBuilderAttribute(typeof(DeviceSpecificChoiceControlBuilder)),
PersistName("Choice"),
PersistChildren(false),
]
[AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
[Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
public class DeviceSpecificChoice : IParserAccessor, IAttributeAccessor
{
private String _deviceFilter = String.Empty;
private String _argument;
private String _xmlns;
private IDictionary _contents;
private IDictionary _templates;
private DeviceSpecific _owner;
private static IComparer _caseInsensitiveComparer =
new CaseInsensitiveComparer(CultureInfo.InvariantCulture);
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoice.Filter"]/*' />
[
DefaultValue("")
]
public String Filter
{
get
{
Debug.Assert(_deviceFilter != null);
return _deviceFilter;
}
set
{
if (value == null)
{
value = String.Empty;
}
_deviceFilter = value;
}
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoice.Argument"]/*' />
public String Argument
{
get
{
return _argument;
}
set
{
_argument = value;
}
}
// This property is used by the Designer, and has no runtime effect
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoice.Xmlns"]/*' />
[
DefaultValue("")
]
public String Xmlns
{
get
{
return _xmlns;
}
set
{
_xmlns = value;
}
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoice.Contents"]/*' />
[
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
]
public IDictionary Contents
{
get
{
if (_contents == null)
{
_contents = new ListDictionary(_caseInsensitiveComparer);
}
return _contents;
}
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoice.Templates"]/*' />
[
PersistenceMode(PersistenceMode.InnerProperty),
]
public IDictionary Templates
{
get
{
if (_templates == null)
{
_templates = new ListDictionary(_caseInsensitiveComparer);
}
return _templates;
}
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoice.HasTemplates"]/*' />
[
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
]
public bool HasTemplates
{
get
{
return _templates != null && _templates.Count > 0;
}
}
internal void ApplyProperties()
{
IDictionaryEnumerator enumerator = Contents.GetEnumerator();
while (enumerator.MoveNext())
{
Object parentObject = Owner.Owner;
String propertyName = (String)enumerator.Key;
String propertyValue = enumerator.Value as String;
// The ID property may not be overridden, according to spec
// (since it will override the parent's ID, not very useful).
if (String.Equals(propertyName, "id", StringComparison.OrdinalIgnoreCase)) {
throw new ArgumentException(
SR.GetString(SR.DeviceSpecificChoice_InvalidPropertyOverride,
propertyName));
}
if (propertyValue != null)
{
// Parse through any "-" syntax items.
int dash;
while ((dash = propertyName.IndexOf("-", StringComparison.Ordinal)) != -1)
{
String containingObjectName = propertyName.Substring(0, dash);
PropertyDescriptor pd = TypeDescriptor.GetProperties(parentObject).Find(
containingObjectName, true);
if (pd == null)
{
throw new ArgumentException(
SR.GetString(SR.DeviceSpecificChoice_OverridingPropertyNotFound,
propertyName));
}
parentObject = pd.GetValue(parentObject);
propertyName = propertyName.Substring(dash + 1);
}
if (!FindAndApplyProperty(parentObject, propertyName, propertyValue) &&
!FindAndApplyEvent(parentObject, propertyName, propertyValue))
{
// If control supports IAttributeAccessor (which it should)
// use it to set a custom attribute.
IAttributeAccessor a = parentObject as IAttributeAccessor;
if (a != null)
{
a.SetAttribute(propertyName, propertyValue);
}
else
{
throw new ArgumentException(
SR.GetString(SR.DeviceSpecificChoice_OverridingPropertyNotFound,
propertyName));
}
}
}
}
}
private bool FindAndApplyProperty(Object parentObject, String name, String value)
{
PropertyDescriptor pd = TypeDescriptor.GetProperties(parentObject).Find(name, true);
if (pd == null)
{
return false;
}
// Make sure the property is declarable.
if (pd.Attributes.Contains(DesignerSerializationVisibilityAttribute.Hidden))
{
throw new ArgumentException(
SR.GetString(SR.DeviceSpecificChoice_OverridingPropertyNotDeclarable, name));
}
Object o;
Type type = pd.PropertyType;
if (type.IsAssignableFrom(typeof(String)))
{
o = value;
}
else if (type.IsAssignableFrom(typeof(int)))
{
o = Int32.Parse(value, CultureInfo.InvariantCulture);
}
else if (type.IsEnum)
{
o = Enum.Parse(type, value, true);
}
else if (value.Length == 0)
{
o = null;
}
else
{
TypeConverter converter = pd.Converter;
if (converter != null)
{
o = converter.ConvertFromInvariantString(value);
}
else
{
throw new InvalidCastException(
SR.GetString(SR.DeviceSpecificChoice_OverridingPropertyTypeCast, name));
}
}
pd.SetValue(parentObject, o);
return true;
}
private bool FindAndApplyEvent(Object parentObject, String name, String value)
{
if (name.Length > 2 &&
Char.ToLower(name[0], CultureInfo.InvariantCulture) == 'o' &&
Char.ToLower(name[1], CultureInfo.InvariantCulture) == 'n')
{
String eventName = name.Substring(2);
EventDescriptor ed = TypeDescriptor.GetEvents(parentObject).Find(eventName, true);
if (ed != null)
{
Delegate d = Delegate.CreateDelegate(ed.EventType, Owner.MobilePage, value);
ed.AddEventHandler(parentObject, d);
return true;
}
}
return false;
}
internal DeviceSpecific Owner
{
get
{
return _owner;
}
set
{
_owner = value;
}
}
internal bool Evaluate(MobileCapabilities capabilities)
{
// Evaluate the <Choice> by first looking to see if it's null, then
// checking against evaluators defined in code on the page, then by
// consulting the MobileCapabilities object.
bool result;
if (_deviceFilter != null && _deviceFilter.Length == 0) {
// indicates device-independent <choice> clause
result = true;
}
else if (CheckOnPageEvaluator(capabilities, out result))
{
// result already been set through the out-bound parameter
// above.
}
else
{
// The exception message generated by HasCapability() failing is
// inappropriate, so we substitute a more specific one.
try
{
result = capabilities.HasCapability(_deviceFilter, _argument);
}
catch
{
throw new ArgumentException(SR.GetString(
SR.DeviceSpecificChoice_CantFindFilter,
_deviceFilter));
}
}
return result;
}
// Return true if specified evaluator exists on the page with the
// correct signature. If it does, return result of invoking it in
// evaluatorResult.
private bool CheckOnPageEvaluator(MobileCapabilities capabilities,
out bool evaluatorResult)
{
evaluatorResult = false;
TemplateControl containingTemplateControl = Owner.ClosestTemplateControl;
MethodInfo methodInfo =
containingTemplateControl.GetType().GetMethod(_deviceFilter,
new Type[]
{
typeof(MobileCapabilities),
typeof(String)
}
);
if (methodInfo == null || methodInfo.ReturnType != typeof(bool))
{
return false;
}
else
{
evaluatorResult = (bool)
methodInfo.Invoke(containingTemplateControl,
new Object[]
{
capabilities,
_argument
}
);
return true;
}
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoice.IAttributeAccessor.GetAttribute"]/*' />
/// <internalonly/>
protected String GetAttribute(String key)
{
Object o = Contents[key];
if (o != null & !(o is String))
{
throw new ArgumentException(SR.GetString(
SR.DeviceSpecificChoice_PropertyNotAnAttribute));
}
return (String)o;
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoice.IAttributeAccessor.SetAttribute"]/*' />
/// <internalonly/>
protected void SetAttribute(String key, String value)
{
Contents[key] = value;
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoice.IParserAccessor.AddParsedSubObject"]/*' />
/// <internalonly/>
protected void AddParsedSubObject(Object obj)
{
DeviceSpecificChoiceTemplateContainer c = obj as DeviceSpecificChoiceTemplateContainer;
if (c != null)
{
Templates[c.Name] = c.Template;
}
}
#region IAttributeAccessor implementation
String IAttributeAccessor.GetAttribute(String name) {
return GetAttribute(name);
}
void IAttributeAccessor.SetAttribute(String name, String value) {
SetAttribute(name, value);
}
#endregion
#region IParserAccessor implementation
void IParserAccessor.AddParsedSubObject(Object obj) {
AddParsedSubObject(obj);
}
#endregion
}
// TEMPLATE BAG
//
// The following classes are public by necessity (since they are exposed to
// the framework), but all internal to the DeviceSpecificChoice. They have to do with
// persistence of arbitrary templates in a choice. Here's a description of what is done:
//
// ASP.NET provides no way for an object or control to allow an arbitrary bag of
// templates. It only allows one way to define templates - the parent object must have
// a property, of type ITemplate, with the same name as the template name. For example,
// the code
//
// <ParentCtl>
// <FirstTemplate>....</FirstTemplate>
// <SecondTemplate>....</SecondTemplate>
// <ThirdTemplate>....</ThirdTemplate>
// </ParentCtl>
//
// only works if the ParentCtl class exposes ITemplate properties with names FirstTemplate,
// SecondTemplate, and ThirdTemplate.
//
// Because Choices apply to any control, that could potentially require any named template,
// what we really need is something like a "template bag" that takes arbitrary templates.
//
// To work around this, here's what is done. First, at compile time:
//
// 1) DeviceSpecificChoice has its own control builder at compile time. When it is given a
// sub-object (in GetChildControlType), it returns DeviceSpecificChoiceTemplateType, which
// is a marker type similar to that used in ASP.NET. However, it is our own class, and
// has DeviceSpecificChoiceTemplateBuilder as its builder.
// 2) DeviceSpecificChoiceTemplateBuilder inherits from TemplateBuilder, and thus has the same
// behavior as TemplateBuilder for parsing and compiling a template. However, it has
// an overriden Init method, which changes the tag name (and thus, the template name)
// to a constant, "Template". It also saves the real template name in a property.
// 3) When parsed, the framework calls the AppendSubBuilder method of the
// DeviceSpecificChoiceBuilder, to add the template builder into it. But this builder
// first creates an intermediate builder, for the class DeviceSpecificChoiceTemplateContainer,
// adding the template name as a property in the builder's attribute dictionary. It then
// adds the intermediate builder into itself, and the template builder into it.
//
// All this has the net effect of automatically transforming something like
//
// <Choice>
// <ItemTemplate>...</ItemTemplate>
// <HeaderTemplate>...</HeaderTemplate>
// </Choice>
//
// into
//
// <Choice>
// <DeviceSpecificChoiceTemplateContainer Name="ItemTemplate">
// <Template>...</Template>
// </DeviceSpecificChoiceTemplateContainer>
// <DeviceSpecificChoiceTemplateContainer Name="HeaderTemplate">
// <Template>...</Template>
// </DeviceSpecificChoiceTemplateContainer>
// </Choice>
//
// Now, at runtime the compiled code creates a DeviceSpecificChoiceTemplateContainer object,
// and calls the AddParsedSubObject method of the DeviceSpecificChoice with it. This code (above)
// then extracts the template referred to by the Template property of the object, and
// uses the Name property to add it to the template bag. Presto, we have a general template bag.
/*
* DeviceSpecificChoice control builder. For more information, see note on "Template Bag" above.
*
* Copyright (c) 2000 Microsoft Corporation
*/
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoiceControlBuilder"]/*' />
[AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
[Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
public class DeviceSpecificChoiceControlBuilder : ControlBuilder
{
private bool _isDeviceIndependent = false;
internal bool IsDeviceIndependent()
{
return _isDeviceIndependent;
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoiceControlBuilder.Init"]/*' />
public override void Init(TemplateParser parser,
ControlBuilder parentBuilder,
Type type,
String tagName,
String id,
IDictionary attributes)
{
if (!(parentBuilder is DeviceSpecificControlBuilder))
{
throw new ArgumentException(
SR.GetString(SR.DeviceSpecificChoice_ChoiceOnlyExistInDeviceSpecific));
}
_isDeviceIndependent = attributes == null || attributes["Filter"] == null;
base.Init (parser, parentBuilder, type, tagName, id, attributes);
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoiceControlBuilder.AppendLiteralString"]/*' />
public override void AppendLiteralString(String text)
{
// Ignore literal strings.
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoiceControlBuilder.GetChildControlType"]/*' />
public override Type GetChildControlType(String tagName, IDictionary attributes)
{
// Assume children are templates.
return typeof(DeviceSpecificChoiceTemplateType);
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoiceControlBuilder.AppendSubBuilder"]/*' />
public override void AppendSubBuilder(ControlBuilder subBuilder)
{
DeviceSpecificChoiceTemplateBuilder tplBuilder =
subBuilder as DeviceSpecificChoiceTemplateBuilder;
if (tplBuilder != null)
{
// Called to add a template. Insert an intermediate control,
// by creating and adding its builder.
ListDictionary dict = new ListDictionary();
// Add the template's name as a Name attribute for the control.
dict["Name"] = tplBuilder.TemplateName;
// 1 and "xxxx" are bogus filename/line number values.
ControlBuilder container = ControlBuilder.CreateBuilderFromType(
Parser, this,
typeof(DeviceSpecificChoiceTemplateContainer),
"Templates",
null, dict, 1, null);
base.AppendSubBuilder(container);
// Now, append the template builder into the new intermediate builder.
container.AppendSubBuilder(subBuilder);
}
else
{
base.AppendSubBuilder(subBuilder);
}
}
}
/*
* DeviceSpecificChoiceTemplateType - marker type for a template that goes inside
* a Choice. Used only at compile time, and never instantiated. See note
* on "Template Bag" above.
*
* Copyright (c) 2000 Microsoft Corporation
*/
[
ControlBuilderAttribute(typeof(DeviceSpecificChoiceTemplateBuilder))
]
[Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
internal class DeviceSpecificChoiceTemplateType : Control, IParserAccessor
{
private DeviceSpecificChoiceTemplateType()
{
}
void IParserAccessor.AddParsedSubObject(Object o)
{
}
}
/*
* DeviceSpecificChoiceTemplateBuilder - builder for a template that goes inside
* a Choice. See note on "Template Bag" above.
* When a Choice is device-independent, it also parses literal text content.
* The code for this is copied from LiteralTextContainerControlBuilder.cs
*
* Copyright (c) 2000 Microsoft Corporation
*/
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoiceTemplateBuilder"]/*' />
[AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
[Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
public class DeviceSpecificChoiceTemplateBuilder : TemplateBuilder
{
private String _templateName;
private bool _doLiteralText = false;
private bool _controlsInserted = false;
internal String TemplateName
{
get
{
return _templateName;
}
}
CompileLiteralTextParser _textParser = null;
internal CompileLiteralTextParser TextParser
{
get
{
if (_textParser == null)
{
_textParser =
new CompileLiteralTextParser(Parser, this, "xxxx", 1);
if (_controlsInserted)
{
_textParser.ResetBreaking();
_textParser.ResetNewParagraph();
}
}
return _textParser;
}
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoiceTemplateBuilder.Init"]/*' />
public override void Init(TemplateParser parser,
ControlBuilder parentBuilder,
Type type,
String tagName,
String id,
IDictionary attributes)
{
// Save off template name, and always pass the name "Template" to the base
// class, because the intermediate object has this property as the name.
_templateName = tagName;
base.Init(parser, parentBuilder, type, "Template", id, attributes);
// Are we a device-independent template?
if (!InDesigner)
{
DeviceSpecificChoiceControlBuilder choiceBuilder =
parentBuilder as DeviceSpecificChoiceControlBuilder;
_doLiteralText = choiceBuilder != null && choiceBuilder.IsDeviceIndependent();
}
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoiceTemplateBuilder.AppendLiteralString"]/*' />
public override void AppendLiteralString(String text)
{
if (_doLiteralText)
{
if (LiteralTextParser.IsValidText(text))
{
TextParser.Parse(text);
}
}
else
{
base.AppendLiteralString(text);
}
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoiceTemplateBuilder.AppendSubBuilder"]/*' />
public override void AppendSubBuilder(ControlBuilder subBuilder)
{
if (_doLiteralText)
{
// The first one is used if ASP.NET is compiled with FAST_DATABINDING off. The second
// is used if it is compiled with FAST_DATABINDING on. Note: We can't do a type
// comparison because CodeBlockBuilder is internal.
// if (typeof(DataBoundLiteralControl).IsAssignableFrom(subBuilder.ControlType))
if (subBuilder.GetType().FullName == "System.Web.UI.CodeBlockBuilder")
{
TextParser.AddDataBinding(subBuilder);
}
else
{
base.AppendSubBuilder(subBuilder);
if (subBuilder.ControlType != typeof(LiteralText))
{
if (_textParser != null)
{
_textParser.ResetBreaking();
}
else
{
_controlsInserted = true;
}
}
}
}
else
{
base.AppendSubBuilder(subBuilder);
}
}
}
/*
* DeviceSpecificChoiceTemplateContainer - "dummy" container object for
* a template that goes inside a Choice. Once the Choice receives and
* extracts the information out of it, this object is simply discarded.
* See note on "Template Bag" above.
*
* Copyright (c) 2000 Microsoft Corporation
*/
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoiceTemplateContainer"]/*' />
[AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
[Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
public class DeviceSpecificChoiceTemplateContainer
{
private ITemplate _template;
private String _name;
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoiceTemplateContainer.Template"]/*' />
[
Filterable(false),
TemplateContainer(typeof(TemplateContainer)),
]
public ITemplate Template
{
get
{
return _template;
}
set
{
_template = value;
}
}
/// <include file='doc\DeviceSpecificChoice.uex' path='docs/doc[@for="DeviceSpecificChoiceTemplateContainer.Name"]/*' />
public String Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
}
}
|