-
Notifications
You must be signed in to change notification settings - Fork 263
General Adaptation Classes
The namespace Sce.Atf.Adaptation
provides several classes fundamental to adaptation.
The Adapters
class contains the key adaptation methods that actually get adapters. Adapters
provides these methods as extension methods. These extension methods are on the base class object
, so they apply to any object. Simply adding using Sce.Atf.Adaptation;
to a file makes these extension methods available.
The As()
methods are the adaptation workhorses, doing the basic work of finding adapters and called by all the other Adapters
methods. They take the following forms, all invoked on the object to be adapted:
-
object As(this object reference, Type type)
: Parameter specifies the type. -
T As<T>(this object reference)
: Type is the generic parameter. -
T As<T>(this IAdaptable adaptable)
: Type is the generic parameter; invoked on object implementingIAdaptable
.
As()
method attempts to return an adapter for the object it is invoked on. Examining this method in detail illuminates how adaptation works. Here's the entire method (link to source):
public static object As(this object reference, Type type)
{
if (reference == null)
return null;
if (type == null)
throw new ArgumentNullException("type");
// is the adapted object compatible?
if (type.IsAssignableFrom(reference.GetType()))
return reference;
// try to get an adapter
var adaptable = reference as IAdaptable;
if (adaptable != null)
{
object adapter = adaptable.GetAdapter(type);
if (adapter != null)
return adapter;
}
return null;
}
After checking parameters, As()
calls the System.Type
method type.IsAssignableFrom(reference.GetType())
to determine whether an object of the given type
is assignable from an object of the type of reference
. For this case, the comments on bool IsAssignableFrom(Type)
say it returns true if any of these are true:
-
reference.GetType()
andtype
represent the same type. -
type
is in the inheritance hierarchy ofreference.GetType()
, that is,reference.GetType()
derives fromtype
through a derivation hierarchy. -
type
is an interface thatreference.GetType()
implements. -
reference.GetType()
is a generic type parameter andtype
represents one of the constraints ofreference.GetType()
.
IsAssignableFrom()
method returns false
if none of these conditions are true or reference.GetType()
is null
. These conditions spell out the type compatibility for the assignment.
When type.IsAssignableFrom(reference.GetType())
is true, an object of type
can be assigned from an object of type reference
. In this case, the object reference
itself is returned, so the object serves as its own adapter. In other words, reference
can be cast as type
.
If casting is not possible, the method then tries to get an adapter for reference
. It can only do this when reference
implements IAdaptable
, so GetAdapter()
can be invoked on reference
to get an adapter to return. Failing this, As()
returns null
to indicate no adapter is available.
This highlights how ATF's adaptation goes beyond what the C# as
operator offers, because type compatibility is not required. If casting fails, the As()
method looks for an adapter for the object it is invoked on. You define the adapter that is returned in your reference
object's implementation of IAdaptable.GetAdapter()
, so adaptation is under your control.
The method As<T>(this object reference)
works similarly (link to source):
public static T As<T>(this object reference)
where T : class
{
if (reference == null)
return null;
// try a normal cast
var converted = reference as T;
// if that fails, try to get an adapter
if (converted == null)
{
var adaptable = reference as IAdaptable;
if (adaptable != null)
converted = adaptable.GetAdapter(typeof(T)) as T;
}
return converted;
}
After parameter checks, the method tries the cast var converted = reference as T;
. If that fails, it attempts to get an adapter. Again, this requires that reference
implement IAdaptable
.
The method As<T>(this IAdaptable adaptable)
is almost the same as the other As()
methods.
Note that the As()
methods always return an object of the type requested, or a type that is assignable from that type.
The Cast()
methods are almost the same as their As()
counterparts, returning an adapter, and are implemented by calling As()
methods. The only difference is that if no adapter is found, they raise an exception instead of returning null
. The available forms are similar to As()
:
-
object Cast(this object reference, Type type)
: Parameter specifies the type. -
T Cast<T>(this object reference)
: Type is the generic parameter. -
T Cast<T>(this IAdaptable adaptable)
: Type is the generic parameter; invoked on object implementingIAdaptable
.
As()
methods, Cast()
methods always return an object of the type requested, or a type that is assignable from that type.
Is()
methods simply determine whether an adapter is available. The are implemented by calling the appropriate As()
method and checking whether null
is returned or not. Their forms are similar to As()
and Cast()
, and all return true if and only if the object is adaptable to the given type:
-
bool Is(this object reference, Type type)
: Parameter specifies the type. -
bool Is<T>(this object reference)
: Type is the generic parameter. -
bool Is<T>(this IAdaptable adaptable)
: Type is the generic parameter; invoked on object implementingIAdaptable
.
public class Prince
{
...
}
public class PrinceMorph : Prince
{
...
}
public void Test()
{
Prince Ralph;
PrinceMorph Frog = null;
Ralph = Frog;
if (Frog.Is<Prince>())
{
...
}
}
Because Frog
's class derives from Prince
, Prince
is assignable from Frog
, and the assignment above is valid. So, in this case, the answer to the question Frog.Is<Prince>()
? is true.
Some methods in Adapters
work on an IEnumerable
of adapters in a variety of ways:
-
IEnumerable<object> AsAll(this object reference, Type type)
: Get all adapters that can convert a reference to the given type. -
IEnumerable<T> AsAll<T>(this object reference)
: Get all adapters that can convert a reference to the given typeT
. -
IEnumerable<T> AsAll<T>(this IDecoratable decoratable)
: Get an enumeration of all adapters that can convert a reference to the given type. -
IEnumerable<object> AsIEnumerable(this IEnumerable enumerable, Type type)
: Get an adapter that converts an enumerable to an enumerable of another type. -
IEnumerable<T> AsIEnumerable<T>(this IEnumerable enumerable)
: Return an enumeration for an adapter that converts an enumerable to an enumerable of another typeT
. -
bool Any(this IEnumerable enumerable, Type type)
: Returns whether any of the items in the enumerable are adaptable to the given type. -
bool Any<T>(this IEnumerable enumerable)
: Return whether any of the items in the enumerable are adaptable to the given typeT
. -
bool All(this IEnumerable enumerable, Type type)
: Return whether all of the items in the enumerable are adaptable to the given type. -
bool All<T>(this IEnumerable enumerable)
: Return whether all of the items in the enumerable are adaptable to the given typeT
.
The adaptation methods in Adapters
are widely used in ATF, especially for the ATF DOM. It is very common to use the As<>T()
and Cast<>T()
methods to adapt a DomNode
to another type for which it has a DOM adapter—an object that adapts the DomNode
to the specified type. The fundamental DOM adapter class DomNodeAdapter
implements the adaptation interfaces previously discussed:
public abstract class DomNodeAdapter : IAdapter, IAdaptable, IDecoratable
Creating adapters in the DOM follows this development sequence:
- A type is defined, typically in a type definition file. The ATF DOM provides excellent support for type definitions in the XML Schema Definition (XSD) language.
- The XML schema is loaded, if one is used.
- A DOM adapter is defined for types whose data is represented by a
DomNode
in the application data. - The DOM adapter is initialized to its
DomNode
by calling any of theAdapters
methodsAs<>T()
,Cast<>T()
, orIs<>T()
. The returned value fromAs<>T()
orCast<>T()
can then be used with the API of the DOM adapter and holds the application data of theDomNode
, because it is still aDomNode
.
EventSequenceContext context = Adapters.As<EventSequenceContext>(document);
EventSequenceContext
is a DOM adapter derived from EditingContext
that ultimately derives from DomNodeAdapter
:
EventSequenceContext : EditingContext
EventSequenceContext
is also defined as a DOM adapter for the root type of the data, "eventSequenceType", which is also the type of the document:
Schema.eventSequenceType.Type.Define(new ExtensionInfo<EventSequenceContext>());
For details on using adaptation in the DOM, see DOM Adapters in the DOM in a Nutshell section. For the full reference, see the ATF Programmer’s Guide: Document Object Model (DOM), which you can download at ATF Documentation.
Adapter
provides an example of an adapter and is used as an adapter a few places in ATF:
public class Adapter : IAdapter, IAdaptable, IDecoratable
In addition to IAdapter
, Adapter
implements IAdaptable
and IDecoratable
. (This is more of a reminder than a requirement, because IAdapter
implements both of these anyway.)
The IAdapter.Adaptee
property gets and sets the adaptee, that is, the object being adapted (link to source):
public object Adaptee
{
get { return m_adaptee; }
set
{
if (m_adaptee != value)
{
object oldAdaptee = m_adaptee;
m_adaptee = value;
OnAdapteeChanged(oldAdaptee);
}
}
}
The Adapter
class gets adapters using the methods in Adapters
. For details on Adapters
methods, see Adapters Class.
Adapter
's casting methods (As<>T()
, Cast<>T()
, and Is<>T()
) simply use the corresponding Adapters
methods. For example, As<T>()
calls Adapters.As<T>()
(link to source):
public T As<T>()
where T : class
{
return Adapters.As<T>(this);
}
The IAdaptable.GetAdapter()
method is more involved than the casting methods (link to source):
public object GetAdapter(Type type)
{
// see if this can adapt
object adapter = Adapt(type);
// if not, let the adaptee handle it
if (adapter == null)
adapter = m_adaptee.As(type);
return adapter;
}
GetAdapter()
first tries the Adapt()
method to determine if the object itself can serve as an adapter (link to source):
protected virtual object Adapt(Type type)
{
// default is to return this if compatible with requested type
if (type.IsAssignableFrom(GetType()))
return this;
return null;
}
Adapt()
calls type.IsAssignableFrom(GetType())
to determine whether an object of the given type
is assignable from an object of the type of the Adapter
object. When IsAssignableFrom()
is true, Adapt()
returns this
, the object itself. GetAdapter()
returns this
in turn.
If the object can't serve as its own adapter, GetAdapter()
makes this assignment:
adapter = m_adaptee.As(type);
m_adaptee.As(type)
invokes Adapters.As()
on the adaptee (the object being adapted) because:
-
m_adaptee
holds theAdaptee
property value. -
Adapters.As()
is an extension method onobject
. -
Adapter
is in theSce.Atf.Adaptation
namespace, same asAdapters
.
IDecoratable.GetDecorators()
returning an IEnumerable
of adapters (link to source):
public IEnumerable<object> GetDecorators(Type type)
{
// see if this can adapt
object adapter = Adapt(type);
if (adapter != null)
yield return adapter;
foreach (object obj in m_adaptee.AsAll(type))
if (obj != adapter)
yield return obj;
}
Several Sce.Atf.Adaptation
classes operate on collections:
-
AdaptableActiveCollection
: Collection representing active items that uses adaptation on items implementingIAdaptable
to convert them to other types. It's used by both theContextRegistry
andDocumentRegistry
components. -
AdaptableCollection
: Wrap anICollection
of one type to implement anICollection
adapted to another type. -
AdaptableList
: Wrap anIList
of one type to implement anIList
adapted to another type. -
AdaptableSelection
: Collection representing a selection. It uses adaptation to convert to other types. It is used inSce.Atf.Dom.EditingContext
from which several samples derive editing contexts, as well as inSce.Atf.Dom.SelectionContext
.
AdapterCreator
implements IAdapterCreator
and appears in all the samples that use the ATF DOM in a line like this:
DomNodeType.BaseOfAllTypes.AddAdapterCreator(new AdapterCreator<CustomTypeDescriptorNodeAdapter>());
AdapterCreator
is used here to make property editors show the attributes listed in the property descriptor definitions. For more information, see Metadata Driven Property Editing.
AdaptablePath
represents a path in a tree or graph, such as a DomNode
tree representing application data. AdaptablePath
can adapt the last element of the path to a requested type. For example, DOM property descriptors (like AttributePropertyDescriptor
) try to adapt an AdaptablePath
object to a DomNode
, and this succeeds if the last element of the path can be adapted to a DomNode
. AdaptablePath
is used in various classes that use paths in trees and lists.
- What is Adaptation: General discussion of what adaptation is and how it is used in ATF.
- General Adaptation Interfaces: Survey of interfaces for adaptation.
- General Adaptation Classes: Describe fundamental classes in adaptation.
- Control Adapters: Discussion of control adapters, which add abilities to controls without changing the control.
- Other Adaptation Classes: Survey of non-control adapter classes that perform various kinds of adaptation.
- Adapting to All Available Interfaces: A detailed example of how adaptation works and what you need to do to make it work showing how adapters can be obtained for any interface DOM adapters implement.
- Home
- Getting Started
- Features & Benefits
- Requirements & Dependencies
- Gallery
- Technology & Samples
- Adoption
- News
- Release Notes
- ATF Community
- Searching Documentation
- Using Documentation
- Videos
- Tutorials
- How To
- Programmer's Guide
- Reference
- Code Samples
- Documentation Files
© 2014-2015, Sony Computer Entertainment America LLC