PageRenderTime 41ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/Element.m

http://tweetphoto-api-objective-c.googlecode.com/
Objective C | 269 lines | 206 code | 40 blank | 23 comment | 34 complexity | ccecdf9577a6e7b3d51b69b7b6ea242f MD5 | raw file
  1. //
  2. // Element.m
  3. // Thumbprint
  4. //
  5. // Created by Lee Buck on 4/18/09.
  6. // Copyright 2009 Blue Bright Ventures. All rights reserved.
  7. //
  8. // This program is free software: you can redistribute it and/or modify
  9. // it under the terms of the GNU General Public License as published by
  10. // the Free Software Foundation, either version 3 of the License, or
  11. // (at your option) any later version.
  12. // This program is distributed in the hope that it will be useful,
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. // GNU General Public License for more details.
  16. // Commercial licences without many of the obligations of GPL
  17. // are available for a nomial fee at sales@touchtankapps.com.
  18. // You should have received a copy of the GNU General Public License
  19. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. //
  21. #import "Element.h"
  22. #import "NSString_HTML.h"
  23. #import "CSSSelectorMatcher.h"
  24. #import "ElementParser.h"
  25. @interface Element ()
  26. -(void)setAttributes:(NSDictionary*)dict;
  27. @end
  28. @implementation Element
  29. @synthesize nextElement, nextSybling, parent, contentsLength, contentsText, key, containsMarkup, domainObject;
  30. +(DocumentRoot*)parseHTML:(NSString*)source{
  31. ElementParser* parser = [[ElementParser alloc] init];
  32. DocumentRoot* root = [parser parseHTML: source];
  33. [[root retain] autorelease];
  34. [parser release];
  35. return root;
  36. }
  37. +(DocumentRoot*)parseXML:(NSString*)source{
  38. ElementParser* parser = [[ElementParser alloc] init];
  39. DocumentRoot* root = [parser parseXML: source];
  40. [[root retain] autorelease];
  41. [parser release];
  42. return root;
  43. }
  44. -(id)initWithString:(NSString*)string{
  45. return [self initWithString: string range: NSMakeRange(0, [string length])];
  46. }
  47. -(id)initWithTag:(TagChunk*)tag caseSensative:(BOOL)aCaseSensative{
  48. self = [self initWithString: tag.source range: tag.range tagName: tag.tagName];
  49. [self setCaseSensative: aCaseSensative];
  50. return self;
  51. }
  52. -(void)dealloc{
  53. [attributes release];
  54. [contentsText release];
  55. [nextElement release];
  56. [nextSybling release];
  57. [key release];
  58. [super dealloc];
  59. }
  60. -(void)setRange: (NSRange)aRange{
  61. attributesParsed = NO;
  62. [attributes removeAllObjects];
  63. [super setRange: aRange];
  64. }
  65. //cleans up nested p tags
  66. -(BOOL)acceptsParent:(Element*)aParent{
  67. if ([self tagNameEquals: @"p"] && [aParent tagNameEquals: @"p"])
  68. return NO;
  69. return YES;
  70. }
  71. -(BOOL)closesTag:(TagChunk*)aTag{
  72. if (self == aTag || [self isEmptyTag]) //former case is true when shouldBeEmptyTag
  73. return self == aTag;
  74. else
  75. return [super closesTag: aTag];
  76. }
  77. -(BOOL)hasAttribute:(NSString*)attr{
  78. return [[[self attributes] allKeys] containsObject: attr];
  79. }
  80. -(NSString*)attribute:(NSString*)attr{
  81. return [[self attributes] objectForKey: attr];
  82. }
  83. // warning, may contain empty classnames
  84. -(NSArray*)classNames{
  85. NSString* classNames = [self attribute: @"class"];
  86. if (!classNames) return [NSArray array];
  87. return [classNames componentsSeparatedByCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
  88. }
  89. -(BOOL)hasClassName:(NSString*)aClassName{
  90. if (![self attribute: @"class"]) return NO;
  91. for (NSString* className in [self classNames])
  92. if ([className isEqualToString: aClassName])
  93. return YES;
  94. return NO;
  95. }
  96. -(NSDictionary*)attributes{
  97. if (!attributesParsed){
  98. [self setAttributes: [source parseElementAttributesWithRange: range caseSensative: [self caseSensative]]];
  99. attributesParsed = YES;
  100. }
  101. return attributes;
  102. }
  103. -(void)setAttributes:(NSDictionary*)dict{
  104. [attributes release];
  105. attributes = [dict retain];
  106. }
  107. -(Element*)firstChild{
  108. if ([nextElement parent] == self)
  109. return nextElement;
  110. else
  111. return nil;
  112. }
  113. -(BOOL)hasAncestor:(Element*)ancestor{
  114. for (Element* p = parent; p; p = p.parent){
  115. if (p == ancestor)
  116. return YES;
  117. }
  118. return NO;
  119. }
  120. -(Element*)nextElementWithinScope:(Element*)scope{
  121. if ((nextElement.parent == self) || nextSybling)
  122. return nextElement;
  123. else
  124. return ([nextElement hasAncestor: scope]) ? nextElement : nil;
  125. }
  126. -(NSString*)contentsText{
  127. if (!contentsText){
  128. // NSRange contentsRange = NSMakeRange(NSMaxRange(range), contentsLength);
  129. self.contentsText = (containsMarkup) ? [[self contentsSource] stripTags] : [self contentsSource];//[source stringByReplacingEntitiesInRange: contentsRange];
  130. }
  131. return contentsText;
  132. }
  133. -(NSString*)contentsSource{
  134. NSRange contentsRange = NSMakeRange(NSMaxRange(range), contentsLength);
  135. NSString* result = [source substringWithRange: contentsRange];
  136. return result;
  137. }
  138. -(NSArray*)selectElements:(NSString*)cssSelectorString{
  139. if (!cssSelectorString) return [NSArray array];
  140. CSSSelector* selector = [[CSSSelector alloc] initWithString: cssSelectorString];
  141. NSArray* result = [self elementsWithCSSSelector: selector];
  142. [selector release];
  143. return result;
  144. }
  145. -(Element*)selectElement:(NSString*)cssSelectorString{
  146. if (!cssSelectorString) return nil;
  147. CSSSelector* selector = [[CSSSelector alloc] initWithString: cssSelectorString];
  148. Element* result = [self elementWithCSSSelector: selector];
  149. [selector release];
  150. return result;
  151. }
  152. -(NSArray*)elementsWithCSSSelector:(CSSSelector*)selector{
  153. CSSSelectorMatcher* matcher = [[CSSSelectorMatcher alloc] initWithSelector: selector];
  154. Element* e = self;
  155. while (e){
  156. [matcher matchElement: e];
  157. e = e.nextElement;
  158. }
  159. NSArray* result = [[[matcher matches] retain] autorelease];
  160. [matcher release];
  161. return result;
  162. }
  163. -(Element*)elementWithCSSSelector:(CSSSelector*)selector{
  164. CSSSelectorMatcher* matcher = [[CSSSelectorMatcher alloc] initWithSelector: selector];
  165. Element* e = self;
  166. BOOL success = NO;
  167. while (e && !success){
  168. success = [matcher matchElement: e];
  169. e = [e nextElementWithinScope: self];
  170. }
  171. Element* result = [matcher firstMatch];
  172. [matcher release];
  173. return result;
  174. }
  175. -(NSArray*)childElements{
  176. NSMutableArray* kids = [NSMutableArray array];
  177. Element* e = [self firstChild];
  178. while (e){
  179. [kids addObject: e];
  180. e = e.nextSybling;
  181. }
  182. return kids;
  183. }
  184. -(NSDictionary*)contentsOfChildren{
  185. NSMutableDictionary* result = [NSMutableDictionary dictionary];
  186. Element* e = [self firstChild];
  187. while (e){
  188. [result setObject: [e contentsText] forKey: [e key]];
  189. e = e.nextSybling;
  190. }
  191. return result;
  192. }
  193. -(BOOL)isEqualToString:(NSString*)string{
  194. return [[self description] isEqualToString: string];
  195. }
  196. -(NSString*)key{
  197. if (!key)
  198. self.key = ([self caseSensative])
  199. ? [self tagName]
  200. : [[self tagName] lowercaseString];
  201. return key;
  202. }
  203. -(NSString*)description{
  204. NSMutableString* result = [NSMutableString string];
  205. if (!source) return result;//root element has no source
  206. [result appendString: @"<"];
  207. [result appendString: [self tagName]];
  208. for (NSString* att in [[self attributes] allKeys]){
  209. [result appendFormat: @" %@='%@'", att, [attributes objectForKey: att]];
  210. }
  211. if ([self isEmptyTag])
  212. [result appendString: @" />"];
  213. else
  214. [result appendString: @">"];
  215. return result;
  216. }
  217. -(NSString*)dumpTree{
  218. NSMutableString* result = [NSMutableString string];
  219. Element* e = self;
  220. while (e){
  221. for (Element* ee = e; ee; ee = [ee parent])
  222. [result appendString: @" "];
  223. [result appendString: [e description]];
  224. [result appendString: @"\n"];
  225. e = e.nextElement;
  226. }
  227. return result;
  228. }
  229. @end