Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
317 changes: 185 additions & 132 deletions Classes/MultibyteDescription.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,161 +9,214 @@
#import "MultibyteDescription.h"
#import <objc/runtime.h>

#define MDINDENT 3

@implementation MultibyteDescription

//
// Great ideas from @yusuga: http://qiita.com/items/85437eba2623f6ffbdbd (in Japanese)
//
+ (void)install
{
Class c;
IMP imp;
SEL sel;
[self hackNSArray];
[self hackNSDictionary];
[self hackNSSet];
[self hackNSOrderedSet];
[self hackNSDate];
}

+ (void)hackNSArray
{
Method sourceMethod1 = class_getInstanceMethod([NSArray class], @selector(descriptionWithLocale:));
Method targetMethod1 = class_getInstanceMethod([self class], @selector(mdNSArrayDescriptionWithLocale:));
method_exchangeImplementations(sourceMethod1, targetMethod1);

//------------------------------
// NSArray
//------------------------------
c = [NSArray class];
Method sourceMethod2 = class_getInstanceMethod([NSArray class], @selector(descriptionWithLocale:indent:));
Method targetMethod2 = class_getInstanceMethod([self class], @selector(mdNSArrayDescriptionWithLocale:indent:));
method_exchangeImplementations(sourceMethod2, targetMethod2);
}

+ (void)hackNSDictionary
{
Method sourceMethod1 = class_getInstanceMethod([NSDictionary class], @selector(descriptionWithLocale:));
Method targetMethod1 = class_getInstanceMethod([self class], @selector(mdNSDictionaryDescriptionWithLocale:));
method_exchangeImplementations(sourceMethod1, targetMethod1);

// descriptionWithLocale:
imp = imp_implementationWithBlock(^NSString*(NSArray *arr, id locale) {
NSMutableString *mStr = [NSMutableString stringWithString:@"(\n"];
[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
if ([obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSDictionary class]]) {
[mStr appendFormat:@"%@,\n", [obj descriptionWithLocale:locale indent:2]];
Method sourceMethod2 = class_getInstanceMethod([NSDictionary class], @selector(descriptionWithLocale:indent:));
Method targetMethod2 = class_getInstanceMethod([self class], @selector(mdNSDictionaryDescriptionWithLocale:indent:));
method_exchangeImplementations(sourceMethod2, targetMethod2);
}

+ (void)hackNSSet
{
Method sourceMethod1 = class_getInstanceMethod([NSSet class], @selector(descriptionWithLocale:));
Method targetMethod1 = class_getInstanceMethod([self class], @selector(mdNSSetDescriptionWithLocale:));
method_exchangeImplementations(sourceMethod1, targetMethod1);
}

+ (void)hackNSOrderedSet
{
Method sourceMethod1 = class_getInstanceMethod([NSOrderedSet class], @selector(descriptionWithLocale:));
Method targetMethod1 = class_getInstanceMethod([self class], @selector(mdNSOrderedSetDescriptionWithLocale:));
method_exchangeImplementations(sourceMethod1, targetMethod1);
}

+ (void)hackNSDate
{
Method sourceMethod1 = class_getInstanceMethod([NSDate class], @selector(descriptionWithLocale:));
Method targetMethod1 = class_getInstanceMethod([self class], @selector(mdNSDateDescriptionWithLocale:));
method_exchangeImplementations(sourceMethod1, targetMethod1);
}


#pragma mark -

NSString *spaceWithLength(NSUInteger length)
{
// 添加空格
NSMutableString *indentStr = [[NSMutableString alloc] initWithCapacity:length];
for(NSInteger i = 0; i < length; i++)
{
[indentStr appendString:@" "];
}
return indentStr;
}

- (NSString *)mdNSArrayDescriptionWithLocale:(NSLocale *)locale
{
NSArray *arr = (NSArray *)self;
NSMutableString *str = [NSMutableString stringWithFormat:@"[\n"];

NSInteger count = [arr count];
for(NSInteger i = 0; i < count; i++)
{
id obj = arr[i];

if ([obj respondsToSelector:@selector(descriptionWithLocale:indent:)])
{
[str appendFormat:@"%@%@\n", spaceWithLength(MDINDENT),
[obj descriptionWithLocale:[NSLocale systemLocale] indent:MDINDENT]];
}
else
{
if (i!=(count-1)) {
[str appendFormat:@"%@\"%@\",\n", spaceWithLength(MDINDENT),[obj description]];
} else {
[mStr appendFormat:@" %@", [obj description]];
if (idx == [arr count] - 1) {
[mStr appendString:@"\n"];
} else {
[mStr appendString:@",\n"];
}
[str appendFormat:@"%@\"%@\"\n", spaceWithLength(MDINDENT), [obj description]];
}
}];
[mStr appendString:@")"];
return mStr;
});
sel = @selector(descriptionWithLocale:);
class_replaceMethod(c, sel, imp, method_getTypeEncoding(class_getInstanceMethod(c, sel)));
}
}
[str appendFormat:@"]"];
return str;
}

