Skip to content

Commit

Permalink
Move class creation to their own classes.
Browse files Browse the repository at this point in the history
A lot of code that used to be in TyphoonFactoryProvider is now in
TyphoonAssistedFactoryCreator and its subclasses (one for one factory block,
one for a definition block, and one for the future implicit with a result
type).

The subclasses are supposed to generate a block that returns a
TyphoonAssistedFactoryDefinition, which the super class will use in case the
class doesn't exists yet (using a block to not instantiate the definition
eagerly).

TODO: the implicit creation strategy.

(appsquickly#102)
Conflicts:
	Pods/Pods.xcodeproj/project.pbxproj
  • Loading branch information
drodriguez committed Dec 4, 2013
1 parent 99f5f47 commit a6c9201
Show file tree
Hide file tree
Showing 12 changed files with 512 additions and 207 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
////////////////////////////////////////////////////////////////////////////////
//
// TYPHOON FRAMEWORK
// Copyright 2013, Jasper Blues & Contributors
// All Rights Reserved.
//
// NOTICE: The authors permit you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

#import "TyphoonAssistedFactoryCreator.h"

typedef TyphoonAssistedFactoryDefinition *(^TyphoonAssistedFactoryDefinitionProvider)(void);

@interface TyphoonAssistedFactoryCreator ()

- (instancetype)initWithProtocol:(Protocol *)protocol factoryDefinitionProvider:(TyphoonAssistedFactoryDefinitionProvider)definitionProvider;

@end
26 changes: 26 additions & 0 deletions Source/Component/FactoryProvider/TyphoonAssistedFactoryCreator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
////////////////////////////////////////////////////////////////////////////////
//
// TYPHOON FRAMEWORK
// Copyright 2013, Jasper Blues & Contributors
// All Rights Reserved.
//
// NOTICE: The authors permit you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

#import <Foundation/Foundation.h>

#import "TyphoonAssistedFactoryDefinition.h"

@interface TyphoonAssistedFactoryCreator : NSObject

+ (instancetype)creatorWithProtocol:(Protocol *)protocol returns:(Class)returnType;

+ (instancetype)creatorWithProtocol:(Protocol *)protocol factoryBlock:(id)factoryBlock;

+ (instancetype)creatorWithProtocol:(Protocol *)protocol factories:(TyphoonAssistedFactoryDefinitionBlock)definitionblock;

- (Class)factoryClass;

@end
202 changes: 202 additions & 0 deletions Source/Component/FactoryProvider/TyphoonAssistedFactoryCreator.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
////////////////////////////////////////////////////////////////////////////////
//
// TYPHOON FRAMEWORK
// Copyright 2013, Jasper Blues & Contributors
// All Rights Reserved.
//
// NOTICE: The authors permit you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

#import "TyphoonAssistedFactoryCreator.h"
#import "TyphoonAssistedFactoryCreator+Private.h"

#include <objc/runtime.h>

#import "TyphoonAssistedFactoryBase.h"
#import "TyphoonAssistedFactoryCreatorImplicit.h"
#import "TyphoonAssistedFactoryCreatorOneFactory.h"
#import "TyphoonAssistedFactoryCreatorManyFactories.h"
#import "TyphoonAssistedFactoryMethodCreator.h"

@implementation TyphoonAssistedFactoryCreator
{
@private
Protocol *_protocol;
TyphoonAssistedFactoryDefinitionProvider _definitionProvider;
}

static dispatch_queue_t sQueue;

static NSString *GetFactoryClassName(Protocol *protocol)
{
return [NSString stringWithFormat:@"%s__TyphoonAssistedFactoryImpl",
protocol_getName(protocol)];
}

static void AssertValidProtocolForFactory(Protocol *protocol, TyphoonAssistedFactoryDefinition *factoryDefinition)
{
unsigned int methodCount = 0;
unsigned int propertiesCount = 0;

struct objc_method_description *methodDescriptions = protocol_copyMethodDescriptionList(protocol, YES, YES, &methodCount);
objc_property_t *properties = protocol_copyPropertyList(protocol, &propertiesCount);
free(methodDescriptions);
free(properties);

// The readonly properties are returned also as their getter methods, so we
// need to remove those to check that there are only n factory methods left.
NSUInteger factoryMethodCount = [factoryDefinition countOfFactoryMethods];
NSCAssert(methodCount - propertiesCount == factoryMethodCount,
@"protocol factory method count (%u) differs from factory defintion method count (%lu)",
methodCount - propertiesCount, (unsigned long)factoryMethodCount);
}

static void AddPropertyGetter(Class factoryClass, objc_property_t property)
{
// This dummy will give us the type encodings of the properties.
// Only object properties are supported.
Method getter = class_getInstanceMethod([TyphoonAssistedFactoryBase class], @selector(_dummyGetter));

const char *cName = property_getName(property);
NSString *name = [NSString stringWithCString:cName encoding:NSASCIIStringEncoding];
SEL getterSEL = sel_registerName(cName);

IMP getterIMP = imp_implementationWithBlock(^id (TyphoonAssistedFactoryBase *_self) {
return [_self injectionValueForProperty:name];
});
class_addMethod(factoryClass, getterSEL, getterIMP, method_getTypeEncoding(getter));
}

static void AddPropertySetter(Class factoryClass, objc_property_t property)
{
// This dummy will give us the type encodings of the properties.
// Only object properties are supported.
Method setter = class_getInstanceMethod([TyphoonAssistedFactoryBase class], @selector(_setDummySetter:));

const char *cName = property_getName(property);
NSString *name = [NSString stringWithCString:cName encoding:NSASCIIStringEncoding];
NSString *setterName = [NSString stringWithFormat:@"set%@%@:",
[[name substringToIndex:1] uppercaseString],
[name substringFromIndex:1]];
SEL setterSEL = sel_registerName([setterName cStringUsingEncoding:NSASCIIStringEncoding]);

IMP setterIMP = imp_implementationWithBlock(^(TyphoonAssistedFactoryBase *_self, id value) {
[_self setInjectionValue:value forProperty:name];
});
class_addMethod(factoryClass, setterSEL, setterIMP, method_getTypeEncoding(setter));
}

static void AddProperty(Class factoryClass, objc_property_t property)
{
unsigned int propertyAttributesCount = 0;
const char *cName = property_getName(property);
objc_property_attribute_t *propertyAttributes = property_copyAttributeList(property, &propertyAttributesCount);
class_addProperty(factoryClass, cName, propertyAttributes, propertyAttributesCount);
}

static void AddPropertiesToFactory(Class factoryClass, Protocol *protocol)
{
unsigned int propertiesCount = 0;
objc_property_t *properties = protocol_copyPropertyList(protocol, &propertiesCount);
for (unsigned int idx = 0; idx < propertiesCount; idx++)
{
objc_property_t property = properties[idx];
AddPropertyGetter(factoryClass, property);
AddPropertySetter(factoryClass, property);
AddProperty(factoryClass, property);
}
free(properties);
}

static void AddFactoryMethodsToFactory(Class factoryClass, Protocol *protocol, TyphoonAssistedFactoryDefinition *definition)
{
[definition enumerateFactoryMethods:^(id<TyphoonAssistedFactoryMethod> factoryMethod) {
[[TyphoonAssistedFactoryMethodCreator creatorFor:factoryMethod]
createFromProtocol:protocol inClass:factoryClass];
}];
}

static Class GenerateFactoryClassWithDefinition(Protocol *protocol, TyphoonAssistedFactoryDefinition *factoryDefinition)
{
NSString *className = GetFactoryClassName(protocol);
const char *cClassName = [className cStringUsingEncoding:NSASCIIStringEncoding];

AssertValidProtocolForFactory(protocol, factoryDefinition);

Class factoryClass = objc_allocateClassPair([TyphoonAssistedFactoryBase class], cClassName, 0);
// Add the factory method first, that way, the setters from the properties
// will not exist yet.
AddFactoryMethodsToFactory(factoryClass, protocol, factoryDefinition);
AddPropertiesToFactory(factoryClass, protocol);
class_addProtocol(factoryClass, protocol);
objc_registerClassPair(factoryClass);

return factoryClass;
}

static Class GetExistingFactoryClass(Protocol *protocol)
{
NSString *className = GetFactoryClassName(protocol);
const char *cClassName = [className cStringUsingEncoding:NSASCIIStringEncoding];
return objc_getClass(cClassName);
}

static Class EnsureFactoryClass(Protocol *protocol, TyphoonAssistedFactoryDefinitionProvider definitionProvider)
{
Class factoryClass = GetExistingFactoryClass(protocol);
if (!factoryClass)
{
factoryClass = GenerateFactoryClassWithDefinition(protocol, definitionProvider());
}

return factoryClass;
}

+ (void)initialize
{
if (self == [TyphoonAssistedFactoryCreator class])
{
sQueue = dispatch_queue_create("org.typhoonframework.TyphoonAssistedFactoryCreator", DISPATCH_QUEUE_SERIAL);
}
}

+ (instancetype)creatorWithProtocol:(Protocol *)protocol returns:(Class)returnType
{
return [[TyphoonAssistedFactoryCreatorImplicit alloc] initWithProtocol:protocol returns:returnType];
}

+ (instancetype)creatorWithProtocol:(Protocol *)protocol factoryBlock:(id)factoryBlock
{
return [[TyphoonAssistedFactoryCreatorOneFactory alloc] initWithProtocol:protocol factoryBlock:factoryBlock];
}

+ (instancetype)creatorWithProtocol:(Protocol *)protocol factories:(TyphoonAssistedFactoryDefinitionBlock)definitionblock
{
return [[TyphoonAssistedFactoryCreatorManyFactories alloc] initWithProtocol:protocol factories:definitionblock];
}

- (instancetype)initWithProtocol:(Protocol *)protocol factoryDefinitionProvider:(TyphoonAssistedFactoryDefinitionProvider)definitionProvider
{
self = [super init];
if (self)
{
_protocol = protocol;
_definitionProvider = definitionProvider;
}

return self;
}

- (Class)factoryClass
{
__block Class factoryClass = nil;
dispatch_sync(sQueue, ^{
factoryClass = EnsureFactoryClass(_protocol, _definitionProvider);
});

return factoryClass;
}

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
////////////////////////////////////////////////////////////////////////////////
//
// TYPHOON FRAMEWORK
// Copyright 2013, Jasper Blues & Contributors
// All Rights Reserved.
//
// NOTICE: The authors permit you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

#import "TyphoonAssistedFactoryCreator.h"

@interface TyphoonAssistedFactoryCreatorImplicit : TyphoonAssistedFactoryCreator

- (instancetype)initWithProtocol:(Protocol *)protocol returns:(Class)returnType;

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
////////////////////////////////////////////////////////////////////////////////
//
// TYPHOON FRAMEWORK
// Copyright 2013, Jasper Blues & Contributors
// All Rights Reserved.
//
// NOTICE: The authors permit you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

#import "TyphoonAssistedFactoryCreatorImplicit.h"
#import "TyphoonAssistedFactoryCreator+Private.h"

@implementation TyphoonAssistedFactoryCreatorImplicit

- (instancetype)initWithProtocol:(Protocol *)protocol returns:(Class)returnType
{
return [super initWithProtocol:protocol factoryDefinitionProvider:^{
// TODO: guess the mapping

TyphoonAssistedFactoryDefinition *factoryDefinition = [[TyphoonAssistedFactoryDefinition alloc] init];

return factoryDefinition;
}];
}

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
////////////////////////////////////////////////////////////////////////////////
//
// TYPHOON FRAMEWORK
// Copyright 2013, Jasper Blues & Contributors
// All Rights Reserved.
//
// NOTICE: The authors permit you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

#import "TyphoonAssistedFactoryCreator.h"

@interface TyphoonAssistedFactoryCreatorManyFactories : TyphoonAssistedFactoryCreator

- (instancetype)initWithProtocol:(Protocol *)protocol factories:(TyphoonAssistedFactoryDefinitionBlock)definitionBlock;

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
////////////////////////////////////////////////////////////////////////////////
//
// TYPHOON FRAMEWORK
// Copyright 2013, Jasper Blues & Contributors
// All Rights Reserved.
//
// NOTICE: The authors permit you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

#import "TyphoonAssistedFactoryCreatorManyFactories.h"
#import "TyphoonAssistedFactoryCreator+Private.h"

@implementation TyphoonAssistedFactoryCreatorManyFactories

- (instancetype)initWithProtocol:(Protocol *)protocol factories:(TyphoonAssistedFactoryDefinitionBlock)definitionBlock
{
return [super initWithProtocol:protocol factoryDefinitionProvider:^{
TyphoonAssistedFactoryDefinition *factoryDefinition = [[TyphoonAssistedFactoryDefinition alloc] init];
[factoryDefinition configure:definitionBlock];

return factoryDefinition;
}];
}

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
////////////////////////////////////////////////////////////////////////////////
//
// TYPHOON FRAMEWORK
// Copyright 2013, Jasper Blues & Contributors
// All Rights Reserved.
//
// NOTICE: The authors permit you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

#import "TyphoonAssistedFactoryCreator.h"

@interface TyphoonAssistedFactoryCreatorOneFactory : TyphoonAssistedFactoryCreator

- (instancetype)initWithProtocol:(Protocol *)protocol factoryBlock:(id)factoryBlock;

@end
Loading

0 comments on commit a6c9201

Please sign in to comment.