Refactoring to v.2.0
This commit is contained in:
@@ -1,601 +1,253 @@
|
||||
//
|
||||
// RSAtomParser.m
|
||||
// RSXML
|
||||
// MIT License (MIT)
|
||||
//
|
||||
// Created by Brent Simmons on 1/15/15.
|
||||
// Copyright (c) 2015 Ranchero Software LLC. All rights reserved.
|
||||
// Copyright (c) 2016 Brent Simmons
|
||||
// Copyright (c) 2018 Oleg Geier
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#import <libxml/xmlstring.h>
|
||||
#import "RSAtomParser.h"
|
||||
#import "RSSAXParser.h"
|
||||
#import "FeedParser.h"
|
||||
#import "RSParsedFeed.h"
|
||||
#import "RSParsedArticle.h"
|
||||
#import "RSXMLData.h"
|
||||
#import "NSString+RSXML.h"
|
||||
#import "RSDateParser.h"
|
||||
|
||||
static NSString *kAlternateValue = @"alternate";
|
||||
static NSString *kRelatedValue = @"related";
|
||||
|
||||
@interface RSAtomParser () <RSSAXParserDelegate>
|
||||
|
||||
@property (nonatomic) NSData *feedData;
|
||||
@property (nonatomic) NSString *urlString;
|
||||
@property (nonatomic) BOOL endFeedFound;
|
||||
@property (nonatomic) BOOL parsingXHTML;
|
||||
@property (nonatomic) BOOL parsingSource;
|
||||
@property (nonatomic) BOOL parsingArticle;
|
||||
@property (nonatomic) BOOL parsingAuthor;
|
||||
@property (nonatomic) NSMutableArray *attributesStack;
|
||||
@property (nonatomic, readonly) NSDictionary *currentAttributes;
|
||||
@property (nonatomic, assign) BOOL endFeedFound;
|
||||
@property (nonatomic, assign) BOOL parsingXHTML;
|
||||
@property (nonatomic, assign) BOOL parsingSource;
|
||||
@property (nonatomic, assign) BOOL parsingArticle;
|
||||
@property (nonatomic, assign) BOOL parsingAuthor;
|
||||
@property (nonatomic) NSMutableString *xhtmlString;
|
||||
@property (nonatomic) NSString *feedLink;
|
||||
@property (nonatomic) NSString *feedTitle;
|
||||
@property (nonatomic) NSString *feedSubtitle;
|
||||
@property (nonatomic) NSMutableArray *articles;
|
||||
@property (nonatomic) NSDate *dateParsed;
|
||||
@property (nonatomic) RSSAXParser *parser;
|
||||
@property (nonatomic, readonly) RSParsedArticle *currentArticle;
|
||||
@property (nonatomic, readonly) NSDate *currentDate;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation RSAtomParser
|
||||
|
||||
#pragma mark - Class Methods
|
||||
#pragma mark - RSXMLParserDelegate
|
||||
|
||||
+ (BOOL)canParseFeed:(RSXMLData *)xmlData {
|
||||
|
||||
// Checking for '<feed' and '<entry' within first n characters should do it.
|
||||
|
||||
@autoreleasepool {
|
||||
|
||||
NSData *feedData = xmlData.data;
|
||||
|
||||
NSString *s = [[NSString alloc] initWithBytesNoCopy:(void *)feedData.bytes length:feedData.length encoding:NSUTF8StringEncoding freeWhenDone:NO];
|
||||
if (!s) {
|
||||
s = [[NSString alloc] initWithData:feedData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
if (!s) {
|
||||
s = [[NSString alloc] initWithData:feedData encoding:NSUnicodeStringEncoding];
|
||||
}
|
||||
if (!s) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
static const NSInteger numberOfCharactersToSearch = 4096;
|
||||
NSRange rangeToSearch = NSMakeRange(0, numberOfCharactersToSearch);
|
||||
if (s.length < numberOfCharactersToSearch) {
|
||||
rangeToSearch.length = s.length;
|
||||
}
|
||||
|
||||
NSRange feedRange = [s rangeOfString:@"<feed" options:NSLiteralSearch range:rangeToSearch];
|
||||
NSRange entryRange = [s rangeOfString:@"<entry" options:NSLiteralSearch range:rangeToSearch];
|
||||
if (feedRange.length < 1 || entryRange.length < 1) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (feedRange.location > entryRange.location) {
|
||||
return NO; // Wrong order.
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
+ (NSArray<const NSString *> *)parserRequireOrderedTags {
|
||||
return @[@"<feed", @"<entry"];
|
||||
}
|
||||
|
||||
#pragma mark - Helper
|
||||
|
||||
#pragma mark - Init
|
||||
- (void)setFeedOrArticleLink:(NSDictionary*)attribs {
|
||||
|
||||
- (instancetype)initWithXMLData:(RSXMLData *)xmlData {
|
||||
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_feedData = xmlData.data;
|
||||
_urlString = xmlData.urlString;
|
||||
_parser = [[RSSAXParser alloc] initWithDelegate:self];
|
||||
_attributesStack = [NSMutableArray new];
|
||||
_articles = [NSMutableArray new];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - API
|
||||
|
||||
- (RSParsedFeed *)parseFeed {
|
||||
|
||||
[self parse];
|
||||
|
||||
RSParsedFeed *parsedFeed = [[RSParsedFeed alloc] initWithURLString:self.urlString title:self.feedTitle link:self.feedLink articles:self.articles];
|
||||
parsedFeed.subtitle = self.feedSubtitle;
|
||||
|
||||
return parsedFeed;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Constants
|
||||
|
||||
static NSString *kTypeKey = @"type";
|
||||
static NSString *kXHTMLType = @"xhtml";
|
||||
static NSString *kRelKey = @"rel";
|
||||
static NSString *kAlternateValue = @"alternate";
|
||||
static NSString *kHrefKey = @"href";
|
||||
static NSString *kXMLKey = @"xml";
|
||||
static NSString *kBaseKey = @"base";
|
||||
static NSString *kLangKey = @"lang";
|
||||
static NSString *kXMLBaseKey = @"xml:base";
|
||||
static NSString *kXMLLangKey = @"xml:lang";
|
||||
static NSString *kTextHTMLValue = @"text/html";
|
||||
static NSString *kRelatedValue = @"related";
|
||||
static NSString *kShortURLValue = @"shorturl";
|
||||
static NSString *kHTMLValue = @"html";
|
||||
static NSString *kEnValue = @"en";
|
||||
static NSString *kTextValue = @"text";
|
||||
static NSString *kSelfValue = @"self";
|
||||
|
||||
static const char *kID = "id";
|
||||
static const NSInteger kIDLength = 3;
|
||||
|
||||
static const char *kTitle = "title";
|
||||
static const NSInteger kTitleLength = 6;
|
||||
|
||||
static const char *kSubtitle = "subtitle";
|
||||
static const NSInteger kSubtitleLength = 9;
|
||||
|
||||
static const char *kContent = "content";
|
||||
static const NSInteger kContentLength = 8;
|
||||
|
||||
static const char *kSummary = "summary";
|
||||
static const NSInteger kSummaryLength = 8;
|
||||
|
||||
static const char *kLink = "link";
|
||||
static const NSInteger kLinkLength = 5;
|
||||
|
||||
static const char *kPublished = "published";
|
||||
static const NSInteger kPublishedLength = 10;
|
||||
|
||||
static const char *kUpdated = "updated";
|
||||
static const NSInteger kUpdatedLength = 8;
|
||||
|
||||
static const char *kAuthor = "author";
|
||||
static const NSInteger kAuthorLength = 7;
|
||||
|
||||
static const char *kEntry = "entry";
|
||||
static const NSInteger kEntryLength = 6;
|
||||
|
||||
static const char *kSource = "source";
|
||||
static const NSInteger kSourceLength = 7;
|
||||
|
||||
static const char *kFeed = "feed";
|
||||
static const NSInteger kFeedLength = 5;
|
||||
|
||||
static const char *kType = "type";
|
||||
static const NSInteger kTypeLength = 5;
|
||||
|
||||
static const char *kRel = "rel";
|
||||
static const NSInteger kRelLength = 4;
|
||||
|
||||
static const char *kAlternate = "alternate";
|
||||
static const NSInteger kAlternateLength = 10;
|
||||
|
||||
static const char *kHref = "href";
|
||||
static const NSInteger kHrefLength = 5;
|
||||
|
||||
static const char *kXML = "xml";
|
||||
static const NSInteger kXMLLength = 4;
|
||||
|
||||
static const char *kBase = "base";
|
||||
static const NSInteger kBaseLength = 5;
|
||||
|
||||
static const char *kLang = "lang";
|
||||
static const NSInteger kLangLength = 5;
|
||||
|
||||
static const char *kTextHTML = "text/html";
|
||||
static const NSInteger kTextHTMLLength = 10;
|
||||
|
||||
static const char *kRelated = "related";
|
||||
static const NSInteger kRelatedLength = 8;
|
||||
|
||||
static const char *kShortURL = "shorturl";
|
||||
static const NSInteger kShortURLLength = 9;
|
||||
|
||||
static const char *kHTML = "html";
|
||||
static const NSInteger kHTMLLength = 5;
|
||||
|
||||
static const char *kEn = "en";
|
||||
static const NSInteger kEnLength = 3;
|
||||
|
||||
static const char *kText = "text";
|
||||
static const NSInteger kTextLength = 5;
|
||||
|
||||
static const char *kSelf = "self";
|
||||
static const NSInteger kSelfLength = 5;
|
||||
|
||||
|
||||
#pragma mark - Parsing
|
||||
|
||||
- (void)parse {
|
||||
|
||||
self.dateParsed = [NSDate date];
|
||||
|
||||
@autoreleasepool {
|
||||
[self.parser parseData:self.feedData];
|
||||
[self.parser finishParsing];
|
||||
}
|
||||
|
||||
// Optimization: make articles do calculations on this background thread.
|
||||
[self.articles makeObjectsPerformSelector:@selector(calculateArticleID)];
|
||||
}
|
||||
|
||||
|
||||
- (void)addArticle {
|
||||
|
||||
RSParsedArticle *article = [[RSParsedArticle alloc] initWithFeedURL:self.urlString];
|
||||
article.dateParsed = self.dateParsed;
|
||||
|
||||
[self.articles addObject:article];
|
||||
}
|
||||
|
||||
|
||||
- (RSParsedArticle *)currentArticle {
|
||||
|
||||
return self.articles.lastObject;
|
||||
}
|
||||
|
||||
|
||||
- (NSDictionary *)currentAttributes {
|
||||
|
||||
return self.attributesStack.lastObject;
|
||||
}
|
||||
|
||||
|
||||
- (NSDate *)currentDate {
|
||||
|
||||
return RSDateWithBytes(self.parser.currentCharacters.bytes, self.parser.currentCharacters.length);
|
||||
}
|
||||
|
||||
|
||||
- (void)addFeedLink {
|
||||
|
||||
if (self.feedLink && self.feedLink.length > 0) {
|
||||
NSString *urlString = attribs[@"href"];
|
||||
if (urlString.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *related = self.currentAttributes[kRelKey];
|
||||
if (related == kAlternateValue) {
|
||||
self.feedLink = self.currentAttributes[kHrefKey];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)addFeedTitle {
|
||||
|
||||
if (self.feedTitle.length < 1) {
|
||||
self.feedTitle = self.parser.currentStringWithTrimmedWhitespace;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addFeedSubtitle {
|
||||
|
||||
if (self.feedSubtitle.length < 1) {
|
||||
self.feedSubtitle = self.parser.currentStringWithTrimmedWhitespace;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addLink {
|
||||
|
||||
NSString *urlString = self.currentAttributes[kHrefKey];
|
||||
if (urlString.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *rel = self.currentAttributes[kRelKey];
|
||||
if (rel.length < 1) {
|
||||
NSString *rel = attribs[@"rel"];
|
||||
if (rel.length == 0) {
|
||||
rel = kAlternateValue;
|
||||
}
|
||||
|
||||
if (rel == kAlternateValue) {
|
||||
if (!self.currentArticle.link) {
|
||||
self.currentArticle.link = urlString;
|
||||
if (!self.parsingArticle) { // Feed
|
||||
if (!self.parsedFeed.link && rel == kAlternateValue) {
|
||||
self.parsedFeed.link = urlString;
|
||||
}
|
||||
}
|
||||
else if (rel == kRelatedValue) {
|
||||
if (!self.currentArticle.permalink) {
|
||||
else if (!self.parsingSource) { // Article
|
||||
if (!self.currentArticle.link && rel == kAlternateValue) {
|
||||
self.currentArticle.link = urlString;
|
||||
}
|
||||
else if (!self.currentArticle.permalink && rel == kRelatedValue) {
|
||||
self.currentArticle.permalink = urlString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)addContent {
|
||||
|
||||
self.currentArticle.body = [self currentStringWithHTMLEntitiesDecoded];
|
||||
}
|
||||
#pragma mark - Parse XHTML
|
||||
|
||||
|
||||
- (void)addSummary {
|
||||
|
||||
self.currentArticle.abstract = [self currentStringWithHTMLEntitiesDecoded];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)currentStringWithHTMLEntitiesDecoded {
|
||||
|
||||
return [self.parser.currentStringWithTrimmedWhitespace rs_stringByDecodingHTMLEntities];
|
||||
}
|
||||
|
||||
|
||||
- (void)addArticleElement:(const xmlChar *)localName prefix:(const xmlChar *)prefix {
|
||||
|
||||
if (prefix) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (RSSAXEqualTags(localName, kID, kIDLength)) {
|
||||
self.currentArticle.guid = self.parser.currentStringWithTrimmedWhitespace;
|
||||
}
|
||||
|
||||
else if (RSSAXEqualTags(localName, kTitle, kTitleLength)) {
|
||||
self.currentArticle.title = [self currentStringWithHTMLEntitiesDecoded];
|
||||
}
|
||||
|
||||
else if (RSSAXEqualTags(localName, kContent, kContentLength)) {
|
||||
[self addContent];
|
||||
}
|
||||
|
||||
else if (RSSAXEqualTags(localName, kSummary, kSummaryLength)) {
|
||||
[self addSummary];
|
||||
}
|
||||
|
||||
else if (RSSAXEqualTags(localName, kLink, kLinkLength)) {
|
||||
[self addLink];
|
||||
}
|
||||
|
||||
else if (RSSAXEqualTags(localName, kPublished, kPublishedLength)) {
|
||||
self.currentArticle.datePublished = self.currentDate;
|
||||
}
|
||||
|
||||
else if (RSSAXEqualTags(localName, kUpdated, kUpdatedLength)) {
|
||||
self.currentArticle.dateModified = self.currentDate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)addXHTMLTag:(const xmlChar *)localName {
|
||||
- (void)addXHTMLTag:(const xmlChar *)localName attributes:(NSDictionary*)attribs {
|
||||
|
||||
if (!localName) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self.xhtmlString appendString:@"<"];
|
||||
[self.xhtmlString appendString:[NSString stringWithUTF8String:(const char *)localName]];
|
||||
[self.xhtmlString appendFormat:@"<%s", localName];
|
||||
|
||||
if (self.currentAttributes.count < 1) {
|
||||
[self.xhtmlString appendString:@">"];
|
||||
return;
|
||||
}
|
||||
|
||||
for (NSString *oneKey in self.currentAttributes) {
|
||||
|
||||
[self.xhtmlString appendString:@" "];
|
||||
|
||||
NSString *oneValue = self.currentAttributes[oneKey];
|
||||
[self.xhtmlString appendString:oneKey];
|
||||
|
||||
[self.xhtmlString appendString:@"=\""];
|
||||
|
||||
oneValue = [oneValue stringByReplacingOccurrencesOfString:@"\"" withString:@"""];
|
||||
[self.xhtmlString appendString:oneValue];
|
||||
|
||||
[self.xhtmlString appendString:@"\""];
|
||||
for (NSString *key in attribs) {
|
||||
NSString *val = [attribs[key] stringByReplacingOccurrencesOfString:@"\"" withString:@"""];
|
||||
[self.xhtmlString appendFormat:@" %@=\"%@\"", key, val];
|
||||
}
|
||||
|
||||
[self.xhtmlString appendString:@">"];
|
||||
}
|
||||
|
||||
- (void)parseXHTMLEndElement:(const xmlChar *)localName length:(int)len {
|
||||
if (len == 7) {
|
||||
if (EqualBytes(localName, "content", 7)) {
|
||||
if (self.parsingArticle) {
|
||||
self.currentArticle.body = [self.xhtmlString copy];
|
||||
}
|
||||
self.parsingXHTML = NO;
|
||||
}
|
||||
else if (EqualBytes(localName, "summary", 7)) {
|
||||
if (self.parsingArticle) {
|
||||
self.currentArticle.abstract = [self.xhtmlString copy];
|
||||
}
|
||||
self.parsingXHTML = NO;
|
||||
}
|
||||
}
|
||||
[self.xhtmlString appendFormat:@"</%s>", localName];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - RSSAXParserDelegate
|
||||
|
||||
|
||||
- (void)saxParser:(RSSAXParser *)SAXParser XMLStartElement:(const xmlChar *)localName prefix:(const xmlChar *)prefix uri:(const xmlChar *)uri numberOfNamespaces:(NSInteger)numberOfNamespaces namespaces:(const xmlChar **)namespaces numberOfAttributes:(NSInteger)numberOfAttributes numberDefaulted:(int)numberDefaulted attributes:(const xmlChar **)attributes {
|
||||
|
||||
if (self.endFeedFound) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *xmlAttributes = [self.parser attributesDictionary:attributes numberOfAttributes:numberOfAttributes];
|
||||
if (!xmlAttributes) {
|
||||
xmlAttributes = [NSDictionary dictionary];
|
||||
}
|
||||
[self.attributesStack addObject:xmlAttributes];
|
||||
|
||||
if (self.parsingXHTML) {
|
||||
[self addXHTMLTag:localName];
|
||||
NSDictionary *attribs = [SAXParser attributesDictionary:attributes numberOfAttributes:numberOfAttributes];
|
||||
[self addXHTMLTag:localName attributes:attribs];
|
||||
return;
|
||||
}
|
||||
|
||||
if (RSSAXEqualTags(localName, kEntry, kEntryLength)) {
|
||||
self.parsingArticle = YES;
|
||||
[self addArticle];
|
||||
return;
|
||||
|
||||
int len = xmlStrlen(localName);
|
||||
switch (len) {
|
||||
case 4:
|
||||
if (EqualBytes(localName, "link", 4)) {
|
||||
NSDictionary *attribs = [SAXParser attributesDictionary:attributes numberOfAttributes:numberOfAttributes];
|
||||
[self setFeedOrArticleLink:attribs];
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (EqualBytes(localName, "entry", 5)) {
|
||||
self.parsingArticle = YES;
|
||||
self.currentArticle = [self.parsedFeed appendNewArticle];
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if (EqualBytes(localName, "author", 6)) {
|
||||
self.parsingAuthor = YES;
|
||||
return;
|
||||
} else if (EqualBytes(localName, "source", 6)) {
|
||||
self.parsingSource = YES;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 7: // uses attrib
|
||||
if (self.parsingArticle) {
|
||||
break;
|
||||
}
|
||||
if (!EqualBytes(localName, "content", 7) && !EqualBytes(localName, "summary", 7)) {
|
||||
break;
|
||||
}
|
||||
NSDictionary *attribs = [SAXParser attributesDictionary:attributes numberOfAttributes:numberOfAttributes];
|
||||
if ([attribs[@"type"] isEqualToString:@"xhtml"]) {
|
||||
self.parsingXHTML = YES;
|
||||
self.xhtmlString = [NSMutableString stringWithString:@""];
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (RSSAXEqualTags(localName, kAuthor, kAuthorLength)) {
|
||||
self.parsingAuthor = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
if (RSSAXEqualTags(localName, kSource, kSourceLength)) {
|
||||
self.parsingSource = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL isContentTag = RSSAXEqualTags(localName, kContent, kContentLength);
|
||||
BOOL isSummaryTag = RSSAXEqualTags(localName, kSummary, kSummaryLength);
|
||||
if (self.parsingArticle && (isContentTag || isSummaryTag)) {
|
||||
|
||||
NSString *contentType = xmlAttributes[kTypeKey];
|
||||
if ([contentType isEqualToString:kXHTMLType]) {
|
||||
self.parsingXHTML = YES;
|
||||
self.xhtmlString = [NSMutableString stringWithString:@""];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!self.parsingArticle && RSSAXEqualTags(localName, kLink, kLinkLength)) {
|
||||
[self addFeedLink];
|
||||
return;
|
||||
}
|
||||
|
||||
[self.parser beginStoringCharacters];
|
||||
[SAXParser beginStoringCharacters];
|
||||
}
|
||||
|
||||
|
||||
- (void)saxParser:(RSSAXParser *)SAXParser XMLEndElement:(const xmlChar *)localName prefix:(const xmlChar *)prefix uri:(const xmlChar *)uri {
|
||||
|
||||
if (RSSAXEqualTags(localName, kFeed, kFeedLength)) {
|
||||
self.endFeedFound = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.endFeedFound) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.parsingXHTML) {
|
||||
|
||||
BOOL isContentTag = RSSAXEqualTags(localName, kContent, kContentLength);
|
||||
BOOL isSummaryTag = RSSAXEqualTags(localName, kSummary, kSummaryLength);
|
||||
|
||||
if (self.parsingArticle) {
|
||||
if (isContentTag) {
|
||||
self.currentArticle.body = [self.xhtmlString copy];
|
||||
}
|
||||
else if (isSummaryTag) {
|
||||
self.currentArticle.abstract = [self.xhtmlString copy];
|
||||
}
|
||||
}
|
||||
|
||||
if (isContentTag || isSummaryTag) {
|
||||
self.parsingXHTML = NO;
|
||||
}
|
||||
|
||||
[self.xhtmlString appendString:@"</"];
|
||||
[self.xhtmlString appendString:[NSString stringWithUTF8String:(const char *)localName]];
|
||||
[self.xhtmlString appendString:@">"];
|
||||
}
|
||||
|
||||
else if (RSSAXEqualTags(localName, kAuthor, kAuthorLength)) {
|
||||
self.parsingAuthor = NO;
|
||||
}
|
||||
|
||||
else if (RSSAXEqualTags(localName, kEntry, kEntryLength)) {
|
||||
self.parsingArticle = NO;
|
||||
}
|
||||
|
||||
else if (self.parsingArticle && !self.parsingSource) {
|
||||
[self addArticleElement:localName prefix:prefix];
|
||||
}
|
||||
int len = xmlStrlen(localName);
|
||||
|
||||
else if (RSSAXEqualTags(localName, kSource, kSourceLength)) {
|
||||
self.parsingSource = NO;
|
||||
if (len == 4 && EqualBytes(localName, "feed", 4)) {
|
||||
self.endFeedFound = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
else if (!self.parsingArticle && !self.parsingSource) {
|
||||
if (RSSAXEqualTags(localName, kTitle, kTitleLength)) {
|
||||
[self addFeedTitle];
|
||||
}
|
||||
else if (RSSAXEqualTags(localName, kSubtitle, kSubtitleLength)) {
|
||||
[self addFeedSubtitle];
|
||||
}
|
||||
}
|
||||
[self.attributesStack removeLastObject];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForName:(const xmlChar *)name prefix:(const xmlChar *)prefix {
|
||||
|
||||
if (prefix && RSSAXEqualTags(prefix, kXML, kXMLLength)) {
|
||||
|
||||
if (RSSAXEqualTags(name, kBase, kBaseLength)) {
|
||||
return kXMLBaseKey;
|
||||
}
|
||||
if (RSSAXEqualTags(name, kLang, kLangLength)) {
|
||||
return kXMLLangKey;
|
||||
}
|
||||
if (self.parsingXHTML) {
|
||||
[self parseXHTMLEndElement:localName length:len];
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
return nil;
|
||||
BOOL isArticle = (self.parsingArticle && !self.parsingSource && !prefix);
|
||||
|
||||
switch (len) {
|
||||
case 2:
|
||||
if (isArticle && EqualBytes(localName, "id", 2)) {
|
||||
self.currentArticle.guid = SAXParser.currentStringWithTrimmedWhitespace;
|
||||
}
|
||||
return;
|
||||
case 5:
|
||||
if (EqualBytes(localName, "entry", 5)) {
|
||||
self.parsingArticle = NO;
|
||||
}
|
||||
else if (isArticle && EqualBytes(localName, "title", 5)) {
|
||||
self.currentArticle.title = [self decodeHTMLEntities:SAXParser.currentStringWithTrimmedWhitespace];
|
||||
}
|
||||
else if (!self.parsingArticle && !self.parsingSource && self.parsedFeed.title.length == 0) {
|
||||
if (EqualBytes(localName, "title", 5)) {
|
||||
self.parsedFeed.title = SAXParser.currentStringWithTrimmedWhitespace;
|
||||
}
|
||||
}
|
||||
return;
|
||||
case 6:
|
||||
if (EqualBytes(localName, "author", 6)) {
|
||||
self.parsingAuthor = NO;
|
||||
}
|
||||
else if (EqualBytes(localName, "source", 6)) {
|
||||
self.parsingSource = NO;
|
||||
}
|
||||
return;
|
||||
case 8:
|
||||
if (!self.parsingArticle && !self.parsingSource && self.parsedFeed.subtitle.length == 0) {
|
||||
if (EqualBytes(localName, "subtitle", 8)) {
|
||||
self.parsedFeed.subtitle = SAXParser.currentStringWithTrimmedWhitespace;
|
||||
}
|
||||
}
|
||||
return;
|
||||
case 7:
|
||||
if (isArticle) {
|
||||
if (EqualBytes(localName, "content", 7)) {
|
||||
self.currentArticle.body = [self decodeHTMLEntities:SAXParser.currentStringWithTrimmedWhitespace];
|
||||
}
|
||||
else if (EqualBytes(localName, "summary", 7)) {
|
||||
self.currentArticle.abstract = [self decodeHTMLEntities:SAXParser.currentStringWithTrimmedWhitespace];
|
||||
}
|
||||
else if (EqualBytes(localName, "updated", 7)) {
|
||||
self.currentArticle.dateModified = [self dateFromCharacters:SAXParser.currentCharacters];
|
||||
}
|
||||
}
|
||||
return;
|
||||
case 9:
|
||||
if (isArticle && EqualBytes(localName, "published", 9)) {
|
||||
self.currentArticle.datePublished = [self dateFromCharacters:SAXParser.currentCharacters];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (RSSAXEqualTags(name, kRel, kRelLength)) {
|
||||
return kRelKey;
|
||||
}
|
||||
|
||||
if (RSSAXEqualTags(name, kType, kTypeLength)) {
|
||||
return kTypeKey;
|
||||
}
|
||||
|
||||
if (RSSAXEqualTags(name, kHref, kHrefLength)) {
|
||||
return kHrefKey;
|
||||
}
|
||||
|
||||
if (RSSAXEqualTags(name, kAlternate, kAlternateLength)) {
|
||||
return kAlternateValue;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForValue:(const void *)bytes length:(NSUInteger)length {
|
||||
|
||||
static const NSUInteger alternateLength = kAlternateLength - 1;
|
||||
static const NSUInteger textHTMLLength = kTextHTMLLength - 1;
|
||||
static const NSUInteger relatedLength = kRelatedLength - 1;
|
||||
static const NSUInteger shortURLLength = kShortURLLength - 1;
|
||||
static const NSUInteger htmlLength = kHTMLLength - 1;
|
||||
static const NSUInteger enLength = kEnLength - 1;
|
||||
static const NSUInteger textLength = kTextLength - 1;
|
||||
static const NSUInteger selfLength = kSelfLength - 1;
|
||||
|
||||
if (length == alternateLength && RSSAXEqualBytes(bytes, kAlternate, alternateLength)) {
|
||||
return kAlternateValue;
|
||||
}
|
||||
|
||||
if (length == textHTMLLength && RSSAXEqualBytes(bytes, kTextHTML, textHTMLLength)) {
|
||||
return kTextHTMLValue;
|
||||
}
|
||||
|
||||
if (length == relatedLength && RSSAXEqualBytes(bytes, kRelated, relatedLength)) {
|
||||
return kRelatedValue;
|
||||
}
|
||||
|
||||
if (length == shortURLLength && RSSAXEqualBytes(bytes, kShortURL, shortURLLength)) {
|
||||
return kShortURLValue;
|
||||
}
|
||||
|
||||
if (length == htmlLength && RSSAXEqualBytes(bytes, kHTML, htmlLength)) {
|
||||
return kHTMLValue;
|
||||
}
|
||||
|
||||
if (length == enLength && RSSAXEqualBytes(bytes, kEn, enLength)) {
|
||||
return kEnValue;
|
||||
}
|
||||
|
||||
if (length == textLength && RSSAXEqualBytes(bytes, kText, textLength)) {
|
||||
return kTextValue;
|
||||
}
|
||||
|
||||
if (length == selfLength && RSSAXEqualBytes(bytes, kSelf, selfLength)) {
|
||||
return kSelfValue;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -606,4 +258,60 @@ static const NSInteger kSelfLength = 5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForName:(const xmlChar *)name prefix:(const xmlChar *)prefix {
|
||||
|
||||
int len = xmlStrlen(name);
|
||||
|
||||
if (prefix) {
|
||||
if (len == 4 && EqualBytes(prefix, "xml", 3)) { // len == 4 is for the next two lines already
|
||||
if (EqualBytes(name, "base", 4)) { return @"xml:base"; }
|
||||
if (EqualBytes(name, "lang", 4)) { return @"xml:lang"; }
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
switch (len) {
|
||||
case 3:
|
||||
if (EqualBytes(name, "rel", 3)) { return @"rel"; }
|
||||
break;
|
||||
case 4:
|
||||
if (EqualBytes(name, "type", 4)) { return @"type"; }
|
||||
if (EqualBytes(name, "href", 4)) { return @"href"; }
|
||||
break;
|
||||
case 9:
|
||||
if (EqualBytes(name, "alternate", 9)) { return kAlternateValue; }
|
||||
break;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForValue:(const void *)bytes length:(NSUInteger)length {
|
||||
|
||||
switch (length) {
|
||||
case 2:
|
||||
if (EqualBytes(bytes, "en", 2)) { return @"en"; }
|
||||
break;
|
||||
case 4:
|
||||
if (EqualBytes(bytes, "html", 4)) { return @"html"; }
|
||||
if (EqualBytes(bytes, "text", 4)) { return @"text"; }
|
||||
if (EqualBytes(bytes, "self", 4)) { return @"self"; }
|
||||
break;
|
||||
case 7:
|
||||
if (EqualBytes(bytes, "related", 7)) { return kRelatedValue; }
|
||||
break;
|
||||
case 8:
|
||||
if (EqualBytes(bytes, "shorturl", 8)) { return @"shorturl"; }
|
||||
break;
|
||||
case 9:
|
||||
if (EqualBytes(bytes, "alternate", 9)) { return kAlternateValue; }
|
||||
if (EqualBytes(bytes, "text/html", 9)) { return @"text/html"; }
|
||||
break;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user