- (NSString *)mdNSArrayDescriptionWithLocale:(NSLocale *)locale indent:(NSUInteger)indent
{
NSArray *arr = (NSArray *)self;

// descriptionWithLocale:indent:
imp = imp_implementationWithBlock(^NSString*(NSArray *arr, id locale, NSUInteger indent) {
NSMutableString *mStr = [NSMutableString string];
for (int i = 0; i < indent; i++) {
[mStr appendString:@" "];
NSMutableString *str = [NSMutableString stringWithFormat:@"[\n"];
NSInteger count = [arr count];
for(NSInteger i = 0; i < count; i++)
{
id obj = arr[i];

if ([obj respondsToSelector:@selector(descriptionWithLocale:indent:)])
{
[str appendFormat:@"%@%@\n", spaceWithLength(indent+MDINDENT), [obj descriptionWithLocale:[NSLocale systemLocale] indent:indent+MDINDENT]];
}
[mStr appendString:@"(\n"];
[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
if ([obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSDictionary class]]) {
[mStr appendFormat:@" %@,\n", [obj descriptionWithLocale:locale indent:indent + 1]];
else
{
if (i!=(count-1)) {
[str appendFormat:@"%@\"%@\",\n", spaceWithLength(indent+MDINDENT), [obj description]];
} else {
for (int i = 0; i < indent; i++) {
[mStr appendString:@" "];
}
[mStr appendFormat:@"%@", [obj description]];
if (idx == [arr count] - 1) {
[mStr appendString:@"\n"];
} else {
[mStr appendString:@",\n"];
}
[str appendFormat:@"%@\"%@\"\n", spaceWithLength(indent+MDINDENT), [obj description]];
}
}];
for (int i = 0; i < indent - 1; i++) {
[mStr appendString:@" "];
}
[mStr appendString:@")"];
return mStr;
});
sel = @selector(descriptionWithLocale:indent:);
class_replaceMethod(c, sel, imp, method_getTypeEncoding(class_getInstanceMethod(c, sel)));
}
[str appendFormat:@"%@]", spaceWithLength(indent)];


//------------------------------
// NSDictionary
// (FIXME: improve indentation)
//------------------------------
c = [NSDictionary class];
return str;
}

- (NSString *)mdNSDictionaryDescriptionWithLocale:(NSLocale *)locale
{
NSDictionary *dict = (NSDictionary *)self;
NSMutableString *str = [NSMutableString stringWithFormat:@"{\n"];

// descriptionWithLocale:
imp = imp_implementationWithBlock(^NSString*(NSDictionary *dict, id locale) {
NSMutableString *mStr = [NSMutableString stringWithString:@"{\n"];
NSArray *keys = [dict allKeys];
NSInteger count = [keys count];
for(NSInteger i = 0; i < count; i++)
{
id key = keys[i];
id value = [self valueForKey:key];

NSLog(@"%@", [key description]);

[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSDictionary class]]) {
[mStr appendFormat:@" %@ = %@;\n", key, [obj descriptionWithLocale:locale indent:2]];
if ([value respondsToSelector:@selector(descriptionWithLocale:indent:)])
{
[str appendFormat:@"%@%@ :\n%@%@\n", spaceWithLength(MDINDENT), [key description], spaceWithLength(MDINDENT),
[value descriptionWithLocale:[NSLocale systemLocale] indent:MDINDENT]];
}
else
{
if (i!=(count-1)) {
[str appendFormat:@"%@%@ :\"%@\",\n", spaceWithLength(MDINDENT), [key description], [value description]];
} else {
[mStr appendFormat:@" %@ = %@;\n", key, [obj description]];
[str appendFormat:@"%@%@ :\"%@\"\n", spaceWithLength(MDINDENT), [key description], [value description]];
}
}];
[mStr appendString:@"}"];
return mStr;
});
sel = @selector(descriptionWithLocale:);
class_replaceMethod(c, sel, imp, method_getTypeEncoding(class_getInstanceMethod(c, sel)));

// descriptionWithLocale:indent:
imp = imp_implementationWithBlock(^NSString*(NSDictionary *dict, id locale, NSUInteger indent) {
NSMutableString *mStr = [NSMutableString string];
for (int i = 0; i < indent; i++) {
[mStr appendString:@" "];
}
[mStr appendString:@"{\n"];
}
[str appendFormat:@"}"];
return str;
}

- (NSString *)mdNSDictionaryDescriptionWithLocale:(NSLocale *)local indent:(NSUInteger)indent
{
NSDictionary *dict = (NSDictionary *)self;
NSMutableString *str = [NSMutableString stringWithFormat:@"{\n"];

NSArray *keys = [dict allKeys];
NSInteger count = [keys count];
for(NSInteger i = 0; i < count; i++)
{
id key = keys[i];
id value = [self valueForKey:key];

[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSDictionary class]]) {
[mStr appendFormat:@"%@ = %@;\n", key, [obj descriptionWithLocale:locale indent:indent + 1]];
if ([value respondsToSelector:@selector(descriptionWithLocale:indent:)])
{
[str appendFormat:@"%@%@\n%@ :%@\n", spaceWithLength(MDINDENT+indent), [key description], spaceWithLength(MDINDENT),
[value descriptionWithLocale:[NSLocale systemLocale] indent:MDINDENT+indent]];
}
else
{
if (i!=(count-1)) {
[str appendFormat:@"%@%@ :\"%@\",\n", spaceWithLength(MDINDENT+indent), [key description], [value description]];
} else {
for (int i = 0; i < indent; i++) {
[mStr appendString:@" "];
}
[mStr appendFormat:@"%@ = %@;\n", key, [obj description]];
[str appendFormat:@"%@%@ :\"%@\"\n", spaceWithLength(MDINDENT+indent), [key description], [value description]];
}
}];
for (int i = 0; i < indent - 1; i++) {
[mStr appendString:@" "];
}
[mStr appendString:@"}"];
return mStr;
});
sel = @selector(descriptionWithLocale:indent:);
class_replaceMethod(c, sel, imp, method_getTypeEncoding(class_getInstanceMethod(c, sel)));

