Unable to add mutator for an existing field of a class - byte-buddy

I'm trying to add a mutator for an existing private final field. I can transform the field modifiers to remove the final specification and add an accessor method:
// accessor interface
public interface UniqueIdAccessor {
Serializable getUniqueId();
}
// mutator interface
public interface UniqueIdMutator {
void setUniqueId(Serializable uniqueId);
}
...
// fragment of Java agent implementation
return new AgentBuilder.Default()
.type(hasSuperType(named("org.junit.runner.Description")))
.transform(new Transformer() {
#Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module) {
return builder.field(named("fUniqueId")).transform(ForField.withModifiers(FieldManifestation.PLAIN))
.implement(UniqueIdAccessor.class).intercept(FieldAccessor.ofField("fUniqueId"))
// .implement(UniqueIdMutator.class).intercept(FieldAccessor.ofField("fUniqueId"))
.implement(Hooked.class);
}
})
.installOn(instrumentation);
...
Here's a method that uses reflection to check the modifiers of the target field and calls the accessor to get the value of the field.
private static void injectProxy(Description description) {
try {
Field bar = Description.class.getDeclaredField("fUniqueId");
System.out.println("isFinal: " + ((bar.getModifiers() & Modifier.FINAL) != 0));
} catch (NoSuchFieldException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Serializable uniqueId = ((UniqueIdAccessor) description).getUniqueId();
System.out.println("uniqueId: " + uniqueId);
}
// isFinal: false
// uniqueId: <description-unique-id>
... but if I uncomment the second "implement" expression to add the mutator, the transform blows up:
// isFinal: true
// java.lang.ClassCastException:
// class org.junit.runner.Description cannot be cast to class com.nordstrom.automation.junit.UniqueIdAccessor
// (org.junit.runner.Description and com.nordstrom.automation.junit.UniqueIdAccessor
// are in unnamed module of loader 'app')
I could set the field value with reflection, but that defeats the purpose of using Byte Buddy in the first place!

The problem with this approach is that the field accessor considers the input type prior to the modification. Byte Buddy prohibits this as it does not consider the mutation to be legal, not knowing about the removed modifier. As a result, the transformation fails in its entirety and you get the error you are seeing. (Register a listener to see this error.)
To avoid this, you can implement a custom Implementation using FieldAccess (without or). You can have a look at the more convenient FieldAccessor to see how this is implemented, only that you need to drop the validity checks.

Thanks for pointing me in the right direction! I assemble the StackManipulation object that defines the mutator method with this:
final TypeDescription description = TypePool.Default.ofSystemLoader().describe("org.junit.runner.Description").resolve();
final Generic _void_ = TypeDescription.VOID.asGenericType();
final Generic serializable = TypePool.Default.ofSystemLoader().describe("java.io.Serializable").resolve().asGenericType();
final MethodDescription.Token setUniqueIdToken = new MethodDescription.Token("setUniqueId", Modifier.PUBLIC, _void_, Arrays.asList(serializable));
final MethodDescription setUniqueId = new MethodDescription.Latent(description, setUniqueIdToken);
final Token fUniqueIdToken = new FieldDescription.Token("fUniqueId", Modifier.PRIVATE, serializable);
final FieldDescription fUniqueId = new FieldDescription.Latent(description, fUniqueIdToken);
final StackManipulation setUniqueIdImpl = new StackManipulation.Compound(
MethodVariableAccess.loadThis(),
MethodVariableAccess.load(setUniqueId.getParameters().get(0)),
Assigner.DEFAULT.assign(serializable, serializable, Typing.STATIC),
FieldAccess.forField(fUniqueId).write(),
MethodReturn.VOID
);
... and I transform the target class with this:
return new AgentBuilder.Default()
.type(hasSuperType(named("org.junit.runner.Description")))
.transform(new Transformer() {
#Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module) {
return builder.field(named("fUniqueId")).transform(ForField.withModifiers(FieldManifestation.PLAIN))
.implement(AnnotationsAccessor.class).intercept(FieldAccessor.ofField("fAnnotations"))
.implement(UniqueIdAccessor.class).intercept(FieldAccessor.ofField("fUniqueId"))
.implement(UniqueIdMutator.class).intercept(new Implementation.Simple(setUniqueIdImpl));
}
})
.installOn(instrumentation);
Here are the definitions of the three interfaces used in the transform:
// annotations accessor interface
public interface AnnotationsAccessor {
Annotation[] annotations();
}
// unique ID accessor interface
public interface UniqueIdAccessor {
Serializable getUniqueId();
}
// unique ID mutator interface
public interface UniqueIdMutator {
void setUniqueId(Serializable uniqueId);
}

