Refactored OPML
This commit is contained in:
@@ -10,14 +10,8 @@
|
|||||||
54FCE5F421493B5E00FABB65 /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 54FCE5F321493B5E00FABB65 /* Resources */; };
|
54FCE5F421493B5E00FABB65 /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 54FCE5F321493B5E00FABB65 /* Resources */; };
|
||||||
8400B0F01B8C20A9004C4CFF /* RSXMLData.h in Headers */ = {isa = PBXBuildFile; fileRef = 8400B0EE1B8C20A9004C4CFF /* RSXMLData.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
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 */; };
|
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, ); }; };
|
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 */; };
|
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 */; };
|
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, ); }; };
|
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 */; };
|
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 */; };
|
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, ); }; };
|
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 */; };
|
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, ); }; };
|
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 */; };
|
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, ); }; };
|
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 */; };
|
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, ); }; };
|
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 = "<group>"; };
|
54FCE5F321493B5E00FABB65 /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = "<group>"; };
|
||||||
8400B0EE1B8C20A9004C4CFF /* RSXMLData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RSXMLData.h; path = RSXML/RSXMLData.h; sourceTree = "<group>"; };
|
8400B0EE1B8C20A9004C4CFF /* RSXMLData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RSXMLData.h; path = RSXML/RSXMLData.h; sourceTree = "<group>"; };
|
||||||
8400B0EF1B8C20A9004C4CFF /* RSXMLData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RSXMLData.m; path = RSXML/RSXMLData.m; sourceTree = "<group>"; };
|
8400B0EF1B8C20A9004C4CFF /* RSXMLData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RSXMLData.m; path = RSXML/RSXMLData.m; sourceTree = "<group>"; };
|
||||||
8429D1AA1C839FFC00F97695 /* RSOPMLDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLDocument.h; sourceTree = "<group>"; };
|
|
||||||
8429D1AB1C839FFC00F97695 /* RSOPMLDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLDocument.m; sourceTree = "<group>"; };
|
|
||||||
8429D1B41C83A03100F97695 /* RSOPMLItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLItem.h; sourceTree = "<group>"; };
|
8429D1B41C83A03100F97695 /* RSOPMLItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLItem.h; sourceTree = "<group>"; };
|
||||||
8429D1B51C83A03100F97695 /* RSOPMLItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLItem.m; sourceTree = "<group>"; };
|
8429D1B51C83A03100F97695 /* RSOPMLItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLItem.m; sourceTree = "<group>"; };
|
||||||
8429D1B81C83A31C00F97695 /* RSOPMLAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLAttributes.h; sourceTree = "<group>"; };
|
|
||||||
8429D1B91C83A31C00F97695 /* RSOPMLAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RSOPMLAttributes.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
|
||||||
8429D1BC1C83AD0F00F97695 /* RSOPMLFeedSpecifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSOPMLFeedSpecifier.h; sourceTree = "<group>"; };
|
|
||||||
8429D1BD1C83AD0F00F97695 /* RSOPMLFeedSpecifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLFeedSpecifier.m; sourceTree = "<group>"; };
|
|
||||||
8429D1C21C83BCCB00F97695 /* RSOPMLTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLTests.m; sourceTree = "<group>"; };
|
8429D1C21C83BCCB00F97695 /* RSOPMLTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSOPMLTests.m; sourceTree = "<group>"; };
|
||||||
842D514A1B52E7FC00E63D52 /* RSAtomParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSAtomParser.h; sourceTree = "<group>"; };
|
842D514A1B52E7FC00E63D52 /* RSAtomParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSAtomParser.h; sourceTree = "<group>"; };
|
||||||
842D514B1B52E7FC00E63D52 /* RSAtomParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSAtomParser.m; sourceTree = "<group>"; };
|
842D514B1B52E7FC00E63D52 /* RSAtomParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSAtomParser.m; sourceTree = "<group>"; };
|
||||||
@@ -223,14 +205,8 @@
|
|||||||
children = (
|
children = (
|
||||||
842D51781B5311AD00E63D52 /* RSOPMLParser.h */,
|
842D51781B5311AD00E63D52 /* RSOPMLParser.h */,
|
||||||
842D51791B5311AD00E63D52 /* RSOPMLParser.m */,
|
842D51791B5311AD00E63D52 /* RSOPMLParser.m */,
|
||||||
8429D1AA1C839FFC00F97695 /* RSOPMLDocument.h */,
|
|
||||||
8429D1AB1C839FFC00F97695 /* RSOPMLDocument.m */,
|
|
||||||
8429D1B41C83A03100F97695 /* RSOPMLItem.h */,
|
8429D1B41C83A03100F97695 /* RSOPMLItem.h */,
|
||||||
8429D1B51C83A03100F97695 /* RSOPMLItem.m */,
|
8429D1B51C83A03100F97695 /* RSOPMLItem.m */,
|
||||||
8429D1BC1C83AD0F00F97695 /* RSOPMLFeedSpecifier.h */,
|
|
||||||
8429D1BD1C83AD0F00F97695 /* RSOPMLFeedSpecifier.m */,
|
|
||||||
8429D1B81C83A31C00F97695 /* RSOPMLAttributes.h */,
|
|
||||||
8429D1B91C83A31C00F97695 /* RSOPMLAttributes.m */,
|
|
||||||
);
|
);
|
||||||
name = OPML;
|
name = OPML;
|
||||||
path = RSXML;
|
path = RSXML;
|
||||||
@@ -332,9 +308,7 @@
|
|||||||
84AD0C161E11B8CA00B38510 /* RSDateParser.h in Headers */,
|
84AD0C161E11B8CA00B38510 /* RSDateParser.h in Headers */,
|
||||||
84AD0C101E11B8CA00B38510 /* RSSAXParser.h in Headers */,
|
84AD0C101E11B8CA00B38510 /* RSSAXParser.h in Headers */,
|
||||||
84AD0C331E11B8DA00B38510 /* RSHTMLLinkParser.h in Headers */,
|
84AD0C331E11B8DA00B38510 /* RSHTMLLinkParser.h in Headers */,
|
||||||
84AD0C201E11B8CF00B38510 /* RSOPMLAttributes.h in Headers */,
|
|
||||||
84AD0C2F1E11B8DA00B38510 /* RSHTMLMetadataParser.h in Headers */,
|
84AD0C2F1E11B8DA00B38510 /* RSHTMLMetadataParser.h in Headers */,
|
||||||
84AD0C1A1E11B8CF00B38510 /* RSOPMLDocument.h in Headers */,
|
|
||||||
84AD0C251E11B8D400B38510 /* RSAtomParser.h in Headers */,
|
84AD0C251E11B8D400B38510 /* RSAtomParser.h in Headers */,
|
||||||
84AD0C121E11B8CA00B38510 /* RSXMLData.h in Headers */,
|
84AD0C121E11B8CA00B38510 /* RSXMLData.h in Headers */,
|
||||||
84AD0C311E11B8DA00B38510 /* RSHTMLMetadata.h in Headers */,
|
84AD0C311E11B8DA00B38510 /* RSHTMLMetadata.h in Headers */,
|
||||||
@@ -342,7 +316,6 @@
|
|||||||
84AD0C221E11B8D400B38510 /* RSFeedParser.h in Headers */,
|
84AD0C221E11B8D400B38510 /* RSFeedParser.h in Headers */,
|
||||||
84AD0C2D1E11B8DA00B38510 /* RSSAXHTMLParser.h in Headers */,
|
84AD0C2D1E11B8DA00B38510 /* RSSAXHTMLParser.h in Headers */,
|
||||||
84AD0C0E1E11B8CA00B38510 /* RSXMLError.h in Headers */,
|
84AD0C0E1E11B8CA00B38510 /* RSXMLError.h in Headers */,
|
||||||
84AD0C1E1E11B8CF00B38510 /* RSOPMLFeedSpecifier.h in Headers */,
|
|
||||||
84AD0C2B1E11B8D400B38510 /* RSParsedArticle.h in Headers */,
|
84AD0C2B1E11B8D400B38510 /* RSParsedArticle.h in Headers */,
|
||||||
84AD0C291E11B8D400B38510 /* RSParsedFeed.h in Headers */,
|
84AD0C291E11B8D400B38510 /* RSParsedFeed.h in Headers */,
|
||||||
84AD0C181E11B8CF00B38510 /* RSOPMLParser.h in Headers */,
|
84AD0C181E11B8CF00B38510 /* RSOPMLParser.h in Headers */,
|
||||||
@@ -361,16 +334,13 @@
|
|||||||
8400B0F01B8C20A9004C4CFF /* RSXMLData.h in Headers */,
|
8400B0F01B8C20A9004C4CFF /* RSXMLData.h in Headers */,
|
||||||
842D51631B53058B00E63D52 /* RSParsedArticle.h in Headers */,
|
842D51631B53058B00E63D52 /* RSParsedArticle.h in Headers */,
|
||||||
842D517A1B5311AD00E63D52 /* RSOPMLParser.h in Headers */,
|
842D517A1B5311AD00E63D52 /* RSOPMLParser.h in Headers */,
|
||||||
8429D1AC1C839FFC00F97695 /* RSOPMLDocument.h in Headers */,
|
|
||||||
84BF3E161C8CDD1A005562D8 /* RSHTMLMetadataParser.h in Headers */,
|
84BF3E161C8CDD1A005562D8 /* RSHTMLMetadataParser.h in Headers */,
|
||||||
842D51761B530BF200E63D52 /* RSParsedFeed.h in Headers */,
|
842D51761B530BF200E63D52 /* RSParsedFeed.h in Headers */,
|
||||||
8429D1BA1C83A31C00F97695 /* RSOPMLAttributes.h in Headers */,
|
|
||||||
84BF3E1C1C8CDD6D005562D8 /* RSHTMLMetadata.h in Headers */,
|
84BF3E1C1C8CDD6D005562D8 /* RSHTMLMetadata.h in Headers */,
|
||||||
8429D1B61C83A03100F97695 /* RSOPMLItem.h in Headers */,
|
8429D1B61C83A03100F97695 /* RSOPMLItem.h in Headers */,
|
||||||
843819001C8CB00400E2A1DD /* RSSAXHTMLParser.h in Headers */,
|
843819001C8CB00400E2A1DD /* RSSAXHTMLParser.h in Headers */,
|
||||||
842D51521B52E80100E63D52 /* RSFeedParser.h in Headers */,
|
842D51521B52E80100E63D52 /* RSFeedParser.h in Headers */,
|
||||||
84E4BE451C8B8FE400A90B41 /* RSXMLError.h in Headers */,
|
84E4BE451C8B8FE400A90B41 /* RSXMLError.h in Headers */,
|
||||||
8429D1BE1C83AD0F00F97695 /* RSOPMLFeedSpecifier.h in Headers */,
|
|
||||||
842D516F1B5308BD00E63D52 /* FeedParser.h in Headers */,
|
842D516F1B5308BD00E63D52 /* FeedParser.h in Headers */,
|
||||||
84F22C111B52DDEA000060CE /* RSXML.h in Headers */,
|
84F22C111B52DDEA000060CE /* RSXML.h in Headers */,
|
||||||
842D514C1B52E7FC00E63D52 /* RSAtomParser.h in Headers */,
|
842D514C1B52E7FC00E63D52 /* RSAtomParser.h in Headers */,
|
||||||
@@ -524,11 +494,8 @@
|
|||||||
84AD0C261E11B8D400B38510 /* RSAtomParser.m in Sources */,
|
84AD0C261E11B8D400B38510 /* RSAtomParser.m in Sources */,
|
||||||
84AD0C1D1E11B8CF00B38510 /* RSOPMLItem.m in Sources */,
|
84AD0C1D1E11B8CF00B38510 /* RSOPMLItem.m in Sources */,
|
||||||
84AD0C131E11B8CA00B38510 /* RSXMLData.m in Sources */,
|
84AD0C131E11B8CA00B38510 /* RSXMLData.m in Sources */,
|
||||||
84AD0C211E11B8CF00B38510 /* RSOPMLAttributes.m in Sources */,
|
|
||||||
84AD0C321E11B8DA00B38510 /* RSHTMLMetadata.m in Sources */,
|
84AD0C321E11B8DA00B38510 /* RSHTMLMetadata.m in Sources */,
|
||||||
84AD0C1F1E11B8CF00B38510 /* RSOPMLFeedSpecifier.m in Sources */,
|
|
||||||
84AD0C111E11B8CA00B38510 /* RSSAXParser.m in Sources */,
|
84AD0C111E11B8CA00B38510 /* RSSAXParser.m in Sources */,
|
||||||
84AD0C1B1E11B8CF00B38510 /* RSOPMLDocument.m in Sources */,
|
|
||||||
84AD0C0F1E11B8CA00B38510 /* RSXMLError.m in Sources */,
|
84AD0C0F1E11B8CA00B38510 /* RSXMLError.m in Sources */,
|
||||||
84AD0C2E1E11B8DA00B38510 /* RSSAXHTMLParser.m in Sources */,
|
84AD0C2E1E11B8DA00B38510 /* RSSAXHTMLParser.m in Sources */,
|
||||||
);
|
);
|
||||||
@@ -542,14 +509,11 @@
|
|||||||
842D515B1B52E81B00E63D52 /* RSRSSParser.m in Sources */,
|
842D515B1B52E81B00E63D52 /* RSRSSParser.m in Sources */,
|
||||||
842D517B1B5311AD00E63D52 /* RSOPMLParser.m in Sources */,
|
842D517B1B5311AD00E63D52 /* RSOPMLParser.m in Sources */,
|
||||||
8486F1161BB646140092794F /* NSString+RSXML.m in Sources */,
|
8486F1161BB646140092794F /* NSString+RSXML.m in Sources */,
|
||||||
8429D1BB1C83A31C00F97695 /* RSOPMLAttributes.m in Sources */,
|
|
||||||
84AD0BF61E11A6FB00B38510 /* RSDateParser.m in Sources */,
|
84AD0BF61E11A6FB00B38510 /* RSDateParser.m in Sources */,
|
||||||
84BF3E171C8CDD1A005562D8 /* RSHTMLMetadataParser.m in Sources */,
|
84BF3E171C8CDD1A005562D8 /* RSHTMLMetadataParser.m in Sources */,
|
||||||
842D51641B53058B00E63D52 /* RSParsedArticle.m in Sources */,
|
842D51641B53058B00E63D52 /* RSParsedArticle.m in Sources */,
|
||||||
84E4BE461C8B8FE400A90B41 /* RSXMLError.m in Sources */,
|
84E4BE461C8B8FE400A90B41 /* RSXMLError.m in Sources */,
|
||||||
8475C4091D57AB4C0076751E /* RSHTMLLinkParser.m in Sources */,
|
8475C4091D57AB4C0076751E /* RSHTMLLinkParser.m in Sources */,
|
||||||
8429D1BF1C83AD0F00F97695 /* RSOPMLFeedSpecifier.m in Sources */,
|
|
||||||
8429D1AD1C839FFC00F97695 /* RSOPMLDocument.m in Sources */,
|
|
||||||
8429D1B71C83A03100F97695 /* RSOPMLItem.m in Sources */,
|
8429D1B71C83A03100F97695 /* RSOPMLItem.m in Sources */,
|
||||||
84BF3E1D1C8CDD6D005562D8 /* RSHTMLMetadata.m in Sources */,
|
84BF3E1D1C8CDD6D005562D8 /* RSHTMLMetadata.m in Sources */,
|
||||||
842D51531B52E80100E63D52 /* RSFeedParser.m in Sources */,
|
842D51531B52E80100E63D52 /* RSFeedParser.m in Sources */,
|
||||||
|
|||||||
@@ -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 {
|
- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForValue:(const void *)bytes length:(NSUInteger)length {
|
||||||
|
|
||||||
static const NSUInteger alternateLength = kAlternateLength - 1;
|
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 textLength = kTextLength - 1;
|
||||||
static const NSUInteger selfLength = kSelfLength - 1;
|
static const NSUInteger selfLength = kSelfLength - 1;
|
||||||
|
|
||||||
if (length == alternateLength && equalBytes(bytes, kAlternate, alternateLength)) {
|
if (length == alternateLength && RSSAXEqualBytes(bytes, kAlternate, alternateLength)) {
|
||||||
return kAlternateValue;
|
return kAlternateValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == textHTMLLength && equalBytes(bytes, kTextHTML, textHTMLLength)) {
|
if (length == textHTMLLength && RSSAXEqualBytes(bytes, kTextHTML, textHTMLLength)) {
|
||||||
return kTextHTMLValue;
|
return kTextHTMLValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == relatedLength && equalBytes(bytes, kRelated, relatedLength)) {
|
if (length == relatedLength && RSSAXEqualBytes(bytes, kRelated, relatedLength)) {
|
||||||
return kRelatedValue;
|
return kRelatedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == shortURLLength && equalBytes(bytes, kShortURL, shortURLLength)) {
|
if (length == shortURLLength && RSSAXEqualBytes(bytes, kShortURL, shortURLLength)) {
|
||||||
return kShortURLValue;
|
return kShortURLValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == htmlLength && equalBytes(bytes, kHTML, htmlLength)) {
|
if (length == htmlLength && RSSAXEqualBytes(bytes, kHTML, htmlLength)) {
|
||||||
return kHTMLValue;
|
return kHTMLValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == enLength && equalBytes(bytes, kEn, enLength)) {
|
if (length == enLength && RSSAXEqualBytes(bytes, kEn, enLength)) {
|
||||||
return kEnValue;
|
return kEnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == textLength && equalBytes(bytes, kText, textLength)) {
|
if (length == textLength && RSSAXEqualBytes(bytes, kText, textLength)) {
|
||||||
return kTextValue;
|
return kTextValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == selfLength && equalBytes(bytes, kSelf, selfLength)) {
|
if (length == selfLength && RSSAXEqualBytes(bytes, kSelf, selfLength)) {
|
||||||
return kSelfValue;
|
return kSelfValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,6 @@
|
|||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
static NSString *kLIBXMLParserErrorDomain = @"LIBXMLParserErrorDomain";
|
|
||||||
static NSString *kRSXMLParserErrorDomain = @"RSXMLParserErrorDomain";
|
|
||||||
|
|
||||||
BOOL RSCanParseFeed(RSXMLData *xmlData);
|
BOOL RSCanParseFeed(RSXMLData *xmlData);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +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 "RSXMLError.h"
|
||||||
#import "RSFeedParser.h"
|
#import "RSFeedParser.h"
|
||||||
#import "FeedParser.h"
|
#import "FeedParser.h"
|
||||||
#import "RSXMLData.h"
|
#import "RSXMLData.h"
|
||||||
@@ -49,43 +49,10 @@ 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;
|
||||||
|
|
||||||
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) {
|
static Class parserClassForXMLData(RSXMLData *xmlData, NSError **error) {
|
||||||
|
|
||||||
if (!feedMayBeParseable(xmlData)) {
|
if (!feedMayBeParseable(xmlData)) {
|
||||||
setError(error, RSXMLErrorNoData);
|
RSXMLSetError(error, RSXMLErrorNoData, nil);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +68,7 @@ static Class parserClassForXMLData(RSXMLData *xmlData, NSError **error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!dataHasLeftCaret(bytes, numberOfBytes)) {
|
if (!dataHasLeftCaret(bytes, numberOfBytes)) {
|
||||||
setError(error, RSXMLErrorMissingLeftCaret);
|
RSXMLSetError(error, RSXMLErrorMissingLeftCaret, nil);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
if (optimisticCanParseRSSData(bytes, numberOfBytes)) {
|
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 ...
|
return [RSRSSParser class]; //TODO: parse RDF feeds, using RSS parser so far ...
|
||||||
}
|
}
|
||||||
if (dataIsProbablyHTML(bytes, numberOfBytes)) {
|
if (dataIsProbablyHTML(bytes, numberOfBytes)) {
|
||||||
setError(error, RSXMLErrorProbablyHTML);
|
RSXMLSetError(error, RSXMLErrorProbablyHTML, nil);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
if (dataIsSomeWeirdException(bytes, numberOfBytes)) {
|
if (dataIsSomeWeirdException(bytes, numberOfBytes)) {
|
||||||
setError(error, RSXMLErrorContainsXMLErrorsTag);
|
RSXMLSetError(error, RSXMLErrorContainsXMLErrorsTag, nil);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,7 +97,7 @@ static Class parserClassForXMLData(RSXMLData *xmlData, NSError **error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Try RSS anyway? libxml would return a parsing error
|
// Try RSS anyway? libxml would return a parsing error
|
||||||
setError(error, RSXMLErrorNoSuitableParser);
|
RSXMLSetError(error, RSXMLErrorNoSuitableParser, nil);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,19 +217,11 @@ RSParsedFeed *RSParseFeedSync(RSXMLData *xmlData, NSError **error) {
|
|||||||
xmlResetLastError();
|
xmlResetLastError();
|
||||||
id<FeedParser> parser = parserForXMLData(xmlData, error);
|
id<FeedParser> parser = parserForXMLData(xmlData, error);
|
||||||
if (error && *error) {
|
if (error && *error) {
|
||||||
//printf("ERROR in parserForXMLData(): %s\n", [[*error localizedDescription] UTF8String]);
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
RSParsedFeed *parsedResult = [parser parseFeed];
|
RSParsedFeed *parsedResult = [parser parseFeed];
|
||||||
|
if (error) {
|
||||||
xmlErrorPtr err = xmlGetLastError();
|
*error = RSXMLMakeErrorFromLIBXMLError(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();
|
xmlResetLastError();
|
||||||
}
|
}
|
||||||
return parsedResult;
|
return parsedResult;
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
@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
|
@interface RSOPMLItem : NSObject
|
||||||
|
@property (nonatomic) NSArray<RSOPMLItem*> *children;
|
||||||
@property (nonatomic) NSDictionary *attributes;
|
@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)addChild:(RSOPMLItem *)child;
|
||||||
|
- (void)setAttribute:(id)value forKey:(NSString *)key;
|
||||||
|
- (id)attributeForKey:(NSString *)key;
|
||||||
|
|
||||||
@property (nonatomic, readonly) RSOPMLFeedSpecifier *OPMLFeedSpecifier; //May be nil.
|
- (NSString *)recursiveDescription;
|
||||||
|
|
||||||
@property (nonatomic, readonly) NSString *titleFromAttributes; //May be nil.
|
|
||||||
@property (nonatomic, readonly) BOOL isFolder;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -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 "RSOPMLItem.h"
|
||||||
#import "RSOPMLAttributes.h"
|
|
||||||
#import "RSOPMLFeedSpecifier.h"
|
|
||||||
#import "RSXMLInternal.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 ()
|
@interface RSOPMLItem ()
|
||||||
|
@property (nonatomic) NSMutableArray<RSOPMLItem*> *mutableChildren;
|
||||||
@property (nonatomic) NSMutableArray *mutableChildren;
|
@property (nonatomic) NSMutableDictionary *mutableAttributes;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@implementation RSOPMLItem
|
@implementation RSOPMLItem
|
||||||
|
|
||||||
@synthesize children = _children;
|
|
||||||
@synthesize OPMLFeedSpecifier = _OPMLFeedSpecifier;
|
|
||||||
|
|
||||||
|
|
||||||
- (NSArray *)children {
|
- (NSArray *)children {
|
||||||
|
|
||||||
return [self.mutableChildren copy];
|
return [self.mutableChildren copy];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setChildren:(NSArray<RSOPMLItem*>*)children {
|
||||||
- (void)setChildren:(NSArray *)children {
|
self.mutableChildren = [children mutableCopy];
|
||||||
|
|
||||||
_children = children;
|
|
||||||
self.mutableChildren = [_children mutableCopy];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)attributes {
|
||||||
- (void)addChild:(RSOPMLItem *)child {
|
return [self.mutableAttributes copy];
|
||||||
|
|
||||||
if (!self.mutableChildren) {
|
|
||||||
self.mutableChildren = [NSMutableArray new];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self.mutableChildren addObject:child];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setAttributes:(NSDictionary *)attributes {
|
||||||
- (RSOPMLFeedSpecifier *)OPMLFeedSpecifier {
|
self.mutableAttributes = [attributes mutableCopy];
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isFolder {
|
- (BOOL)isFolder {
|
||||||
|
|
||||||
return self.mutableChildren.count > 0;
|
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:@"%@</group>\n", prefix];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)recursiveDescription {
|
||||||
|
NSMutableString *mStr = [NSMutableString new];
|
||||||
|
[self appendStringRecursive:mStr indent:@""];
|
||||||
|
return mStr;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -10,10 +10,10 @@
|
|||||||
|
|
||||||
|
|
||||||
@class RSXMLData;
|
@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.
|
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;
|
- (instancetype)initWithXMLData:(RSXMLData *)xmlData;
|
||||||
|
|
||||||
@property (nonatomic, readonly) RSOPMLDocument *OPMLDocument;
|
@property (nonatomic, readonly) RSOPMLItem *opmlDocument;
|
||||||
@property (nonatomic, readonly) NSError *error;
|
@property (nonatomic, readonly) NSError *error;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -11,8 +11,6 @@
|
|||||||
#import "RSXMLData.h"
|
#import "RSXMLData.h"
|
||||||
#import "RSSAXParser.h"
|
#import "RSSAXParser.h"
|
||||||
#import "RSOPMLItem.h"
|
#import "RSOPMLItem.h"
|
||||||
#import "RSOPMLDocument.h"
|
|
||||||
#import "RSOPMLAttributes.h"
|
|
||||||
#import "RSXMLError.h"
|
#import "RSXMLError.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -27,7 +25,7 @@ void RSParseOPML(RSXMLData *xmlData, RSParsedOPMLBlock callback) {
|
|||||||
|
|
||||||
RSOPMLParser *parser = [[RSOPMLParser alloc] initWithXMLData:xmlData];
|
RSOPMLParser *parser = [[RSOPMLParser alloc] initWithXMLData:xmlData];
|
||||||
|
|
||||||
RSOPMLDocument *document = parser.OPMLDocument;
|
RSOPMLItem *document = parser.opmlDocument;
|
||||||
NSError *error = parser.error;
|
NSError *error = parser.error;
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
@@ -41,9 +39,9 @@ void RSParseOPML(RSXMLData *xmlData, RSParsedOPMLBlock callback) {
|
|||||||
|
|
||||||
@interface RSOPMLParser () <RSSAXParserDelegate>
|
@interface RSOPMLParser () <RSSAXParserDelegate>
|
||||||
|
|
||||||
@property (nonatomic, readwrite) RSOPMLDocument *OPMLDocument;
|
@property (nonatomic, readwrite) RSOPMLItem *opmlDocument;
|
||||||
@property (nonatomic, readwrite) NSError *error;
|
@property (nonatomic, readwrite) NSError *error;
|
||||||
@property (nonatomic) NSMutableArray *itemStack;
|
@property (nonatomic) NSMutableArray<RSOPMLItem*> *itemStack;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -72,31 +70,28 @@ void RSParseOPML(RSXMLData *xmlData, RSParsedOPMLBlock callback) {
|
|||||||
|
|
||||||
@autoreleasepool {
|
@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;
|
NSString *filename = nil;
|
||||||
NSURL *url = [NSURL URLWithString:XMLData.urlString];
|
NSURL *url = [NSURL URLWithString:XMLData.urlString];
|
||||||
if (url && url.isFileURL) {
|
if (url && url.isFileURL) {
|
||||||
filename = url.path.lastPathComponent;
|
filename = url.path.lastPathComponent;
|
||||||
}
|
}
|
||||||
if ([XMLData.urlString hasPrefix:@"http"]) {
|
|
||||||
filename = XMLData.urlString;
|
|
||||||
}
|
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
filename = XMLData.urlString;
|
filename = XMLData.urlString;
|
||||||
}
|
}
|
||||||
self.error = RSOPMLWrongFormatError(filename);
|
self.error = RSXMLMakeError(RSXMLErrorFileNotOPML, filename);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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:@"<opml" options:NSCaseInsensitiveSearch range:rangeToSearch];
|
NSRange opmlRange = [s rangeOfString:@"<opml" options:NSCaseInsensitiveSearch range:rangeToSearch];
|
||||||
if (opmlRange.length < 1) {
|
if (opmlRange.location == NSNotFound) {
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSRange outlineRange = [s rangeOfString:@"<outline" options:NSLiteralSearch range:rangeToSearch];
|
NSRange outlineRange = [s rangeOfString:@"<outline" options:NSLiteralSearch range:rangeToSearch];
|
||||||
if (outlineRange.length < 1) {
|
if (outlineRange.location == NSNotFound) {
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,11 +134,6 @@ void RSParseOPML(RSXMLData *xmlData, RSParsedOPMLBlock callback) {
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)pushItem:(RSOPMLItem *)item {
|
|
||||||
|
|
||||||
[self.itemStack addObject:item];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)popItem {
|
- (void)popItem {
|
||||||
|
|
||||||
@@ -158,28 +148,27 @@ void RSParseOPML(RSXMLData *xmlData, RSParsedOPMLBlock callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (RSOPMLItem *)currentItem {
|
|
||||||
|
|
||||||
return self.itemStack.lastObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - RSSAXParserDelegate
|
#pragma mark - RSSAXParserDelegate
|
||||||
|
|
||||||
static const char *kOutline = "outline";
|
static const char *kOutline = "outline";
|
||||||
static const char kOutlineLength = 8;
|
static const char kOutlineLength = 8;
|
||||||
|
static const char *kHead = "head";
|
||||||
|
static const char kHeadLength = 5;
|
||||||
|
static BOOL isHead = NO;
|
||||||
|
|
||||||
- (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 {
|
- (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 (!RSSAXEqualTags(localName, kOutline, kOutlineLength)) {
|
if (RSSAXEqualTags(localName, kOutline, kOutlineLength)) {
|
||||||
return;
|
RSOPMLItem *item = [RSOPMLItem new];
|
||||||
|
item.attributes = [SAXParser attributesDictionary:attributes numberOfAttributes:numberOfAttributes];
|
||||||
|
|
||||||
|
[self.itemStack.lastObject addChild:item];
|
||||||
|
[self.itemStack addObject:item];
|
||||||
|
} else if (RSSAXEqualTags(localName, kHead, kHeadLength)) {
|
||||||
|
isHead = YES;
|
||||||
|
} else if (isHead) {
|
||||||
|
[SAXParser beginStoringCharacters];
|
||||||
}
|
}
|
||||||
|
|
||||||
RSOPMLItem *item = [RSOPMLItem new];
|
|
||||||
item.attributes = [SAXParser attributesDictionary:attributes numberOfAttributes:numberOfAttributes];
|
|
||||||
|
|
||||||
[[self currentItem] addChild:item];
|
|
||||||
[self pushItem:item];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -187,31 +176,15 @@ static const char kOutlineLength = 8;
|
|||||||
|
|
||||||
if (RSSAXEqualTags(localName, kOutline, kOutlineLength)) {
|
if (RSSAXEqualTags(localName, kOutline, kOutlineLength)) {
|
||||||
[self popItem];
|
[self popItem];
|
||||||
|
} else if (RSSAXEqualTags(localName, kHead, kHeadLength)) {
|
||||||
|
isHead = NO;
|
||||||
|
} else if (isHead) {
|
||||||
|
NSString *key = [NSString stringWithFormat:@"%s", localName];
|
||||||
|
[self.itemStack.lastObject setAttribute:[SAXParser currentString] forKey:key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const char *kText = "text";
|
|
||||||
static const NSInteger kTextLength = 5;
|
|
||||||
|
|
||||||
static const char *kTitle = "title";
|
|
||||||
static const NSInteger kTitleLength = 6;
|
|
||||||
|
|
||||||
static const char *kDescription = "description";
|
|
||||||
static const NSInteger kDescriptionLength = 12;
|
|
||||||
|
|
||||||
static const char *kType = "type";
|
|
||||||
static const NSInteger kTypeLength = 5;
|
|
||||||
|
|
||||||
static const char *kVersion = "version";
|
|
||||||
static const NSInteger kVersionLength = 8;
|
|
||||||
|
|
||||||
static const char *kHTMLURL = "htmlUrl";
|
|
||||||
static const NSInteger kHTMLURLLength = 8;
|
|
||||||
|
|
||||||
static const char *kXMLURL = "xmlUrl";
|
|
||||||
static const NSInteger kXMLURLLength = 7;
|
|
||||||
|
|
||||||
- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForName:(const xmlChar *)name prefix:(const xmlChar *)prefix {
|
- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForName:(const xmlChar *)name prefix:(const xmlChar *)prefix {
|
||||||
|
|
||||||
if (prefix) {
|
if (prefix) {
|
||||||
@@ -219,77 +192,37 @@ static const NSInteger kXMLURLLength = 7;
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t nameLength = strlen((const char *)name);
|
size_t nameLength = strlen((const char *)name);
|
||||||
|
switch (nameLength) {
|
||||||
if (nameLength == kTextLength - 1) {
|
case 4:
|
||||||
if (RSSAXEqualTags(name, kText, kTextLength)) {
|
if (RSSAXEqualTags(name, "text", 5)) return OPMLTextKey;
|
||||||
return OPMLTextKey;
|
if (RSSAXEqualTags(name, "type", 5)) return OPMLTypeKey;
|
||||||
}
|
break;
|
||||||
if (RSSAXEqualTags(name, kType, kTypeLength)) {
|
case 5:
|
||||||
return OPMLTypeKey;
|
if (RSSAXEqualTags(name, "title", 6)) return OPMLTitleKey;
|
||||||
}
|
break;
|
||||||
|
case 6:
|
||||||
|
if (RSSAXEqualTags(name, "xmlUrl", 7)) return OPMLXMLURLKey;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
if (RSSAXEqualTags(name, "version", 8)) return OPMLVersionKey;
|
||||||
|
if (RSSAXEqualTags(name, "htmlUrl", 8)) return OPMLHMTLURLKey;
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
if (RSSAXEqualTags(name, "description", 12)) return OPMLDescriptionKey;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (nameLength == kTitleLength - 1) {
|
|
||||||
if (RSSAXEqualTags(name, kTitle, kTitleLength)) {
|
|
||||||
return OPMLTitleKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (nameLength == kXMLURLLength - 1) {
|
|
||||||
if (RSSAXEqualTags(name, kXMLURL, kXMLURLLength)) {
|
|
||||||
return OPMLXMLURLKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (nameLength == kVersionLength - 1) {
|
|
||||||
if (RSSAXEqualTags(name, kVersion, kVersionLength)) {
|
|
||||||
return OPMLVersionKey;
|
|
||||||
}
|
|
||||||
if (RSSAXEqualTags(name, kHTMLURL, kHTMLURLLength)) {
|
|
||||||
return OPMLHMTLURLKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (nameLength == kDescriptionLength - 1) {
|
|
||||||
if (RSSAXEqualTags(name, kDescription, kDescriptionLength)) {
|
|
||||||
return OPMLDescriptionKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const char *kRSSUppercase = "RSS";
|
|
||||||
static const char *kRSSLowercase = "rss";
|
|
||||||
static const NSUInteger kRSSLength = 3;
|
|
||||||
static NSString *RSSUppercaseValue = @"RSS";
|
|
||||||
static NSString *RSSLowercaseValue = @"rss";
|
|
||||||
static NSString *emptyString = @"";
|
|
||||||
|
|
||||||
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 {
|
- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForValue:(const void *)bytes length:(NSUInteger)length {
|
||||||
|
|
||||||
|
|
||||||
if (length < 1) {
|
if (length < 1) {
|
||||||
return emptyString;
|
return @"";
|
||||||
|
} else if (length == 3) {
|
||||||
|
if (RSSAXEqualBytes(bytes, "RSS", 3)) return @"RSS";
|
||||||
|
if (RSSAXEqualBytes(bytes, "rss", 3)) return @"rss";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == kRSSLength) {
|
|
||||||
|
|
||||||
if (equalBytes(bytes, kRSSUppercase, kRSSLength)) {
|
|
||||||
return RSSUppercaseValue;
|
|
||||||
}
|
|
||||||
else if (equalBytes(bytes, kRSSLowercase, kRSSLength)) {
|
|
||||||
return RSSLowercaseValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -446,22 +446,16 @@ static const NSInteger kTrueLength = 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 {
|
- (NSString *)saxParser:(RSSAXParser *)SAXParser internedStringForValue:(const void *)bytes length:(NSUInteger)length {
|
||||||
|
|
||||||
static const NSUInteger falseLength = kFalseLength - 1;
|
static const NSUInteger falseLength = kFalseLength - 1;
|
||||||
static const NSUInteger trueLength = kTrueLength - 1;
|
static const NSUInteger trueLength = kTrueLength - 1;
|
||||||
|
|
||||||
if (length == falseLength && equalBytes(bytes, kFalse, falseLength)) {
|
if (length == falseLength && RSSAXEqualBytes(bytes, kFalse, falseLength)) {
|
||||||
return kFalseValue;
|
return kFalseValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == trueLength && equalBytes(bytes, kTrue, trueLength)) {
|
if (length == trueLength && RSSAXEqualBytes(bytes, kTrue, trueLength)) {
|
||||||
return kTrueValue;
|
return kTrueValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ void RSSAXInitLibXMLParser(void); // Needed by RSSAXHTMLParser.
|
|||||||
/*For use by delegate.*/
|
/*For use by delegate.*/
|
||||||
|
|
||||||
BOOL RSSAXEqualTags(const unsigned char *localName, const char *tag, NSInteger tagLength);
|
BOOL RSSAXEqualTags(const unsigned char *localName, const char *tag, NSInteger tagLength);
|
||||||
|
BOOL RSSAXEqualBytes(const void *bytes1, const void *bytes2, NSUInteger length);
|
||||||
|
|
||||||
|
|
||||||
@interface RSSAXParser : NSObject
|
@interface RSSAXParser : NSObject
|
||||||
|
|||||||
@@ -225,6 +225,11 @@ BOOL RSSAXEqualTags(const xmlChar *localName, const char *tag, NSInteger tagLeng
|
|||||||
return !strncmp((const char *)localName, tag, (size_t)tagLength);
|
return !strncmp((const char *)localName, tag, (size_t)tagLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL RSSAXEqualBytes(const void *bytes1, const void *bytes2, NSUInteger length) {
|
||||||
|
|
||||||
|
return memcmp(bytes1, bytes2, length) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - Callbacks
|
#pragma mark - Callbacks
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,7 @@
|
|||||||
#import <RSXML/RSParsedArticle.h>
|
#import <RSXML/RSParsedArticle.h>
|
||||||
|
|
||||||
#import <RSXML/RSOPMLParser.h>
|
#import <RSXML/RSOPMLParser.h>
|
||||||
#import <RSXML/RSOPMLDocument.h>
|
|
||||||
#import <RSXML/RSOPMLItem.h>
|
#import <RSXML/RSOPMLItem.h>
|
||||||
#import <RSXML/RSOPMLAttributes.h>
|
|
||||||
#import <RSXML/RSOPMLFeedSpecifier.h>
|
|
||||||
|
|
||||||
#import <RSXML/RSXMLError.h>
|
#import <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 Foundation;
|
||||||
|
#import <libxml/xmlerror.h>
|
||||||
|
|
||||||
extern NSString *RSXMLErrorDomain;
|
extern NSErrorDomain kLIBXMLParserErrorDomain;
|
||||||
|
extern NSErrorDomain kRSXMLParserErrorDomain;
|
||||||
|
|
||||||
|
/// Error codes for RSXML error domain @c (kRSXMLParserErrorDomain)
|
||||||
typedef NS_ENUM(NSInteger, RSXMLErrorCode) {
|
typedef NS_ENUM(NSInteger, RSXMLError) {
|
||||||
RSXMLErrorCodeDataIsWrongFormat = 1024
|
/// Error codes
|
||||||
|
RSXMLErrorNoData = 100,
|
||||||
|
RSXMLErrorMissingLeftCaret = 110,
|
||||||
|
RSXMLErrorProbablyHTML = 120,
|
||||||
|
RSXMLErrorContainsXMLErrorsTag = 130,
|
||||||
|
RSXMLErrorNoSuitableParser = 140,
|
||||||
|
RSXMLErrorFileNotOPML = 1024 // original value
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void RSXMLSetError(NSError **error, RSXMLError code, NSString *filename);
|
||||||
NSError *RSOPMLWrongFormatError(NSString *fileName);
|
NSError * RSXMLMakeError(RSXMLError code, NSString *filename);
|
||||||
|
NSError * RSXMLMakeErrorFromLIBXMLError(xmlErrorPtr err);
|
||||||
|
|||||||
@@ -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"
|
#import "RSXMLError.h"
|
||||||
|
|
||||||
NSString *RSXMLErrorDomain = @"com.ranchero.RSXML";
|
NSErrorDomain kLIBXMLParserErrorDomain = @"LIBXMLParserErrorDomain";
|
||||||
|
NSErrorDomain kRSXMLParserErrorDomain = @"RSXMLParserErrorDomain";
|
||||||
|
|
||||||
NSError *RSOPMLWrongFormatError(NSString *fileName) {
|
NSString * getErrorMessageForRSXMLError(RSXMLError code, id paramA);
|
||||||
|
NSString * getErrorMessageForRSXMLError(RSXMLError code, id paramA) {
|
||||||
NSString *localizedDescriptionFormatString = NSLocalizedString(@"The file ‘%@’ can’t be parsed because it’s not an OPML file.", @"OPML wrong format");
|
switch (code) { // switch statement will warn if an enum value is missing
|
||||||
NSString *localizedDescription = [NSString stringWithFormat:localizedDescriptionFormatString, fileName];
|
case RSXMLErrorNoData:
|
||||||
|
return @"Couldn't parse feed. No data available.";
|
||||||
NSString *localizedFailureString = NSLocalizedString(@"The file is not an OPML file.", @"OPML wrong format");
|
case RSXMLErrorMissingLeftCaret:
|
||||||
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: localizedDescription, NSLocalizedFailureReasonErrorKey: localizedFailureString};
|
return @"Couldn't parse feed. Missing left caret character ('<').";
|
||||||
|
case RSXMLErrorProbablyHTML:
|
||||||
return [[NSError alloc] initWithDomain:RSXMLErrorDomain code:RSXMLErrorCodeDataIsWrongFormat userInfo:userInfo];
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,8 @@
|
|||||||
RSXMLData *xmlData = [[RSXMLData alloc] initWithData:d urlString:@"http://example.org/"];
|
RSXMLData *xmlData = [[RSXMLData alloc] initWithData:d urlString:@"http://example.org/"];
|
||||||
RSOPMLParser *parser = [[RSOPMLParser alloc] initWithXMLData:xmlData];
|
RSOPMLParser *parser = [[RSOPMLParser alloc] initWithXMLData:xmlData];
|
||||||
XCTAssertNotNil(parser.error);
|
XCTAssertNotNil(parser.error);
|
||||||
|
XCTAssert(parser.error.code == RSXMLErrorFileNotOPML);
|
||||||
|
XCTAssert([parser.error.domain isEqualTo:kRSXMLParserErrorDomain]);
|
||||||
|
|
||||||
d = [[NSData alloc] initWithContentsOfFile:@"/System/Library/Kernels/kernel"];
|
d = [[NSData alloc] initWithContentsOfFile:@"/System/Library/Kernels/kernel"];
|
||||||
xmlData = [[RSXMLData alloc] initWithData:d urlString:@"/System/Library/Kernels/kernel"];
|
xmlData = [[RSXMLData alloc] initWithData:d urlString:@"/System/Library/Kernels/kernel"];
|
||||||
@@ -61,40 +63,37 @@
|
|||||||
RSOPMLParser *parser = [[RSOPMLParser alloc] initWithXMLData:xmlData];
|
RSOPMLParser *parser = [[RSOPMLParser alloc] initWithXMLData:xmlData];
|
||||||
XCTAssertNotNil(parser);
|
XCTAssertNotNil(parser);
|
||||||
|
|
||||||
RSOPMLDocument *document = parser.OPMLDocument;
|
RSOPMLItem *document = parser.opmlDocument;
|
||||||
XCTAssertNotNil(document);
|
XCTAssertNotNil(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];
|
||||||
|
|
||||||
[self checkStructureForOPMLItem:document];
|
//NSLog(@"\n%@", [document recursiveDescription]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)checkStructureForOPMLItem:(RSOPMLItem *)item {
|
- (void)checkStructureForOPMLItem:(RSOPMLItem *)item isRoot:(BOOL)root {
|
||||||
|
|
||||||
RSOPMLFeedSpecifier *feedSpecifier = item.OPMLFeedSpecifier;
|
if (!root) {
|
||||||
|
XCTAssertNotNil([item attributeForKey:OPMLTextKey]);
|
||||||
if (![item isKindOfClass:[RSOPMLDocument class]]) {
|
XCTAssertNotNil([item attributeForKey:OPMLTitleKey]);
|
||||||
XCTAssertNotNil(item.attributes.opml_text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it has no children, it should have a feed specifier. The converse is also true.
|
// If it has no children, it should have a feed specifier. The converse is also true.
|
||||||
BOOL isFolder = (item.children.count > 0);
|
BOOL isFolder = (item.children.count > 0);
|
||||||
if (!isFolder && [item.attributes.opml_title isEqualToString:@"Skip"]) {
|
if (!isFolder && [[item attributeForKey:OPMLTitleKey] isEqualToString:@"Skip"]) {
|
||||||
isFolder = YES;
|
isFolder = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isFolder) {
|
if (!isFolder) {
|
||||||
XCTAssertNotNil(feedSpecifier.title);
|
XCTAssertNotNil([item attributeForKey:OPMLHMTLURLKey]);
|
||||||
XCTAssertNotNil(feedSpecifier.feedURL);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
XCTAssertNil(feedSpecifier);
|
|
||||||
if (![item isKindOfClass:[RSOPMLDocument class]]) {
|
|
||||||
XCTAssertNotNil(item.attributes.opml_title);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.children.count > 0) {
|
if (item.children.count > 0) {
|
||||||
for (RSOPMLItem *oneItem in item.children) {
|
for (RSOPMLItem *oneItem in item.children) {
|
||||||
[self checkStructureForOPMLItem:oneItem];
|
[self checkStructureForOPMLItem:oneItem isRoot:NO];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user