v2.0.0 release; move all docref to header + optional NSXML

This commit is contained in:
relikd
2019-09-14 14:58:11 +02:00
parent ea7cc9c36f
commit 631ddc9064
25 changed files with 108 additions and 83 deletions

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>2.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@@ -27,6 +27,7 @@
// <feed> <entry>
// https://validator.w3.org/feed/docs/rfc4287.html
/// Feed parser for Atom xml feeds. Expects the tags @c <feed> and @c <entry> to be existent.
@interface RSAtomParser : RSFeedParser
@end

View File

@@ -26,10 +26,13 @@
@class RSParsedFeed, RSParsedArticle;
/// Generic feed parser. Used for atom, RSS, and RDF feeds.
@interface RSFeedParser : RSXMLParser<RSParsedFeed*>
@property (nonatomic, readonly) RSParsedFeed *parsedFeed;
@property (nonatomic, weak) RSParsedArticle *currentArticle;
/// @return @c NSDate by parsing RFC 822 and 8601 date strings.
- (NSDate *)dateFromCharacters:(NSData *)data;
/// @return currentString by removing HTML encoded entities.
- (NSString *)decodeHTMLEntities:(NSString *)str;
@end

View File

@@ -45,12 +45,12 @@
return _parsedFeed;
}
/// @return @c NSDate by parsing RFC 822 and 8601 date strings.
// docref in header
- (NSDate *)dateFromCharacters:(NSData *)data {
return RSDateWithBytes(data.bytes, data.length);
}
/// @return currentString by removing HTML encoded entities.
// docref in header
- (NSString *)decodeHTMLEntities:(NSString *)str {
return [str rsxml_stringByDecodingHTMLEntities];
}

View File

@@ -27,5 +27,6 @@
@class RSHTMLMetadataAnchor;
/// HTML parser for html header metadata. Used to extract generic anchor tags.
@interface RSHTMLLinkParser : RSXMLParser<NSArray<RSHTMLMetadataAnchor*>*>
@end

View File

@@ -36,6 +36,7 @@ RSFeedType RSFeedTypeFromLinkTypeAttribute(NSString * typeStr);
@class RSHTMLMetadataIconLink, RSHTMLMetadataFeedLink;
/// Parsed result type for HTML metadata.
@interface RSHTMLMetadata : NSObject
@property (nonatomic, copy, nullable) NSString *faviconLink;
@property (nonatomic, nonnull) NSArray <RSHTMLMetadataIconLink *> *iconLinks;
@@ -51,6 +52,7 @@ RSFeedType RSFeedTypeFromLinkTypeAttribute(NSString * typeStr);
@interface RSHTMLMetadataIconLink : RSHTMLMetadataLink
@property (nonatomic, copy, nullable) NSString *sizes;
/// Parses size on the fly. Expects the following format: @c "{int}x{int}" . Returns @c CGSizeZero otherwise.
- (CGSize)getSize;
@end

View File

