Added support for error messages & fixed bugs:

- parserClassForXMLData: did return instance instead of class
- DateParser: array index typo
This commit is contained in:
relikd
2018-12-10 17:59:01 +01:00
parent dd9ec4b63d
commit 10d37ec3f4
6 changed files with 73 additions and 16 deletions

View File

@@ -18,7 +18,7 @@
- (nonnull instancetype)initWithXMLData:(RSXMLData * _Nonnull)xmlData; - (nonnull instancetype)initWithXMLData:(RSXMLData * _Nonnull)xmlData;
- (nullable RSParsedFeed *)parseFeed:(NSError * _Nullable * _Nullable)error; - (nullable RSParsedFeed *)parseFeed;
@end @end

View File

@@ -106,7 +106,7 @@
#pragma mark - API #pragma mark - API
- (RSParsedFeed *)parseFeed:(NSError **)error { - (RSParsedFeed *)parseFeed {
[self parse]; [self parse];

View File

@@ -108,7 +108,7 @@ static NSInteger nextMonthValue(const char *bytes, NSUInteger numberOfBytes, NSU
return NSNotFound; return NSNotFound;
if (monthCharacters[0] == 'J' || monthCharacters[0] == 'j') { //Jan, Jun, Jul if (monthCharacters[0] == 'J' || monthCharacters[0] == 'j') { //Jan, Jun, Jul
if (monthCharacters[1] == 'a' || monthCharacters[i] == 'A') if (monthCharacters[1] == 'a' || monthCharacters[1] == 'A')
return RSJanuary; return RSJanuary;
if (monthCharacters[1] == 'u' || monthCharacters[1] == 'U') { if (monthCharacters[1] == 'u' || monthCharacters[1] == 'U') {
if (monthCharacters[2] == 'n' || monthCharacters[2] == 'N') if (monthCharacters[2] == 'n' || monthCharacters[2] == 'N')

View File

@@ -16,6 +16,9 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
static NSString *kLIBXMLParserErrorDomain = @"LIBXMLParserErrorDomain";
static NSString *kRSXMLParserErrorDomain = @"RSXMLParserErrorDomain";
BOOL RSCanParseFeed(RSXMLData *xmlData); BOOL RSCanParseFeed(RSXMLData *xmlData);

View File

@@ -6,6 +6,7 @@
// Copyright (c) 2015 Ranchero Software LLC. All rights reserved. // Copyright (c) 2015 Ranchero Software LLC. All rights reserved.
// //
#import <libxml/xmlerror.h>
#import "RSFeedParser.h" #import "RSFeedParser.h"
#import "FeedParser.h" #import "FeedParser.h"
#import "RSXMLData.h" #import "RSXMLData.h"
@@ -48,9 +49,43 @@ static BOOL dataHasLeftCaret(const char *bytes, NSUInteger numberOfBytes);
static const NSUInteger maxNumberOfBytesToSearch = 4096; static const NSUInteger maxNumberOfBytesToSearch = 4096;
static const NSUInteger minNumberOfBytesToSearch = 20; static const NSUInteger minNumberOfBytesToSearch = 20;
static Class parserClassForXMLData(RSXMLData *xmlData) { 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)) { if (!feedMayBeParseable(xmlData)) {
setError(error, RSXMLErrorNoData);
return nil; return nil;
} }
@@ -66,40 +101,42 @@ static Class parserClassForXMLData(RSXMLData *xmlData) {
} }
if (!dataHasLeftCaret(bytes, numberOfBytes)) { if (!dataHasLeftCaret(bytes, numberOfBytes)) {
setError(error, RSXMLErrorMissingLeftCaret);
return nil; return nil;
} }
if (optimisticCanParseRSSData(bytes, numberOfBytes)) { if (optimisticCanParseRSSData(bytes, numberOfBytes)) {
return [RSRSSParser class]; return [RSRSSParser class];
} }
if (optimisticCanParseAtomData(bytes, numberOfBytes)) { if (optimisticCanParseAtomData(bytes, numberOfBytes)) {
return [RSAtomParser class]; return [RSAtomParser class];
} }
if (optimisticCanParseRDF(bytes, numberOfBytes)) { if (optimisticCanParseRDF(bytes, numberOfBytes)) {
return nil; //TODO: parse RDF feeds return [RSRSSParser class]; //TODO: parse RDF feeds, using RSS parser so far ...
} }
if (dataIsProbablyHTML(bytes, numberOfBytes)) { if (dataIsProbablyHTML(bytes, numberOfBytes)) {
setError(error, RSXMLErrorProbablyHTML);
return nil; return nil;
} }
if (dataIsSomeWeirdException(bytes, numberOfBytes)) { if (dataIsSomeWeirdException(bytes, numberOfBytes)) {
setError(error, RSXMLErrorContainsXMLErrorsTag);
return nil; return nil;
} }
} }
for (Class parserClass in parserClasses()) { for (Class parserClass in parserClasses()) {
if ([parserClass canParseFeed:xmlData]) { if ([parserClass canParseFeed:xmlData]) {
return [[parserClass alloc] initWithXMLData:xmlData]; return parserClass;
//return [[parserClass alloc] initWithXMLData:xmlData]; // does not make sense to return instance
} }
} }
// Try RSS anyway? libxml would return a parsing error
setError(error, RSXMLErrorNoSuitableParser);
return nil; return nil;
} }
static id<FeedParser> parserForXMLData(RSXMLData *xmlData) { static id<FeedParser> parserForXMLData(RSXMLData *xmlData, NSError **error) {
Class parserClass = parserClassForXMLData(xmlData); Class parserClass = parserClassForXMLData(xmlData, error);
if (!parserClass) { if (!parserClass) {
return nil; return nil;
} }
@@ -108,7 +145,7 @@ static id<FeedParser> parserForXMLData(RSXMLData *xmlData) {
static BOOL canParseXMLData(RSXMLData *xmlData) { static BOOL canParseXMLData(RSXMLData *xmlData) {
return parserClassForXMLData(xmlData) != nil; return parserClassForXMLData(xmlData, nil) != nil;
} }
static BOOL didFindString(const char *string, const char *bytes, NSUInteger numberOfBytes) { static BOOL didFindString(const char *string, const char *bytes, NSUInteger numberOfBytes) {
@@ -210,7 +247,24 @@ void RSParseFeed(RSXMLData *xmlData, RSParsedFeedBlock callback) {
RSParsedFeed *RSParseFeedSync(RSXMLData *xmlData, NSError **error) { RSParsedFeed *RSParseFeedSync(RSXMLData *xmlData, NSError **error) {
id<FeedParser> parser = parserForXMLData(xmlData); xmlResetLastError();
return [parser parseFeed:error]; id<FeedParser> 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]);
xmlResetLastError();
}
return parsedResult;
} }

View File

@@ -101,7 +101,7 @@
#pragma mark - API #pragma mark - API
- (RSParsedFeed *)parseFeed:(NSError **)error { - (RSParsedFeed *)parseFeed {
[self parse]; [self parse];