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

257 lines
9.2 KiB
Objective-C

//
// PicoXMLWriter.m
// pico
//
// Created by bulldog on 13-2-26.
// Copyright (c) 2013 LeanSoft Technology. All rights reserved.
//
#import "PicoXMLWriter.h"
#import "PicoConverter.h"
#import "XMLWriter.h"
#import "PicoConfig.h"
#import "PicoBindingSchema.h"
#import "PicoClassSchema.h"
#import "PicoPropertySchema.h"
#import "PicoConstants.h"
#import "PicoXMLElement.h"
#import "PicoBindable.h"
@implementation PicoXMLWriter
@synthesize config = _config;
- (instancetype)init {
return [self initWithConfig:[[PicoConfig alloc] init]];
}
- (instancetype)initWithConfig:(PicoConfig *)config {
self = [super init];
if (self) {
self.config = config;
}
return self;
}
-(NSData *)toData:(id)obj {
NSString *xmlString = [self toString:obj] ;
NSData *data = [xmlString dataUsingEncoding: CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)self.config.encoding)) allowLossyConversion:NO];
return data;
}
-(NSString *)toString:(id)obj {
if (obj == nil) {
@throw [NSException exceptionWithName:@"WriterException" reason:@"Can't write nil object." userInfo:nil];
}
XMLWriter *xmlWriter = [[XMLWriter alloc] init];
_autoPrefixCount = 0;
[xmlWriter writeStartDocumentWithEncodingAndVersion:self.config.encoding version:@"1.0"];
PicoBindingSchema *bindingSchema = [PicoBindingSchema fromObject:obj];
PicoClassSchema *classSchema = [bindingSchema classSchema];
NSString *xmlName = classSchema.xmlName;
NSString *namespace = classSchema.nsURI;
if (!xmlName) { // workaround, in case xml name is not provided in class schema, use class name instead
xmlName = bindingSchema.className;
}
if ([namespace length] != 0) {
[xmlWriter setDefaultNamespace:namespace]; // bind to default namespace
}
[xmlWriter writeStartElementWithNamespace:namespace localName:xmlName];
[self writeObject:xmlWriter source:obj];
[xmlWriter writeEndElementWithNamespace:namespace localName:xmlName];
[xmlWriter writeEndDocument];
NSString *xmlString = [xmlWriter toString];
xmlString = [NSMutableString stringWithString:xmlString];
return xmlString;
}
-(void)writeObject:(XMLWriter *)xmlWriter source:(id)source {
PicoBindingSchema *bindingSchema = [PicoBindingSchema fromObject:source];
NSString *namespace = nil;
PicoClassSchema *classSchema = [bindingSchema classSchema];
if (classSchema) {
namespace = classSchema.nsURI;
}
[self writeAttributes:xmlWriter source:source schema:bindingSchema];
[self writeValue:xmlWriter source:source schema:bindingSchema];
// automatic prefixing
if (namespace && ![xmlWriter getPrefix:namespace]) {
NSString *prefix = [NSString stringWithFormat:@"ns%d", _autoPrefixCount++];
[xmlWriter setPrefix:prefix namespaceURI:namespace];
}
[self writeElements:xmlWriter source:source schema:bindingSchema namespace:namespace];
[self writeAnyElements:xmlWriter source:source schema:bindingSchema];
}
-(void)writeAttributes:(XMLWriter *)xmlWriter source:(id)source schema:(PicoBindingSchema *)bindingSchema {
NSDictionary *map = bindingSchema.property2AttributeSchemaMapping;
if (map.count > 0) {
for(NSString *propertyName in map) {
PicoPropertySchema *ps = map[propertyName];
id propertyValue = [source valueForKey:propertyName];
if (propertyValue) {
NSString *xmlString = [PicoConverter write:propertyValue withType:ps.propertyType config:self.config];
if (xmlString.length > 0) {
[xmlWriter writeAttribute:ps.xmlName value:xmlString];
}
}
}
}
}
-(void)writeValue:(XMLWriter *)xmlWriter source:(id)source schema:(PicoBindingSchema *)bindingSchema {
PicoPropertySchema *valuePs = bindingSchema.valueSchema;
if (valuePs) {
id propertyValue = [source valueForKey:valuePs.propertyName];
NSString *xmlString = [PicoConverter write:propertyValue withType:valuePs.propertyType config:self.config];
if (xmlString.length > 0) {
[xmlWriter writeCharacters:xmlString];
}
}
}
-(void)writeAnyElements:(XMLWriter *)xmlWriter source:(id)source schema:(PicoBindingSchema *)bindingSchema {
PicoPropertySchema *anyPs = bindingSchema.anyElementSchema;
if(anyPs) {
NSArray *anyArray = [source valueForKey: anyPs.propertyName];
if (anyArray) {
for(id entry in anyArray) {
if (entry) {
if ([entry isMemberOfClass:[PicoXMLElement class]]) {
[self writePicoXMLElement:xmlWriter xmlElement:entry];
} else if ([entry conformsToProtocol:@protocol(PicoBindable)]) {
[self writePicoObject:xmlWriter source:entry];
}
}
}
}
}
}
-(void)writePicoXMLElement:(XMLWriter *)xmlWriter xmlElement:(PicoXMLElement *)element {
if (!element) return; // to be cautious
if (element.name) {
NSString *namespace = element.nsUri;
if (namespace.length == 0) {
namespace = nil;
}
// automatic prefixing
if (namespace && ![xmlWriter getPrefix:namespace]) {
NSString *prefix = [NSString stringWithFormat:@"ns%d", _autoPrefixCount++];
[xmlWriter setPrefix:prefix namespaceURI:namespace];
}
[xmlWriter writeStartElementWithNamespace:namespace localName:element.name];
if (element.attributes) { // write attributes
for(NSString *key in element.attributes) {
NSString *value = [element.attributes valueForKey:key];
[xmlWriter writeAttribute:key value:value];
}
}
if (element.value) { // write element text
[xmlWriter writeCharacters:element.value];
}
if (element.children) { // write element children
for(PicoXMLElement *child in element.children) {
[self writePicoXMLElement:xmlWriter xmlElement:child];
}
}
[xmlWriter writeEndElementWithNamespace:namespace localName:element.name];
}
}
-(void)writePicoObject:(XMLWriter *)xmlWriter source:(id)source {
if (!source) return; // to be cautious
PicoBindingSchema *bindingSchema = [PicoBindingSchema fromObject:source];
PicoClassSchema *classSchema = [bindingSchema classSchema];
NSString *xmlName = classSchema.xmlName;
NSString *namespace = classSchema.nsURI;
if (!xmlName) { // workaround, in case xml name is not provided in class schema, use class name instead
xmlName = bindingSchema.className;
}
// automatic prefixing
if (namespace && ![xmlWriter getPrefix:namespace]) {
NSString *prefix = [NSString stringWithFormat:@"ns%d", _autoPrefixCount++];
[xmlWriter setPrefix:prefix namespaceURI:namespace];
}
[xmlWriter writeStartElementWithNamespace:namespace localName:xmlName];
[self writeObject:xmlWriter source:source];
[xmlWriter writeEndElementWithNamespace:namespace localName:xmlName];
}
-(void)writeElements:(XMLWriter *)xmlWriter source:(id)source schema:(PicoBindingSchema *)bindingSchema namespace:(NSString *)nsURI {
NSDictionary *map = bindingSchema.property2ElementSchemaMapping;
if (map.count > 0) {
for(NSString *propertyName in map) {
PicoPropertySchema *ps = map[propertyName];
id propertyValue = [source valueForKey:propertyName];
if (propertyValue) {
if ([ps.propertyKind isEqualToString:PICO_KIND_ELEMENT]) {
[self writeElement:xmlWriter source:propertyValue schema:ps namespace:nsURI];
}
// array elements
else if ([ps.propertyKind isEqualToString:PICO_KIND_ELEMENT_ARRAY]) {
NSArray *array = propertyValue;
for(id entry in array) {
if (entry) {
[self writeElement:xmlWriter source:entry schema:ps namespace:nsURI];
}
}
}
}
}
}
}
-(void)writeElement:(XMLWriter *)xmlWriter source:(id)source schema:(PicoPropertySchema *)propertySchema namespace:(NSString *)nsURI {
if (!source) return;
if ([PicoConverter isPrimitive:propertySchema.propertyType]) {
NSString *xmlString = [PicoConverter write:source withType:propertySchema.propertyType config:self.config];
if (xmlString.length > 0) {
[xmlWriter writeStartElementWithNamespace:nsURI localName:propertySchema.xmlName];
[xmlWriter writeCharacters:xmlString];
[xmlWriter writeEndElementWithNamespace:nsURI localName:propertySchema.xmlName];
}
return;
}
if ([propertySchema.propertyType isEqualToString:PICO_TYPE_OBJECT]) {
[xmlWriter writeStartElementWithNamespace:nsURI localName:propertySchema.xmlName];
[self writeObject:xmlWriter source:source];
[xmlWriter writeEndElementWithNamespace:nsURI localName:propertySchema.xmlName];
}
}
@end