@@ -44,6 +44,7 @@ RSFeedType RSFeedTypeFromLinkTypeAttribute(NSString * typeStr) {
@implementation RSHTMLMetadataIconLink
// docref in header
- (CGSize)getSize {
if (self.sizes && self.sizes.length > 0) {
NSArray<NSString*> *parts = [self.sizes componentsSeparatedByString:@"x"];

View File

@@ -27,6 +27,7 @@
@class RSHTMLMetadata;
/// HTML parser for html header metadata. Used to extract feed and favicon URLs.
@interface RSHTMLMetadataParser : RSXMLParser<RSHTMLMetadata*>
@end

View File

@@ -23,32 +23,44 @@
@import Foundation;
#ifndef TARGET_IOS
#define OPML_EXPORT 0
#endif
// 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
/** Constant: @c \@"text" */ extern NSString *OPMLTextKey;
/** Constant: @c \@"title" */ extern NSString *OPMLTitleKey;
/** Constant: @c \@"description" */ extern NSString *OPMLDescriptionKey;
/** Constant: @c \@"type" */ extern NSString *OPMLTypeKey;
/** Constant: @c \@"version" */ extern NSString *OPMLVersionKey;
/** Constant: @c \@"htmlUrl" */ extern NSString *OPMLHMTLURLKey;
/** Constant: @c \@"xmlUrl" */ extern NSString *OPMLXMLURLKey;
/// Parsed result type for opml files. @c children can be arbitrary nested.
@interface RSOPMLItem : NSObject
/// Can be arbitrary nested.
@property (nonatomic) NSArray<RSOPMLItem*> *children;
@property (nonatomic) NSDictionary *attributes;
@property (nonatomic, readonly) BOOL isFolder; // true if children.count > 0
@property (nonatomic, readonly) NSString *displayName; //May be nil.
/// Returns @c YES if @c children.count @c > @c 0
@property (nonatomic, readonly) BOOL isFolder;
@property (nonatomic, readonly, nullable) NSString *displayName;
+ (instancetype)itemWithAttributes:(NSDictionary *)attribs;
/// Appends one child to the internal children array (creates new empty array if necessary).
- (void)addChild:(RSOPMLItem *)child;
/// Sets a value in the internal dictionary (creates new empty dictionary if necessary).
- (void)setAttribute:(id)value forKey:(NSString *)key;
/// @return Value for key (case-independent).
- (id)attributeForKey:(NSString *)key;
/// Print object description for debugging purposes.
- (NSString *)recursiveDescription;
#ifdef TARGET_MAC
#if OPML_EXPORT
/// Can be used to export directly to @c .opml file.
- (NSXMLDocument *)exportXML;
#endif
@end

View File

@@ -74,7 +74,7 @@ NSString *OPMLXMLURLKey = @"xmlUrl";
}
/// @return Value for @c OPMLTitleKey. If not set, use @c OPMLTextKey, else return @c nil.
- (NSString *)displayName {
- (nullable NSString *)displayName {
NSString *title = [self attributeForKey:OPMLTitleKey];
if (!title) {
title = [self attributeForKey:OPMLTextKey];
@@ -82,7 +82,7 @@ NSString *OPMLXMLURLKey = @"xmlUrl";
return title;
}
/// Appends one child to the internal children array (creates new empty array if necessary).
// docref in header
- (void)addChild:(RSOPMLItem *)child {
if (!self.mutableChildren) {
self.mutableChildren = [NSMutableArray new];
@@ -90,7 +90,7 @@ NSString *OPMLXMLURLKey = @"xmlUrl";
[self.mutableChildren addObject:child];
}
/// Sets a value in the internal dictionary (creates new empty dictionary if necessary).
// docref in header
- (void)setAttribute:(id)value forKey:(NSString *)key {
if (!self.mutableAttributes) {
self.mutableAttributes = [NSMutableDictionary new];
@@ -98,7 +98,7 @@ NSString *OPMLXMLURLKey = @"xmlUrl";
[self.mutableAttributes setValue:value forKey:key];
}
/// @return Value for key (case-independent).
// docref in header
- (id)attributeForKey:(NSString *)key {
if (self.mutableAttributes.count > 0 && key && key.length > 0) {
return [self.mutableAttributes rsxml_objectForCaseInsensitiveKey:key];
@@ -128,16 +128,16 @@ NSString *OPMLXMLURLKey = @"xmlUrl";
}
}
/// Print object description for debugging purposes.
// docref in header
- (NSString *)recursiveDescription {
NSMutableString *mStr = [NSMutableString new];
[self appendStringRecursive:mStr indent:@""];
return mStr;
}
#ifdef TARGET_MAC
#if OPML_EXPORT
/// Can be used to export directly to @c .opml file.
// docref in header
- (NSXMLDocument *)exportXML {
NSXMLElement *head = [NSXMLElement elementWithName:@"head"];
for (NSString *key in _mutableAttributes) {

View File

@@ -29,6 +29,7 @@
@class RSOPMLItem;
/// OPML parser for structured opml files. Expects the tags @c <opml> and @c <outline> to be existent.
@interface RSOPMLParser: RSXMLParser<RSOPMLItem*>
@end

View File

@@ -24,14 +24,12 @@
@import Foundation;
/// Parsed result type for articles. Does contain article specific attributes like abstract and content.
@interface RSParsedArticle : NSObject
- (nonnull instancetype)initWithFeedURL:(NSURL * _Nonnull)feedURL dateParsed:(NSDate*)parsed;
@property (nonatomic, readonly, nonnull) NSURL *feedURL;
@property (nonatomic, readonly, nonnull) NSDate *dateParsed;
@property (nonatomic, readonly, nonnull) NSString *articleID; //Calculated. Don't get until other properties have been set.
/// Calculated. Don't get until other properties have been set.
@property (nonatomic, readonly, nonnull) NSString *articleID;
@property (nonatomic, nullable) NSString *guid;
@property (nonatomic, nullable) NSString *title;
@@ -43,7 +41,9 @@
@property (nonatomic, nullable) NSDate *datePublished;
@property (nonatomic, nullable) NSDate *dateModified;
- (void)calculateArticleID; // Optimization. Call after all properties have been set. Call on a background thread.
- (nonnull instancetype)initWithFeedURL:(NSURL * _Nonnull)feedURL dateParsed:(NSDate*)parsed;
///Initiate calculation of article id. For optimization, call on a background thread after all properties have been set.
- (void)calculateArticleID;
@end

View File

@@ -46,9 +46,7 @@
#pragma mark - Unique Article ID
/**
Article ID will be generated on the first access.
*/
// docref in header
- (NSString *)articleID {
if (!_internalArticleID) {
_internalArticleID = self.calculatedUniqueID;
@@ -56,9 +54,7 @@
return _internalArticleID;
}
/**
Initiate calculation of article id.
*/
// docref in header
- (void)calculateArticleID {
(void)self.articleID;
}

View File

@@ -26,6 +26,7 @@
@class RSParsedArticle;
/// Parsed result type for feeds. Does contain feed specific attributes and a sorted list or articles.
@interface RSParsedFeed : NSObject
@property (nonatomic, readonly, nonnull) NSURL *url;
@property (nonatomic, readonly, nonnull) NSDate *dateParsed;
@@ -36,6 +37,7 @@
@property (nonatomic, nullable) NSString *subtitle;
- (nonnull instancetype)initWithURL:(NSURL * _Nonnull)url;
/// Append new @c RSParsedArticle object to @c .articles and return newly inserted instance.
- (RSParsedArticle *)appendNewArticle;
@end

View File

@@ -46,9 +46,7 @@
return _mutableArticles;
}
/**
Append new @c RSParsedArticle object to @c .articles and return newly inserted instance.
*/
// docref in header
- (RSParsedArticle *)appendNewArticle {
RSParsedArticle *article = [[RSParsedArticle alloc] initWithFeedURL:self.url dateParsed:_dateParsed];
[_mutableArticles addObject:article];

View File

@@ -27,6 +27,7 @@
// <channel> <item>
// https://cyber.harvard.edu/rss/rss.html
/// Feed parser for RSS xml and RDF xml feeds. Expects the tags @c <channel> and @c <item> to be existent.
@interface RSRSSParser : RSFeedParser
@end

View File

@@ -68,11 +68,20 @@
- (instancetype)initWithDelegate:(id<RSSAXParserDelegate>)delegate;
/// Initialize new xml or html parser context and start processing of data.
- (void)parseBytes:(const void *)bytes numberOfBytes:(NSUInteger)numberOfBytes;
/// Will stop the sax parser from processing any further. @c saxParserDidReachEndOfDocument: will not be called.
- (void)cancel;
/**
Delegate can call from @c XMLStartElement.
Characters will be available in @c XMLEndElement as @c currentCharacters property.
Storing characters is stopped after each @c XMLEndElement.
*/
- (void)beginStoringCharacters;
/// Delegate can call from within @c XMLStartElement. Returns @c nil if @c numberOfAttributes @c < @c 1 .
- (NSDictionary *)attributesDictionary:(const unsigned char **)attributes numberOfAttributes:(NSInteger)numberOfAttributes;
/// Delegate can call from within @c XMLStartElement. Returns @c nil if @c attributes is @c nil .
- (NSDictionary *)attributesDictionaryHTML:(const unsigned char **)attributes;
@end

View File

@@ -94,9 +94,7 @@ const NSErrorDomain kLIBXMLParserErrorDomain = @"LIBXMLParserErrorDomain";
static xmlSAXHandler saxHandlerStruct;
/**
Initialize new xml or html parser context and start processing of data.
*/
// docref in header
- (void)parseBytes:(const void *)bytes numberOfBytes:(NSUInteger)numberOfBytes {
_parsingError = nil;
@@ -145,18 +143,14 @@ static xmlSAXHandler saxHandlerStruct;
}
}
/// Will stop the sax parser from processing any further. @c saxParserDidReachEndOfDocument: will not be called.
// docref in header
- (void)cancel {
@autoreleasepool {
xmlStopParser(self.context);
}
}
/**
Delegate can call from @c XMLStartElement.
Characters will be available in @c XMLEndElement as @c currentCharacters property.
Storing characters is stopped after each @c XMLEndElement.
*/
// docref in header
- (void)beginStoringCharacters {
self.storingCharacters = YES;
self.characters = [NSMutableData new];
@@ -194,9 +188,7 @@ static xmlSAXHandler saxHandlerStruct;
#pragma mark - Attributes Dictionary
/**
Delegate can call from within @c XMLStartElement. Returns @c nil if @c numberOfAttributes @c < @c 1.
*/
// docref in header
- (NSDictionary *)attributesDictionary:(const xmlChar **)attributes numberOfAttributes:(NSInteger)numberOfAttributes {
if (numberOfAttributes < 1 || !attributes) {
@@ -240,9 +232,7 @@ static xmlSAXHandler saxHandlerStruct;
return d;
}
/**
Delegate can call from within @c XMLStartElement. Returns @c nil if @c numberOfAttributes @c < @c 1.
*/
// docref in header
- (NSDictionary *)attributesDictionaryHTML:(const xmlChar **)attributes {
if (!attributes) {

View File

@@ -27,6 +27,7 @@
@class RSXMLParser;
/// Wrapper class for xml data. Returns the designated parser for any given xml data.
@interface RSXMLData <__covariant T : RSXMLParser *> : NSObject
@property (nonatomic, readonly, nonnull) NSURL *url;
@property (nonatomic, readonly, nullable) NSData *data;
@@ -35,7 +36,9 @@
- (instancetype)initWithData:(NSData * _Nonnull)data url:(NSURL * _Nonnull)url;
/// @return Kind of @c RSXMLParser or @c nil if no suitable parser found.
- (T _Nullable)getParser;
/// @return @c YES if any parser, regardless of type, is suitable.
- (BOOL)canParseData;
@end

View File

@@ -195,12 +195,12 @@ static const NSUInteger numberOfCharactersToSearch = 4096;
#pragma mark - Check Methods to Determine Parser Type
/// @return Kind of @c RSXMLParser or @c nil if no suitable parser found.
// docref in header
- (id)getParser {
return [_parserClass parserWithXMLData:self];
}
/// @return @c YES if any parser, regardless of type, is suitable.
// docref in header
- (BOOL)canParseData {
return (_parserClass != nil && _parserError == nil);
}

View File

@@ -29,6 +29,9 @@
@class RSXMLData;
// ---------------------------------------------------------------
// | MARK: - Parser Delegate
// ---------------------------------------------------------------
@protocol RSXMLParserDelegate <NSObject>
@optional
@@ -56,14 +59,34 @@
@end
// ---------------------------------------------------------------
// | MARK: - Parser
// ---------------------------------------------------------------
/**
Generic wrapper class for @c libxml parsing.
Could be one of @c RSRSSParser, @c RSAtomParser, @c RSOPMLParser, @c RSHTMLMetadataParser, and @c RSHTMLLinkParser
*/
@interface RSXMLParser<__covariant T> : NSObject <RSXMLParserDelegate, RSSAXParserDelegate>
@property (nonatomic, readonly, nonnull, copy) NSURL *documentURI;
@property (nonatomic, assign) BOOL dontStopOnLowerAsciiBytes;
/**
Designated initializer. Runs a check whether it matches the detected parser in @c RSXMLData.
Keeps an internal pointer to the @c RSXMLData and initializes a new @c RSSAXParser.
*/
+ (instancetype)parserWithXMLData:(RSXMLData * _Nonnull)xmlData;
/**
Parse the XML data on whatever thread this method is called.
@param error Sets @c error if parser gets unrecognized data or @c libxml runs into a parsing error.
@return The parsed object. The object type depends on the underlying data. @c RSParsedFeed, @c RSOPMLItem or @c RSHTMLMetadata.
*/
- (T _Nullable)parseSync:(NSError ** _Nullable)error;
/// Dispatch new background thread, parse the data synchroniously on the background thread and exec callback on the main thread.
- (void)parseAsync:(void(^)(T _Nullable parsedDocument, NSError * _Nullable error))block;
/// @return @c YES if @c .xmlInputError is @c nil.
- (BOOL)canParse;
@end

View File

@@ -39,10 +39,7 @@
+ (BOOL)isHTMLParser { return NO; } // override
- (id)xmlParserWillReturnDocument { return nil; } // override
/**
Designated initializer. Runs a check whether it matches the detected parser in @c RSXMLData.
Keeps an internal pointer to the @c RSXMLData and initializes a new @c RSSAXParser.
*/
// docref in header
+ (instancetype)parserWithXMLData:(nonnull RSXMLData *)xmlData {
if ([xmlData.parserClass isSubclassOfClass:[super class]]) {
return [[xmlData.parserClass alloc] initWithXMLData:xmlData];
@@ -85,12 +82,7 @@
}];
}
/**
Parse the XML data on whatever thread this method is called.
@param error Sets @c error if parser gets unrecognized data or libxml runs into a parsing error.
@return The parsed object. The object type depends on the underlying data. @c RSParsedFeed, @c RSOPMLItem or @c RSHTMLMetadata.
*/
// docref in header
- (id _Nullable)parseSync:(NSError **)error {
if (_xmlInputError) {
if (error) *error = _xmlInputError;
@@ -109,9 +101,7 @@
return [self xmlParserWillReturnDocument];
}
/**
Dispatch new background thread, parse the data synchroniously on the background thread and exec callback on the main thread.
*/
// docref in header
- (void)parseAsync:(void(^)(id parsedDocument, NSError *error))block {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{ // QOS_CLASS_DEFAULT
@autoreleasepool {
@@ -124,7 +114,7 @@
});
}
/// @return @c YES if @c .xmlInputError is @c nil.
// docref in header
- (BOOL)canParse {
return (self.xmlInputError == nil);
}