From f9e672661ac13176bce81698fd25f8bbdc76bd66 Mon Sep 17 00:00:00 2001 From: relikd Date: Sun, 16 Dec 2018 19:18:49 +0100 Subject: [PATCH] Refactored OPML --- RSXML.xcodeproj/project.pbxproj | 36 ------- RSXML/RSAtomParser.m | 22 ++-- RSXML/RSFeedParser.h | 3 - RSXML/RSFeedParser.m | 57 ++-------- RSXML/RSOPMLAttributes.h | 36 ------- RSXML/RSOPMLAttributes.m | 66 ------------ RSXML/RSOPMLDocument.h | 17 --- RSXML/RSOPMLDocument.m | 13 --- RSXML/RSOPMLFeedSpecifier.h | 23 ---- RSXML/RSOPMLFeedSpecifier.m | 50 --------- RSXML/RSOPMLItem.h | 31 +++--- RSXML/RSOPMLItem.m | 133 ++++++++++++----------- RSXML/RSOPMLParser.h | 6 +- RSXML/RSOPMLParser.m | 183 ++++++++++---------------------- RSXML/RSRSSParser.m | 10 +- RSXML/RSSAXParser.h | 1 + RSXML/RSSAXParser.m | 5 + RSXML/RSXML.h | 3 - RSXML/RSXMLError.h | 28 ++--- RSXML/RSXMLError.m | 60 ++++++++--- RSXMLTests/RSOPMLTests.m | 35 +++--- 21 files changed, 249 insertions(+), 569 deletions(-) delete mode 100644 RSXML/RSOPMLAttributes.h delete mode 100644 RSXML/RSOPMLAttributes.m delete mode 100644 RSXML/RSOPMLDocument.h delete mode 100644 RSXML/RSOPMLDocument.m delete mode 100644 RSXML/RSOPMLFeedSpecifier.h delete mode 100644 RSXML/RSOPMLFeedSpecifier.m diff --git a/RSXML.xcodeproj/project.pbxproj b/RSXML.xcodeproj/project.pbxproj index 5d4edc1..1005768 100644 --- a/RSXML.xcodeproj/project.pbxproj +++ b/RSXML.xcodeproj/project.pbxproj @@ -10,14 +10,8 @@ 54FCE5F421493B5E00FABB65 /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 54FCE5F321493B5E00FABB65 /* Resources */; }; 8400B0F01B8C20A9004C4CFF /* RSXMLData.h in Headers */ = {isa = PBXBuildFile; fileRef = 8400B0EE1B8C20A9004C4CFF /* RSXMLData.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8400B0F11B8C20A9004C4CFF /* RSXMLData.m in Sources */ = {isa = PBXBuildFile; fileRef = 8400B0EF1B8C20A9004C4CFF /* RSXMLData.m */; }; - 8429D1AC1C839FFC00F97695 /* RSOPMLDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 8429D1AA1C839FFC00F97695 /* RSOPMLDocument.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8429D1AD1C839FFC00F97695 /* RSOPMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 8429D1AB1C839FFC00F97695 /* RSOPMLDocument.m */; }; 8429D1B61C83A03100F97695 /* RSOPMLItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 8429D1B41C83A03100F97695 /* RSOPMLItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8429D1B71C83A03100F97695 /* RSOPMLItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 8429D1B51C83A03100F97695 /* RSOPMLItem.m */; }; - 8429D1BA1C83A31C00F97695 /* RSOPMLAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 8429D1B81C83A31C00F97695 /* RSOPMLAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8429D1BB1C83A31C00F97695 /* RSOPMLAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 8429D1B91C83A31C00F97695 /* RSOPMLAttributes.m */; }; - 8429D1BE1C83AD0F00F97695 /* RSOPMLFeedSpecifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 8429D1BC1C83AD0F00F97695 /* RSOPMLFeedSpecifier.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8429D1BF1C83AD0F00F97695 /* RSOPMLFeedSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8429D1BD1C83AD0F00F97695 /* RSOPMLFeedSpecifier.m */; }; 8429D1C31C83BCCB00F97695 /* RSOPMLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8429D1C21C83BCCB00F97695 /* RSOPMLTests.m */; }; 842D514C1B52E7FC00E63D52 /* RSAtomParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 842D514A1B52E7FC00E63D52 /* RSAtomParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; 842D514D1B52E7FC00E63D52 /* RSAtomParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 842D514B1B52E7FC00E63D52 /* RSAtomParser.m */; }; @@ -55,14 +49,8 @@ 84AD0C171E11B8CA00B38510 /* RSDateParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 84AD0BF41E11A6FB00B38510 /* RSDateParser.m */; }; 84AD0C181E11B8CF00B38510 /* RSOPMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 842D51781B5311AD00E63D52 /* RSOPMLParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; 84AD0C191E11B8CF00B38510 /* RSOPMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 842D51791B5311AD00E63D52 /* RSOPMLParser.m */; }; - 84AD0C1A1E11B8CF00B38510 /* RSOPMLDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 8429D1AA1C839FFC00F97695 /* RSOPMLDocument.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84AD0C1B1E11B8CF00B38510 /* RSOPMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 8429D1AB1C839FFC00F97695 /* RSOPMLDocument.m */; }; 84AD0C1C1E11B8CF00B38510 /* RSOPMLItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 8429D1B41C83A03100F97695 /* RSOPMLItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; 84AD0C1D1E11B8CF00B38510 /* RSOPMLItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 8429D1B51C83A03100F97695 /* RSOPMLItem.m */; }; - 84AD0C1E1E11B8CF00B38510 /* RSOPMLFeedSpecifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 8429D1BC1C83AD0F00F97695 /* RSOPMLFeedSpecifier.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84AD0C1F1E11B8CF00B38510 /* RSOPMLFeedSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8429D1BD1C83AD0F00F97695 /* RSOPMLFeedSpecifier.m */; }; - 84AD0C201E11B8CF00B38510 /* RSOPMLAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 8429D1B81C83A31C00F97695 /* RSOPMLAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 84AD0C211E11B8CF00B38510 /* RSOPMLAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 8429D1B91C83A31C00F97695 /* RSOPMLAttributes.m */; }; 84AD0C221E11B8D400B38510 /* RSFeedParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 842D51501B52E80100E63D52 /* RSFeedParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; 84AD0C231E11B8D400B38510 /* RSFeedParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 842D51511B52E80100E63D52 /* RSFeedParser.m */; }; 84AD0C241E11B8D400B38510 /* FeedParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 842D516D1B5308BD00E63D52 /* FeedParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -116,14 +104,8 @@ 54FCE5F321493B5E00FABB65 /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = ""; }; 8400B0EE1B8C20A9004C4CFF /* RSXMLData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RSXMLData.h; path = RSXML/RSXMLData.h; sourceTree = ""; }; 8400B0EF1B8C20A9004C4CFF /* RSXMLData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RSXMLData.m; path = RSXML/RSXMLData.m; sourceTree = ""; }; - 8429D1AA1C839FFC00F97695 /* RSOPMLDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLDocument.h; sourceTree = ""; }; - 8429D1AB1C839FFC00F97695 /* RSOPMLDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLDocument.m; sourceTree = ""; }; 8429D1B41C83A03100F97695 /* RSOPMLItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLItem.h; sourceTree = ""; }; 8429D1B51C83A03100F97695 /* RSOPMLItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLItem.m; sourceTree = ""; }; - 8429D1B81C83A31C00F97695 /* RSOPMLAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLAttributes.h; sourceTree = ""; }; - 8429D1B91C83A31C00F97695 /* RSOPMLAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RSOPMLAttributes.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - 8429D1BC1C83AD0F00F97695 /* RSOPMLFeedSpecifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLFeedSpecifier.h; sourceTree = ""; }; - 8429D1BD1C83AD0F00F97695 /* RSOPMLFeedSpecifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLFeedSpecifier.m; sourceTree = ""; }; 8429D1C21C83BCCB00F97695 /* RSOPMLTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLTests.m; sourceTree = ""; }; 842D514A1B52E7FC00E63D52 /* RSAtomParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSAtomParser.h; sourceTree = ""; }; 842D514B1B52E7FC00E63D52 /* RSAtomParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSAtomParser.m; sourceTree = ""; }; @@ -223,14 +205,8 @@ children = ( 842D51781B5311AD00E63D52 /* RSOPMLParser.h */, 842D51791B5311AD00E63D52 /* RSOPMLParser.m */, - 8429D1AA1C839FFC00F97695 /* RSOPMLDocument.h */, - 8429D1AB1C839FFC00F97695 /* RSOPMLDocument.m */, 8429D1B41C83A03100F97695 /* RSOPMLItem.h */, 8429D1B51C83A03100F97695 /* RSOPMLItem.m */, - 8429D1BC1C83AD0F00F97695 /* RSOPMLFeedSpecifier.h */, - 8429D1BD1C83AD0F00F97695 /* RSOPMLFeedSpecifier.m */, - 8429D1B81C83A31C00F97695 /* RSOPMLAttributes.h */, - 8429D1B91C83A31C00F97695 /* RSOPMLAttributes.m */, ); name = OPML; path = RSXML; @@ -332,9 +308,7 @@ 84AD0C161E11B8CA00B38510 /* RSDateParser.h in Headers */, 84AD0C101E11B8CA00B38510 /* RSSAXParser.h in Headers */, 84AD0C331E11B8DA00B38510 /* RSHTMLLinkParser.h in Headers */, - 84AD0C201E11B8CF00B38510 /* RSOPMLAttributes.h in Headers */, 84AD0C2F1E11B8DA00B38510 /* RSHTMLMetadataParser.h in Headers */, - 84AD0C1A1E11B8CF00B38510 /* RSOPMLDocument.h in Headers */, 84AD0C251E11B8D400B38510 /* RSAtomParser.h in Headers */, 84AD0C121E11B8CA00B38510 /* RSXMLData.h in Headers */, 84AD0C311E11B8DA00B38510 /* RSHTMLMetadata.h in Headers */, @@ -342,7 +316,6 @@ 84AD0C221E11B8D400B38510 /* RSFeedParser.h in Headers */, 84AD0C2D1E11B8DA00B38510 /* RSSAXHTMLParser.h in Headers */, 84AD0C0E1E11B8CA00B38510 /* RSXMLError.h in Headers */, - 84AD0C1E1E11B8CF00B38510 /* RSOPMLFeedSpecifier.h in Headers */, 84AD0C2B1E11B8D400B38510 /* RSParsedArticle.h in Headers */, 84AD0C291E11B8D400B38510 /* RSParsedFeed.h in Headers */, 84AD0C181E11B8CF00B38510 /* RSOPMLParser.h in Headers */, @@ -361,16 +334,13 @@ 8400B0F01B8C20A9004C4CFF /* RSXMLData.h in Headers */, 842D51631B53058B00E63D52 /* RSParsedArticle.h in Headers */, 842D517A1B5311AD00E63D52 /* RSOPMLParser.h in Headers */, - 8429D1AC1C839FFC00F97695 /* RSOPMLDocument.h in Headers */, 84BF3E161C8CDD1A005562D8 /* RSHTMLMetadataParser.h in Headers */, 842D51761B530BF200E63D52 /* RSParsedFeed.h in Headers */, - 8429D1BA1C83A31C00F97695 /* RSOPMLAttributes.h in Headers */, 84BF3E1C1C8CDD6D005562D8 /* RSHTMLMetadata.h in Headers */, 8429D1B61C83A03100F97695 /* RSOPMLItem.h in Headers */, 843819001C8CB00400E2A1DD /* RSSAXHTMLParser.h in Headers */, 842D51521B52E80100E63D52 /* RSFeedParser.h in Headers */, 84E4BE451C8B8FE400A90B41 /* RSXMLError.h in Headers */, - 8429D1BE1C83AD0F00F97695 /* RSOPMLFeedSpecifier.h in Headers */, 842D516F1B5308BD00E63D52 /* FeedParser.h in Headers */, 84F22C111B52DDEA000060CE /* RSXML.h in Headers */, 842D514C1B52E7FC00E63D52 /* RSAtomParser.h in Headers */, @@ -524,11 +494,8 @@ 84AD0C261E11B8D400B38510 /* RSAtomParser.m in Sources */, 84AD0C1D1E11B8CF00B38510 /* RSOPMLItem.m in Sources */, 84AD0C131E11B8CA00B38510 /* RSXMLData.m in Sources */, - 84AD0C211E11B8CF00B38510 /* RSOPMLAttributes.m in Sources */, 84AD0C321E11B8DA00B38510 /* RSHTMLMetadata.m in Sources */, - 84AD0C1F1E11B8CF00B38510 /* RSOPMLFeedSpecifier.m in Sources */, 84AD0C111E11B8CA00B38510 /* RSSAXParser.m in Sources */, - 84AD0C1B1E11B8CF00B38510 /* RSOPMLDocument.m in Sources */, 84AD0C0F1E11B8CA00B38510 /* RSXMLError.m in Sources */, 84AD0C2E1E11B8DA00B38510 /* RSSAXHTMLParser.m in Sources */, ); @@ -542,14 +509,11 @@ 842D515B1B52E81B00E63D52 /* RSRSSParser.m in Sources */, 842D517B1B5311AD00E63D52 /* RSOPMLParser.m in Sources */, 8486F1161BB646140092794F /* NSString+RSXML.m in Sources */, - 8429D1BB1C83A31C00F97695 /* RSOPMLAttributes.m in Sources */, 84AD0BF61E11A6FB00B38510 /* RSDateParser.m in Sources */, 84BF3E171C8CDD1A005562D8 /* RSHTMLMetadataParser.m in Sources */, 842D51641B53058B00E63D52 /* RSParsedArticle.m in Sources */, 84E4BE461C8B8FE400A90B41 /* RSXMLError.m in Sources */, 8475C4091D57AB4C0076751E /* RSHTMLLinkParser.m in Sources */, - 8429D1BF1C83AD0F00F97695 /* RSOPMLFeedSpecifier.m in Sources */, - 8429D1AD1C839FFC00F97695 /* RSOPMLDocument.m in Sources */, 8429D1B71C83A03100F97695 /* RSOPMLItem.m in Sources */, 84BF3E1D1C8CDD6D005562D8 /* RSHTMLMetadata.m in Sources */, 842D51531B52E80100E63D52 /* RSFeedParser.m in Sources */, diff --git a/RSXML/RSAtomParser.m b/RSXML/RSAtomParser.m index 6f9428c..2a46a19 100755 --- a/RSXML/RSAtomParser.m +++ b/RSXML/RSAtomParser.m @@ -552,12 +552,6 @@ static const NSInteger kSelfLength = 5; } -static BOOL equalBytes(const void *bytes1, const void *bytes2, NSUInteger length) { - - return memcmp(bytes1, bytes2, length) == 0; -} - - - (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForValue:(const void *)bytes length:(NSUInteger)length { static const NSUInteger alternateLength = kAlternateLength - 1; @@ -569,35 +563,35 @@ static BOOL equalBytes(const void *bytes1, const void *bytes2, NSUInteger length static const NSUInteger textLength = kTextLength - 1; static const NSUInteger selfLength = kSelfLength - 1; - if (length == alternateLength && equalBytes(bytes, kAlternate, alternateLength)) { + if (length == alternateLength && RSSAXEqualBytes(bytes, kAlternate, alternateLength)) { return kAlternateValue; } - if (length == textHTMLLength && equalBytes(bytes, kTextHTML, textHTMLLength)) { + if (length == textHTMLLength && RSSAXEqualBytes(bytes, kTextHTML, textHTMLLength)) { return kTextHTMLValue; } - if (length == relatedLength && equalBytes(bytes, kRelated, relatedLength)) { + if (length == relatedLength && RSSAXEqualBytes(bytes, kRelated, relatedLength)) { return kRelatedValue; } - if (length == shortURLLength && equalBytes(bytes, kShortURL, shortURLLength)) { + if (length == shortURLLength && RSSAXEqualBytes(bytes, kShortURL, shortURLLength)) { return kShortURLValue; } - if (length == htmlLength && equalBytes(bytes, kHTML, htmlLength)) { + if (length == htmlLength && RSSAXEqualBytes(bytes, kHTML, htmlLength)) { return kHTMLValue; } - if (length == enLength && equalBytes(bytes, kEn, enLength)) { + if (length == enLength && RSSAXEqualBytes(bytes, kEn, enLength)) { return kEnValue; } - if (length == textLength && equalBytes(bytes, kText, textLength)) { + if (length == textLength && RSSAXEqualBytes(bytes, kText, textLength)) { return kTextValue; } - if (length == selfLength && equalBytes(bytes, kSelf, selfLength)) { + if (length == selfLength && RSSAXEqualBytes(bytes, kSelf, selfLength)) { return kSelfValue; } diff --git a/RSXML/RSFeedParser.h b/RSXML/RSFeedParser.h index 2b9d1e2..40b2fef 100644 --- a/RSXML/RSFeedParser.h +++ b/RSXML/RSFeedParser.h @@ -16,9 +16,6 @@ NS_ASSUME_NONNULL_BEGIN -static NSString *kLIBXMLParserErrorDomain = @"LIBXMLParserErrorDomain"; -static NSString *kRSXMLParserErrorDomain = @"RSXMLParserErrorDomain"; - BOOL RSCanParseFeed(RSXMLData *xmlData); diff --git a/RSXML/RSFeedParser.m b/RSXML/RSFeedParser.m index 6e4f36d..5367922 100644 --- a/RSXML/RSFeedParser.m +++ b/RSXML/RSFeedParser.m @@ -6,7 +6,7 @@ // Copyright (c) 2015 Ranchero Software LLC. All rights reserved. // -#import +#import "RSXMLError.h" #import "RSFeedParser.h" #import "FeedParser.h" #import "RSXMLData.h" @@ -49,43 +49,10 @@ static BOOL dataHasLeftCaret(const char *bytes, NSUInteger numberOfBytes); static const NSUInteger maxNumberOfBytesToSearch = 4096; static const NSUInteger minNumberOfBytesToSearch = 20; -typedef enum { - RSXMLErrorNoData = 100, - RSXMLErrorMissingLeftCaret, - RSXMLErrorProbablyHTML, - RSXMLErrorContainsXMLErrorsTag, - RSXMLErrorNoSuitableParser -} RSXMLError; - -static void setError(NSError **error, RSXMLError code) { - if (!error) { - return; - } - NSString *msg = @""; - switch (code) { // switch statement will warn if an enum value is missing - case RSXMLErrorNoData: - msg = @"Couldn't parse feed. No data available."; - break; - case RSXMLErrorMissingLeftCaret: - msg = @"Couldn't parse feed. Missing left caret character ('<')."; - break; - case RSXMLErrorProbablyHTML: - msg = @"Couldn't parse feed. Expecting XML data but found html data."; - break; - case RSXMLErrorContainsXMLErrorsTag: - msg = @"Couldn't parse feed. XML contains 'errors' tag."; - break; - case RSXMLErrorNoSuitableParser: - msg = @"Couldn't parse feed. No suitable parser found. XML document not well-formed."; - break; - } - *error = [NSError errorWithDomain:kRSXMLParserErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: msg}]; -} - static Class parserClassForXMLData(RSXMLData *xmlData, NSError **error) { if (!feedMayBeParseable(xmlData)) { - setError(error, RSXMLErrorNoData); + RSXMLSetError(error, RSXMLErrorNoData, nil); return nil; } @@ -101,7 +68,7 @@ static Class parserClassForXMLData(RSXMLData *xmlData, NSError **error) { } if (!dataHasLeftCaret(bytes, numberOfBytes)) { - setError(error, RSXMLErrorMissingLeftCaret); + RSXMLSetError(error, RSXMLErrorMissingLeftCaret, nil); return nil; } if (optimisticCanParseRSSData(bytes, numberOfBytes)) { @@ -114,11 +81,11 @@ static Class parserClassForXMLData(RSXMLData *xmlData, NSError **error) { return [RSRSSParser class]; //TODO: parse RDF feeds, using RSS parser so far ... } if (dataIsProbablyHTML(bytes, numberOfBytes)) { - setError(error, RSXMLErrorProbablyHTML); + RSXMLSetError(error, RSXMLErrorProbablyHTML, nil); return nil; } if (dataIsSomeWeirdException(bytes, numberOfBytes)) { - setError(error, RSXMLErrorContainsXMLErrorsTag); + RSXMLSetError(error, RSXMLErrorContainsXMLErrorsTag, nil); return nil; } } @@ -130,7 +97,7 @@ static Class parserClassForXMLData(RSXMLData *xmlData, NSError **error) { } } // Try RSS anyway? libxml would return a parsing error - setError(error, RSXMLErrorNoSuitableParser); + RSXMLSetError(error, RSXMLErrorNoSuitableParser, nil); return nil; } @@ -250,19 +217,11 @@ RSParsedFeed *RSParseFeedSync(RSXMLData *xmlData, NSError **error) { xmlResetLastError(); id parser = parserForXMLData(xmlData, error); if (error && *error) { - //printf("ERROR in parserForXMLData(): %s\n", [[*error localizedDescription] UTF8String]); return nil; } RSParsedFeed *parsedResult = [parser parseFeed]; - - xmlErrorPtr err = xmlGetLastError(); - if (err && error) { - int errCode = err->code; - char * msg = err->message; - //if (err->level == XML_ERR_FATAL) - NSString *errMsg = [[NSString stringWithFormat:@"%s", msg] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - *error = [NSError errorWithDomain:kLIBXMLParserErrorDomain code:errCode userInfo:@{NSLocalizedDescriptionKey: errMsg}]; - //printf("ERROR in [parseFeed] (%d): %s\n", err->level, [[*error localizedDescription] UTF8String]); + if (error) { + *error = RSXMLMakeErrorFromLIBXMLError(xmlGetLastError()); xmlResetLastError(); } return parsedResult; diff --git a/RSXML/RSOPMLAttributes.h b/RSXML/RSOPMLAttributes.h deleted file mode 100644 index 0ffd202..0000000 --- a/RSXML/RSOPMLAttributes.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// RSOPMLAttributes.h -// RSXML -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - -// OPML allows for arbitrary attributes. -// These are the common attributes in OPML files used as RSS subscription lists. - -extern NSString *OPMLTextKey; //text -extern NSString *OPMLTitleKey; //title -extern NSString *OPMLDescriptionKey; //description -extern NSString *OPMLTypeKey; //type -extern NSString *OPMLVersionKey; //version -extern NSString *OPMLHMTLURLKey; //htmlUrl -extern NSString *OPMLXMLURLKey; //xmlUrl - - -@interface NSDictionary (RSOPMLAttributes) - -// A frequent error in OPML files is to mess up the capitalization, -// so these do a case-insensitive lookup. - -@property (nonatomic, readonly) NSString *opml_text; -@property (nonatomic, readonly) NSString *opml_title; -@property (nonatomic, readonly) NSString *opml_description; -@property (nonatomic, readonly) NSString *opml_type; -@property (nonatomic, readonly) NSString *opml_version; -@property (nonatomic, readonly) NSString *opml_htmlUrl; -@property (nonatomic, readonly) NSString *opml_xmlUrl; - -@end diff --git a/RSXML/RSOPMLAttributes.m b/RSXML/RSOPMLAttributes.m deleted file mode 100644 index 4fb5e9b..0000000 --- a/RSXML/RSOPMLAttributes.m +++ /dev/null @@ -1,66 +0,0 @@ -// -// RSOPMLAttributes.m -// RSXML -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import "RSOPMLAttributes.h" -#import "RSXMLInternal.h" - - -NSString *OPMLTextKey = @"text"; -NSString *OPMLTitleKey = @"title"; -NSString *OPMLDescriptionKey = @"description"; -NSString *OPMLTypeKey = @"type"; -NSString *OPMLVersionKey = @"version"; -NSString *OPMLHMTLURLKey = @"htmlUrl"; -NSString *OPMLXMLURLKey = @"xmlUrl"; - - -@implementation NSDictionary (RSOPMLAttributes) - -- (NSString *)opml_text { - - return [self rsxml_objectForCaseInsensitiveKey:OPMLTextKey]; -} - - -- (NSString *)opml_title { - - return [self rsxml_objectForCaseInsensitiveKey:OPMLTitleKey]; -} - - -- (NSString *)opml_description { - - return [self rsxml_objectForCaseInsensitiveKey:OPMLDescriptionKey]; -} - - -- (NSString *)opml_type { - - return [self rsxml_objectForCaseInsensitiveKey:OPMLTypeKey]; -} - - -- (NSString *)opml_version { - - return [self rsxml_objectForCaseInsensitiveKey:OPMLVersionKey]; -} - - -- (NSString *)opml_htmlUrl { - - return [self rsxml_objectForCaseInsensitiveKey:OPMLHMTLURLKey]; -} - - -- (NSString *)opml_xmlUrl { - - return [self rsxml_objectForCaseInsensitiveKey:OPMLXMLURLKey]; -} - - -@end diff --git a/RSXML/RSOPMLDocument.h b/RSXML/RSOPMLDocument.h deleted file mode 100644 index bd33f16..0000000 --- a/RSXML/RSOPMLDocument.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// RSOPMLDocument.h -// RSXML -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; -#import "RSOPMLItem.h" - - -@interface RSOPMLDocument : RSOPMLItem - -@property (nonatomic) NSString *title; - -@end diff --git a/RSXML/RSOPMLDocument.m b/RSXML/RSOPMLDocument.m deleted file mode 100644 index ec22cd7..0000000 --- a/RSXML/RSOPMLDocument.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// RSOPMLDocument.m -// RSXML -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import "RSOPMLDocument.h" - -@implementation RSOPMLDocument - -@end diff --git a/RSXML/RSOPMLFeedSpecifier.h b/RSXML/RSOPMLFeedSpecifier.h deleted file mode 100644 index 785c4ca..0000000 --- a/RSXML/RSOPMLFeedSpecifier.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// RSOPMLFeedSpecifier.h -// RSXML -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -@import Foundation; - - -@interface RSOPMLFeedSpecifier : NSObject - - -- (instancetype)initWithTitle:(NSString *)title feedDescription:(NSString *)feedDescription homePageURL:(NSString *)homePageURL feedURL:(NSString *)feedURL; - -@property (nonatomic, readonly) NSString *title; -@property (nonatomic, readonly) NSString *feedDescription; -@property (nonatomic, readonly) NSString *homePageURL; -@property (nonatomic, readonly) NSString *feedURL; - - -@end diff --git a/RSXML/RSOPMLFeedSpecifier.m b/RSXML/RSOPMLFeedSpecifier.m deleted file mode 100644 index e645589..0000000 --- a/RSXML/RSOPMLFeedSpecifier.m +++ /dev/null @@ -1,50 +0,0 @@ -// -// RSOPMLFeedSpecifier.m -// RSXML -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// - -#import "RSOPMLFeedSpecifier.h" -#import "RSXMLInternal.h" - - -@implementation RSOPMLFeedSpecifier - -- (instancetype)initWithTitle:(NSString *)title feedDescription:(NSString *)feedDescription homePageURL:(NSString *)homePageURL feedURL:(NSString *)feedURL { - - NSParameterAssert(!RSXMLIsEmpty(feedURL)); - - self = [super init]; - if (!self) { - return nil; - } - - if (RSXMLIsEmpty(title)) { - _title = nil; - } - else { - _title = title; - } - - if (RSXMLIsEmpty(feedDescription)) { - _feedDescription = nil; - } - else { - _feedDescription = feedDescription; - } - - if (RSXMLIsEmpty(homePageURL)) { - _homePageURL = nil; - } - else { - _homePageURL = homePageURL; - } - - _feedURL = feedURL; - - return self; -} - -@end diff --git a/RSXML/RSOPMLItem.h b/RSXML/RSOPMLItem.h index 853cf9b..a609012 100644 --- a/RSXML/RSOPMLItem.h +++ b/RSXML/RSOPMLItem.h @@ -1,26 +1,27 @@ -// -// RSOPMLItem.h -// RSXML -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// @import Foundation; -@class RSOPMLFeedSpecifier; +// OPML allows for arbitrary attributes. +// These are the common attributes in OPML files used as RSS subscription lists. + +extern NSString *OPMLTextKey; //text +extern NSString *OPMLTitleKey; //title +extern NSString *OPMLDescriptionKey; //description +extern NSString *OPMLTypeKey; //type +extern NSString *OPMLVersionKey; //version +extern NSString *OPMLHMTLURLKey; //htmlUrl +extern NSString *OPMLXMLURLKey; //xmlUrl @interface RSOPMLItem : NSObject - +@property (nonatomic) NSArray *children; @property (nonatomic) NSDictionary *attributes; -@property (nonatomic) NSArray *children; +@property (nonatomic, readonly) BOOL isFolder; // true if children.count > 0 +@property (nonatomic, readonly) NSString *displayName; //May be nil. - (void)addChild:(RSOPMLItem *)child; +- (void)setAttribute:(id)value forKey:(NSString *)key; +- (id)attributeForKey:(NSString *)key; -@property (nonatomic, readonly) RSOPMLFeedSpecifier *OPMLFeedSpecifier; //May be nil. - -@property (nonatomic, readonly) NSString *titleFromAttributes; //May be nil. -@property (nonatomic, readonly) BOOL isFolder; - +- (NSString *)recursiveDescription; @end diff --git a/RSXML/RSOPMLItem.m b/RSXML/RSOPMLItem.m index f73efbd..49ac893 100644 --- a/RSXML/RSOPMLItem.m +++ b/RSXML/RSOPMLItem.m @@ -1,86 +1,99 @@ -// -// RSOPMLItem.m -// RSXML -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// #import "RSOPMLItem.h" -#import "RSOPMLAttributes.h" -#import "RSOPMLFeedSpecifier.h" #import "RSXMLInternal.h" +NSString *OPMLTextKey = @"text"; +NSString *OPMLTitleKey = @"title"; +NSString *OPMLDescriptionKey = @"description"; +NSString *OPMLTypeKey = @"type"; +NSString *OPMLVersionKey = @"version"; +NSString *OPMLHMTLURLKey = @"htmlUrl"; +NSString *OPMLXMLURLKey = @"xmlUrl"; + + @interface RSOPMLItem () - -@property (nonatomic) NSMutableArray *mutableChildren; - +@property (nonatomic) NSMutableArray *mutableChildren; +@property (nonatomic) NSMutableDictionary *mutableAttributes; @end @implementation RSOPMLItem -@synthesize children = _children; -@synthesize OPMLFeedSpecifier = _OPMLFeedSpecifier; - - - (NSArray *)children { - return [self.mutableChildren copy]; } - -- (void)setChildren:(NSArray *)children { - - _children = children; - self.mutableChildren = [_children mutableCopy]; +- (void)setChildren:(NSArray*)children { + self.mutableChildren = [children mutableCopy]; } - -- (void)addChild:(RSOPMLItem *)child { - - if (!self.mutableChildren) { - self.mutableChildren = [NSMutableArray new]; - } - - [self.mutableChildren addObject:child]; +- (NSDictionary *)attributes { + return [self.mutableAttributes copy]; } - -- (RSOPMLFeedSpecifier *)OPMLFeedSpecifier { - - if (_OPMLFeedSpecifier) { - return _OPMLFeedSpecifier; - } - - NSString *feedURL = self.attributes.opml_xmlUrl; - if (RSXMLIsEmpty(feedURL)) { - return nil; - } - - _OPMLFeedSpecifier = [[RSOPMLFeedSpecifier alloc] initWithTitle:self.attributes.opml_title feedDescription:self.attributes.opml_description homePageURL:self.attributes.opml_htmlUrl feedURL:feedURL]; - - return _OPMLFeedSpecifier; -} - -- (NSString *)titleFromAttributes { - - NSString *title = self.attributes.opml_title; - if (title) { - return title; - } - title = self.attributes.opml_text; - if (title) { - return title; - } - - return nil; +- (void)setAttributes:(NSDictionary *)attributes { + self.mutableAttributes = [attributes mutableCopy]; } - (BOOL)isFolder { - return self.mutableChildren.count > 0; } +- (NSString *)displayName { + NSString *title = [self attributeForKey:OPMLTitleKey]; + if (!title) { + title = [self attributeForKey:OPMLTextKey]; + } + return title; +} + +- (void)addChild:(RSOPMLItem *)child { + if (!self.mutableChildren) { + self.mutableChildren = [NSMutableArray new]; + } + [self.mutableChildren addObject:child]; +} + +- (void)setAttribute:(id)value forKey:(NSString *)key { + if (!self.mutableAttributes) { + self.mutableAttributes = [NSMutableDictionary new]; + } + [self.mutableAttributes setValue:value forKey:key]; +} + +- (id)attributeForKey:(NSString *)key { + if (self.attributes.count > 0 && !RSXMLStringIsEmpty(key)) { + return [self.attributes rsxml_objectForCaseInsensitiveKey:key]; + } + return nil; +} + +#pragma mark - Printing + +- (NSString *)description { + NSMutableString *str = [NSMutableString stringWithFormat:@"<%@ group: %d", [self class], self.isFolder]; + for (NSString *key in _mutableAttributes) { + [str appendFormat:@", %@: '%@'", key, _mutableAttributes[key]]; + } + [str appendString:@">"]; + return str; +} + +- (void)appendStringRecursive:(NSMutableString *)str indent:(NSString *)prefix { + [str appendFormat:@"%@%@\n", prefix, self]; + if (self.isFolder) { + for (RSOPMLItem *child in self.children) { + [child appendStringRecursive:str indent:[prefix stringByAppendingString:@" "]]; + } + [str appendFormat:@"%@\n", prefix]; + } +} + +- (NSString *)recursiveDescription { + NSMutableString *mStr = [NSMutableString new]; + [self appendStringRecursive:mStr indent:@""]; + return mStr; +} + @end diff --git a/RSXML/RSOPMLParser.h b/RSXML/RSOPMLParser.h index 825b9ac..61f3aec 100644 --- a/RSXML/RSOPMLParser.h +++ b/RSXML/RSOPMLParser.h @@ -10,10 +10,10 @@ @class RSXMLData; -@class RSOPMLDocument; +@class RSOPMLItem; -typedef void (^RSParsedOPMLBlock)(RSOPMLDocument *OPMLDocument, NSError *error); +typedef void (^RSParsedOPMLBlock)(RSOPMLItem *opmlDocument, NSError *error); void RSParseOPML(RSXMLData *xmlData, RSParsedOPMLBlock callback); //async; calls back on main thread. @@ -22,7 +22,7 @@ void RSParseOPML(RSXMLData *xmlData, RSParsedOPMLBlock callback); //async; calls - (instancetype)initWithXMLData:(RSXMLData *)xmlData; -@property (nonatomic, readonly) RSOPMLDocument *OPMLDocument; +@property (nonatomic, readonly) RSOPMLItem *opmlDocument; @property (nonatomic, readonly) NSError *error; @end diff --git a/RSXML/RSOPMLParser.m b/RSXML/RSOPMLParser.m index 842f30a..8dbeaaf 100644 --- a/RSXML/RSOPMLParser.m +++ b/RSXML/RSOPMLParser.m @@ -11,8 +11,6 @@ #import "RSXMLData.h" #import "RSSAXParser.h" #import "RSOPMLItem.h" -#import "RSOPMLDocument.h" -#import "RSOPMLAttributes.h" #import "RSXMLError.h" @@ -27,7 +25,7 @@ void RSParseOPML(RSXMLData *xmlData, RSParsedOPMLBlock callback) { RSOPMLParser *parser = [[RSOPMLParser alloc] initWithXMLData:xmlData]; - RSOPMLDocument *document = parser.OPMLDocument; + RSOPMLItem *document = parser.opmlDocument; NSError *error = parser.error; dispatch_async(dispatch_get_main_queue(), ^{ @@ -41,9 +39,9 @@ void RSParseOPML(RSXMLData *xmlData, RSParsedOPMLBlock callback) { @interface RSOPMLParser () -@property (nonatomic, readwrite) RSOPMLDocument *OPMLDocument; +@property (nonatomic, readwrite) RSOPMLItem *opmlDocument; @property (nonatomic, readwrite) NSError *error; -@property (nonatomic) NSMutableArray *itemStack; +@property (nonatomic) NSMutableArray *itemStack; @end @@ -72,31 +70,28 @@ void RSParseOPML(RSXMLData *xmlData, RSParsedOPMLBlock callback) { @autoreleasepool { - if (![self canParseData:XMLData.data]) { - + if ([self canParseData:XMLData.data]) { + RSSAXParser *parser = [[RSSAXParser alloc] initWithDelegate:self]; + + self.itemStack = [NSMutableArray new]; + self.opmlDocument = [RSOPMLItem new]; + [self.itemStack addObject:self.opmlDocument]; + + [parser parseData:XMLData.data]; + [parser finishParsing]; + + } else { + NSString *filename = nil; NSURL *url = [NSURL URLWithString:XMLData.urlString]; if (url && url.isFileURL) { filename = url.path.lastPathComponent; } - if ([XMLData.urlString hasPrefix:@"http"]) { - filename = XMLData.urlString; - } if (!filename) { filename = XMLData.urlString; } - self.error = RSOPMLWrongFormatError(filename); - return; + self.error = RSXMLMakeError(RSXMLErrorFileNotOPML, filename); } - - RSSAXParser *parser = [[RSSAXParser alloc] initWithDelegate:self]; - - self.itemStack = [NSMutableArray new]; - self.OPMLDocument = [RSOPMLDocument new]; - [self pushItem:self.OPMLDocument]; - - [parser parseData:XMLData.data]; - [parser finishParsing]; } } @@ -122,12 +117,12 @@ void RSParseOPML(RSXMLData *xmlData, RSParsedOPMLBlock callback) { } NSRange opmlRange = [s rangeOfString:@" #import -#import #import -#import -#import #import diff --git a/RSXML/RSXMLError.h b/RSXML/RSXMLError.h index 95b0f78..f9cfff4 100644 --- a/RSXML/RSXMLError.h +++ b/RSXML/RSXMLError.h @@ -1,19 +1,21 @@ -// -// RSXMLError.h -// RSXML -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// @import Foundation; +#import -extern NSString *RSXMLErrorDomain; +extern NSErrorDomain kLIBXMLParserErrorDomain; +extern NSErrorDomain kRSXMLParserErrorDomain; - -typedef NS_ENUM(NSInteger, RSXMLErrorCode) { - RSXMLErrorCodeDataIsWrongFormat = 1024 +/// Error codes for RSXML error domain @c (kRSXMLParserErrorDomain) +typedef NS_ENUM(NSInteger, RSXMLError) { + /// Error codes + RSXMLErrorNoData = 100, + RSXMLErrorMissingLeftCaret = 110, + RSXMLErrorProbablyHTML = 120, + RSXMLErrorContainsXMLErrorsTag = 130, + RSXMLErrorNoSuitableParser = 140, + RSXMLErrorFileNotOPML = 1024 // original value }; - -NSError *RSOPMLWrongFormatError(NSString *fileName); +void RSXMLSetError(NSError **error, RSXMLError code, NSString *filename); +NSError * RSXMLMakeError(RSXMLError code, NSString *filename); +NSError * RSXMLMakeErrorFromLIBXMLError(xmlErrorPtr err); diff --git a/RSXML/RSXMLError.m b/RSXML/RSXMLError.m index c713906..abc195d 100644 --- a/RSXML/RSXMLError.m +++ b/RSXML/RSXMLError.m @@ -1,22 +1,48 @@ -// -// RSXMLError.m -// RSXML -// -// Created by Brent Simmons on 2/28/16. -// Copyright © 2016 Ranchero Software, LLC. All rights reserved. -// #import "RSXMLError.h" -NSString *RSXMLErrorDomain = @"com.ranchero.RSXML"; +NSErrorDomain kLIBXMLParserErrorDomain = @"LIBXMLParserErrorDomain"; +NSErrorDomain kRSXMLParserErrorDomain = @"RSXMLParserErrorDomain"; -NSError *RSOPMLWrongFormatError(NSString *fileName) { - - NSString *localizedDescriptionFormatString = NSLocalizedString(@"The file ‘%@’ can’t be parsed because it’s not an OPML file.", @"OPML wrong format"); - NSString *localizedDescription = [NSString stringWithFormat:localizedDescriptionFormatString, fileName]; - - NSString *localizedFailureString = NSLocalizedString(@"The file is not an OPML file.", @"OPML wrong format"); - NSDictionary *userInfo = @{NSLocalizedDescriptionKey: localizedDescription, NSLocalizedFailureReasonErrorKey: localizedFailureString}; - - return [[NSError alloc] initWithDomain:RSXMLErrorDomain code:RSXMLErrorCodeDataIsWrongFormat userInfo:userInfo]; +NSString * getErrorMessageForRSXMLError(RSXMLError code, id paramA); +NSString * getErrorMessageForRSXMLError(RSXMLError code, id paramA) { + switch (code) { // switch statement will warn if an enum value is missing + case RSXMLErrorNoData: + return @"Couldn't parse feed. No data available."; + case RSXMLErrorMissingLeftCaret: + return @"Couldn't parse feed. Missing left caret character ('<')."; + case RSXMLErrorProbablyHTML: + return @"Couldn't parse feed. Expecting XML data but found html data."; + case RSXMLErrorContainsXMLErrorsTag: + return @"Couldn't parse feed. XML contains 'errors' tag."; + case RSXMLErrorNoSuitableParser: + return @"Couldn't parse feed. No suitable parser found. XML document not well-formed."; + case RSXMLErrorFileNotOPML: + if (paramA) { + return [NSString stringWithFormat:@"The file ‘%@’ can't be parsed because it's not an OPML file.", paramA]; + } + return @"The file can't be parsed because it's not an OPML file."; + } +} + +void RSXMLSetError(NSError **error, RSXMLError code, NSString *filename) { + if (error) { + *error = RSXMLMakeError(code, filename); + } +} + +NSError * RSXMLMakeError(RSXMLError code, NSString *filename) { + return [NSError errorWithDomain:kRSXMLParserErrorDomain code:code + userInfo:@{NSLocalizedDescriptionKey: getErrorMessageForRSXMLError(code, nil)}]; +} + +NSError * RSXMLMakeErrorFromLIBXMLError(xmlErrorPtr err) { + if (err) { + int errCode = err->code; + char * msg = err->message; + //if (err->level == XML_ERR_FATAL) + NSString *errMsg = [[NSString stringWithFormat:@"%s", msg] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + return [NSError errorWithDomain:kLIBXMLParserErrorDomain code:errCode userInfo:@{NSLocalizedDescriptionKey: errMsg}]; + } + return nil; } diff --git a/RSXMLTests/RSOPMLTests.m b/RSXMLTests/RSOPMLTests.m index de55599..19a34d0 100644 --- a/RSXMLTests/RSOPMLTests.m +++ b/RSXMLTests/RSOPMLTests.m @@ -36,6 +36,8 @@ RSXMLData *xmlData = [[RSXMLData alloc] initWithData:d urlString:@"http://example.org/"]; RSOPMLParser *parser = [[RSOPMLParser alloc] initWithXMLData:xmlData]; XCTAssertNotNil(parser.error); + XCTAssert(parser.error.code == RSXMLErrorFileNotOPML); + XCTAssert([parser.error.domain isEqualTo:kRSXMLParserErrorDomain]); d = [[NSData alloc] initWithContentsOfFile:@"/System/Library/Kernels/kernel"]; xmlData = [[RSXMLData alloc] initWithData:d urlString:@"/System/Library/Kernels/kernel"]; @@ -61,40 +63,37 @@ RSOPMLParser *parser = [[RSOPMLParser alloc] initWithXMLData:xmlData]; XCTAssertNotNil(parser); - RSOPMLDocument *document = parser.OPMLDocument; + RSOPMLItem *document = parser.opmlDocument; XCTAssertNotNil(document); - - [self checkStructureForOPMLItem:document]; + XCTAssert([document.displayName isEqualToString:@"Subs"]); + XCTAssert([document.children.firstObject.displayName isEqualToString:@"Daring Fireball"]); + XCTAssert([document.children.lastObject.displayName isEqualToString:@"Writers"]); + XCTAssert([document.children.lastObject.children.lastObject.displayName isEqualToString:@"Gerrold"]); + [self checkStructureForOPMLItem:document isRoot:YES]; + + //NSLog(@"\n%@", [document recursiveDescription]); } -- (void)checkStructureForOPMLItem:(RSOPMLItem *)item { +- (void)checkStructureForOPMLItem:(RSOPMLItem *)item isRoot:(BOOL)root { - RSOPMLFeedSpecifier *feedSpecifier = item.OPMLFeedSpecifier; - - if (![item isKindOfClass:[RSOPMLDocument class]]) { - XCTAssertNotNil(item.attributes.opml_text); + if (!root) { + XCTAssertNotNil([item attributeForKey:OPMLTextKey]); + XCTAssertNotNil([item attributeForKey:OPMLTitleKey]); } // If it has no children, it should have a feed specifier. The converse is also true. BOOL isFolder = (item.children.count > 0); - if (!isFolder && [item.attributes.opml_title isEqualToString:@"Skip"]) { + if (!isFolder && [[item attributeForKey:OPMLTitleKey] isEqualToString:@"Skip"]) { isFolder = YES; } if (!isFolder) { - XCTAssertNotNil(feedSpecifier.title); - XCTAssertNotNil(feedSpecifier.feedURL); - } - else { - XCTAssertNil(feedSpecifier); - if (![item isKindOfClass:[RSOPMLDocument class]]) { - XCTAssertNotNil(item.attributes.opml_title); - } + XCTAssertNotNil([item attributeForKey:OPMLHMTLURLKey]); } if (item.children.count > 0) { for (RSOPMLItem *oneItem in item.children) { - [self checkStructureForOPMLItem:oneItem]; + [self checkStructureForOPMLItem:oneItem isRoot:NO]; } } }