//------------------------------
// NSSet
//------------------------------
c = [NSSet class];

// descriptionWithLocale:
imp = imp_implementationWithBlock(^NSString*(NSSet* set, id locale) {
return [NSString stringWithFormat:@"{%@}",[[set allObjects] descriptionWithLocale:locale]];
});
sel = @selector(descriptionWithLocale:);
class_replaceMethod(c, sel, imp, method_getTypeEncoding(class_getInstanceMethod(c, sel)));

//------------------------------
// NSOrderedSet
//------------------------------
c = [NSOrderedSet class];

// descriptionWithLocale:
imp = imp_implementationWithBlock(^NSString*(NSOrderedSet* orderedSet, id locale) {
return [NSString stringWithFormat:@"{%@}",[[orderedSet array] descriptionWithLocale:locale]];
});
sel = @selector(descriptionWithLocale:);
class_replaceMethod(c, sel, imp, method_getTypeEncoding(class_getInstanceMethod(c, sel)));

//------------------------------
// NSDate
//------------------------------
c = [NSDate class];

// descriptionWithLocale:
imp = imp_implementationWithBlock(^NSString*(NSDate* date, id locale) {
return [NSDateFormatter localizedStringFromDate:date
dateStyle:NSDateFormatterMediumStyle
timeStyle:NSDateFormatterMediumStyle];
});
sel = @selector(descriptionWithLocale:);
class_replaceMethod(c, sel, imp, method_getTypeEncoding(class_getInstanceMethod(c, sel)));
}
[str appendFormat:@"}"];
return str;
}

- (NSString *)mdNSSetDescriptionWithLocale:(NSLocale *)locale
{
NSSet *set = (NSSet *)self;
return [NSString stringWithFormat:@"{%@}", [[set allObjects] descriptionWithLocale:locale]];
}

- (NSString *)mdNSOrderedSetDescriptionWithLocale:(NSLocale *)locale
{
NSOrderedSet *set = (NSOrderedSet *)self;
NSLog(@"%@", [set array]);
return [NSString stringWithFormat:@"{%@}", [[set array] description]];
}

- (NSString *)mdNSDateDescriptionWithLocale:(NSLocale *)locale
{
NSDate *date = (NSDate *)self;
return [NSDateFormatter localizedStringFromDate:date
dateStyle:NSDateFormatterMediumStyle
timeStyle:NSDateFormatterMediumStyle];
}

@end