VoiceMac/Classes/VoiceBase/XML/MGMXMLNode.m
GRMrGecko 62734d7fef Added support for iPhone to MGMFFmpeg. Updated settings in Theme Tester. Updated settings in VoiceMac for the Theme Manager. Added the ability to hide icons in themes. Added the ability to change the font in themes. Fixed issue in MGMAddressBook where getting group members is not accurate. Made it so SIP addresses and Email addresses would not be recognized as phone numbers. Added support for 2 step verification. Added the ability to reload the user phone numbers. Added the ability to place the next message at any point in the previous message. Added the ability to do css based on the class of the message view with %MESSAGECLASSES%. Added the ability to get the codecs available and change the top codec in MGMSIP. Made it stop ring back before hanging up. Added initWithRootElement: to MGMXMLDocument. Added initWithName: and initWithName:stringValue: to MGMXMLElement. Added initWithKind:, setStringValue:, and fixed issues with reading attributes value. Made MGMVoiceUser support 2 step verification API, reload the user phones when it becomes active, and display a message if there is no phone numbers in Google Voice. Fixed issue where Google Chat will not appear in the User Phones list. Added a 2 Step Verification Dialog. Added Codec selection in MGMSIPPane. Fixed issues with MGMMultiSMS where it wouldn't alert the user if the message was blank and it wouldn't make the text field non editable. Added support for new SMS protocol to MGMSMSMessageView. Added support in the FFmpeg install script to build for iOS. Added ability to compile for the Simulator only in the PJProject build script. Made iOS and Mac OS final path go to 2 different folders. Updated to Revision 3466 of PJProject.
2011-09-23 22:02:03 -05:00

547 lines
15 KiB
Objective-C