Related

Customized parameter logging when using aspect oriented programing

All the examples I've seen that use aspect oriented programming for logging either log just class, method name and duration, and if they log parameters and return values they simply use ToString(). I need to have more control over what is logged. For example I want to skip passwords, or in some cases log all properties of an object but in other cases just the id property.
Any suggestions? I looked at AspectJ in Java and Unity interception in C# and could not find a solution.
You could try introducing parameter annotations to augment your parameters with some attributes. One of those attributes could signal to skip logging the parameter, another one could be used to specify a converter class for the string representation.
With the following annotations:
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Log {
}
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface SkipLogging {
}
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface ToStringWith {
Class<? extends Function<?, String>> value();
}
the aspect could look like this:
import java.lang.reflect.Parameter;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public aspect LoggingAspect {
private final static Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
pointcut loggableMethod(): execution(#Log * *..*.*(..));
before(): loggableMethod() {
MethodSignature signature = (MethodSignature) thisJoinPoint.getSignature();
Parameter[] parameters = signature.getMethod()
.getParameters();
String message = IntStream.range(0, parameters.length)
.filter(i -> this.isLoggable(parameters[i]))
.<String>mapToObj(i -> toString(parameters[i], thisJoinPoint.getArgs()[i]))
.collect(Collectors.joining(", ",
"method execution " + signature.getName() + "(", ")"));
Logger methodLogger = LoggerFactory.getLogger(
thisJoinPointStaticPart.getSignature().getDeclaringType());
methodLogger.debug(message);
}
private boolean isLoggable(Parameter parameter) {
return parameter.getAnnotation(SkipLogging.class) == null;
}
private String toString(Parameter parameter, Object value) {
ToStringWith toStringWith = parameter.getAnnotation(ToStringWith.class);
if (toStringWith != null) {
Class<? extends Function<?, String>> converterClass =
toStringWith.value();
try {
#SuppressWarnings("unchecked")
Function<Object, String> converter = (Function<Object, String>)
converterClass.newInstance();
String str = converter.apply(value);
return String.format("%s='%s'", parameter.getName(), str);
} catch (Exception e) {
logger.error("Couldn't instantiate toString converter for logging "
+ converterClass.getName(), e);
return String.format("%s=<error converting to string>",
parameter.getName());
}
} else {
return String.format("%s='%s'", parameter.getName(), String.valueOf(value));
}
}
}
Test code:
public static class SomethingToStringConverter implements Function<Something, String> {
#Override
public String apply(Something something) {
return "Something nice";
}
}
#Log
public void test(
#ToStringWith(SomethingToStringConverter.class) Something something,
String string,
#SkipLogging Class<?> cls,
Object object) {
}
public static void main(String[] args) {
// execution of this method should log the following message:
// method execution test(something='Something nice', string='some string', object='null')
test(new Something(), "some string", Object.class, null);
}
I used Java 8 Streams API in my answer for it's compactness, you could convert the code to normal Java code if you don't use Java 8 features or need better efficiency. It's just to give you an idea.

Wrong approach or Wrong OOP design?

Following is my code isolation.
Interactable Interface.
public interface Interactable <E extends Interactable> {
List<Person> personsInteracting = new ArrayList<>();
List<Person> personsWaiting = new ArrayList<>();
long INTERACTION_TIME = 5 * 60;
default int getNumberOfPeopleInteracting () {
return personsInteracting.size();
}
default int getNumberOfPeopleWaiting () {
return personsWaiting.size();
}
boolean isMultipleActionsAllowed ();
boolean isFurtherActionsAllowed ();
public abstract boolean tryOccupiedBy (final Person person, final Interactions interaction)
throws InteractionNotPossibleException;
E getObject ();
EnumSet<Interactions> getInteractions ();
}
InteractiveObject Abstract Class
public abstract class InteractiveObject implements Interactable {
protected final String name;
protected int numberOfSimultaneousInteractions;
protected Interactions currentInteraction;
public InteractiveObject (final String name) {
this.name = name;
}
#Override
public boolean isMultipleActionsAllowed () {
return numberOfSimultaneousInteractions > 1;
}
#Override
public boolean isFurtherActionsAllowed () {
return personsInteracting.isEmpty() ||
(getNumberOfPeopleInteracting() > numberOfSimultaneousInteractions);
}
#Override
public boolean tryOccupiedBy (final Person person, final Interactions interaction)
throws InteractionNotPossibleException {
boolean isOccupied = false;
if (!isFurtherActionsAllowed()) {
throw new InteractionNotPossibleException(this + " is already in use by some other " +
"person.");
}
personsInteracting.add(person);
currentInteraction = interaction;
return isOccupied;
}
#Override
public String toString () {
return name;
}
public int getNumberOfSimultaneousInteractions () {
return numberOfSimultaneousInteractions;
}
}
Chair (One of the child class)
public class Chair extends InteractiveObject {
private final EnumSet<Interactions> INTERACTIONS = EnumSet.copyOf(Arrays.asList(
new Interactions[] {Interactions.DRAG, Interactions.SIT}));
public Chair (final String objectName) {
super(objectName);
super.numberOfSimultaneousInteractions = 1;
}
#Override
public Interactable getObject () {
return this;
}
#Override
public EnumSet<Interactions> getInteractions () {
return INTERACTIONS;
}
}
Here is the piece of code that executes and brings the problem, this question is asked for.
final InteractiveObject chair1 = new Chair("Chair1");
final Person person1 = new Person("Person1");
final Room room = new Room("Room1", 2, 2);
room.personEnters(person1);
room.putObject(chair1);
person1.tryOccupying(chair1);
Above piece of code, successfully occupies the chair object. Now,
final InteractiveObject chair2 = new Chair("Chair2");
final Person person2 = new Person("Person2");
final Room room2 = new Room("Room2", 2, 2);
room2.personEnters(person2);
room2.putObject(chair2);
person2.tryOccupying(chair2);
This piece of code doesn't let the person2 occupy since my code states that 1 person is already interacting with chair2, where as no one is interacting with it.
Solution of my problem:
I moved my List of personInteracting to InteractiveObject and function tryOccupiedBy to each child class and everything works fine.
Questions:
I put personsInteracting in Interactable interface since I believe that every future implementation of Interactable will have it. Developers won't have to implement themselves. (But perhaps this idea seems to be wrong)
If tryOccupiedBy function has same implementation, what is the purpose of whole OOP?
I now know that the isolation was wrong and I know where to place the pieces to get the results. But can someone kindly point me out about some OOP concept which I did not understand and should be implemented in a much better way?
The default keyword was not added to the Java language to do the kind of thing which you seem to be trying to achieve. Data defined in an interface is intended to be constant - the modifiers 'public static' are automatically applied to any field definitions in an interface. If you create a default method in the interface then it must either be stateless or act directly only on purely statically available state. Default methods can call other interface methods to modify instance state, .
By placing personsInteracting field in the interface, you made the same instance common to every object implementing that interface, and so your tryOccupying method was acting on purely global state.
So, the purpose of having default methods in the Java language is to support adding new methods to interfaces in a backwards compatible fashion, nothing more. You shouldn't reuse it as a generic form of code re-use - it was never intended for that and you'll get (as you did) weird behaviour.
You didn't have to put tryOccupiedBy in the child classes, however, so you didn't have to have a load of duplicated code. You could still declare the method signature in the interface (which is what interfaces are generally supposed to do) and then implement the common method in your abstract base class. By putting the data fields in the base class, you make them instance fields and so they are not shared between objects.
public interface Interactable <E extends Interactable> {
...
boolean tryOccupiedBy (final Person person, final Interactions interaction)
throws InteractionNotPossibleException;
...
}
public abstract class InteractiveObject implements Interactable {
private final List<Person> personsInteracting = new ArrayList<>();
private final List<Person> personsWaiting = new ArrayList<>();
...
#Override
public final boolean tryOccupiedBy (final Person person, final Interactions interaction)
throws InteractionNotPossibleException {
boolean isOccupied = false;
if (!isFurtherActionsAllowed()) {
throw new InteractionNotPossibleException(this + " is already in use by some other " +
"person.");
}
personsInteracting.add(person);
currentInteraction = interaction;
return isOccupied;
}
...
}

Accesing arraylist property from another class using constructor

So i have a class that makes an array list for me and i need to access it in another class through a constructor but i don't know what to put into the constructor because all my methods in that class are just for manipulating that list. im either getting a null pointer exception or a out of bounds exception. ive tried just leaving the constructor empty but that dosent seem to help. thanks in advance. i would show you code but my professor is very strict on academic dishonesty so i cant sorry if that makes it hard.
You are confusing the main question, with a potential solution.
Main Question:
I have a class ArrayListOwnerClass with an enclosed arraylist property or field.
How should another class ArrayListFriendClass access that property.
Potential Solution:
Should I pass the arraylist from ArrayListOwnerClass to ArrayListFriendClass,
in the ArrayListFriendClass constructor ?
It depends on what the second class does with the arraylist.
Instead of passing the list thru the constructor, you may add functions to read or change, as public, the elements of the hidden internal arraylist.
Note: You did not specify a programming language. I'll use C#, altought Java, C++, or similar O.O.P. could be used, instead.
public class ArrayListOwnerClass
{
protected int F_Length;
protected ArrayList F_List;
public ArrayListOwnerClass(int ALength)
{
this.F_Length = ALength;
this.F_List = new ArrayList(ALength);
// ...
} // ArrayListOwnerClass(...)
public int Length()
{
return this.F_Length;
} // int Length(...)
public object getAt(int AIndex)
{
return this.F_List[AIndex];
} // object getAt(...)
public void setAt(int AIndex, object AValue)
{
this.F_List[AIndex] = AValue;
} // void setAt(...)
public void DoOtherStuff()
{
// ...
} // void DoOtherStuff(...)
// ...
} // class ArrayListOwnerClass
public class ArrayListFriendClass
{
public void UseArrayList(ArrayListOwnerClass AListOwner)
{
bool CanContinue =
(AListOwner != null) && (AListOwner.Length() > 0);
if (CanContinue)
{
int AItem = AListOwner.getAt(5);
DoSomethingWith(Item);
} // if (CanContinue)
} // void UseArrayList(...)
public void AlsoDoesOtherStuff()
{
// ...
} // void AlsoDoesOtherStuff(...)
// ...
} // class ArrayListFriendClass
Note, that I could use an indexed property.

Can I specify the jackson #JsonView to use for method result transformation in RestEasy?

I'm working with a serialization model based on #JsonView. I normally configure jackson with a ContextResolver like this:
#Override
public ObjectMapper getContext(Class<?> aClass) {
// enable a view by default, else Views are not processed
Class view = Object.class;
if (aClass.getPackage().getName().startsWith("my.company.entity")) {
view = getViewNameForClass(aClass);
}
objectMapper.setSerializationConfig(
objectMapper.getSerializationConfig().withView(view));
return objectMapper;
}
This works fine if I serialize single entities. However, for certain use cases I want to serialize lists of my entities using the same view as for single entities. In this case, aClass is ArrayList, so the usual logic doesn't help much.
So I'm looking for a way to tell Jackson which view to use. Ideally, I'd write:
#GET #Produces("application/json; charset=UTF-8")
#JsonView(JSONEntity.class)
public List<T> getAll(#Context UriInfo uriInfo) {
return getAll(uriInfo.getQueryParameters());
}
And have that serialized under the view JSONEntity. Is this possible with RestEasy? If not, how can I emulate that?
Edit: I know I can do the serialization myself:
public String getAll(#Context UriInfo info, #Context Providers factory) {
List<T> entities = getAll(info.getQueryParameters());
ObjectMapper mapper = factory.getContextResolver(
ObjectMapper.class, MediaType.APPLICATION
).getContext(entityClass);
return mapper.writeValueAsString(entities);
}
However, this is clumsy at best and defeats the whole idea of having the framework deal with this boilerplate.
Turns out, it is possible to simply annotate a specific endpoint with #JsonView (just as in my question) and jackson will use this view. Who would have guessed.
You can even do this in the generic way (more context in my other question), but that ties me to RestEasy:
#Override
public void writeTo(Object value, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHd,
OutputStream out) throws IOException {
Class view = getViewFromType(type, genericType);
ObjectMapper mapper = locateMapper(type, mediaType);
Annotation[] myAnn = Arrays.copyOf(annotations, annotations.length + 1);
myAnn[annotations.length] = new JsonViewQualifier(view);
super.writeTo(value, type, genericType, myAnn, mediaType, httpHd, out);
}
private Class getViewFromType(Class<?> type, Type genericType) {
// unwrap collections
Class target = org.jboss.resteasy.util.Types.getCollectionBaseType(
type, genericType);
target = target != null ? target : type;
try {
// use my mix-in as view class
return Class.forName("example.jackson.JSON" + target.getSimpleName());
} catch (ClassNotFoundException e) {
LOGGER.info("No view found for {}", target.getSimpleName());
}
return Object.class;
}

How do I bind generic types with inheritance using Ninject Conventions extensions

How can I bind InitializerForXXX (non-generic implementation) to IInitializer<XXX> (generic interface) using Ninject Conventions so that requests for an IInitializer<T> resolve a non-generic implementation whose name starts with InitializerFor and end with typeof(T).Name like:
initializerFactory.CreateFor<Blue>(); //resolves InitializerOfBlue
initializerFactory.CreateFor<ShadeOfBlue>(); //resolves InitializerOfShadeOfBlue
where no non-abstract class directly implement IInitializer<T>, and some implementations inherit from other implementations:
InitializerForShadeOfBlue inherits from InitializerForBlue
InitializerForBlue inherits from abstract Initializer<Blue>
abstract Initializer<T> directly implements IInitializer<T>
I'm hoping I can use a .EndsWith(typeof(T).Name) for a given IInitializer<T> convention I can use, because there are literally hundreds of initializers in the ShadeOfxxx vein. If I have to map all of them, I'm better off finding a way to resolve with reflection at runtime.
Given the following:
UPDATE: bindings with custom binding generator (see my answer below for implementation)
void Bootstrap(IBindingRoot kernel)
{
kernel.Bind<IInitializerFactory>()
.To<InitializerFactory>()
.InSingletonScope();
kernel.Bind(scanner =>
scanner.FromThisAssembly().SelectAllClasses()
.WhichAreNotGeneric()
.InheritedFrom(typeof(IComplexContent))
.BindAllInterfaces());
kernel.Bind(scanner =>
scanner.FromThisAssembly().SelectAllClasses()
.WhichAreNotGeneric()
.InheritedFrom(typeof(IInitializer<>))
.BindWith<FirstTypeParameterNameMatchesEndOfBoundClassNameGenerator>());
}
main method
void Main(IEnumerable<string> values)
{
// setup bindings
var kernel = new StandardKernel();
Bootstrap(kernel);
IInitializerFactory initializerFactory =
kernel.Get<IInitializerFactory>();
IInitializer<ShadeOfBlueComplexContent> initializer =
initializerFactory.CreateFor<ShadeOfBlueComplexContent>();
initializer.Initialize(values);
}
initializer factory
interface IInitializerFactory
{
IInitializer<T> CreateFor<T>() where T : class, IComplexContent, new();
}
class InitializerFactory : IInitializerFactory
{
public IInitializer<T> CreateFor<T>() where T : class, IComplexContent, new()
{
return MagicallyGetInitializer<T>();
}
//behind the curtain, whirring noises are heard as 't' is resolved...
private static IInitializer<T> MagicallyGetInitializer<T>()
where T : class, IComplexContent, new()
{
IInitializer<T> i = null;
return i;
}
}
initializers
interface IInitializer<out T> where T : IComplexContent
{
T Initialize(IEnumerable<string> values);
}
abstract class Initializer<T> : IInitializer<T> where T : IComplexContent
{
public abstract T Initialize(IEnumerable<string> values);
}
class InitializerOfBlue : Initializer<Blue>
{
private readonly Blue _content;
public InitializerOfBlue(Blue content) {_content = content;}
public override Blue Initialize(IEnumerable<string> values)
{
_content.BlueSpecificProperty = values.ElementAt(0);
//... populate other blue-specific properties like this
return _content;
}
}
class InitializerOfShadeOfBlue : InitializerOfBlue
{
public InitializerOfShadeOfBlue(ShadeOfBlue content) : base(content){}
}
content models
interface IComplexContent
{
string OneBasicProperty { get; set; }
// other properties are specific to implementation
string UniqueOperation();
}
abstract class BaseComplexContent : IComplexContent
{
public string OneBasicProperty { get; set; }
public abstract string UniqueOperation();
}
class Blue : BaseComplexContent
{
// initializer sets this
public string PropertyForAllKindsOfBlue { get; set; }
// initializer doesn't interact with this
public override string UniqueOperation() {return "I'm plain.";}
}
class ShadeOfBlue : Blue
{
// initializer doesn't interact with this
public override string UniqueOperation() {return "I'm fabulous!";}
}
You are over specifying the class selection
kernel.Bind(scanner =>
scanner.FromThisAssembly().SelectAllClasses()
.WhichAreNotGeneric()
.InheritedFrom(typeof (IInitializer<>))
This is already enough. What you need to do though is to add a custom Binding Generator. That selects IInitializer<Blue> for InitializerForBlue and IInitializer<ShadeOfBlue> for InitializerForShadeOfBlue
https://github.com/ninject/ninject.extensions.conventions/wiki/Projecting-Services-to-Bind
BEGIN SOLUTION CANDIDATE - custom binding generator:
custom binding generator
Thanks for the advice, #RemoGloor and #RubenBartelink. I'm stumped though - the problem is that I wind up binding the IInitializer<Blue> to InitializerOfShadeOfBlue. I need to be able to somehow change the generic type argument from Blue to ShadeOfBlue in the IInitializer<Blue> binding candidate, since IInitializer<ShadeOfBlue> is what will be requested from the factory method at runtime.
Is there a way to modify the generic type argument list of the binding candidate? Or am I barking up the wrong implementation? Any edit suggestions to my OP or this answer are appreciated.
/// <summary>Creates bindings on open generic types where bound implementations'
/// names end with the name of the generic type argument</summary>
public class FirstTypeParameterNameMatchesEndOfBoundClassNameGenerator : IBindingGenerator
{
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
if (type == null) throw new ArgumentNullException("type");
if (bindingRoot == null) throw new ArgumentNullException("bindingRoot");
// only consider concrete, non-abstract classes
if (type.IsInterface || type.IsAbstract) yield break;
var bindingType = GetBindingType(type);
if (bindingType != null)
yield return bindingRoot.Bind(bindingType).To(type);
// ARGH! bindingType == IInitializer`1[[Blue]] but I want
// IInitializer`1[[ShadeOfBlue]] for type == ShadeOfBlue
}
private static Type GetBindingType(Type type)
{
Type goodMatch = null;
foreach (var candidate in type.GetInterfaces())
{
// skip non-generic interfaces
if (!candidate.IsGenericType) continue;
// assumption: using argument in first position
var firstArg = candidate.GetGenericArguments().First();
if (!type.Name.EndsWith(firstArg.Name)) continue;
// IInitializer<XXX> matches InitializerOfXXX
goodMatch = candidate;
break;
}
if (goodMatch == null)
{
// if no match on interfaces, walk through the ancestor types
foreach (var candidate in type.GetAllAncestors())
{
goodMatch = GetBindingType(candidate);
if (goodMatch != null) break;
}
}
return goodMatch;
}
Type Extension helper
public static class TypeExtensions
{
// returns all ancestor types starting with the parent
public static IEnumerable<Type> GetAllAncestors(this Type type)
{
for (var current = type.BaseType; current != null; current = current.BaseType)
yield return current;
}
}
END SOLUTION CANDIDATE - custom binding generator