vendoo_v1.0/Pods/PicoKit/Pod/Core/PicoXMLReader.m

281 lines
10 KiB
Objective-C

//
// PicoTBXMLReader.m
// Pico
//
// Created by bulldog on 13-3-11.
// Copyright (c) 2013 LeanSoft Technology. All rights reserved.
//
#import "PicoXMLReader.h"
#import "GDataXMLNode.h"
#import "PicoBindingSchema.h"
#import "PicoPropertySchema.h"
#import "PicoClassSchema.h"
#import "PicoConstants.h"
#import "PicoConverter.h"
#import "PicoXMLElement.h"
@implementation PicoXMLReader
@synthesize config = _config;
- (instancetype) init {
PicoConfig *config = [[PicoConfig alloc] init] ;
return [self initWithConfig:config];
}
- (instancetype)initWithConfig:(PicoConfig *)config {
self = [super init];
if (self) {
self.config = config;
}
return self;
}
-(id)fromData:(NSData *)data withClass:(Class)clazz {
NSError *error;
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:data error:&error];
if (!doc) {
@throw [NSException exceptionWithName:@"ReaderException" reason:[NSString stringWithFormat:@"fail to parse xml data , Error : %@", error] userInfo:[error userInfo]];
}
GDataXMLElement *rootElement = [doc rootElement];
PicoBindingSchema *bs = [PicoBindingSchema fromClass:clazz];
PicoClassSchema *cs = bs.classSchema;
NSString *xmlName = cs.xmlName;
if ([xmlName length] == 0) {
xmlName = bs.className;
}
NSString *rootName = [rootElement localName];
if (![xmlName isEqualToString: rootName]) {
@throw [NSException exceptionWithName:@"ReaderException" reason:[NSString stringWithFormat:@"root name mismatch , xml name : %@, root name : %@", xmlName, rootName] userInfo:nil];
}
id obj = [clazz new];
[self read: obj element: rootElement];
return obj;
}
-(void)read:(id)value element:(GDataXMLElement *)element {
[self readAttribute:value element:element];
BOOL hasText = [self readText:value element:element];
if (hasText) {
return; // no further read if xml text presents
}
[self readElement:value element:element];
[self readAnyElement:value element:element];
}
-(void)readAttribute:(id)value element:(GDataXMLElement *)element {
PicoBindingSchema *bs = [PicoBindingSchema fromObject:value];
// read xml attributes
NSDictionary *attrMap = bs.xml2AttributeSchemaMapping;
if(attrMap.count > 0) {
for(NSString *xmlName in attrMap) {
PicoPropertySchema *ps = attrMap[xmlName];
GDataXMLNode *attrNode = [element attributeForName:xmlName];
if (attrNode) {
NSString *attrValue = [attrNode stringValue];
if ([attrValue length] > 0) {
id objValue = [PicoConverter read:attrValue withType:ps.propertyType config:self.config];
if (objValue) {
[value setValue:objValue forKey: ps.propertyName];
}
}
}
}
}
}
-(BOOL)readText:(id)value element:(GDataXMLElement *) element {
PicoBindingSchema *bs = [PicoBindingSchema fromObject:value];
// read xml value if any
PicoPropertySchema *valuePs = bs.valueSchema;
if (valuePs) {
NSString *text = [element stringValue];
if (text.length > 0) {
id objValue = [PicoConverter read: text withType: valuePs.propertyType config:self.config];
if (objValue) {
[value setValue: objValue forKey: valuePs.propertyName];
}
}
return YES;
}
return NO;
}
-(void)readElement:(id)value element:(GDataXMLElement *)element {
PicoBindingSchema *bs = [PicoBindingSchema fromObject:value];
// read xml element
NSDictionary *elementMap = bs.xml2ElementSchemaMapping;
NSArray *children = [element children];
if (children.count > 0) {
for(GDataXMLNode *node in children) {
if ([node kind] == GDataXMLElementKind) {
GDataXMLElement *childElement = (GDataXMLElement *) node;
NSString *localName = childElement.localName;
PicoPropertySchema *ps = elementMap[localName];
if (ps) { // found element match
if ([ps.propertyKind isEqualToString:PICO_KIND_ELEMENT]) {
// primitive
if ([PicoConverter isPrimitive:ps.propertyType]) {
NSString *xmlValue = [childElement stringValue];
if (xmlValue.length > 0) {
id objValue = [PicoConverter read: xmlValue withType: ps.propertyType config:self.config];
if (objValue) {
[value setValue: objValue forKey: ps.propertyName];
}
}
} else if ([ps.propertyType isEqualToString:PICO_TYPE_OBJECT]) { // object
id obj = [ps.clazz new];
[value setValue:obj forKey: ps.propertyName];
[self read: obj element: childElement];
}
} else if ([ps.propertyKind isEqualToString: PICO_KIND_ELEMENT_ARRAY]) {
NSMutableArray *array = [value valueForKey:ps.propertyName];
if (!array) {
array = [[NSMutableArray alloc] init];
[value setValue:array forKey: ps.propertyName];
}
// primitive
if ([PicoConverter isPrimitive: ps.propertyType]) {
NSString *xmlValue = [childElement stringValue];
if (xmlValue.length > 0) {
id objValue = [PicoConverter read:xmlValue withType:ps.propertyType config:self.config];
[array addObject: objValue];
}
} else if ([ps.propertyType isEqualToString:PICO_TYPE_OBJECT]) { // object
id obj = [ps.clazz new];
[array addObject:obj];
[self read: obj element: childElement];
}
}
}
}
}
}
}
-(void)readAnyElement:(id)value element:(GDataXMLElement *)element {
PicoBindingSchema *bs = [PicoBindingSchema fromObject:value];
// read xml any element
PicoPropertySchema *anyPs = bs.anyElementSchema;
if (anyPs) {
if (anyPs.clazz) { // target class specified
[self readAnyElement:value element:element bindClass:anyPs.clazz];
} else {
NSMutableArray *anyChildElements = [[NSMutableArray alloc] init];
NSArray *children = [element children];
NSDictionary *elementMap = bs.xml2ElementSchemaMapping;
for(GDataXMLNode *node in children) {
if ([node kind] == GDataXMLElementKind) {
if (elementMap.count == 0 || !elementMap[[node localName]]) {
PicoXMLElement *picoElement = [self convertToPicoElement:(GDataXMLElement *)node];
[anyChildElements addObject:picoElement];
}
}
}
[value setValue:anyChildElements forKey: anyPs.propertyName];
}
}
}
-(BOOL)readAnyElement:(id)value element:(GDataXMLElement *)element bindClass:(Class) clazz {
BOOL result = NO;
PicoBindingSchema *bs = [PicoBindingSchema fromClass:clazz];
PicoClassSchema *cs = [bs classSchema];;
NSString *xmlName = cs.xmlName;
if ([xmlName length] == 0) {
xmlName = bs.className;
}
NSMutableArray *childElements = [[NSMutableArray alloc] init];
NSArray *children = [element children];
for(GDataXMLNode *node in children) {
if ([node kind] == GDataXMLElementKind && [xmlName isEqualToString:[node localName]]) {
[childElements addObject:node];
}
}
if (childElements.count > 0) {
PicoBindingSchema *bs = [PicoBindingSchema fromObject:value];
// read xml any element
PicoPropertySchema *anyPs = bs.anyElementSchema;
NSMutableArray *array = [[NSMutableArray alloc] init];
[value setValue:array forKey: anyPs.propertyName];
for(GDataXMLElement *childElement in childElements) {
id obj = [clazz new];
[array addObject:obj];
[self read: obj element: childElement];
}
result = YES;
}
return result;
}
-(PicoXMLElement *)convertToPicoElement:(GDataXMLElement *)element {
PicoXMLElement *picoElement = [[PicoXMLElement alloc] init] ;
picoElement.name = element.localName;
picoElement.nsUri = element.URI;
if ([element childCount] == 1) {
GDataXMLNode *node = (element.children)[0];
if (node.kind == GDataXMLTextKind) {
picoElement.value = node.stringValue;
}
}
if (element.attributes) {
NSMutableDictionary *attrDic = [[NSMutableDictionary alloc] init];
picoElement.attributes = attrDic;
for(GDataXMLNode *attr in element.attributes) {
attrDic[attr.localName] = attr.stringValue;
}
}
if (element.children) {
NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
picoElement.children = childrenArray;
for(GDataXMLNode *node in element.children) {
if ([node kind] == GDataXMLElementKind) {
PicoXMLElement *childPicoElement = [self convertToPicoElement:(GDataXMLElement *)node];
childPicoElement.parent = picoElement;
[childrenArray addObject: childPicoElement];
}
}
}
return picoElement;
}
-(id)fromString:(NSString *)string withClass:(Class)clazz {
NSData *data = [string dataUsingEncoding: CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)self.config.encoding)) allowLossyConversion:NO];
return [self fromData:data withClass:clazz];
}
@end