//
// MGMXMLNode.m
// MGMXML
//
// Created by Mr. Gecko on 9/22/10.
// Copyright (c) 2011 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/
//
// Permission to use, copy, modify, and/or distribute this software for any purpose
// with or without fee is hereby granted, provided that the above copyright notice
// and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
#import "MGMXMLNode.h"
#import "MGMXMLAddons.h"
#import "MGMXMLDocument.h"
#import "MGMXMLElement.h"
#import "MGMXMLNodeOptions.h"
NSString * const MGMXMLErrorDomain = @"MGMXMLErrorDomain";
NSString * const MGMXMLLastError = @"MGMXMLLastError";
static NSMutableDictionary *MGMXMLInfo = nil;
static void MGMXMLErrorHandler(void *userData, xmlErrorPtr error) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];;
if (error==NULL) {
[MGMXMLInfo removeObjectForKey:MGMXMLLastError];
} else {
[MGMXMLInfo setObject:[NSValue valueWithXMLError:error] forKey:MGMXMLLastError];
}
[pool drain];
}
@implementation MGMXMLNode
+ (void)initialize {
if (MGMXMLInfo==nil) {
MGMXMLInfo = [NSMutableDictionary new];
initGenericErrorDefaultFunc(NULL);
xmlSetStructuredErrorFunc(NULL, MGMXMLErrorHandler);
xmlKeepBlanksDefault(0);
}
}
- (id)init {
if ((self = [super init])) {
commonXML = NULL;
namespaceXML = NULL;
parentNode = NULL;
type = MGMXMLInvalidKind;
}
return self;
}
+ (id)nodeWithTypeXMLPtr:(xmlTypPtr)theXMLPtr {
return [[[self alloc] initWithTypeXMLPtr:theXMLPtr] autorelease];
}
- (id)initWithTypeXMLPtr:(xmlTypPtr)theXMLPtr {
if ((self = [self init])) {
if (theXMLPtr==NULL) {
//NSLog(@"NULL XMLPtr");
[self release];
self = nil;
} else {
if (theXMLPtr->type==MGMXMLNamespaceKind) {
xmlNsPtr xmlPtr = (xmlNsPtr)theXMLPtr;
if (xmlPtr->_private!=NULL) {
[self release];
self = nil;
return [(MGMXMLNode *)xmlPtr->_private retain];
}
} else {
xmlComPtr comXML = (xmlComPtr)theXMLPtr;
if (comXML->_private!=NULL) {
[self release];
self = nil;
return [(MGMXMLNode *)comXML->_private retain];
}
}
[self setTypeXMLPtr:theXMLPtr];
}
}
return self;
}
- (void)clearParent {
parentNode = NULL;
}
- (void)freeXML {
if (namespaceXML!=NULL) {
namespaceXML->_private = NULL;
if (parentNode==NULL)
xmlFreeNs(namespaceXML);
namespaceXML = NULL;
parentNode = NULL;
}
if (commonXML!=NULL) {
commonXML->_private = NULL;
if (type!=MGMXMLDocumentKind)
[self releaseDocument];
if (commonXML->parent==NULL) {
if (type==MGMXMLDocumentKind) {
xmlNodePtr child = commonXML->children;
while (child!=NULL) {
xmlNodePtr nextChild = child->next;
if (child->type==MGMXMLElementKind) {
if (child->prev!=NULL)
child->prev->next = child->next;
if (child->next!=NULL)
child->next->prev = child->prev;
if (commonXML->children==child)
commonXML->children = child->next;
if (commonXML->last==child)
commonXML->last = child->prev;
[[self class] freeNode:child];
}
child = nextChild;
}
xmlFreeDoc(MGMXMLDocPtr);
} else if (type==MGMXMLAttributeKind) {
xmlFreeProp(MGMXMLAttrPtr);
} else if (type==MGMXMLDTDKind) {
xmlFreeDtd(MGMXMLDTDPtr);
} else {
[[self class] freeNode:MGMXMLNodePtr];
}
}
commonXML = NULL;
}
type = MGMXMLInvalidKind;
}
- (void)dealloc {
[self freeXML];
[super dealloc];
}
+ (void)stripDocumentFromAttribute:(xmlAttrPtr)theAttribute {
xmlNodePtr child = theAttribute->children;
while (child!=NULL) {
child->doc = NULL;
child = child->next;
}
theAttribute->doc = NULL;
}
+ (void)stripDocumentFromNode:(xmlNodePtr)theNode {
xmlAttrPtr attribute = theNode->properties;
while (attribute!=NULL) {
[self stripDocumentFromAttribute:attribute];
attribute = attribute->next;
}
xmlNodePtr child = theNode->children;
while (child!=NULL) {
[self stripDocumentFromNode:child];
child = child->next;
}
theNode->doc = NULL;
}
+ (void)removeAttributesFromNode:(xmlNodePtr)theNode {
xmlAttrPtr attribute = theNode->properties;
while (attribute!=NULL) {
xmlAttrPtr nextAttribute = attribute->next;
if (attribute->_private==NULL) {
xmlFreeProp(attribute);
} else {
attribute->parent = NULL;
attribute->prev = NULL;
attribute->next = NULL;
if (attribute->doc!=NULL) [self stripDocumentFromAttribute:attribute];
}
attribute = nextAttribute;
}
theNode->properties = NULL;
}
+ (void)removeNamespacesFromNode:(xmlNodePtr)theNode {
xmlNsPtr namespace = theNode->nsDef;
while (namespace!=NULL){
xmlNsPtr nextNamespace = namespace->next;
if (namespace->_private!=NULL) {
[(MGMXMLNode *)namespace->_private clearParent];
namespace->next = NULL;
} else {
xmlFreeNs(namespace);
}
namespace = nextNamespace;
}
theNode->nsDef = NULL;
theNode->ns = NULL;
}
+ (void)removeChildrenFromNode:(xmlNodePtr)theNode {
xmlNodePtr child = theNode->children;
while (child!=NULL) {
xmlNodePtr nextChild = child->next;
[self freeNode:child];
child = nextChild;
}
theNode->children = NULL;
theNode->last = NULL;
}
+ (void)freeNode:(xmlNodePtr)theNode {
if (![[self class] isNode:theNode->type]) {
NSLog(@"Cannot free node as it is the wrong type %d.", theNode->type);
} else {
if (theNode->_private==NULL) {
[self removeAttributesFromNode:theNode];
[self removeNamespacesFromNode:theNode];
[self removeChildrenFromNode:theNode];
xmlFreeNode(theNode);
} else {
theNode->parent = NULL;
theNode->prev = NULL;
theNode->next = NULL;
if (theNode->doc!=NULL) {
[(MGMXMLNode *)theNode->_private releaseDocument];
[self stripDocumentFromNode:theNode];
}
}
}
}
- (void)setTypeXMLPtr:(xmlTypPtr)theXMLPtr {
[self freeXML];
type = theXMLPtr->type;
if (type==XML_HTML_DOCUMENT_NODE)
type = MGMXMLDocumentKind;
if (type==MGMXMLNamespaceKind) {
namespaceXML = (xmlNsPtr)theXMLPtr;
namespaceXML->_private = self;
} else {
commonXML = (xmlComPtr)theXMLPtr;
commonXML->_private = self;
if (type==MGMXMLDocumentKind && [self isMemberOfClass:[MGMXMLNode class]])
self->isa = [MGMXMLDocument class];
else if (type==MGMXMLElementKind && [self isMemberOfClass:[MGMXMLNode class]])
self->isa = [MGMXMLElement class];
if (type!=MGMXMLDocumentKind)
documentNode = [[MGMXMLDocument alloc] initWithTypeXMLPtr:(xmlTypPtr)commonXML->doc];
}
}
- (void)setDocument:(MGMXMLDocument *)theDocument {
[documentNode release];
documentNode = [theDocument retain];
}
- (void)releaseDocument {
[documentNode release];
documentNode = nil;
}
+ (BOOL)isNode:(MGMXMLNodeKind)theType {
switch (theType) {
case MGMXMLElementKind:
case MGMXMLProcessingInstructionKind:
case MGMXMLCommentKind:
case MGMXMLTextKind:
case XML_CDATA_SECTION_NODE:
return YES;
break;
default:
break;
}
return NO;
}
- (BOOL)isNode {
return [[self class] isNode:type];
}
+ (NSError *)lastError {
if ([MGMXMLInfo objectForKey:MGMXMLLastError]!=nil) {
xmlErrorPtr lastError = [[MGMXMLInfo objectForKey:MGMXMLLastError] xmlErrorValue];
NSString *description = [[NSString stringWithUTF8String:lastError->message] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
return [NSError errorWithDomain:MGMXMLErrorDomain code:lastError->code userInfo:[NSDictionary dictionaryWithObject:description forKey:NSLocalizedDescriptionKey]];
}
return nil;
}
- (NSError *)lastError {
return [[self class] lastError];
}
- (id)initWithKind:(MGMXMLNodeKind)kind {
if (kind==MGMXMLDocumentKind) {
[self release];
return [[MGMXMLDocument alloc] initWithRootElement:nil];
} else if (kind==MGMXMLElementKind) {
return [self initWithTypeXMLPtr:(xmlTypPtr)xmlNewNode(NULL, NULL)];
} else if (kind==MGMXMLAttributeKind) {
return [self initWithTypeXMLPtr:(xmlTypPtr)xmlNewProp(NULL, (const xmlChar *)"", NULL)];
} else if (kind==MGMXMLNamespaceKind) {
return [self initWithTypeXMLPtr:(xmlTypPtr)xmlNewNs(NULL, NULL, NULL)];
} else if (kind==MGMXMLProcessingInstructionKind) {
return [self initWithTypeXMLPtr:(xmlTypPtr)xmlNewPI(NULL, NULL)];
} else if (kind==MGMXMLCommentKind) {
return [self initWithTypeXMLPtr:(xmlTypPtr)xmlNewComment(NULL)];
} else if (kind==MGMXMLTextKind) {
return [self initWithTypeXMLPtr:(xmlTypPtr)xmlNewText(NULL)];
}
[self release];
return nil;
}
- (MGMXMLNodeKind)kind {
return type;
}
- (xmlComPtr)commonXML {
return commonXML;
}
- (xmlNsPtr)nameSpaceXML {
return namespaceXML;
}
- (void)setName:(NSString *)name {
if (type==MGMXMLNamespaceKind) {
if (namespaceXML->prefix!=NULL)
xmlFree((xmlChar *)namespaceXML->prefix);
namespaceXML->prefix = xmlStrdup([name xmlString]);
} else {
xmlNodeSetName(MGMXMLNodePtr, [name xmlString]);
}
}
- (NSString *)name {
if (type==MGMXMLNamespaceKind) {
if (namespaceXML->prefix!=NULL)
return [NSString stringWithXMLString:namespaceXML->prefix];
} else {
if (MGMXMLNodePtr->name!=NULL)
return [NSString stringWithXMLString:(xmlChar *)MGMXMLNodePtr->name];
}
return nil;
}
- (void)setStringValue:(NSString *)string {
if (type==MGMXMLNamespaceKind) {
namespaceXML->href = [string xmlString];
} else if ([self isNode] || type==MGMXMLAttributeKind) {
xmlChar *contentString = xmlEncodeEntitiesReentrant(commonXML->doc, [string xmlString]);
xmlNodeSetContent(MGMXMLNodePtr, contentString);
xmlFree(contentString);
}
}
- (NSString *)stringValue {
if (type==MGMXMLNamespaceKind) {
return [NSString stringWithXMLString:namespaceXML->href];
} else if ([self isNode] || type==MGMXMLAttributeKind) {
xmlChar *contentString = xmlNodeGetContent(MGMXMLNodePtr);
NSString *stringValue = [NSString stringWithXMLString:contentString];
xmlFree(contentString);
return stringValue;
}
return nil;
}
- (MGMXMLDocument *)rootDocument {
if (MGMXMLNodePtr->doc!=NULL)
return [MGMXMLDocument nodeWithTypeXMLPtr:(xmlTypPtr)MGMXMLNodePtr->doc];
return nil;
}
- (MGMXMLNode *)parent {
return [MGMXMLNode nodeWithTypeXMLPtr:(xmlTypPtr)MGMXMLNodePtr->parent];
}
- (MGMXMLNode *)childAtIndex:(NSUInteger)index {
if (type==MGMXMLNamespaceKind)
return nil;
NSUInteger i = 0;
xmlNodePtr child = commonXML->children;
while (child!=NULL) {
if (i==index)
return [MGMXMLNode nodeWithTypeXMLPtr:(xmlTypPtr)child];
i++;
child = child->next;
}
return nil;
}
+ (void)detatchAttribute:(xmlAttrPtr)theAttribute fromNode:(xmlNodePtr)theNode {
if (theAttribute->prev==NULL && theAttribute->next==NULL) {
theNode->properties = NULL;
} else if (theAttribute->prev==NULL) {
theNode->properties = theAttribute->next;
theAttribute->next->prev = NULL;
} else if (theAttribute->next==NULL) {
theAttribute->prev->next = NULL;
} else {
theAttribute->prev->next = theAttribute->next;
theAttribute->next->prev = theAttribute->prev;
}
theAttribute->parent = NULL;
theAttribute->prev = NULL;
theAttribute->next = NULL;
if (theAttribute->doc!=NULL) [[self class] stripDocumentFromAttribute:theAttribute];
}
- (void)detach {
if (type==MGMXMLNamespaceKind) {
if (parentNode!=NULL) {
xmlNsPtr previousNamespace = NULL;
xmlNsPtr currentNamespace = parentNode->nsDef;
while (currentNamespace!=NULL) {
if (currentNamespace==namespaceXML) {
if (previousNamespace!=NULL)
previousNamespace->next = currentNamespace->next;
else
parentNode->nsDef = currentNamespace->next;
break;
}
previousNamespace = currentNamespace;
currentNamespace = currentNamespace->next;
}
namespaceXML->next = NULL;
if (parentNode->ns==namespaceXML)
parentNode->ns = NULL;
parentNode = NULL;
}
return;
}
if (commonXML->parent==NULL) return;
if (type==MGMXMLAttributeKind) {
[[self class] detatchAttribute:MGMXMLAttrPtr fromNode:MGMXMLAttrPtr->parent];
} else if ([self isNode]) {
if (commonXML->prev==NULL && commonXML->next==NULL) {
commonXML->parent->children = NULL;
commonXML->parent->last = NULL;
} else if (commonXML->prev==NULL) {
commonXML->parent->children = commonXML->next;
commonXML->next->prev = NULL;
} else if (commonXML->next==NULL) {
commonXML->parent->last = commonXML->prev;
commonXML->prev->next = NULL;
} else {
commonXML->prev->next = commonXML->next;
commonXML->next->prev = commonXML->prev;
}
commonXML->parent = NULL;
commonXML->prev = NULL;
commonXML->next = NULL;
if (commonXML->doc!=NULL) [[self class] stripDocumentFromNode:MGMXMLNodePtr];
}
}
+ (NSString *)localNameForName:(NSString *)name {
if (name!=nil && [name length]>0) {
NSRange range = [name rangeOfString:@":"];
if (range.location!=NSNotFound)
return [name substringFromIndex:range.location+range.length];
else
return name;
}
return nil;
}
+ (NSString *)prefixForName:(NSString *)name {
if (name!=nil && [name length]>0) {
NSRange range = [name rangeOfString:@":"];
if (range.location!=NSNotFound)
return [name substringToIndex:range.location];
}
return nil;
}
- (NSString *)description {
return [self XMLString];
}
- (NSString *)XMLString {
return [self XMLStringWithOptions:0];
}
- (NSString *)XMLStringWithOptions:(NSUInteger)options {
if(options & MGMXMLNodeCompactEmptyElement)
xmlSaveNoEmptyTags = 0;
else
xmlSaveNoEmptyTags = 1;
int format = 0;
if (options & MGMXMLNodePrettyPrint) {
format = 1;
xmlIndentTreeOutput = 1;
}
xmlBufferPtr bufferPtr = xmlBufferCreate();
if (type==MGMXMLNamespaceKind)
xmlNodeDump(bufferPtr, NULL, MGMXMLNodePtr, 0, format);
else
xmlNodeDump(bufferPtr, commonXML->doc, MGMXMLNodePtr, 0, format);
NSString *result = [NSString stringWithXMLString:bufferPtr->content];
if (type!=MGMXMLTextKind)
result = [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
xmlBufferFree(bufferPtr);
return result;
}
- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error {
BOOL shouldRemoveDocument = NO;
xmlDocPtr document;
if ([self isNode]) {
document = MGMXMLNodePtr->doc;
if (document==NULL) {
shouldRemoveDocument = YES;
document = xmlNewDoc(NULL);
xmlDocSetRootElement(document, MGMXMLNodePtr);
}
} else if (type==MGMXMLDocumentKind) {
document = MGMXMLDocPtr;
} else {
return nil;
}
xmlXPathContextPtr xPathContext = xmlXPathNewContext(document);
xPathContext->node = MGMXMLNodePtr;
xmlNodePtr rootNode = document->children;
if (rootNode!=NULL) {
xmlNsPtr namespace = rootNode->nsDef;
while (namespace!=NULL) {
xmlXPathRegisterNs(xPathContext, namespace->prefix, namespace->href);
namespace = namespace->next;
}
}
xmlXPathObjectPtr xPathObject = xmlXPathEvalExpression([xpath xmlString], xPathContext);
NSMutableArray *nodes = [NSMutableArray array];
if (xPathObject==NULL) {
if (error!=nil) *error = [self lastError];
nodes = nil;
} else {
int count = xmlXPathNodeSetGetLength(xPathObject->nodesetval);
if (count!=0) {
for (int i=0; i<count; i++)
[nodes addObject:[MGMXMLNode nodeWithTypeXMLPtr:(xmlTypPtr)xPathObject->nodesetval->nodeTab[i]]];
}
}
if (xPathObject) xmlXPathFreeObject(xPathObject);
if (xPathContext) xmlXPathFreeContext(xPathContext);
if (shouldRemoveDocument) {
xmlUnlinkNode(MGMXMLNodePtr);
xmlFreeDoc(document);
[[self class] stripDocumentFromNode:MGMXMLNodePtr];
}
return nodes;
}
@end